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 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * DR control module for LDoms 31 */ 32 33 #include <sys/sysmacros.h> 34 #include <sys/modctl.h> 35 #include <sys/conf.h> 36 #include <sys/ddi.h> 37 #include <sys/sunddi.h> 38 #include <sys/ddi_impldefs.h> 39 #include <sys/stat.h> 40 #include <sys/door.h> 41 #include <sys/open.h> 42 #include <sys/note.h> 43 #include <sys/ldoms.h> 44 #include <sys/dr_util.h> 45 #include <sys/drctl.h> 46 #include <sys/drctl_impl.h> 47 48 49 static int drctl_attach(dev_info_t *, ddi_attach_cmd_t); 50 static int drctl_detach(dev_info_t *, ddi_detach_cmd_t); 51 static int drctl_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 52 53 static int drctl_open(dev_t *, int, int, cred_t *); 54 static int drctl_close(dev_t, int, int, cred_t *); 55 static int drctl_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 56 57 static void *pack_message(int, int, int, void *, size_t *); 58 static int send_message(void *, size_t, void **, size_t *); 59 60 61 /* 62 * Configuration data structures 63 */ 64 static struct cb_ops drctl_cb_ops = { 65 drctl_open, /* open */ 66 drctl_close, /* close */ 67 nodev, /* strategy */ 68 nodev, /* print */ 69 nodev, /* dump */ 70 nodev, /* read */ 71 nodev, /* write */ 72 drctl_ioctl, /* ioctl */ 73 nodev, /* devmap */ 74 nodev, /* mmap */ 75 nodev, /* segmap */ 76 nochpoll, /* poll */ 77 ddi_prop_op, /* prop_op */ 78 NULL, /* streamtab */ 79 D_MP | D_NEW, /* driver compatibility flag */ 80 CB_REV, /* cb_ops revision */ 81 nodev, /* async read */ 82 nodev /* async write */ 83 }; 84 85 86 static struct dev_ops drctl_ops = { 87 DEVO_REV, /* devo_rev */ 88 0, /* refcnt */ 89 drctl_getinfo, /* info */ 90 nulldev, /* identify */ 91 nulldev, /* probe */ 92 drctl_attach, /* attach */ 93 drctl_detach, /* detach */ 94 nodev, /* reset */ 95 &drctl_cb_ops, /* driver operations */ 96 NULL, /* bus operations */ 97 NULL, /* power */ 98 }; 99 100 static struct modldrv modldrv = { 101 &mod_driverops, /* type of module - driver */ 102 "DR Control pseudo driver v%I%", 103 &drctl_ops 104 }; 105 106 static struct modlinkage modlinkage = { 107 MODREV_1, 108 &modldrv, 109 NULL 110 }; 111 112 113 /* 114 * Locking strategy 115 * 116 * One of the reasons for this module's existence is to serialize 117 * DR requests which might be coming from different sources. Only 118 * one operation is allowed to be in progress at any given time. 119 * 120 * A single lock word (the 'drc_busy' element below) is NULL 121 * when there is no operation in progress. When a client of this 122 * module initiates an operation it grabs the mutex 'drc_lock' in 123 * order to examine the lock word ('drc_busy'). If no other 124 * operation is in progress, the lock word will be NULL. If so, 125 * a cookie which uniquely identifies the requestor is stored in 126 * the lock word, and the mutex is released. Attempts by other 127 * clients to initiate an operation will fail. 128 * 129 * When the lock-holding client's operation is completed, the 130 * client will call a "finalize" function in this module, providing 131 * the cookie passed with the original request. Since the cookie 132 * matches, the operation will succeed and the lock word will be 133 * cleared. At this point, an new operation may be initiated. 134 */ 135 136 /* 137 * Driver private data 138 */ 139 static struct drctl_unit { 140 kmutex_t drc_lock; /* global driver lock */ 141 dev_info_t *drc_dip; /* dev_info pointer */ 142 kcondvar_t drc_busy_cv; /* block for !busy */ 143 drctl_cookie_t drc_busy; /* NULL if free else a unique */ 144 /* identifier for caller */ 145 int drc_cmd; /* the cmd underway (or -1) */ 146 int drc_flags; /* saved flag from above cmd */ 147 int drc_inst; /* our single instance */ 148 uint_t drc_state; /* driver state */ 149 } drctl_state; 150 151 static struct drctl_unit *drctlp = &drctl_state; 152 153 int 154 _init(void) 155 { 156 drctlp->drc_inst = -1; 157 mutex_init(&drctlp->drc_lock, NULL, MUTEX_DRIVER, NULL); 158 return (mod_install(&modlinkage)); 159 } 160 161 162 int 163 _fini(void) 164 { 165 mutex_destroy(&drctlp->drc_lock); 166 return (mod_remove(&modlinkage)); 167 } 168 169 170 int 171 _info(struct modinfo *modinfop) 172 { 173 return (mod_info(&modlinkage, modinfop)); 174 } 175 176 177 /* 178 * Do the attach work 179 */ 180 static int 181 drctl_do_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 182 { 183 _NOTE(ARGUNUSED(cmd)) 184 185 char *str = "drctl_do_attach"; 186 int retval = DDI_SUCCESS; 187 188 if (drctlp->drc_inst != -1) { 189 cmn_err(CE_WARN, "%s: an instance is already attached!", str); 190 return (DDI_FAILURE); 191 } 192 drctlp->drc_inst = ddi_get_instance(dip); 193 194 retval = ddi_create_minor_node(dip, "drctl", S_IFCHR, 195 drctlp->drc_inst, DDI_PSEUDO, 0); 196 if (retval != DDI_SUCCESS) { 197 cmn_err(CE_WARN, "%s: can't create minor node", str); 198 drctlp->drc_inst = -1; 199 return (retval); 200 } 201 202 drctlp->drc_dip = dip; 203 ddi_report_dev(dip); 204 205 return (retval); 206 } 207 208 209 static int 210 drctl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 211 { 212 switch (cmd) { 213 case DDI_ATTACH: 214 return (drctl_do_attach(dip, cmd)); 215 216 default: 217 return (DDI_FAILURE); 218 } 219 } 220 221 222 /* ARGSUSED */ 223 static int 224 drctl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 225 { 226 switch (cmd) { 227 case DDI_DETACH: 228 drctlp->drc_inst = -1; 229 ddi_remove_minor_node(dip, "drctl"); 230 return (DDI_SUCCESS); 231 232 default: 233 return (DDI_FAILURE); 234 } 235 } 236 237 static int 238 drctl_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 239 { 240 _NOTE(ARGUNUSED(dip, cmd, arg, resultp)) 241 242 return (0); 243 } 244 245 static int 246 drctl_open(dev_t *devp, int flag, int otyp, cred_t *cred_p) 247 { 248 _NOTE(ARGUNUSED(devp, flag, cred_p)) 249 250 if (otyp != OTYP_CHR) 251 return (EINVAL); 252 253 return (0); 254 } 255 256 static int 257 drctl_close(dev_t dev, int flag, int otyp, cred_t *cred_p) 258 { 259 _NOTE(ARGUNUSED(dev, flag, otyp, cred_p)) 260 261 return (0); 262 } 263 264 /* 265 * This driver guarantees that if drctl_config_init returns 0, 266 * a valid response buffer will be passed back to the caller. This 267 * routine can be used to generate that response in cases where the 268 * upcall has not resulted in a response message from userland. 269 */ 270 static drctl_rsrc_t * 271 drctl_generate_resp(drctl_rsrc_t *res, 272 int count, size_t *rsize, drctl_status_t status) 273 { 274 int idx; 275 size_t size; 276 drctl_rsrc_t *rbuf; 277 278 size = count * sizeof (*res); 279 rbuf = kmem_alloc(size, KM_SLEEP); 280 281 bcopy(res, rbuf, size); 282 283 for (idx = 0; idx < count; idx++) { 284 rbuf[idx].status = status; 285 rbuf[idx].offset = 0; 286 } 287 288 *rsize = size; 289 return (rbuf); 290 } 291 292 static int 293 drctl_config_common(int cmd, int flags, drctl_rsrc_t *res, 294 int count, drctl_rsrc_t **rbuf, size_t *rsize) 295 { 296 int rv = 0; 297 size_t size; 298 char *bufp; 299 static const char me[] = "drctl_config_common"; 300 301 switch (cmd) { 302 case DRCTL_CPU_CONFIG_REQUEST: 303 case DRCTL_CPU_CONFIG_NOTIFY: 304 case DRCTL_CPU_UNCONFIG_REQUEST: 305 case DRCTL_CPU_UNCONFIG_NOTIFY: 306 rv = 0; 307 break; 308 case DRCTL_MEM_CONFIG_REQUEST: 309 case DRCTL_MEM_CONFIG_NOTIFY: 310 case DRCTL_MEM_UNCONFIG_REQUEST: 311 case DRCTL_MEM_UNCONFIG_NOTIFY: 312 case DRCTL_IO_CONFIG_REQUEST: 313 case DRCTL_IO_CONFIG_NOTIFY: 314 case DRCTL_IO_UNCONFIG_REQUEST: 315 case DRCTL_IO_UNCONFIG_NOTIFY: 316 rv = ENOTSUP; 317 break; 318 } 319 320 if (rv != 0) { 321 DR_DBG_CTL("%s: invalid cmd %d\n", me, cmd); 322 return (rv); 323 } 324 325 /* 326 * If the operation is a FORCE, we don't send a message to 327 * the daemon. But, the upstream clients still expect a 328 * response, so generate a response with all ops 'allowed'. 329 */ 330 if (flags == DRCTL_FLAG_FORCE) { 331 if (rbuf != NULL) { 332 *rbuf = drctl_generate_resp(res, count, &size, 333 DRCTL_STATUS_ALLOW); 334 *rsize = size; 335 } 336 return (0); 337 } 338 339 bufp = pack_message(cmd, flags, count, (void *)res, &size); 340 DR_DBG_CTL("%s: from pack_message, bufp = %p size %ld\n", 341 me, (void *)bufp, size); 342 if (bufp == NULL || size == 0) 343 return (EIO); 344 345 rv = send_message(bufp, size, (void **)rbuf, rsize); 346 347 /* 348 * For failure, as part of our contract with the caller, 349 * generate a response message, but mark all proposed 350 * changes as 'denied'. 351 */ 352 if (rv != 0) { 353 *rbuf = drctl_generate_resp(res, count, &size, 354 DRCTL_STATUS_DENY); 355 *rsize = size; 356 } 357 358 return (rv); 359 } 360 361 /* 362 * Since the response comes from userland, make sure it is 363 * at least the minimum size and, if it contains error 364 * strings, that the string area is null-terminated. 365 */ 366 static int 367 verify_response(int count, drctl_rsrc_t *resp, size_t size) 368 { 369 int idx; 370 int need_terminator = 0; 371 static const char me[] = "verify_response"; 372 373 if (resp == NULL || size < count * sizeof (*resp)) { 374 DR_DBG_CTL("%s: BAD size - count %d size %ld\n", 375 me, count, size); 376 return (EIO); 377 } 378 379 for (idx = 0; idx < count; idx++) { 380 381 if (resp[idx].offset != 0) 382 need_terminator++; 383 } 384 385 if (need_terminator && *((caddr_t)(resp) + size - 1) != '\0') { 386 DR_DBG_CTL("%s: unterm. strings: resp %p size %ld char %d\n", 387 me, (void *)resp, size, *((caddr_t)(resp) + size - 1)); 388 /* Don't fail the transaction, but don't advertise strings */ 389 for (idx = 0; idx < count; idx++) 390 resp[idx].offset = 0; 391 } 392 393 return (0); 394 } 395 396 397 /* 398 * Prepare for a reconfig operation. 399 */ 400 int 401 drctl_config_init(int cmd, int flags, drctl_rsrc_t *res, 402 int count, drctl_rsrc_t **rbuf, size_t *rsize, drctl_cookie_t ck) 403 { 404 static char me[] = "drctl_config_init"; 405 int idx; 406 int rv; 407 408 if (ck == 0) 409 return (EINVAL); 410 411 mutex_enter(&drctlp->drc_lock); 412 413 if (drctlp->drc_busy != NULL) { 414 mutex_exit(&drctlp->drc_lock); 415 return (EBUSY); 416 } 417 418 DR_DBG_CTL("%s: cmd %d flags %d res %p count %d\n", 419 me, cmd, flags, (void *)res, count); 420 421 /* Mark the link busy. Below we will fill in the actual cookie. */ 422 drctlp->drc_busy = (drctl_cookie_t)-1; 423 mutex_exit(&drctlp->drc_lock); 424 425 if ((rv = drctl_config_common(cmd, 426 flags, res, count, rbuf, rsize)) == 0 && 427 verify_response(count, *rbuf, *rsize) == 0) { 428 drctlp->drc_busy = ck; 429 drctlp->drc_cmd = cmd; 430 drctlp->drc_flags = flags; 431 432 /* 433 * If there wasn't a valid response msg passed back, 434 * create a response with each resource op denied. 435 */ 436 if (*rbuf == NULL || *rsize == 0) { 437 drctl_rsrc_t *bp = *rbuf; 438 439 *rsize = count * sizeof (*bp); 440 bp = kmem_zalloc(*rsize, KM_SLEEP); 441 bcopy(res, bp, *rsize); 442 443 for (idx = 0; idx < count; idx++) { 444 bp[idx].status = DRCTL_STATUS_DENY; 445 bp[idx].offset = 0; 446 } 447 } 448 } else { 449 drctlp->drc_cmd = -1; 450 drctlp->drc_flags = 0; 451 drctlp->drc_busy = NULL; 452 } 453 454 return (rv); 455 } 456 457 /* 458 * Complete a reconfig operation. 459 */ 460 int 461 drctl_config_fini(drctl_cookie_t ck, drctl_rsrc_t *res, int count) 462 { 463 int rv; 464 int notify_cmd; 465 int flags; 466 467 mutex_enter(&drctlp->drc_lock); 468 469 if (drctlp->drc_busy != ck) { 470 mutex_exit(&drctlp->drc_lock); 471 return (EBUSY); 472 } 473 474 mutex_exit(&drctlp->drc_lock); 475 476 flags = drctlp->drc_flags; 477 /* 478 * Flip the saved _REQUEST command to its corresponding 479 * _NOTIFY command. 480 */ 481 switch (drctlp->drc_cmd) { 482 case DRCTL_CPU_CONFIG_REQUEST: 483 notify_cmd = DRCTL_CPU_CONFIG_NOTIFY; 484 break; 485 486 case DRCTL_CPU_UNCONFIG_REQUEST: 487 notify_cmd = DRCTL_CPU_UNCONFIG_NOTIFY; 488 break; 489 490 case DRCTL_MEM_CONFIG_REQUEST: 491 case DRCTL_MEM_CONFIG_NOTIFY: 492 case DRCTL_MEM_UNCONFIG_REQUEST: 493 case DRCTL_MEM_UNCONFIG_NOTIFY: 494 case DRCTL_IO_CONFIG_REQUEST: 495 case DRCTL_IO_CONFIG_NOTIFY: 496 case DRCTL_IO_UNCONFIG_REQUEST: 497 case DRCTL_IO_UNCONFIG_NOTIFY: 498 default: 499 /* none of the above should have been accepted in _init */ 500 ASSERT(0); 501 cmn_err(CE_CONT, 502 "drctl_config_fini: bad cmd %d\n", drctlp->drc_cmd); 503 rv = EINVAL; 504 goto done; 505 } 506 507 rv = drctl_config_common(notify_cmd, flags, res, count, NULL, 0); 508 509 done: 510 drctlp->drc_cmd = -1; 511 drctlp->drc_flags = 0; 512 drctlp->drc_busy = NULL; 513 514 return (rv); 515 } 516 517 static int 518 drctl_ioctl(dev_t dev, 519 int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p) 520 { 521 _NOTE(ARGUNUSED(dev, mode, cred_p, rval_p)) 522 523 int rv; 524 525 switch (cmd) { 526 case DRCTL_IOCTL_CONNECT_SERVER: 527 rv = i_drctl_ioctl(cmd, arg); 528 break; 529 default: 530 rv = ENOTSUP; 531 } 532 533 *rval_p = (rv == 0) ? 0 : -1; 534 535 return (rv); 536 } 537 538 /* 539 * Accept a preformatted request from caller and send a message to 540 * the daemon. A pointer to the daemon's response buffer is passed 541 * back in obufp, its size in osize. 542 */ 543 static int 544 send_message(void *msg, size_t size, void **obufp, size_t *osize) 545 { 546 int rv; 547 548 rv = i_drctl_send(msg, size, obufp, osize); 549 550 kmem_free(msg, size); 551 552 return (rv); 553 } 554 555 static void * 556 pack_message(int cmd, int flags, int count, void *data, size_t *osize) 557 { 558 drd_msg_t *msgp; 559 size_t hdr_size = offsetof(drd_msg_t, data); 560 561 switch (cmd) { 562 case DRCTL_CPU_CONFIG_REQUEST: 563 case DRCTL_CPU_CONFIG_NOTIFY: 564 case DRCTL_CPU_UNCONFIG_REQUEST: 565 case DRCTL_CPU_UNCONFIG_NOTIFY: 566 567 *osize = hdr_size + count * sizeof (drctl_rsrc_t); 568 569 msgp = kmem_alloc(*osize, KM_SLEEP); 570 msgp->cmd = cmd; 571 msgp->count = count; 572 msgp->flags = flags; 573 bcopy(data, msgp->data, count * sizeof (drctl_rsrc_t)); 574 break; 575 default: 576 cmn_err(CE_WARN, 577 "drctl: pack_message received invalid cmd %d", cmd); 578 msgp = NULL; 579 } 580 581 return (msgp); 582 } 583