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 int rv; 157 158 drctlp->drc_inst = -1; 159 mutex_init(&drctlp->drc_lock, NULL, MUTEX_DRIVER, NULL); 160 161 if ((rv = mod_install(&modlinkage)) != 0) 162 mutex_destroy(&drctlp->drc_lock); 163 164 return (rv); 165 } 166 167 168 int 169 _fini(void) 170 { 171 int rv; 172 173 if ((rv = mod_remove(&modlinkage)) != 0) 174 return (rv); 175 176 mutex_destroy(&drctlp->drc_lock); 177 return (0); 178 } 179 180 181 int 182 _info(struct modinfo *modinfop) 183 { 184 return (mod_info(&modlinkage, modinfop)); 185 } 186 187 188 /* 189 * Do the attach work 190 */ 191 static int 192 drctl_do_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 193 { 194 _NOTE(ARGUNUSED(cmd)) 195 196 char *str = "drctl_do_attach"; 197 int retval = DDI_SUCCESS; 198 199 if (drctlp->drc_inst != -1) { 200 cmn_err(CE_WARN, "%s: an instance is already attached!", str); 201 return (DDI_FAILURE); 202 } 203 drctlp->drc_inst = ddi_get_instance(dip); 204 205 retval = ddi_create_minor_node(dip, "drctl", S_IFCHR, 206 drctlp->drc_inst, DDI_PSEUDO, 0); 207 if (retval != DDI_SUCCESS) { 208 cmn_err(CE_WARN, "%s: can't create minor node", str); 209 drctlp->drc_inst = -1; 210 return (retval); 211 } 212 213 drctlp->drc_dip = dip; 214 ddi_report_dev(dip); 215 216 return (retval); 217 } 218 219 220 static int 221 drctl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 222 { 223 switch (cmd) { 224 case DDI_ATTACH: 225 return (drctl_do_attach(dip, cmd)); 226 227 default: 228 return (DDI_FAILURE); 229 } 230 } 231 232 233 /* ARGSUSED */ 234 static int 235 drctl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 236 { 237 switch (cmd) { 238 case DDI_DETACH: 239 drctlp->drc_inst = -1; 240 ddi_remove_minor_node(dip, "drctl"); 241 return (DDI_SUCCESS); 242 243 default: 244 return (DDI_FAILURE); 245 } 246 } 247 248 static int 249 drctl_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 250 { 251 _NOTE(ARGUNUSED(dip, cmd, arg, resultp)) 252 253 return (0); 254 } 255 256 static int 257 drctl_open(dev_t *devp, int flag, int otyp, cred_t *cred_p) 258 { 259 _NOTE(ARGUNUSED(devp, flag, cred_p)) 260 261 if (otyp != OTYP_CHR) 262 return (EINVAL); 263 264 return (0); 265 } 266 267 static int 268 drctl_close(dev_t dev, int flag, int otyp, cred_t *cred_p) 269 { 270 _NOTE(ARGUNUSED(dev, flag, otyp, cred_p)) 271 272 return (0); 273 } 274 275 /* 276 * This driver guarantees that if drctl_config_init returns 0, 277 * a valid response buffer will be passed back to the caller. This 278 * routine can be used to generate that response in cases where the 279 * upcall has not resulted in a response message from userland. 280 */ 281 static drctl_rsrc_t * 282 drctl_generate_resp(drctl_rsrc_t *res, 283 int count, size_t *rsize, drctl_status_t status) 284 { 285 int idx; 286 size_t size; 287 drctl_rsrc_t *rbuf; 288 289 size = count * sizeof (*res); 290 rbuf = kmem_alloc(size, KM_SLEEP); 291 292 bcopy(res, rbuf, size); 293 294 for (idx = 0; idx < count; idx++) { 295 rbuf[idx].status = status; 296 rbuf[idx].offset = 0; 297 } 298 299 *rsize = size; 300 return (rbuf); 301 } 302 303 static int 304 drctl_config_common(int cmd, int flags, drctl_rsrc_t *res, 305 int count, drctl_rsrc_t **rbuf, size_t *rsize) 306 { 307 int rv = 0; 308 size_t size; 309 char *bufp; 310 static const char me[] = "drctl_config_common"; 311 312 switch (cmd) { 313 case DRCTL_CPU_CONFIG_REQUEST: 314 case DRCTL_CPU_CONFIG_NOTIFY: 315 case DRCTL_CPU_UNCONFIG_REQUEST: 316 case DRCTL_CPU_UNCONFIG_NOTIFY: 317 rv = 0; 318 break; 319 case DRCTL_MEM_CONFIG_REQUEST: 320 case DRCTL_MEM_CONFIG_NOTIFY: 321 case DRCTL_MEM_UNCONFIG_REQUEST: 322 case DRCTL_MEM_UNCONFIG_NOTIFY: 323 case DRCTL_IO_CONFIG_REQUEST: 324 case DRCTL_IO_CONFIG_NOTIFY: 325 case DRCTL_IO_UNCONFIG_REQUEST: 326 case DRCTL_IO_UNCONFIG_NOTIFY: 327 rv = ENOTSUP; 328 break; 329 } 330 331 if (rv != 0) { 332 DR_DBG_CTL("%s: invalid cmd %d\n", me, cmd); 333 return (rv); 334 } 335 336 /* 337 * If the operation is a FORCE, we don't send a message to 338 * the daemon. But, the upstream clients still expect a 339 * response, so generate a response with all ops 'allowed'. 340 */ 341 if (flags == DRCTL_FLAG_FORCE) { 342 if (rbuf != NULL) { 343 *rbuf = drctl_generate_resp(res, count, &size, 344 DRCTL_STATUS_ALLOW); 345 *rsize = size; 346 } 347 return (0); 348 } 349 350 bufp = pack_message(cmd, flags, count, (void *)res, &size); 351 DR_DBG_CTL("%s: from pack_message, bufp = %p size %ld\n", 352 me, (void *)bufp, size); 353 if (bufp == NULL || size == 0) 354 return (EIO); 355 356 rv = send_message(bufp, size, (void **)rbuf, rsize); 357 358 /* 359 * For failure, as part of our contract with the caller, 360 * generate a response message, but mark all proposed 361 * changes as 'denied'. 362 */ 363 if (rv != 0 && rbuf != NULL) { 364 *rbuf = drctl_generate_resp(res, count, &size, 365 DRCTL_STATUS_DENY); 366 *rsize = size; 367 } 368 369 return (rv); 370 } 371 372 /* 373 * Since the response comes from userland, make sure it is 374 * at least the minimum size and, if it contains error 375 * strings, that the string area is null-terminated. 376 */ 377 static int 378 verify_response(int count, drctl_rsrc_t *resp, size_t size) 379 { 380 int idx; 381 int need_terminator = 0; 382 static const char me[] = "verify_response"; 383 384 if (resp == NULL || size < count * sizeof (*resp)) { 385 DR_DBG_CTL("%s: BAD size - count %d size %ld\n", 386 me, count, size); 387 return (EIO); 388 } 389 390 for (idx = 0; idx < count; idx++) { 391 392 if (resp[idx].offset != 0) 393 need_terminator++; 394 } 395 396 if (need_terminator && *((caddr_t)(resp) + size - 1) != '\0') { 397 DR_DBG_CTL("%s: unterm. strings: resp %p size %ld char %d\n", 398 me, (void *)resp, size, *((caddr_t)(resp) + size - 1)); 399 /* Don't fail the transaction, but don't advertise strings */ 400 for (idx = 0; idx < count; idx++) 401 resp[idx].offset = 0; 402 } 403 404 return (0); 405 } 406 407 408 /* 409 * Prepare for a reconfig operation. 410 */ 411 int 412 drctl_config_init(int cmd, int flags, drctl_rsrc_t *res, 413 int count, drctl_rsrc_t **rbuf, size_t *rsize, drctl_cookie_t ck) 414 { 415 static char me[] = "drctl_config_init"; 416 int idx; 417 int rv; 418 419 if (ck == 0) 420 return (EINVAL); 421 422 mutex_enter(&drctlp->drc_lock); 423 424 if (drctlp->drc_busy != NULL) { 425 mutex_exit(&drctlp->drc_lock); 426 return (EBUSY); 427 } 428 429 DR_DBG_CTL("%s: cmd %d flags %d res %p count %d\n", 430 me, cmd, flags, (void *)res, count); 431 432 /* Mark the link busy. Below we will fill in the actual cookie. */ 433 drctlp->drc_busy = (drctl_cookie_t)-1; 434 mutex_exit(&drctlp->drc_lock); 435 436 if ((rv = drctl_config_common(cmd, 437 flags, res, count, rbuf, rsize)) == 0 && 438 verify_response(count, *rbuf, *rsize) == 0) { 439 drctlp->drc_busy = ck; 440 drctlp->drc_cmd = cmd; 441 drctlp->drc_flags = flags; 442 443 /* 444 * If there wasn't a valid response msg passed back, 445 * create a response with each resource op denied. 446 */ 447 if (*rbuf == NULL || *rsize == 0) { 448 drctl_rsrc_t *bp = *rbuf; 449 450 *rsize = count * sizeof (*bp); 451 bp = kmem_zalloc(*rsize, KM_SLEEP); 452 bcopy(res, bp, *rsize); 453 454 for (idx = 0; idx < count; idx++) { 455 bp[idx].status = DRCTL_STATUS_DENY; 456 bp[idx].offset = 0; 457 } 458 } 459 } else { 460 drctlp->drc_cmd = -1; 461 drctlp->drc_flags = 0; 462 drctlp->drc_busy = NULL; 463 } 464 465 return (rv); 466 } 467 468 /* 469 * Complete a reconfig operation. 470 */ 471 int 472 drctl_config_fini(drctl_cookie_t ck, drctl_rsrc_t *res, int count) 473 { 474 int rv; 475 int notify_cmd; 476 int flags; 477 478 mutex_enter(&drctlp->drc_lock); 479 480 if (drctlp->drc_busy != ck) { 481 mutex_exit(&drctlp->drc_lock); 482 return (EBUSY); 483 } 484 485 mutex_exit(&drctlp->drc_lock); 486 487 flags = drctlp->drc_flags; 488 /* 489 * Flip the saved _REQUEST command to its corresponding 490 * _NOTIFY command. 491 */ 492 switch (drctlp->drc_cmd) { 493 case DRCTL_CPU_CONFIG_REQUEST: 494 notify_cmd = DRCTL_CPU_CONFIG_NOTIFY; 495 break; 496 497 case DRCTL_CPU_UNCONFIG_REQUEST: 498 notify_cmd = DRCTL_CPU_UNCONFIG_NOTIFY; 499 break; 500 501 case DRCTL_MEM_CONFIG_REQUEST: 502 case DRCTL_MEM_CONFIG_NOTIFY: 503 case DRCTL_MEM_UNCONFIG_REQUEST: 504 case DRCTL_MEM_UNCONFIG_NOTIFY: 505 case DRCTL_IO_CONFIG_REQUEST: 506 case DRCTL_IO_CONFIG_NOTIFY: 507 case DRCTL_IO_UNCONFIG_REQUEST: 508 case DRCTL_IO_UNCONFIG_NOTIFY: 509 default: 510 /* none of the above should have been accepted in _init */ 511 ASSERT(0); 512 cmn_err(CE_CONT, 513 "drctl_config_fini: bad cmd %d\n", drctlp->drc_cmd); 514 rv = EINVAL; 515 goto done; 516 } 517 518 rv = drctl_config_common(notify_cmd, flags, res, count, NULL, 0); 519 520 done: 521 drctlp->drc_cmd = -1; 522 drctlp->drc_flags = 0; 523 drctlp->drc_busy = NULL; 524 525 return (rv); 526 } 527 528 static int 529 drctl_ioctl(dev_t dev, 530 int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p) 531 { 532 _NOTE(ARGUNUSED(dev, mode, cred_p, rval_p)) 533 534 int rv; 535 536 switch (cmd) { 537 case DRCTL_IOCTL_CONNECT_SERVER: 538 rv = i_drctl_ioctl(cmd, arg); 539 break; 540 default: 541 rv = ENOTSUP; 542 } 543 544 *rval_p = (rv == 0) ? 0 : -1; 545 546 return (rv); 547 } 548 549 /* 550 * Accept a preformatted request from caller and send a message to 551 * the daemon. A pointer to the daemon's response buffer is passed 552 * back in obufp, its size in osize. 553 */ 554 static int 555 send_message(void *msg, size_t size, void **obufp, size_t *osize) 556 { 557 int rv; 558 559 rv = i_drctl_send(msg, size, obufp, osize); 560 561 kmem_free(msg, size); 562 563 return (rv); 564 } 565 566 static void * 567 pack_message(int cmd, int flags, int count, void *data, size_t *osize) 568 { 569 drd_msg_t *msgp; 570 size_t hdr_size = offsetof(drd_msg_t, data); 571 572 switch (cmd) { 573 case DRCTL_CPU_CONFIG_REQUEST: 574 case DRCTL_CPU_CONFIG_NOTIFY: 575 case DRCTL_CPU_UNCONFIG_REQUEST: 576 case DRCTL_CPU_UNCONFIG_NOTIFY: 577 578 *osize = hdr_size + count * sizeof (drctl_rsrc_t); 579 580 msgp = kmem_alloc(*osize, KM_SLEEP); 581 msgp->cmd = cmd; 582 msgp->count = count; 583 msgp->flags = flags; 584 bcopy(data, msgp->data, count * sizeof (drctl_rsrc_t)); 585 break; 586 default: 587 cmn_err(CE_WARN, 588 "drctl: pack_message received invalid cmd %d", cmd); 589 msgp = NULL; 590 } 591 592 return (msgp); 593 } 594