/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * sun4v DR daemon */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "drd.h" boolean_t drd_debug = B_FALSE; boolean_t drd_daemonized = B_FALSE; #define DRD_DOOR_FILE "/tmp/drd_door" #define DRD_DOOR_RETURN_ERR() (void) door_return(NULL, 0, NULL, 0) static char *cmdname; static int drctl_fd; static drctl_rsrc_t *drd_result = NULL; /* * Currently, the only supported backend is for the Reconfiguration * Coordination Manager (RCM). When there are other backends, this * variable should be set dynamically. */ static drd_backend_t *drd_backend = &drd_rcm_backend; static void drd_daemonize(void); static int drd_init_drctl_dev(boolean_t standalone); static int drd_init_door_server(boolean_t standalone); static void drd_door_server(void *, char *, size_t, door_desc_t *, uint_t); int main(int argc, char **argv) { int opt; boolean_t standalone = B_FALSE; cmdname = basename(argv[0]); /* * Process command line arguments */ opterr = 0; /* disable getopt error messages */ while ((opt = getopt(argc, argv, "ds")) != EOF) { switch (opt) { case 'd': drd_debug = B_TRUE; break; case 's': standalone = B_TRUE; break; default: drd_err("unkown option: -%c", optopt); exit(1); } } drd_dbg("initializing %s...", cmdname); /* must be root */ if (geteuid() != 0) { drd_err("permission denied: must run as root"); exit(1); } /* open the drctl device */ if (drd_init_drctl_dev(standalone) != 0) { drd_err("unable to initialize drctl device"); exit(1); } /* daemonize */ if (!standalone) { drd_daemonize(); } /* initialize door server */ if (drd_init_door_server(standalone) != 0) { drd_err("unable to initialize door server"); exit(1); } /* initialize the backend */ if ((*drd_backend->init)() != 0) { drd_err("unable to initialize backend processor"); exit(1); } /* loop forever */ for (;;) { pause(); } /*NOTREACHED*/ return (0); } static void drd_daemonize(void) { pid_t pid; if ((pid = fork()) == -1) { drd_err("failed to fork: %s", strerror(errno)); exit(1); } if (pid != 0) { /* parent */ exit(0); } /* * Initialize child process */ (void) setsid(); (void) chdir("/"); (void) umask(0); /* * Initialize file descriptors. Do not touch stderr * which is initialized by SMF to point to the drd * specific log file. */ assert(drctl_fd == (STDERR_FILENO + 1)); (void) close(STDIN_FILENO); (void) open("/dev/null", O_RDWR); (void) dup2(STDIN_FILENO, STDOUT_FILENO); closefrom(drctl_fd + 1); /* initialize logging */ openlog(cmdname, LOG_CONS | LOG_NDELAY, LOG_DAEMON); drd_daemonized = B_TRUE; } static int drd_init_drctl_dev(boolean_t standalone) { void (*drd_output)(char *, ...); drd_output = (standalone) ? drd_info : drd_err; /* open the drctl device */ if ((drctl_fd = open(DRCTL_DEV, O_RDWR)) == -1) { drd_output("open %s failed: %s", DRCTL_DEV, strerror(errno)); return ((standalone) ? 0 : -1); } return (0); } static int drd_init_door_server(boolean_t standalone) { int door_fd; int dbg_fd; drctl_setup_t setup; assert((drctl_fd != -1) || standalone); /* create the door */ if ((door_fd = door_create(drd_door_server, NULL, 0)) == -1) { drd_err("door_create failed: %s", strerror(errno)); return (-1); } if (drctl_fd != -1) { setup.did = door_fd; /* send the door descriptor to drctl */ if (ioctl(drctl_fd, DRCTL_IOCTL_CONNECT_SERVER, &setup) == -1) { drd_err("drctl ioctl failed: %s", strerror(errno)); (void) door_revoke(door_fd); return (-1); } drd_dbg("connection to drctl established"); /* setup is complete in daemon mode */ if (!standalone) { return (0); } } /* * At this point, the daemon is running in standalone * mode for testing purposes. This allows the daemon * to be controlled directly through a door exported * to the filesystem. No drctl device is required in * this mode. */ /* create the door file */ unlink(DRD_DOOR_FILE); if ((dbg_fd = creat(DRD_DOOR_FILE, 0644)) == -1) { drd_err("failed to create door file '%s': %s", DRD_DOOR_FILE, strerror(errno)); (void) door_revoke(door_fd); return (-1); } close(dbg_fd); /* attach the door file to the door descriptor */ if (fattach(door_fd, DRD_DOOR_FILE) == -1) { drd_err("failed to fattach door file '%s': %s", DRD_DOOR_FILE, strerror(errno)); unlink(DRD_DOOR_FILE); (void) door_revoke(door_fd); return (-1); } drd_dbg("door server attached to '%s'", DRD_DOOR_FILE); return (0); } static size_t drd_pack_response(drctl_rsrc_t *rsrcs, int nrsrc) { drctl_rsrc_t *orsrcsp; void *resizep; size_t osize; char *str; size_t offset; char *off; int idx; size_t len; drd_dbg("drd_pack_response..."); /* * Deallocate the global response buffer if it is * in use. This assumes that there will only ever * be one pending operation in the daemon. This is * enforced by the kernel. */ s_free(drd_result); orsrcsp = calloc(sizeof (*orsrcsp), nrsrc); osize = sizeof (*orsrcsp) * nrsrc; bcopy(rsrcs, orsrcsp, osize); offset = osize; /* * Loop through all the resources and concatenate * all the error strings to the end of the resource * array. Also, update the offset field of each * resource. */ for (idx = 0; idx < nrsrc; idx++) { str = (char *)(uintptr_t)rsrcs[idx].offset; /* skip if no error string */ if (str == NULL) continue; len = strlen(str) + 1; /* increase the size of the buffer */ resizep = realloc(orsrcsp, osize + len); if (resizep == NULL) { drd_err("realloc failed: %s", strerror(errno)); s_free(orsrcsp); /* clean up any remaining strings */ while (idx < nrsrc) { str = (char *)(uintptr_t)rsrcs[idx++].offset; s_free(str); } return (0); } orsrcsp = resizep; /* copy the error string into the response */ off = (char *)orsrcsp + offset; bcopy(str, off, len); orsrcsp[idx].offset = offset; /* * Now that the error string has been copied * into the response message, the memory that * was allocated for it is no longer needed. */ s_free(str); rsrcs[idx].offset = 0; /* update size and offset */ offset += len; osize += len; } drd_result = orsrcsp; return (osize); } /*ARGSUSED*/ static void drd_door_server(void *cookie, char *argp, size_t arg_sz, door_desc_t *dp, uint_t n_desc) { drd_msg_t *msg = (drd_msg_t *)(uintptr_t)argp; drctl_rsrc_t *rsrcs; size_t osize; int nrsrc; drd_dbg("drd_door_server..."); drd_dbg("message received: %d bytes", arg_sz); /* sanity check incoming arg */ if ((argp == NULL) || (arg_sz == 0)) DRD_DOOR_RETURN_ERR(); drd_dbg(" cmd=%d, count=%d, flags=%d", msg->cmd, msg->count, msg->flags); rsrcs = (drctl_rsrc_t *)(uintptr_t)msg->data; nrsrc = msg->count; /* pass off to backend for processing */ switch (msg->cmd) { case DRCTL_CPU_CONFIG_REQUEST: (*drd_backend->cpu_config_request)(rsrcs, nrsrc); break; case DRCTL_CPU_CONFIG_NOTIFY: (*drd_backend->cpu_config_notify)(rsrcs, nrsrc); break; case DRCTL_CPU_UNCONFIG_REQUEST: (*drd_backend->cpu_unconfig_request)(rsrcs, nrsrc); break; case DRCTL_CPU_UNCONFIG_NOTIFY: (*drd_backend->cpu_unconfig_notify)(rsrcs, nrsrc); break; case DRCTL_MEM_CONFIG_REQUEST: (*drd_backend->mem_config_request)(rsrcs, nrsrc); break; case DRCTL_MEM_CONFIG_NOTIFY: (*drd_backend->mem_config_notify)(rsrcs, nrsrc); break; case DRCTL_MEM_UNCONFIG_REQUEST: (*drd_backend->mem_unconfig_request)(rsrcs, nrsrc); break; case DRCTL_MEM_UNCONFIG_NOTIFY: (*drd_backend->mem_unconfig_notify)(rsrcs, nrsrc); break; case DRCTL_IO_CONFIG_REQUEST: (*drd_backend->io_config_request)(rsrcs, nrsrc); break; case DRCTL_IO_CONFIG_NOTIFY: (*drd_backend->io_config_notify)(rsrcs, nrsrc); break; case DRCTL_IO_UNCONFIG_REQUEST: (*drd_backend->io_unconfig_request)(rsrcs, nrsrc); break; case DRCTL_IO_UNCONFIG_NOTIFY: (*drd_backend->io_unconfig_notify)(rsrcs, nrsrc); break; default: drd_err("unknown command: %d", msg->cmd); DRD_DOOR_RETURN_ERR(); break; } osize = drd_pack_response(rsrcs, nrsrc); if (osize == 0) DRD_DOOR_RETURN_ERR(); (void) door_return((char *)drd_result, osize, NULL, 0); }