1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * sun4v DR daemon 29 */ 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 #include <string.h> 35 #include <strings.h> 36 #include <fcntl.h> 37 #include <errno.h> 38 #include <libgen.h> 39 #include <syslog.h> 40 #include <door.h> 41 #include <assert.h> 42 #include <sys/types.h> 43 #include <sys/stat.h> 44 45 #include <sys/drctl_impl.h> 46 #include <sys/drctl.h> 47 #include "drd.h" 48 49 boolean_t drd_debug = B_FALSE; 50 boolean_t drd_daemonized = B_FALSE; 51 52 #define DRD_DOOR_FILE "/tmp/drd_door" 53 #define DRD_DOOR_RETURN_ERR() (void) door_return(NULL, 0, NULL, 0) 54 55 static char *cmdname; 56 static int drctl_fd; 57 static drctl_rsrc_t *drd_result = NULL; 58 59 /* 60 * Currently, the only supported backend is for the Reconfiguration 61 * Coordination Manager (RCM). When there are other backends, this 62 * variable should be set dynamically. 63 */ 64 static drd_backend_t *drd_backend = &drd_rcm_backend; 65 66 static void drd_daemonize(void); 67 static int drd_init_drctl_dev(boolean_t standalone); 68 static int drd_init_door_server(boolean_t standalone); 69 static void drd_door_server(void *, char *, size_t, door_desc_t *, uint_t); 70 71 int 72 main(int argc, char **argv) 73 { 74 int opt; 75 boolean_t standalone = B_FALSE; 76 77 cmdname = basename(argv[0]); 78 79 /* 80 * Process command line arguments 81 */ 82 opterr = 0; /* disable getopt error messages */ 83 while ((opt = getopt(argc, argv, "ds")) != EOF) { 84 85 switch (opt) { 86 case 'd': 87 drd_debug = B_TRUE; 88 break; 89 case 's': 90 standalone = B_TRUE; 91 break; 92 default: 93 drd_err("unkown option: -%c", optopt); 94 exit(1); 95 } 96 } 97 98 drd_dbg("initializing %s...", cmdname); 99 100 /* must be root */ 101 if (geteuid() != 0) { 102 drd_err("permission denied: must run as root"); 103 exit(1); 104 } 105 106 /* open the drctl device */ 107 if (drd_init_drctl_dev(standalone) != 0) { 108 drd_err("unable to initialize drctl device"); 109 exit(1); 110 } 111 112 /* daemonize */ 113 if (!standalone) { 114 drd_daemonize(); 115 } 116 117 /* initialize door server */ 118 if (drd_init_door_server(standalone) != 0) { 119 drd_err("unable to initialize door server"); 120 exit(1); 121 } 122 123 /* initialize the backend */ 124 if ((*drd_backend->init)() != 0) { 125 drd_err("unable to initialize backend processor"); 126 exit(1); 127 } 128 129 /* loop forever */ 130 for (;;) { 131 pause(); 132 } 133 134 /*NOTREACHED*/ 135 return (0); 136 } 137 138 static void 139 drd_daemonize(void) 140 { 141 pid_t pid; 142 143 if ((pid = fork()) == -1) { 144 drd_err("failed to fork: %s", strerror(errno)); 145 exit(1); 146 } 147 148 if (pid != 0) { 149 /* parent */ 150 exit(0); 151 } 152 153 /* 154 * Initialize child process 155 */ 156 (void) setsid(); 157 (void) chdir("/"); 158 (void) umask(0); 159 160 /* 161 * Initialize file descriptors. Do not touch stderr 162 * which is initialized by SMF to point to the drd 163 * specific log file. 164 */ 165 assert(drctl_fd == (STDERR_FILENO + 1)); 166 167 (void) close(STDIN_FILENO); 168 (void) open("/dev/null", O_RDWR); 169 (void) dup2(STDIN_FILENO, STDOUT_FILENO); 170 171 closefrom(drctl_fd + 1); 172 173 /* initialize logging */ 174 openlog(cmdname, LOG_CONS | LOG_NDELAY, LOG_DAEMON); 175 176 drd_daemonized = B_TRUE; 177 } 178 179 static int 180 drd_init_drctl_dev(boolean_t standalone) 181 { 182 void (*drd_output)(char *, ...); 183 184 drd_output = (standalone) ? drd_info : drd_err; 185 186 /* open the drctl device */ 187 if ((drctl_fd = open(DRCTL_DEV, O_RDWR)) == -1) { 188 drd_output("open %s failed: %s", DRCTL_DEV, strerror(errno)); 189 return ((standalone) ? 0 : -1); 190 } 191 192 return (0); 193 } 194 195 static int 196 drd_init_door_server(boolean_t standalone) 197 { 198 int door_fd; 199 int dbg_fd; 200 drctl_setup_t setup; 201 202 assert((drctl_fd != -1) || standalone); 203 204 /* create the door */ 205 if ((door_fd = door_create(drd_door_server, NULL, 0)) == -1) { 206 drd_err("door_create failed: %s", strerror(errno)); 207 return (-1); 208 } 209 210 if (drctl_fd != -1) { 211 212 setup.did = door_fd; 213 214 /* send the door descriptor to drctl */ 215 if (ioctl(drctl_fd, DRCTL_IOCTL_CONNECT_SERVER, &setup) == -1) { 216 drd_err("drctl ioctl failed: %s", strerror(errno)); 217 (void) door_revoke(door_fd); 218 return (-1); 219 } 220 221 drd_dbg("connection to drctl established"); 222 223 /* setup is complete in daemon mode */ 224 if (!standalone) { 225 return (0); 226 } 227 } 228 229 /* 230 * At this point, the daemon is running in standalone 231 * mode for testing purposes. This allows the daemon 232 * to be controlled directly through a door exported 233 * to the filesystem. No drctl device is required in 234 * this mode. 235 */ 236 237 /* create the door file */ 238 unlink(DRD_DOOR_FILE); 239 if ((dbg_fd = creat(DRD_DOOR_FILE, 0644)) == -1) { 240 drd_err("failed to create door file '%s': %s", 241 DRD_DOOR_FILE, strerror(errno)); 242 (void) door_revoke(door_fd); 243 return (-1); 244 } 245 close(dbg_fd); 246 247 /* attach the door file to the door descriptor */ 248 if (fattach(door_fd, DRD_DOOR_FILE) == -1) { 249 drd_err("failed to fattach door file '%s': %s", 250 DRD_DOOR_FILE, strerror(errno)); 251 unlink(DRD_DOOR_FILE); 252 (void) door_revoke(door_fd); 253 return (-1); 254 } 255 256 drd_dbg("door server attached to '%s'", DRD_DOOR_FILE); 257 258 return (0); 259 } 260 261 static size_t 262 drd_pack_response(drctl_rsrc_t *rsrcs, int nrsrc) 263 { 264 drctl_rsrc_t *orsrcsp; 265 void *resizep; 266 size_t osize; 267 char *str; 268 size_t offset; 269 char *off; 270 int idx; 271 size_t len; 272 273 drd_dbg("drd_pack_response..."); 274 275 /* 276 * Deallocate the global response buffer if it is 277 * in use. This assumes that there will only ever 278 * be one pending operation in the daemon. This is 279 * enforced by the kernel. 280 */ 281 s_free(drd_result); 282 283 orsrcsp = calloc(sizeof (*orsrcsp), nrsrc); 284 osize = sizeof (*orsrcsp) * nrsrc; 285 bcopy(rsrcs, orsrcsp, osize); 286 287 offset = osize; 288 289 /* 290 * Loop through all the resources and concatenate 291 * all the error strings to the end of the resource 292 * array. Also, update the offset field of each 293 * resource. 294 */ 295 for (idx = 0; idx < nrsrc; idx++) { 296 297 str = (char *)(uintptr_t)rsrcs[idx].offset; 298 299 /* skip if no error string */ 300 if (str == NULL) 301 continue; 302 303 len = strlen(str) + 1; 304 305 /* increase the size of the buffer */ 306 resizep = realloc(orsrcsp, osize + len); 307 if (resizep == NULL) { 308 drd_err("realloc failed: %s", strerror(errno)); 309 s_free(orsrcsp); 310 311 /* clean up any remaining strings */ 312 while (idx < nrsrc) { 313 str = (char *)(uintptr_t)rsrcs[idx++].offset; 314 s_free(str); 315 } 316 return (0); 317 } 318 319 orsrcsp = resizep; 320 321 /* copy the error string into the response */ 322 off = (char *)orsrcsp + offset; 323 bcopy(str, off, len); 324 orsrcsp[idx].offset = offset; 325 326 /* 327 * Now that the error string has been copied 328 * into the response message, the memory that 329 * was allocated for it is no longer needed. 330 */ 331 s_free(str); 332 rsrcs[idx].offset = 0; 333 334 /* update size and offset */ 335 offset += len; 336 osize += len; 337 } 338 339 drd_result = orsrcsp; 340 return (osize); 341 } 342 343 /*ARGSUSED*/ 344 static void 345 drd_door_server(void *cookie, char *argp, size_t arg_sz, door_desc_t *dp, 346 uint_t n_desc) 347 { 348 drd_msg_t *msg = (drd_msg_t *)(uintptr_t)argp; 349 drctl_rsrc_t *rsrcs; 350 size_t osize; 351 int nrsrc; 352 353 drd_dbg("drd_door_server..."); 354 drd_dbg("message received: %d bytes", arg_sz); 355 356 /* sanity check incoming arg */ 357 if ((argp == NULL) || (arg_sz == 0)) 358 DRD_DOOR_RETURN_ERR(); 359 360 drd_dbg(" cmd=%d, count=%d, flags=%d", msg->cmd, 361 msg->count, msg->flags); 362 363 rsrcs = (drctl_rsrc_t *)(uintptr_t)msg->data; 364 nrsrc = msg->count; 365 366 /* pass off to backend for processing */ 367 switch (msg->cmd) { 368 case DRCTL_CPU_CONFIG_REQUEST: 369 (*drd_backend->cpu_config_request)(rsrcs, nrsrc); 370 break; 371 372 case DRCTL_CPU_CONFIG_NOTIFY: 373 (*drd_backend->cpu_config_notify)(rsrcs, nrsrc); 374 break; 375 376 case DRCTL_CPU_UNCONFIG_REQUEST: 377 (*drd_backend->cpu_unconfig_request)(rsrcs, nrsrc); 378 break; 379 380 case DRCTL_CPU_UNCONFIG_NOTIFY: 381 (*drd_backend->cpu_unconfig_notify)(rsrcs, nrsrc); 382 break; 383 384 case DRCTL_MEM_CONFIG_REQUEST: 385 (*drd_backend->mem_config_request)(rsrcs, nrsrc); 386 break; 387 388 case DRCTL_MEM_CONFIG_NOTIFY: 389 (*drd_backend->mem_config_notify)(rsrcs, nrsrc); 390 break; 391 392 case DRCTL_MEM_UNCONFIG_REQUEST: 393 (*drd_backend->mem_unconfig_request)(rsrcs, nrsrc); 394 break; 395 396 case DRCTL_MEM_UNCONFIG_NOTIFY: 397 (*drd_backend->mem_unconfig_notify)(rsrcs, nrsrc); 398 break; 399 400 case DRCTL_IO_CONFIG_REQUEST: 401 (*drd_backend->io_config_request)(rsrcs, nrsrc); 402 break; 403 404 case DRCTL_IO_CONFIG_NOTIFY: 405 (*drd_backend->io_config_notify)(rsrcs, nrsrc); 406 break; 407 408 case DRCTL_IO_UNCONFIG_REQUEST: 409 (*drd_backend->io_unconfig_request)(rsrcs, nrsrc); 410 break; 411 412 case DRCTL_IO_UNCONFIG_NOTIFY: 413 (*drd_backend->io_unconfig_notify)(rsrcs, nrsrc); 414 break; 415 416 default: 417 drd_err("unknown command: %d", msg->cmd); 418 DRD_DOOR_RETURN_ERR(); 419 break; 420 } 421 422 osize = drd_pack_response(rsrcs, nrsrc); 423 if (osize == 0) 424 DRD_DOOR_RETURN_ERR(); 425 426 (void) door_return((char *)drd_result, osize, NULL, 0); 427 } 428