12309Srsmaeda /*
22309Srsmaeda * CDDL HEADER START
32309Srsmaeda *
42309Srsmaeda * The contents of this file are subject to the terms of the
52309Srsmaeda * Common Development and Distribution License (the "License").
62309Srsmaeda * You may not use this file except in compliance with the License.
72309Srsmaeda *
82309Srsmaeda * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92309Srsmaeda * or http://www.opensolaris.org/os/licensing.
102309Srsmaeda * See the License for the specific language governing permissions
112309Srsmaeda * and limitations under the License.
122309Srsmaeda *
132309Srsmaeda * When distributing Covered Code, include this CDDL HEADER in each
142309Srsmaeda * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152309Srsmaeda * If applicable, add the following below this CDDL HEADER, with the
162309Srsmaeda * fields enclosed by brackets "[]" replaced with your own identifying
172309Srsmaeda * information: Portions Copyright [yyyy] [name of copyright owner]
182309Srsmaeda *
192309Srsmaeda * CDDL HEADER END
202309Srsmaeda */
212309Srsmaeda
222309Srsmaeda /*
23*10106SJason.Beloro@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
242309Srsmaeda * Use is subject to license terms.
252309Srsmaeda */
262309Srsmaeda
272309Srsmaeda /*
282309Srsmaeda * sun4v DR daemon
292309Srsmaeda */
302309Srsmaeda
312309Srsmaeda #include <stdio.h>
322309Srsmaeda #include <stdlib.h>
332309Srsmaeda #include <unistd.h>
342309Srsmaeda #include <string.h>
352309Srsmaeda #include <strings.h>
362309Srsmaeda #include <fcntl.h>
372309Srsmaeda #include <errno.h>
382309Srsmaeda #include <libgen.h>
392309Srsmaeda #include <syslog.h>
402309Srsmaeda #include <door.h>
412309Srsmaeda #include <assert.h>
422309Srsmaeda #include <sys/types.h>
432309Srsmaeda #include <sys/stat.h>
442309Srsmaeda
452309Srsmaeda #include <sys/drctl_impl.h>
462309Srsmaeda #include <sys/drctl.h>
472309Srsmaeda #include "drd.h"
482309Srsmaeda
492309Srsmaeda boolean_t drd_debug = B_FALSE;
502309Srsmaeda boolean_t drd_daemonized = B_FALSE;
512309Srsmaeda
522309Srsmaeda #define DRD_DOOR_FILE "/tmp/drd_door"
532309Srsmaeda #define DRD_DOOR_RETURN_ERR() (void) door_return(NULL, 0, NULL, 0)
542309Srsmaeda
552309Srsmaeda static char *cmdname;
562309Srsmaeda static int drctl_fd;
572309Srsmaeda static drctl_rsrc_t *drd_result = NULL;
582309Srsmaeda
592309Srsmaeda /*
602309Srsmaeda * Currently, the only supported backend is for the Reconfiguration
612309Srsmaeda * Coordination Manager (RCM). When there are other backends, this
622309Srsmaeda * variable should be set dynamically.
632309Srsmaeda */
642309Srsmaeda static drd_backend_t *drd_backend = &drd_rcm_backend;
652309Srsmaeda
662309Srsmaeda static void drd_daemonize(void);
672309Srsmaeda static int drd_init_drctl_dev(boolean_t standalone);
682309Srsmaeda static int drd_init_door_server(boolean_t standalone);
692309Srsmaeda static void drd_door_server(void *, char *, size_t, door_desc_t *, uint_t);
702309Srsmaeda
712309Srsmaeda int
main(int argc,char ** argv)722309Srsmaeda main(int argc, char **argv)
732309Srsmaeda {
742309Srsmaeda int opt;
753263Sjm22469 boolean_t standalone = B_FALSE;
762309Srsmaeda
772309Srsmaeda cmdname = basename(argv[0]);
782309Srsmaeda
792309Srsmaeda /*
802309Srsmaeda * Process command line arguments
812309Srsmaeda */
822309Srsmaeda opterr = 0; /* disable getopt error messages */
832309Srsmaeda while ((opt = getopt(argc, argv, "ds")) != EOF) {
842309Srsmaeda
852309Srsmaeda switch (opt) {
862309Srsmaeda case 'd':
872309Srsmaeda drd_debug = B_TRUE;
882309Srsmaeda break;
892309Srsmaeda case 's':
902309Srsmaeda standalone = B_TRUE;
912309Srsmaeda break;
922309Srsmaeda default:
932309Srsmaeda drd_err("unkown option: -%c", optopt);
942309Srsmaeda exit(1);
952309Srsmaeda }
962309Srsmaeda }
972309Srsmaeda
982309Srsmaeda drd_dbg("initializing %s...", cmdname);
992309Srsmaeda
1002309Srsmaeda /* must be root */
1012309Srsmaeda if (geteuid() != 0) {
1022309Srsmaeda drd_err("permission denied: must run as root");
1032309Srsmaeda exit(1);
1042309Srsmaeda }
1052309Srsmaeda
1062309Srsmaeda /* open the drctl device */
1072309Srsmaeda if (drd_init_drctl_dev(standalone) != 0) {
1082309Srsmaeda drd_err("unable to initialize drctl device");
1092309Srsmaeda exit(1);
1102309Srsmaeda }
1112309Srsmaeda
1122309Srsmaeda /* daemonize */
1132309Srsmaeda if (!standalone) {
1142309Srsmaeda drd_daemonize();
1152309Srsmaeda }
1162309Srsmaeda
1172309Srsmaeda /* initialize door server */
1182309Srsmaeda if (drd_init_door_server(standalone) != 0) {
1192309Srsmaeda drd_err("unable to initialize door server");
1202309Srsmaeda exit(1);
1212309Srsmaeda }
1222309Srsmaeda
1232309Srsmaeda /* initialize the backend */
1242309Srsmaeda if ((*drd_backend->init)() != 0) {
1252309Srsmaeda drd_err("unable to initialize backend processor");
1262309Srsmaeda exit(1);
1272309Srsmaeda }
1282309Srsmaeda
1292309Srsmaeda /* loop forever */
1302309Srsmaeda for (;;) {
1312309Srsmaeda pause();
1322309Srsmaeda }
1332309Srsmaeda
1342309Srsmaeda /*NOTREACHED*/
1352309Srsmaeda return (0);
1362309Srsmaeda }
1372309Srsmaeda
1382309Srsmaeda static void
drd_daemonize(void)1392309Srsmaeda drd_daemonize(void)
1402309Srsmaeda {
1412309Srsmaeda pid_t pid;
1422309Srsmaeda
1432309Srsmaeda if ((pid = fork()) == -1) {
1442309Srsmaeda drd_err("failed to fork: %s", strerror(errno));
1452309Srsmaeda exit(1);
1462309Srsmaeda }
1472309Srsmaeda
1482309Srsmaeda if (pid != 0) {
1492309Srsmaeda /* parent */
1502309Srsmaeda exit(0);
1512309Srsmaeda }
1522309Srsmaeda
1532309Srsmaeda /*
1542309Srsmaeda * Initialize child process
1552309Srsmaeda */
1562309Srsmaeda (void) setsid();
1572309Srsmaeda (void) chdir("/");
1582309Srsmaeda (void) umask(0);
1592309Srsmaeda
1602309Srsmaeda /*
1613263Sjm22469 * Initialize file descriptors. Do not touch stderr
1623263Sjm22469 * which is initialized by SMF to point to the drd
1633263Sjm22469 * specific log file.
1642309Srsmaeda */
1652309Srsmaeda assert(drctl_fd == (STDERR_FILENO + 1));
1662309Srsmaeda
1672309Srsmaeda (void) close(STDIN_FILENO);
1682309Srsmaeda (void) open("/dev/null", O_RDWR);
1692309Srsmaeda (void) dup2(STDIN_FILENO, STDOUT_FILENO);
1702309Srsmaeda
1712309Srsmaeda closefrom(drctl_fd + 1);
1722309Srsmaeda
1732309Srsmaeda /* initialize logging */
1742309Srsmaeda openlog(cmdname, LOG_CONS | LOG_NDELAY, LOG_DAEMON);
1752309Srsmaeda
1762309Srsmaeda drd_daemonized = B_TRUE;
1772309Srsmaeda }
1782309Srsmaeda
1792309Srsmaeda static int
drd_init_drctl_dev(boolean_t standalone)1802309Srsmaeda drd_init_drctl_dev(boolean_t standalone)
1812309Srsmaeda {
1822309Srsmaeda void (*drd_output)(char *, ...);
1832309Srsmaeda
1842309Srsmaeda drd_output = (standalone) ? drd_info : drd_err;
1852309Srsmaeda
1862309Srsmaeda /* open the drctl device */
1872309Srsmaeda if ((drctl_fd = open(DRCTL_DEV, O_RDWR)) == -1) {
1882309Srsmaeda drd_output("open %s failed: %s", DRCTL_DEV, strerror(errno));
1892309Srsmaeda return ((standalone) ? 0 : -1);
1902309Srsmaeda }
1912309Srsmaeda
1922309Srsmaeda return (0);
1932309Srsmaeda }
1942309Srsmaeda
1952309Srsmaeda static int
drd_init_door_server(boolean_t standalone)1962309Srsmaeda drd_init_door_server(boolean_t standalone)
1972309Srsmaeda {
1982309Srsmaeda int door_fd;
1992309Srsmaeda int dbg_fd;
2002309Srsmaeda drctl_setup_t setup;
2012309Srsmaeda
2022309Srsmaeda assert((drctl_fd != -1) || standalone);
2032309Srsmaeda
2042309Srsmaeda /* create the door */
2052309Srsmaeda if ((door_fd = door_create(drd_door_server, NULL, 0)) == -1) {
2062309Srsmaeda drd_err("door_create failed: %s", strerror(errno));
2072309Srsmaeda return (-1);
2082309Srsmaeda }
2092309Srsmaeda
2102309Srsmaeda if (drctl_fd != -1) {
2112309Srsmaeda
2122309Srsmaeda setup.did = door_fd;
2132309Srsmaeda
2142309Srsmaeda /* send the door descriptor to drctl */
2152309Srsmaeda if (ioctl(drctl_fd, DRCTL_IOCTL_CONNECT_SERVER, &setup) == -1) {
2162309Srsmaeda drd_err("drctl ioctl failed: %s", strerror(errno));
2172309Srsmaeda (void) door_revoke(door_fd);
2182309Srsmaeda return (-1);
2192309Srsmaeda }
2202309Srsmaeda
2212309Srsmaeda drd_dbg("connection to drctl established");
2222309Srsmaeda
2232309Srsmaeda /* setup is complete in daemon mode */
2242309Srsmaeda if (!standalone) {
2252309Srsmaeda return (0);
2262309Srsmaeda }
2272309Srsmaeda }
2282309Srsmaeda
2292309Srsmaeda /*
2302309Srsmaeda * At this point, the daemon is running in standalone
2312309Srsmaeda * mode for testing purposes. This allows the daemon
2322309Srsmaeda * to be controlled directly through a door exported
2332309Srsmaeda * to the filesystem. No drctl device is required in
2342309Srsmaeda * this mode.
2352309Srsmaeda */
2362309Srsmaeda
2372309Srsmaeda /* create the door file */
2382309Srsmaeda unlink(DRD_DOOR_FILE);
2392309Srsmaeda if ((dbg_fd = creat(DRD_DOOR_FILE, 0644)) == -1) {
2402309Srsmaeda drd_err("failed to create door file '%s': %s",
2412309Srsmaeda DRD_DOOR_FILE, strerror(errno));
2422309Srsmaeda (void) door_revoke(door_fd);
2432309Srsmaeda return (-1);
2442309Srsmaeda }
2452309Srsmaeda close(dbg_fd);
2462309Srsmaeda
2472309Srsmaeda /* attach the door file to the door descriptor */
2482309Srsmaeda if (fattach(door_fd, DRD_DOOR_FILE) == -1) {
2492309Srsmaeda drd_err("failed to fattach door file '%s': %s",
2502309Srsmaeda DRD_DOOR_FILE, strerror(errno));
2512309Srsmaeda unlink(DRD_DOOR_FILE);
2522309Srsmaeda (void) door_revoke(door_fd);
2532309Srsmaeda return (-1);
2542309Srsmaeda }
2552309Srsmaeda
2562309Srsmaeda drd_dbg("door server attached to '%s'", DRD_DOOR_FILE);
2572309Srsmaeda
2582309Srsmaeda return (0);
2592309Srsmaeda }
2602309Srsmaeda
2612309Srsmaeda static size_t
drd_pack_response(drctl_rsrc_t * rsrcs,int nrsrc)2622309Srsmaeda drd_pack_response(drctl_rsrc_t *rsrcs, int nrsrc)
2632309Srsmaeda {
2642309Srsmaeda drctl_rsrc_t *orsrcsp;
2652309Srsmaeda void *resizep;
2662309Srsmaeda size_t osize;
2672309Srsmaeda char *str;
2682309Srsmaeda size_t offset;
2692309Srsmaeda char *off;
2702309Srsmaeda int idx;
2712309Srsmaeda size_t len;
2722309Srsmaeda
2732309Srsmaeda drd_dbg("drd_pack_response...");
2742309Srsmaeda
2752309Srsmaeda /*
2762309Srsmaeda * Deallocate the global response buffer if it is
2772309Srsmaeda * in use. This assumes that there will only ever
2782309Srsmaeda * be one pending operation in the daemon. This is
2792309Srsmaeda * enforced by the kernel.
2802309Srsmaeda */
2812309Srsmaeda s_free(drd_result);
2822309Srsmaeda
2832309Srsmaeda orsrcsp = calloc(sizeof (*orsrcsp), nrsrc);
2842309Srsmaeda osize = sizeof (*orsrcsp) * nrsrc;
2852309Srsmaeda bcopy(rsrcs, orsrcsp, osize);
2862309Srsmaeda
2872309Srsmaeda offset = osize;
2882309Srsmaeda
2892309Srsmaeda /*
2902309Srsmaeda * Loop through all the resources and concatenate
2912309Srsmaeda * all the error strings to the end of the resource
2922309Srsmaeda * array. Also, update the offset field of each
2932309Srsmaeda * resource.
2942309Srsmaeda */
2952309Srsmaeda for (idx = 0; idx < nrsrc; idx++) {
2962309Srsmaeda
2972309Srsmaeda str = (char *)(uintptr_t)rsrcs[idx].offset;
2982309Srsmaeda
2992309Srsmaeda /* skip if no error string */
3002309Srsmaeda if (str == NULL)
3012309Srsmaeda continue;
3022309Srsmaeda
3032309Srsmaeda len = strlen(str) + 1;
3042309Srsmaeda
3052309Srsmaeda /* increase the size of the buffer */
3062309Srsmaeda resizep = realloc(orsrcsp, osize + len);
3072309Srsmaeda if (resizep == NULL) {
3082309Srsmaeda drd_err("realloc failed: %s", strerror(errno));
3092309Srsmaeda s_free(orsrcsp);
3102309Srsmaeda
3112309Srsmaeda /* clean up any remaining strings */
3122309Srsmaeda while (idx < nrsrc) {
3132309Srsmaeda str = (char *)(uintptr_t)rsrcs[idx++].offset;
3142309Srsmaeda s_free(str);
3152309Srsmaeda }
3162309Srsmaeda return (0);
3172309Srsmaeda }
3182309Srsmaeda
3192309Srsmaeda orsrcsp = resizep;
3202309Srsmaeda
3212309Srsmaeda /* copy the error string into the response */
3222309Srsmaeda off = (char *)orsrcsp + offset;
3232309Srsmaeda bcopy(str, off, len);
3242309Srsmaeda orsrcsp[idx].offset = offset;
3252309Srsmaeda
3262309Srsmaeda /*
3272309Srsmaeda * Now that the error string has been copied
3282309Srsmaeda * into the response message, the memory that
3292309Srsmaeda * was allocated for it is no longer needed.
3302309Srsmaeda */
3312309Srsmaeda s_free(str);
3322309Srsmaeda rsrcs[idx].offset = 0;
3332309Srsmaeda
3342309Srsmaeda /* update size and offset */
3352309Srsmaeda offset += len;
3362309Srsmaeda osize += len;
3372309Srsmaeda }
3382309Srsmaeda
3392309Srsmaeda drd_result = orsrcsp;
3402309Srsmaeda return (osize);
3412309Srsmaeda }
3422309Srsmaeda
3432309Srsmaeda /*ARGSUSED*/
3442309Srsmaeda static void
drd_door_server(void * cookie,char * argp,size_t arg_sz,door_desc_t * dp,uint_t n_desc)3452309Srsmaeda drd_door_server(void *cookie, char *argp, size_t arg_sz, door_desc_t *dp,
3462309Srsmaeda uint_t n_desc)
3472309Srsmaeda {
3482309Srsmaeda drd_msg_t *msg = (drd_msg_t *)(uintptr_t)argp;
3492309Srsmaeda drctl_rsrc_t *rsrcs;
3502309Srsmaeda size_t osize;
3512309Srsmaeda int nrsrc;
3522309Srsmaeda
3532309Srsmaeda drd_dbg("drd_door_server...");
3542309Srsmaeda drd_dbg("message received: %d bytes", arg_sz);
3552309Srsmaeda
3562309Srsmaeda /* sanity check incoming arg */
3572309Srsmaeda if ((argp == NULL) || (arg_sz == 0))
3582309Srsmaeda DRD_DOOR_RETURN_ERR();
3592309Srsmaeda
3602309Srsmaeda drd_dbg(" cmd=%d, count=%d, flags=%d", msg->cmd,
3612309Srsmaeda msg->count, msg->flags);
3622309Srsmaeda
3632309Srsmaeda rsrcs = (drctl_rsrc_t *)(uintptr_t)msg->data;
3642309Srsmaeda nrsrc = msg->count;
3652309Srsmaeda
3662309Srsmaeda /* pass off to backend for processing */
3672309Srsmaeda switch (msg->cmd) {
3682309Srsmaeda case DRCTL_CPU_CONFIG_REQUEST:
3692309Srsmaeda (*drd_backend->cpu_config_request)(rsrcs, nrsrc);
3702309Srsmaeda break;
3712309Srsmaeda
3722309Srsmaeda case DRCTL_CPU_CONFIG_NOTIFY:
3732309Srsmaeda (*drd_backend->cpu_config_notify)(rsrcs, nrsrc);
3742309Srsmaeda break;
3752309Srsmaeda
3762309Srsmaeda case DRCTL_CPU_UNCONFIG_REQUEST:
3772309Srsmaeda (*drd_backend->cpu_unconfig_request)(rsrcs, nrsrc);
3782309Srsmaeda break;
3792309Srsmaeda
3802309Srsmaeda case DRCTL_CPU_UNCONFIG_NOTIFY:
3812309Srsmaeda (*drd_backend->cpu_unconfig_notify)(rsrcs, nrsrc);
3822309Srsmaeda break;
3832309Srsmaeda
3842309Srsmaeda case DRCTL_MEM_CONFIG_REQUEST:
385*10106SJason.Beloro@Sun.COM (*drd_backend->mem_config_request)(rsrcs, nrsrc);
386*10106SJason.Beloro@Sun.COM break;
387*10106SJason.Beloro@Sun.COM
3882309Srsmaeda case DRCTL_MEM_CONFIG_NOTIFY:
389*10106SJason.Beloro@Sun.COM (*drd_backend->mem_config_notify)(rsrcs, nrsrc);
390*10106SJason.Beloro@Sun.COM break;
391*10106SJason.Beloro@Sun.COM
3922309Srsmaeda case DRCTL_MEM_UNCONFIG_REQUEST:
393*10106SJason.Beloro@Sun.COM (*drd_backend->mem_unconfig_request)(rsrcs, nrsrc);
394*10106SJason.Beloro@Sun.COM break;
395*10106SJason.Beloro@Sun.COM
3962309Srsmaeda case DRCTL_MEM_UNCONFIG_NOTIFY:
397*10106SJason.Beloro@Sun.COM (*drd_backend->mem_unconfig_notify)(rsrcs, nrsrc);
3982309Srsmaeda break;
3992309Srsmaeda
4002309Srsmaeda case DRCTL_IO_CONFIG_REQUEST:
4016441Sjm22469 (*drd_backend->io_config_request)(rsrcs, nrsrc);
4026441Sjm22469 break;
4036441Sjm22469
4042309Srsmaeda case DRCTL_IO_CONFIG_NOTIFY:
4056441Sjm22469 (*drd_backend->io_config_notify)(rsrcs, nrsrc);
4066441Sjm22469 break;
4076441Sjm22469
4082309Srsmaeda case DRCTL_IO_UNCONFIG_REQUEST:
4096441Sjm22469 (*drd_backend->io_unconfig_request)(rsrcs, nrsrc);
4106441Sjm22469 break;
4116441Sjm22469
4122309Srsmaeda case DRCTL_IO_UNCONFIG_NOTIFY:
4136441Sjm22469 (*drd_backend->io_unconfig_notify)(rsrcs, nrsrc);
4142309Srsmaeda break;
4152309Srsmaeda
4162309Srsmaeda default:
4172309Srsmaeda drd_err("unknown command: %d", msg->cmd);
4182309Srsmaeda DRD_DOOR_RETURN_ERR();
4192309Srsmaeda break;
4202309Srsmaeda }
4212309Srsmaeda
4222309Srsmaeda osize = drd_pack_response(rsrcs, nrsrc);
4232309Srsmaeda if (osize == 0)
4242309Srsmaeda DRD_DOOR_RETURN_ERR();
4252309Srsmaeda
4262309Srsmaeda (void) door_return((char *)drd_result, osize, NULL, 0);
4272309Srsmaeda }
428