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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* 26 * Copyright (c) 2010, Intel Corporation. 27 * All rights reserved. 28 */ 29 30 /* 31 * Copyright 2023 Oxide Computer Company 32 */ 33 34 #include <sys/types.h> 35 #include <sys/cmn_err.h> 36 #include <sys/conf.h> 37 #include <sys/debug.h> 38 #include <sys/errno.h> 39 #include <sys/note.h> 40 #include <sys/dditypes.h> 41 #include <sys/ddi.h> 42 #include <sys/sunddi.h> 43 #include <sys/sunndi.h> 44 #include <sys/ddi_impldefs.h> 45 #include <sys/ndi_impldefs.h> 46 #include <sys/varargs.h> 47 #include <sys/modctl.h> 48 #include <sys/kmem.h> 49 #include <sys/cpuvar.h> 50 #include <sys/cpupart.h> 51 #include <sys/mem_config.h> 52 #include <sys/mem_cage.h> 53 #include <sys/memnode.h> 54 #include <sys/callb.h> 55 #include <sys/ontrap.h> 56 #include <sys/obpdefs.h> 57 #include <sys/promif.h> 58 #include <sys/synch.h> 59 #include <sys/systm.h> 60 #include <sys/sysmacros.h> 61 #include <sys/archsystm.h> 62 #include <sys/machsystm.h> 63 #include <sys/x_call.h> 64 #include <sys/x86_archext.h> 65 #include <sys/fastboot_impl.h> 66 #include <sys/sysevent.h> 67 #include <sys/sysevent/dr.h> 68 #include <sys/sysevent/eventdefs.h> 69 #include <sys/acpi/acpi.h> 70 #include <sys/acpica.h> 71 #include <sys/acpidev.h> 72 #include <sys/acpidev_rsc.h> 73 #include <sys/acpidev_dr.h> 74 #include <sys/dr.h> 75 #include <sys/dr_util.h> 76 #include <sys/drmach.h> 77 #include "drmach_acpi.h" 78 79 /* utility */ 80 #define MBYTE (1048576ull) 81 #define _ptob64(p) ((uint64_t)(p) << PAGESHIFT) 82 #define _b64top(b) ((pgcnt_t)((b) >> PAGESHIFT)) 83 84 static int drmach_init(void); 85 static void drmach_fini(void); 86 static int drmach_name2type_idx(char *); 87 static sbd_error_t *drmach_mem_update_lgrp(drmachid_t); 88 89 static void drmach_board_dispose(drmachid_t id); 90 static sbd_error_t *drmach_board_release(drmachid_t); 91 static sbd_error_t *drmach_board_status(drmachid_t, drmach_status_t *); 92 93 static void drmach_io_dispose(drmachid_t); 94 static sbd_error_t *drmach_io_release(drmachid_t); 95 static sbd_error_t *drmach_io_status(drmachid_t, drmach_status_t *); 96 97 static void drmach_cpu_dispose(drmachid_t); 98 static sbd_error_t *drmach_cpu_release(drmachid_t); 99 static sbd_error_t *drmach_cpu_status(drmachid_t, drmach_status_t *); 100 101 static void drmach_mem_dispose(drmachid_t); 102 static sbd_error_t *drmach_mem_release(drmachid_t); 103 static sbd_error_t *drmach_mem_status(drmachid_t, drmach_status_t *); 104 105 #ifdef DEBUG 106 int drmach_debug = 1; /* set to non-zero to enable debug messages */ 107 #endif /* DEBUG */ 108 109 drmach_domain_info_t drmach_domain; 110 111 static char *drmach_ie_fmt = "drmach_acpi.c %d"; 112 static drmach_array_t *drmach_boards; 113 114 /* rwlock to protect drmach_boards. */ 115 static krwlock_t drmach_boards_rwlock; 116 117 /* rwlock to block out CPR thread. */ 118 static krwlock_t drmach_cpr_rwlock; 119 120 /* CPR callb id. */ 121 static callb_id_t drmach_cpr_cid; 122 123 static struct { 124 const char *name; 125 const char *type; 126 sbd_error_t *(*new)(drmach_device_t *, drmachid_t *); 127 } drmach_name2type[] = { 128 { ACPIDEV_NODE_NAME_CPU, DRMACH_DEVTYPE_CPU, drmach_cpu_new }, 129 { ACPIDEV_NODE_NAME_MEMORY, DRMACH_DEVTYPE_MEM, drmach_mem_new }, 130 { ACPIDEV_NODE_NAME_PCI, DRMACH_DEVTYPE_PCI, drmach_io_new }, 131 }; 132 133 /* 134 * drmach autoconfiguration data structures and interfaces 135 */ 136 static struct modlmisc modlmisc = { 137 &mod_miscops, 138 "ACPI based DR v1.0" 139 }; 140 141 static struct modlinkage modlinkage = { 142 MODREV_1, 143 (void *)&modlmisc, 144 NULL 145 }; 146 147 int 148 _init(void) 149 { 150 int err; 151 152 if ((err = drmach_init()) != 0) { 153 return (err); 154 } 155 156 if ((err = mod_install(&modlinkage)) != 0) { 157 drmach_fini(); 158 } 159 160 return (err); 161 } 162 163 int 164 _fini(void) 165 { 166 int err; 167 168 if ((err = mod_remove(&modlinkage)) == 0) { 169 drmach_fini(); 170 } 171 172 return (err); 173 } 174 175 int 176 _info(struct modinfo *modinfop) 177 { 178 return (mod_info(&modlinkage, modinfop)); 179 } 180 181 /* 182 * Internal support functions. 183 */ 184 static DRMACH_HANDLE 185 drmach_node_acpi_get_dnode(drmach_node_t *np) 186 { 187 return ((DRMACH_HANDLE)(uintptr_t)np->here); 188 } 189 190 static dev_info_t * 191 drmach_node_acpi_get_dip(drmach_node_t *np) 192 { 193 dev_info_t *dip = NULL; 194 195 if (ACPI_FAILURE(acpica_get_devinfo((DRMACH_HANDLE)(np->here), &dip))) { 196 return (NULL); 197 } 198 199 return (dip); 200 } 201 202 static int 203 drmach_node_acpi_get_prop(drmach_node_t *np, char *name, void *buf, int len) 204 { 205 int rv = 0; 206 DRMACH_HANDLE hdl; 207 208 hdl = np->get_dnode(np); 209 if (hdl == NULL) { 210 DRMACH_PR("!drmach_node_acpi_get_prop: NULL handle"); 211 rv = -1; 212 } else { 213 rv = acpidev_dr_device_getprop(hdl, name, buf, len); 214 if (rv >= 0) { 215 ASSERT(rv <= len); 216 rv = 0; 217 } 218 } 219 220 return (rv); 221 } 222 223 static int 224 drmach_node_acpi_get_proplen(drmach_node_t *np, char *name, int *len) 225 { 226 int rv = 0; 227 DRMACH_HANDLE hdl; 228 229 hdl = np->get_dnode(np); 230 if (hdl == NULL) { 231 DRMACH_PR("!drmach_node_acpi_get_proplen: NULL handle"); 232 rv = -1; 233 } else { 234 rv = acpidev_dr_device_getprop(hdl, name, NULL, 0); 235 if (rv >= 0) { 236 *len = rv; 237 return (0); 238 } 239 } 240 241 return (-1); 242 } 243 244 static ACPI_STATUS 245 drmach_node_acpi_callback(ACPI_HANDLE hdl, uint_t lvl, void *ctx, void **retval) 246 { 247 _NOTE(ARGUNUSED(lvl)); 248 249 int rv; 250 dev_info_t *dip; 251 drmach_node_walk_args_t *argp = ctx; 252 int (*cb)(drmach_node_walk_args_t *args); 253 acpidev_class_id_t clsid; 254 255 ASSERT(hdl != NULL); 256 ASSERT(ctx != NULL); 257 ASSERT(retval != NULL); 258 259 /* Skip subtree if the device is not powered. */ 260 if (!acpidev_dr_device_is_powered(hdl)) { 261 return (AE_CTRL_DEPTH); 262 } 263 264 /* 265 * Keep scanning subtree if it fails to lookup device node. 266 * There may be some ACPI objects without device nodes created. 267 */ 268 if (ACPI_FAILURE(acpica_get_devinfo(hdl, &dip))) { 269 return (AE_OK); 270 } 271 272 argp->node->here = hdl; 273 cb = (int (*)(drmach_node_walk_args_t *args))argp->func; 274 rv = (*cb)(argp); 275 argp->node->here = NULL; 276 if (rv) { 277 *(int *)retval = rv; 278 return (AE_CTRL_TERMINATE); 279 } 280 281 /* 282 * Skip descendants of PCI/PCIex host bridges. 283 * PCI/PCIex devices will be handled by pcihp. 284 */ 285 clsid = acpidev_dr_device_get_class(hdl); 286 if (clsid == ACPIDEV_CLASS_ID_PCI || clsid == ACPIDEV_CLASS_ID_PCIEX) { 287 return (AE_CTRL_DEPTH); 288 } 289 290 return (AE_OK); 291 } 292 293 static int 294 drmach_node_acpi_walk(drmach_node_t *np, void *data, 295 int (*cb)(drmach_node_walk_args_t *args)) 296 { 297 DRMACH_HANDLE hdl; 298 int rv = 0; 299 drmach_node_walk_args_t args; 300 301 /* initialize the args structure for callback */ 302 args.node = np; 303 args.data = data; 304 args.func = (void *)cb; 305 306 /* save the handle, it will be modified when walking the tree. */ 307 hdl = np->get_dnode(np); 308 if (hdl == NULL) { 309 DRMACH_PR("!drmach_node_acpi_walk: failed to get device node."); 310 return (EX86_INAPPROP); 311 } 312 313 if (ACPI_FAILURE(acpidev_dr_device_walk_device(hdl, 314 ACPIDEV_MAX_ENUM_LEVELS, drmach_node_acpi_callback, 315 &args, (void *)&rv))) { 316 /* 317 * If acpidev_dr_device_walk_device() itself fails, rv won't 318 * be set to suitable error code. Set it here. 319 */ 320 if (rv == 0) { 321 cmn_err(CE_WARN, "!drmach_node_acpi_walk: failed to " 322 "walk ACPI namespace."); 323 rv = EX86_ACPIWALK; 324 } 325 } 326 327 /* restore the handle to original value after walking the tree. */ 328 np->here = (void *)hdl; 329 330 return ((int)rv); 331 } 332 333 static drmach_node_t * 334 drmach_node_new(void) 335 { 336 drmach_node_t *np; 337 338 np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP); 339 340 np->get_dnode = drmach_node_acpi_get_dnode; 341 np->getdip = drmach_node_acpi_get_dip; 342 np->getproplen = drmach_node_acpi_get_proplen; 343 np->getprop = drmach_node_acpi_get_prop; 344 np->walk = drmach_node_acpi_walk; 345 346 return (np); 347 } 348 349 static drmachid_t 350 drmach_node_dup(drmach_node_t *np) 351 { 352 drmach_node_t *dup; 353 354 dup = drmach_node_new(); 355 dup->here = np->here; 356 dup->get_dnode = np->get_dnode; 357 dup->getdip = np->getdip; 358 dup->getproplen = np->getproplen; 359 dup->getprop = np->getprop; 360 dup->walk = np->walk; 361 362 return (dup); 363 } 364 365 static void 366 drmach_node_dispose(drmach_node_t *np) 367 { 368 kmem_free(np, sizeof (*np)); 369 } 370 371 static int 372 drmach_node_walk(drmach_node_t *np, void *param, 373 int (*cb)(drmach_node_walk_args_t *args)) 374 { 375 return (np->walk(np, param, cb)); 376 } 377 378 static DRMACH_HANDLE 379 drmach_node_get_dnode(drmach_node_t *np) 380 { 381 return (np->get_dnode(np)); 382 } 383 384 /* 385 * drmach_array provides convenient array construction, access, 386 * bounds checking and array destruction logic. 387 */ 388 static drmach_array_t * 389 drmach_array_new(uint_t min_index, uint_t max_index) 390 { 391 drmach_array_t *arr; 392 393 arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP); 394 395 arr->arr_sz = (max_index - min_index + 1) * sizeof (void *); 396 if (arr->arr_sz > 0) { 397 arr->min_index = min_index; 398 arr->max_index = max_index; 399 400 arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP); 401 return (arr); 402 } else { 403 kmem_free(arr, sizeof (*arr)); 404 return (0); 405 } 406 } 407 408 static int 409 drmach_array_set(drmach_array_t *arr, uint_t idx, drmachid_t val) 410 { 411 if (idx < arr->min_index || idx > arr->max_index) 412 return (-1); 413 arr->arr[idx - arr->min_index] = val; 414 return (0); 415 } 416 417 /* 418 * Get the item with index idx. 419 * Return 0 with the value stored in val if succeeds, otherwise return -1. 420 */ 421 static int 422 drmach_array_get(drmach_array_t *arr, uint_t idx, drmachid_t *val) 423 { 424 if (idx < arr->min_index || idx > arr->max_index) 425 return (-1); 426 *val = arr->arr[idx - arr->min_index]; 427 return (0); 428 } 429 430 static int 431 drmach_array_first(drmach_array_t *arr, uint_t *idx, drmachid_t *val) 432 { 433 int rv; 434 435 *idx = arr->min_index; 436 while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL) 437 *idx += 1; 438 439 return (rv); 440 } 441 442 static int 443 drmach_array_next(drmach_array_t *arr, uint_t *idx, drmachid_t *val) 444 { 445 int rv; 446 447 *idx += 1; 448 while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL) 449 *idx += 1; 450 451 return (rv); 452 } 453 454 static void 455 drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t)) 456 { 457 drmachid_t val; 458 uint_t idx; 459 int rv; 460 461 rv = drmach_array_first(arr, &idx, &val); 462 while (rv == 0) { 463 (*disposer)(val); 464 rv = drmach_array_next(arr, &idx, &val); 465 } 466 467 kmem_free(arr->arr, arr->arr_sz); 468 kmem_free(arr, sizeof (*arr)); 469 } 470 471 static drmach_board_t * 472 drmach_get_board_by_bnum(uint_t bnum) 473 { 474 drmachid_t id; 475 476 if (drmach_array_get(drmach_boards, bnum, &id) == 0) 477 return ((drmach_board_t *)id); 478 else 479 return (NULL); 480 } 481 482 sbd_error_t * 483 drmach_device_new(drmach_node_t *node, 484 drmach_board_t *bp, int portid, drmachid_t *idp) 485 { 486 int i; 487 int rv; 488 drmach_device_t proto; 489 sbd_error_t *err; 490 char name[OBP_MAXDRVNAME]; 491 492 rv = node->getprop(node, ACPIDEV_DR_PROP_DEVNAME, name, OBP_MAXDRVNAME); 493 if (rv) { 494 /* every node is expected to have a name */ 495 err = drerr_new(1, EX86_GETPROP, "device node %s: property %s", 496 ddi_node_name(node->getdip(node)), 497 ACPIDEV_DR_PROP_DEVNAME); 498 return (err); 499 } 500 501 /* 502 * The node currently being examined is not listed in the name2type[] 503 * array. In this case, the node is no interest to drmach. Both 504 * dp and err are initialized here to yield nothing (no device or 505 * error structure) for this case. 506 */ 507 i = drmach_name2type_idx(name); 508 if (i < 0) { 509 *idp = (drmachid_t)0; 510 return (NULL); 511 } 512 513 /* device specific new function will set unum */ 514 bzero(&proto, sizeof (proto)); 515 proto.type = drmach_name2type[i].type; 516 proto.bp = bp; 517 proto.node = node; 518 proto.portid = portid; 519 520 return (drmach_name2type[i].new(&proto, idp)); 521 } 522 523 static void 524 drmach_device_dispose(drmachid_t id) 525 { 526 drmach_device_t *self = id; 527 528 self->cm.dispose(id); 529 } 530 531 static sbd_error_t * 532 drmach_device_status(drmachid_t id, drmach_status_t *stat) 533 { 534 drmach_common_t *cp; 535 536 if (!DRMACH_IS_ID(id)) 537 return (drerr_new(0, EX86_NOTID, NULL)); 538 cp = id; 539 540 return (cp->status(id, stat)); 541 } 542 543 drmach_board_t * 544 drmach_board_new(uint_t bnum, int boot_board) 545 { 546 sbd_error_t *err; 547 drmach_board_t *bp; 548 dev_info_t *dip = NULL; 549 550 bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP); 551 bp->cm.isa = (void *)drmach_board_new; 552 bp->cm.release = drmach_board_release; 553 bp->cm.status = drmach_board_status; 554 555 bp->bnum = bnum; 556 bp->devices = NULL; 557 bp->tree = drmach_node_new(); 558 559 acpidev_dr_lock_all(); 560 if (ACPI_FAILURE(acpidev_dr_get_board_handle(bnum, &bp->tree->here))) { 561 acpidev_dr_unlock_all(); 562 drmach_board_dispose(bp); 563 return (NULL); 564 } 565 acpidev_dr_unlock_all(); 566 ASSERT(bp->tree->here != NULL); 567 568 err = drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name)); 569 if (err != NULL) { 570 sbd_err_clear(&err); 571 drmach_board_dispose(bp); 572 return (NULL); 573 } 574 575 if (acpidev_dr_device_is_powered(bp->tree->here)) { 576 bp->boot_board = boot_board; 577 bp->powered = 1; 578 } else { 579 bp->boot_board = 0; 580 bp->powered = 0; 581 } 582 bp->assigned = boot_board; 583 if (ACPI_SUCCESS(acpica_get_devinfo(bp->tree->here, &dip))) { 584 bp->connected = 1; 585 } else { 586 bp->connected = 0; 587 } 588 589 (void) drmach_array_set(drmach_boards, bnum, bp); 590 591 return (bp); 592 } 593 594 static void 595 drmach_board_dispose(drmachid_t id) 596 { 597 drmach_board_t *bp; 598 599 ASSERT(DRMACH_IS_BOARD_ID(id)); 600 bp = id; 601 602 if (bp->tree) 603 drmach_node_dispose(bp->tree); 604 605 if (bp->devices) 606 drmach_array_dispose(bp->devices, drmach_device_dispose); 607 608 kmem_free(bp, sizeof (drmach_board_t)); 609 } 610 611 static sbd_error_t * 612 drmach_board_release(drmachid_t id) 613 { 614 if (!DRMACH_IS_BOARD_ID(id)) 615 return (drerr_new(0, EX86_INAPPROP, NULL)); 616 617 return (NULL); 618 } 619 620 static int 621 drmach_board_check_power(drmach_board_t *bp) 622 { 623 DRMACH_HANDLE hdl; 624 625 hdl = drmach_node_get_dnode(bp->tree); 626 627 return (acpidev_dr_device_is_powered(hdl)); 628 } 629 630 struct drmach_board_list_dep_arg { 631 int count; 632 size_t len; 633 ssize_t off; 634 char *buf; 635 char temp[MAXPATHLEN]; 636 }; 637 638 static ACPI_STATUS 639 drmach_board_generate_name(ACPI_HANDLE hdl, UINT32 lvl, void *ctx, 640 void **retval) 641 { 642 _NOTE(ARGUNUSED(retval)); 643 644 struct drmach_board_list_dep_arg *argp = ctx; 645 646 ASSERT(hdl != NULL); 647 ASSERT(lvl == UINT32_MAX); 648 ASSERT(ctx != NULL); 649 650 /* Skip non-board devices. */ 651 if (!acpidev_dr_device_is_board(hdl)) { 652 return (AE_OK); 653 } 654 655 if (ACPI_FAILURE(acpidev_dr_get_board_name(hdl, argp->temp, 656 sizeof (argp->temp)))) { 657 DRMACH_PR("!drmach_board_generate_name: failed to " 658 "generate board name for handle %p.", hdl); 659 /* Keep on walking. */ 660 return (AE_OK); 661 } 662 argp->count++; 663 argp->off += snprintf(argp->buf + argp->off, argp->len - argp->off, 664 " %s", argp->temp); 665 if (argp->off >= argp->len) { 666 return (AE_CTRL_TERMINATE); 667 } 668 669 return (AE_OK); 670 } 671 672 static ssize_t 673 drmach_board_list_dependency(ACPI_HANDLE hdl, boolean_t edl, char *prefix, 674 char *buf, size_t len) 675 { 676 ACPI_STATUS rc; 677 ssize_t off; 678 struct drmach_board_list_dep_arg *ap; 679 680 ASSERT(buf != NULL && len != 0); 681 if (buf == NULL || len == 0) { 682 return (-1); 683 } 684 685 ap = kmem_zalloc(sizeof (*ap), KM_SLEEP); 686 ap->buf = buf; 687 ap->len = len; 688 ap->off = snprintf(buf, len, "%s", prefix); 689 if (ap->off >= len) { 690 *buf = '\0'; 691 kmem_free(ap, sizeof (*ap)); 692 return (-1); 693 } 694 695 /* Generate the device dependency list. */ 696 if (edl) { 697 rc = acpidev_dr_device_walk_edl(hdl, 698 drmach_board_generate_name, ap, NULL); 699 } else { 700 rc = acpidev_dr_device_walk_ejd(hdl, 701 drmach_board_generate_name, ap, NULL); 702 } 703 if (ACPI_FAILURE(rc)) { 704 *buf = '\0'; 705 ap->off = -1; 706 /* No device has dependency on this board. */ 707 } else if (ap->count == 0) { 708 *buf = '\0'; 709 ap->off = 0; 710 } 711 712 off = ap->off; 713 kmem_free(ap, sizeof (*ap)); 714 715 return (off); 716 } 717 718 static sbd_error_t * 719 drmach_board_status(drmachid_t id, drmach_status_t *stat) 720 { 721 sbd_error_t *err = NULL; 722 drmach_board_t *bp; 723 DRMACH_HANDLE hdl; 724 size_t off; 725 726 if (!DRMACH_IS_BOARD_ID(id)) 727 return (drerr_new(0, EX86_INAPPROP, NULL)); 728 bp = id; 729 730 if (bp->tree == NULL) 731 return (drerr_new(0, EX86_INAPPROP, NULL)); 732 hdl = drmach_node_get_dnode(bp->tree); 733 if (hdl == NULL) 734 return (drerr_new(0, EX86_INAPPROP, NULL)); 735 736 stat->busy = 0; /* assume not busy */ 737 stat->configured = 0; /* assume not configured */ 738 stat->assigned = bp->assigned; 739 stat->powered = bp->powered = acpidev_dr_device_is_powered(hdl); 740 stat->empty = !acpidev_dr_device_is_present(hdl); 741 if (ACPI_SUCCESS(acpidev_dr_device_check_status(hdl))) { 742 stat->cond = bp->cond = SBD_COND_OK; 743 } else { 744 stat->cond = bp->cond = SBD_COND_FAILED; 745 } 746 stat->info[0] = '\0'; 747 748 /* Generate the eject device list. */ 749 if (drmach_board_list_dependency(hdl, B_TRUE, "EDL:", 750 stat->info, sizeof (stat->info)) < 0) { 751 DRMACH_PR("!drmach_board_status: failed to generate " 752 "eject device list for board %d.", bp->bnum); 753 stat->info[0] = '\0'; 754 } 755 off = strlen(stat->info); 756 if (off < sizeof (stat->info)) { 757 if (drmach_board_list_dependency(hdl, B_FALSE, 758 off ? ", EJD:" : "EJD:", 759 stat->info + off, sizeof (stat->info) - off) < 0) { 760 DRMACH_PR("!drmach_board_status: failed to generate " 761 "eject dependent device for board %d.", bp->bnum); 762 stat->info[off] = '\0'; 763 } 764 } 765 766 switch (acpidev_dr_get_board_type(bp->tree->get_dnode(bp->tree))) { 767 case ACPIDEV_CPU_BOARD: 768 (void) strlcpy(stat->type, "CPU Board", sizeof (stat->type)); 769 break; 770 case ACPIDEV_MEMORY_BOARD: 771 (void) strlcpy(stat->type, "MemoryBoard", sizeof (stat->type)); 772 break; 773 case ACPIDEV_IO_BOARD: 774 (void) strlcpy(stat->type, "IO Board", sizeof (stat->type)); 775 break; 776 case ACPIDEV_SYSTEM_BOARD: 777 /*FALLTHROUGH*/ 778 default: 779 (void) strlcpy(stat->type, "SystemBoard", sizeof (stat->type)); 780 break; 781 } 782 783 if (bp->devices) { 784 int rv; 785 uint_t d_idx; 786 drmachid_t d_id; 787 788 rv = drmach_array_first(bp->devices, &d_idx, &d_id); 789 while (rv == 0) { 790 drmach_status_t d_stat; 791 792 err = drmach_device_status(d_id, &d_stat); 793 if (err) 794 break; 795 796 stat->busy |= d_stat.busy; 797 stat->configured |= d_stat.configured; 798 799 rv = drmach_array_next(bp->devices, &d_idx, &d_id); 800 } 801 } 802 803 return (err); 804 } 805 806 /* 807 * When DR is initialized, we walk the device tree and acquire a hold on 808 * all the nodes that are interesting to DR. This is so that the corresponding 809 * branches cannot be deleted. 810 */ 811 static int 812 drmach_hold_rele_devtree(dev_info_t *rdip, void *arg) 813 { 814 int *holdp = (int *)arg; 815 ACPI_HANDLE hdl = NULL; 816 acpidev_data_handle_t dhdl; 817 818 /* Skip nodes and subtrees which are not created by acpidev. */ 819 if (ACPI_FAILURE(acpica_get_handle(rdip, &hdl))) { 820 return (DDI_WALK_PRUNECHILD); 821 } 822 ASSERT(hdl != NULL); 823 dhdl = acpidev_data_get_handle(hdl); 824 if (dhdl == NULL) { 825 return (DDI_WALK_PRUNECHILD); 826 } 827 828 /* Hold/release devices which are interesting to DR operations. */ 829 if (acpidev_data_dr_ready(dhdl)) { 830 if (*holdp) { 831 ASSERT(!e_ddi_branch_held(rdip)); 832 e_ddi_branch_hold(rdip); 833 } else { 834 ASSERT(e_ddi_branch_held(rdip)); 835 e_ddi_branch_rele(rdip); 836 } 837 } 838 839 return (DDI_WALK_CONTINUE); 840 } 841 842 static void 843 drmach_hold_devtree(void) 844 { 845 dev_info_t *dip; 846 int hold = 1; 847 848 dip = ddi_root_node(); 849 ndi_devi_enter(dip); 850 ddi_walk_devs(ddi_get_child(dip), drmach_hold_rele_devtree, &hold); 851 ndi_devi_exit(dip); 852 } 853 854 static void 855 drmach_release_devtree(void) 856 { 857 dev_info_t *dip; 858 int hold = 0; 859 860 dip = ddi_root_node(); 861 ndi_devi_enter(dip); 862 ddi_walk_devs(ddi_get_child(dip), drmach_hold_rele_devtree, &hold); 863 ndi_devi_exit(dip); 864 } 865 866 static boolean_t 867 drmach_cpr_callb(void *arg, int code) 868 { 869 _NOTE(ARGUNUSED(arg)); 870 871 if (code == CB_CODE_CPR_CHKPT) { 872 /* 873 * Temporarily block CPR operations if there are DR operations 874 * ongoing. 875 */ 876 rw_enter(&drmach_cpr_rwlock, RW_WRITER); 877 } else { 878 rw_exit(&drmach_cpr_rwlock); 879 } 880 881 return (B_TRUE); 882 } 883 884 static int 885 drmach_init(void) 886 { 887 DRMACH_HANDLE hdl; 888 drmachid_t id; 889 uint_t bnum; 890 891 if (MAX_BOARDS > SHRT_MAX) { 892 cmn_err(CE_WARN, "!drmach_init: system has too many (%d) " 893 "hotplug capable boards.", MAX_BOARDS); 894 return (ENXIO); 895 } else if (MAX_CMP_UNITS_PER_BOARD > 1) { 896 cmn_err(CE_WARN, "!drmach_init: DR doesn't support multiple " 897 "(%d) physical processors on one board.", 898 MAX_CMP_UNITS_PER_BOARD); 899 return (ENXIO); 900 } else if (!ISP2(MAX_CORES_PER_CMP)) { 901 cmn_err(CE_WARN, "!drmach_init: number of logical CPUs (%d) in " 902 "physical processor is not power of 2.", 903 MAX_CORES_PER_CMP); 904 return (ENXIO); 905 } else if (MAX_CPU_UNITS_PER_BOARD > DEVSET_CPU_NUMBER || 906 MAX_MEM_UNITS_PER_BOARD > DEVSET_MEM_NUMBER || 907 MAX_IO_UNITS_PER_BOARD > DEVSET_IO_NUMBER) { 908 cmn_err(CE_WARN, "!drmach_init: system has more CPU/memory/IO " 909 "units than the DR driver can handle."); 910 return (ENXIO); 911 } 912 913 rw_init(&drmach_cpr_rwlock, NULL, RW_DEFAULT, NULL); 914 drmach_cpr_cid = callb_add(drmach_cpr_callb, NULL, 915 CB_CL_CPR_PM, "drmach"); 916 917 rw_init(&drmach_boards_rwlock, NULL, RW_DEFAULT, NULL); 918 drmach_boards = drmach_array_new(0, MAX_BOARDS - 1); 919 drmach_domain.allow_dr = acpidev_dr_capable(); 920 921 for (bnum = 0; bnum < MAX_BOARDS; bnum++) { 922 hdl = NULL; 923 if (ACPI_FAILURE(acpidev_dr_get_board_handle(bnum, &hdl)) || 924 hdl == NULL) { 925 cmn_err(CE_WARN, "!drmach_init: failed to lookup ACPI " 926 "handle for board %d.", bnum); 927 continue; 928 } 929 if (drmach_array_get(drmach_boards, bnum, &id) == -1) { 930 DRMACH_PR("!drmach_init: failed to get handle " 931 "for board %d.", bnum); 932 ASSERT(0); 933 goto error; 934 } else if (id == NULL) { 935 (void) drmach_board_new(bnum, 1); 936 } 937 } 938 939 /* 940 * Walk descendants of the devinfo root node and hold 941 * all devinfo branches of interest. 942 */ 943 drmach_hold_devtree(); 944 945 return (0); 946 947 error: 948 drmach_array_dispose(drmach_boards, drmach_board_dispose); 949 rw_destroy(&drmach_boards_rwlock); 950 rw_destroy(&drmach_cpr_rwlock); 951 return (ENXIO); 952 } 953 954 static void 955 drmach_fini(void) 956 { 957 rw_enter(&drmach_boards_rwlock, RW_WRITER); 958 if (drmach_boards != NULL) { 959 drmach_array_dispose(drmach_boards, drmach_board_dispose); 960 drmach_boards = NULL; 961 } 962 rw_exit(&drmach_boards_rwlock); 963 964 /* 965 * Walk descendants of the root devinfo node 966 * release holds acquired on branches in drmach_init() 967 */ 968 drmach_release_devtree(); 969 970 (void) callb_delete(drmach_cpr_cid); 971 rw_destroy(&drmach_cpr_rwlock); 972 rw_destroy(&drmach_boards_rwlock); 973 } 974 975 sbd_error_t * 976 drmach_io_new(drmach_device_t *proto, drmachid_t *idp) 977 { 978 drmach_io_t *ip; 979 int portid; 980 981 portid = proto->portid; 982 ASSERT(portid != -1); 983 proto->unum = portid; 984 985 ip = kmem_zalloc(sizeof (drmach_io_t), KM_SLEEP); 986 bcopy(proto, &ip->dev, sizeof (ip->dev)); 987 ip->dev.node = drmach_node_dup(proto->node); 988 ip->dev.cm.isa = (void *)drmach_io_new; 989 ip->dev.cm.dispose = drmach_io_dispose; 990 ip->dev.cm.release = drmach_io_release; 991 ip->dev.cm.status = drmach_io_status; 992 (void) snprintf(ip->dev.cm.name, sizeof (ip->dev.cm.name), "%s%d", 993 ip->dev.type, ip->dev.unum); 994 995 *idp = (drmachid_t)ip; 996 997 return (NULL); 998 } 999 1000 static void 1001 drmach_io_dispose(drmachid_t id) 1002 { 1003 drmach_io_t *self; 1004 1005 ASSERT(DRMACH_IS_IO_ID(id)); 1006 1007 self = id; 1008 if (self->dev.node) 1009 drmach_node_dispose(self->dev.node); 1010 1011 kmem_free(self, sizeof (*self)); 1012 } 1013 1014 static sbd_error_t * 1015 drmach_io_release(drmachid_t id) 1016 { 1017 if (!DRMACH_IS_IO_ID(id)) 1018 return (drerr_new(0, EX86_INAPPROP, NULL)); 1019 1020 return (NULL); 1021 } 1022 1023 static sbd_error_t * 1024 drmach_io_status(drmachid_t id, drmach_status_t *stat) 1025 { 1026 drmach_device_t *dp; 1027 sbd_error_t *err; 1028 int configured; 1029 1030 ASSERT(DRMACH_IS_IO_ID(id)); 1031 dp = id; 1032 1033 err = drmach_io_is_attached(id, &configured); 1034 if (err) 1035 return (err); 1036 1037 stat->assigned = dp->bp->assigned; 1038 stat->powered = dp->bp->powered; 1039 stat->configured = (configured != 0); 1040 stat->busy = dp->busy; 1041 (void) strlcpy(stat->type, dp->type, sizeof (stat->type)); 1042 stat->info[0] = '\0'; 1043 1044 return (NULL); 1045 } 1046 1047 sbd_error_t * 1048 drmach_cpu_new(drmach_device_t *proto, drmachid_t *idp) 1049 { 1050 int portid; 1051 processorid_t cpuid; 1052 drmach_cpu_t *cp = NULL; 1053 1054 /* the portid is APIC ID of the node */ 1055 portid = proto->portid; 1056 ASSERT(portid != -1); 1057 1058 /* 1059 * Assume all CPUs are homogeneous and have the same number of 1060 * cores/threads. 1061 */ 1062 proto->unum = portid % MAX_CPU_UNITS_PER_BOARD; 1063 1064 cp = kmem_zalloc(sizeof (drmach_cpu_t), KM_SLEEP); 1065 bcopy(proto, &cp->dev, sizeof (cp->dev)); 1066 cp->dev.node = drmach_node_dup(proto->node); 1067 cp->dev.cm.isa = (void *)drmach_cpu_new; 1068 cp->dev.cm.dispose = drmach_cpu_dispose; 1069 cp->dev.cm.release = drmach_cpu_release; 1070 cp->dev.cm.status = drmach_cpu_status; 1071 (void) snprintf(cp->dev.cm.name, sizeof (cp->dev.cm.name), "%s%d", 1072 cp->dev.type, cp->dev.unum); 1073 1074 cp->apicid = portid; 1075 if (ACPI_SUCCESS(acpica_get_cpu_id_by_object( 1076 drmach_node_get_dnode(proto->node), &cpuid))) { 1077 cp->cpuid = cpuid; 1078 } else { 1079 cp->cpuid = -1; 1080 } 1081 1082 /* Mark CPU0 as busy, many other components have dependency on it. */ 1083 if (cp->cpuid == 0) { 1084 cp->dev.busy = 1; 1085 } 1086 1087 *idp = (drmachid_t)cp; 1088 1089 return (NULL); 1090 } 1091 1092 static void 1093 drmach_cpu_dispose(drmachid_t id) 1094 { 1095 drmach_cpu_t *self; 1096 1097 ASSERT(DRMACH_IS_CPU_ID(id)); 1098 1099 self = id; 1100 if (self->dev.node) 1101 drmach_node_dispose(self->dev.node); 1102 1103 kmem_free(self, sizeof (*self)); 1104 } 1105 1106 static sbd_error_t * 1107 drmach_cpu_release(drmachid_t id) 1108 { 1109 if (!DRMACH_IS_CPU_ID(id)) 1110 return (drerr_new(0, EX86_INAPPROP, NULL)); 1111 1112 return (NULL); 1113 } 1114 1115 static sbd_error_t * 1116 drmach_cpu_status(drmachid_t id, drmach_status_t *stat) 1117 { 1118 drmach_cpu_t *cp; 1119 drmach_device_t *dp; 1120 1121 ASSERT(DRMACH_IS_CPU_ID(id)); 1122 cp = (drmach_cpu_t *)id; 1123 dp = &cp->dev; 1124 1125 stat->assigned = dp->bp->assigned; 1126 stat->powered = dp->bp->powered; 1127 mutex_enter(&cpu_lock); 1128 stat->configured = (cpu_get(cp->cpuid) != NULL); 1129 mutex_exit(&cpu_lock); 1130 stat->busy = dp->busy; 1131 (void) strlcpy(stat->type, dp->type, sizeof (stat->type)); 1132 stat->info[0] = '\0'; 1133 1134 return (NULL); 1135 } 1136 1137 static int 1138 drmach_setup_mc_info(DRMACH_HANDLE hdl, drmach_mem_t *mp) 1139 { 1140 uint_t i, j, count; 1141 struct memlist *ml = NULL, *ml2 = NULL; 1142 acpidev_regspec_t *regp; 1143 uint64_t align, addr_min, addr_max, total_size, skipped_size; 1144 1145 if (hdl == NULL) { 1146 return (-1); 1147 } else if (ACPI_FAILURE(acpidev_dr_get_mem_alignment(hdl, &align))) { 1148 return (-1); 1149 } else { 1150 ASSERT((align & (align - 1)) == 0); 1151 mp->mem_alignment = align; 1152 } 1153 1154 addr_min = UINT64_MAX; 1155 addr_max = 0; 1156 total_size = 0; 1157 skipped_size = 0; 1158 /* 1159 * There's a memory hole just below 4G on x86, which needs special 1160 * handling. All other addresses assigned to a specific memory device 1161 * should be contiguous. 1162 */ 1163 if (ACPI_FAILURE(acpidev_dr_device_get_regspec(hdl, TRUE, ®p, 1164 &count))) { 1165 return (-1); 1166 } 1167 for (i = 0, j = 0; i < count; i++) { 1168 uint64_t addr, size; 1169 1170 addr = (uint64_t)regp[i].phys_mid << 32; 1171 addr |= (uint64_t)regp[i].phys_low; 1172 size = (uint64_t)regp[i].size_hi << 32; 1173 size |= (uint64_t)regp[i].size_low; 1174 if (size == 0) 1175 continue; 1176 else 1177 j++; 1178 1179 total_size += size; 1180 if (addr < addr_min) 1181 addr_min = addr; 1182 if (addr + size > addr_max) 1183 addr_max = addr + size; 1184 if (mp->dev.bp->boot_board || 1185 j <= acpidev_dr_max_segments_per_mem_device()) { 1186 ml = memlist_add_span(ml, addr, size); 1187 } else { 1188 skipped_size += size; 1189 } 1190 } 1191 acpidev_dr_device_free_regspec(regp, count); 1192 1193 if (skipped_size != 0) { 1194 cmn_err(CE_WARN, "!drmach: too many (%d) segments on memory " 1195 "device, max (%d) segments supported, 0x%" PRIx64 " bytes " 1196 "of memory skipped.", 1197 j, acpidev_dr_max_segments_per_mem_device(), skipped_size); 1198 } 1199 1200 mp->slice_base = addr_min; 1201 mp->slice_top = addr_max; 1202 mp->slice_size = total_size; 1203 1204 if (mp->dev.bp->boot_board) { 1205 uint64_t endpa = _ptob64(physmax + 1); 1206 1207 /* 1208 * we intersect phys_install to get base_pa. 1209 * This only works at boot-up time. 1210 */ 1211 memlist_read_lock(); 1212 ml2 = memlist_dup(phys_install); 1213 memlist_read_unlock(); 1214 1215 ml2 = memlist_del_span(ml2, 0ull, mp->slice_base); 1216 if (ml2 && endpa > addr_max) { 1217 ml2 = memlist_del_span(ml2, addr_max, endpa - addr_max); 1218 } 1219 } 1220 1221 /* 1222 * Create a memlist for the memory board. 1223 * The created memlist only contains configured memory if there's 1224 * configured memory on the board, otherwise it contains all memory 1225 * on the board. 1226 */ 1227 if (ml2) { 1228 uint64_t nbytes = 0; 1229 struct memlist *p; 1230 1231 for (p = ml2; p; p = p->ml_next) { 1232 nbytes += p->ml_size; 1233 } 1234 if (nbytes == 0) { 1235 memlist_delete(ml2); 1236 ml2 = NULL; 1237 } else { 1238 /* Node has configured memory at boot time. */ 1239 mp->base_pa = ml2->ml_address; 1240 mp->nbytes = nbytes; 1241 mp->memlist = ml2; 1242 if (ml) 1243 memlist_delete(ml); 1244 } 1245 } 1246 if (ml2 == NULL) { 1247 /* Not configured at boot time. */ 1248 mp->base_pa = UINT64_MAX; 1249 mp->nbytes = 0; 1250 mp->memlist = ml; 1251 } 1252 1253 return (0); 1254 } 1255 1256 sbd_error_t * 1257 drmach_mem_new(drmach_device_t *proto, drmachid_t *idp) 1258 { 1259 DRMACH_HANDLE hdl; 1260 drmach_mem_t *mp; 1261 int portid; 1262 1263 mp = kmem_zalloc(sizeof (drmach_mem_t), KM_SLEEP); 1264 portid = proto->portid; 1265 ASSERT(portid != -1); 1266 proto->unum = portid; 1267 1268 bcopy(proto, &mp->dev, sizeof (mp->dev)); 1269 mp->dev.node = drmach_node_dup(proto->node); 1270 mp->dev.cm.isa = (void *)drmach_mem_new; 1271 mp->dev.cm.dispose = drmach_mem_dispose; 1272 mp->dev.cm.release = drmach_mem_release; 1273 mp->dev.cm.status = drmach_mem_status; 1274 1275 (void) snprintf(mp->dev.cm.name, sizeof (mp->dev.cm.name), "%s%d", 1276 mp->dev.type, proto->unum); 1277 hdl = mp->dev.node->get_dnode(mp->dev.node); 1278 ASSERT(hdl != NULL); 1279 if (drmach_setup_mc_info(hdl, mp) != 0) { 1280 kmem_free(mp, sizeof (drmach_mem_t)); 1281 *idp = (drmachid_t)NULL; 1282 return (drerr_new(1, EX86_MC_SETUP, NULL)); 1283 } 1284 1285 /* make sure we do not create memoryless nodes */ 1286 if (mp->nbytes == 0 && mp->slice_size == 0) { 1287 kmem_free(mp, sizeof (drmach_mem_t)); 1288 *idp = (drmachid_t)NULL; 1289 } else 1290 *idp = (drmachid_t)mp; 1291 1292 return (NULL); 1293 } 1294 1295 static void 1296 drmach_mem_dispose(drmachid_t id) 1297 { 1298 drmach_mem_t *mp; 1299 1300 ASSERT(DRMACH_IS_MEM_ID(id)); 1301 1302 mp = id; 1303 1304 if (mp->dev.node) 1305 drmach_node_dispose(mp->dev.node); 1306 1307 if (mp->memlist) { 1308 memlist_delete(mp->memlist); 1309 mp->memlist = NULL; 1310 } 1311 1312 kmem_free(mp, sizeof (*mp)); 1313 } 1314 1315 static sbd_error_t * 1316 drmach_mem_release(drmachid_t id) 1317 { 1318 if (!DRMACH_IS_MEM_ID(id)) 1319 return (drerr_new(0, EX86_INAPPROP, NULL)); 1320 1321 return (NULL); 1322 } 1323 1324 static sbd_error_t * 1325 drmach_mem_status(drmachid_t id, drmach_status_t *stat) 1326 { 1327 uint64_t pa; 1328 drmach_mem_t *dp; 1329 struct memlist *ml = NULL; 1330 1331 ASSERT(DRMACH_IS_MEM_ID(id)); 1332 dp = id; 1333 1334 /* get starting physical address of target memory */ 1335 pa = dp->base_pa; 1336 /* round down to slice boundary */ 1337 pa &= ~(dp->mem_alignment - 1); 1338 1339 /* stop at first span that is in slice */ 1340 memlist_read_lock(); 1341 for (ml = phys_install; ml; ml = ml->ml_next) 1342 if (ml->ml_address >= pa && ml->ml_address < dp->slice_top) 1343 break; 1344 memlist_read_unlock(); 1345 1346 stat->assigned = dp->dev.bp->assigned; 1347 stat->powered = dp->dev.bp->powered; 1348 stat->configured = (ml != NULL); 1349 stat->busy = dp->dev.busy; 1350 (void) strlcpy(stat->type, dp->dev.type, sizeof (stat->type)); 1351 stat->info[0] = '\0'; 1352 1353 return (NULL); 1354 } 1355 1356 /* 1357 * Public interfaces exported to support platform independent dr driver. 1358 */ 1359 uint_t 1360 drmach_max_boards(void) 1361 { 1362 return (acpidev_dr_max_boards()); 1363 } 1364 1365 uint_t 1366 drmach_max_io_units_per_board(void) 1367 { 1368 return (acpidev_dr_max_io_units_per_board()); 1369 } 1370 1371 uint_t 1372 drmach_max_cmp_units_per_board(void) 1373 { 1374 return (acpidev_dr_max_cmp_units_per_board()); 1375 } 1376 1377 uint_t 1378 drmach_max_mem_units_per_board(void) 1379 { 1380 return (acpidev_dr_max_mem_units_per_board()); 1381 } 1382 1383 uint_t 1384 drmach_max_core_per_cmp(void) 1385 { 1386 return (acpidev_dr_max_cpu_units_per_cmp()); 1387 } 1388 1389 sbd_error_t * 1390 drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts, void *argp) 1391 { 1392 drmach_board_t *bp = (drmach_board_t *)id; 1393 sbd_error_t *err = NULL; 1394 1395 /* allow status and ncm operations to always succeed */ 1396 if ((cmd == SBD_CMD_STATUS) || (cmd == SBD_CMD_GETNCM)) { 1397 return (NULL); 1398 } 1399 1400 switch (cmd) { 1401 case SBD_CMD_POWERON: 1402 case SBD_CMD_POWEROFF: 1403 /* 1404 * Disable fast reboot if CPU/MEM/IOH hotplug event happens. 1405 * Note: this is a temporary solution and will be revised when 1406 * fast reboot can support CPU/MEM/IOH DR operations in future. 1407 * 1408 * ACPI BIOS generates some static ACPI tables, such as MADT, 1409 * SRAT and SLIT, to describe system hardware configuration on 1410 * power-on. When CPU/MEM/IOH hotplug event happens, those 1411 * static tables won't be updated and will become stale. 1412 * 1413 * If we reset system by fast reboot, BIOS will have no chance 1414 * to regenerate those staled static tables. Fast reboot can't 1415 * tolerate such inconsistency between staled ACPI tables and 1416 * real hardware configuration yet. 1417 * 1418 * A temporary solution is introduced to disable fast reboot if 1419 * CPU/MEM/IOH hotplug event happens. This solution should be 1420 * revised when fast reboot is enhanced to support CPU/MEM/IOH 1421 * DR operations. 1422 */ 1423 fastreboot_disable(FBNS_HOTPLUG); 1424 /*FALLTHROUGH*/ 1425 1426 default: 1427 /* Block out the CPR thread. */ 1428 rw_enter(&drmach_cpr_rwlock, RW_READER); 1429 break; 1430 } 1431 1432 /* check all other commands for the required option string */ 1433 if ((opts->size > 0) && (opts->copts != NULL)) { 1434 if (strstr(opts->copts, ACPIDEV_CMD_OST_PREFIX) == NULL) { 1435 err = drerr_new(1, EX86_SUPPORT, NULL); 1436 } 1437 } else { 1438 err = drerr_new(1, EX86_SUPPORT, NULL); 1439 } 1440 1441 if (!err && id && DRMACH_IS_BOARD_ID(id)) { 1442 switch (cmd) { 1443 case SBD_CMD_TEST: 1444 break; 1445 case SBD_CMD_CONNECT: 1446 if (bp->connected) 1447 err = drerr_new(0, ESBD_STATE, NULL); 1448 else if (!drmach_domain.allow_dr) 1449 err = drerr_new(1, EX86_SUPPORT, NULL); 1450 break; 1451 case SBD_CMD_DISCONNECT: 1452 if (!bp->connected) 1453 err = drerr_new(0, ESBD_STATE, NULL); 1454 else if (!drmach_domain.allow_dr) 1455 err = drerr_new(1, EX86_SUPPORT, NULL); 1456 break; 1457 default: 1458 if (!drmach_domain.allow_dr) 1459 err = drerr_new(1, EX86_SUPPORT, NULL); 1460 break; 1461 1462 } 1463 } 1464 1465 /* 1466 * CPU/memory/IO DR operations will be supported in stages on x86. 1467 * With early versions, some operations should be blocked here. 1468 * This temporary hook will be removed when all CPU/memory/IO DR 1469 * operations are supported on x86 systems. 1470 * 1471 * We only need to filter unsupported device types for 1472 * SBD_CMD_CONFIGURE/SBD_CMD_UNCONFIGURE commands, all other 1473 * commands are supported by all device types. 1474 */ 1475 if (!err && (cmd == SBD_CMD_CONFIGURE || cmd == SBD_CMD_UNCONFIGURE)) { 1476 int i; 1477 dr_devset_t *devsetp = (dr_devset_t *)argp; 1478 dr_devset_t devset = *devsetp; 1479 1480 switch (cmd) { 1481 case SBD_CMD_CONFIGURE: 1482 if (!plat_dr_support_cpu()) { 1483 DEVSET_DEL(devset, SBD_COMP_CPU, 1484 DEVSET_ANYUNIT); 1485 } else { 1486 for (i = MAX_CPU_UNITS_PER_BOARD; 1487 i < DEVSET_CPU_NUMBER; i++) { 1488 DEVSET_DEL(devset, SBD_COMP_CPU, i); 1489 } 1490 } 1491 1492 if (!plat_dr_support_memory()) { 1493 DEVSET_DEL(devset, SBD_COMP_MEM, 1494 DEVSET_ANYUNIT); 1495 } else { 1496 for (i = MAX_MEM_UNITS_PER_BOARD; 1497 i < DEVSET_MEM_NUMBER; i++) { 1498 DEVSET_DEL(devset, SBD_COMP_MEM, i); 1499 } 1500 } 1501 1502 /* No support of configuring IOH devices yet. */ 1503 DEVSET_DEL(devset, SBD_COMP_IO, DEVSET_ANYUNIT); 1504 break; 1505 1506 case SBD_CMD_UNCONFIGURE: 1507 if (!plat_dr_support_cpu()) { 1508 DEVSET_DEL(devset, SBD_COMP_CPU, 1509 DEVSET_ANYUNIT); 1510 } else { 1511 for (i = MAX_CPU_UNITS_PER_BOARD; 1512 i < DEVSET_CPU_NUMBER; i++) { 1513 DEVSET_DEL(devset, SBD_COMP_CPU, i); 1514 } 1515 } 1516 1517 /* No support of unconfiguring MEM/IOH devices yet. */ 1518 DEVSET_DEL(devset, SBD_COMP_MEM, DEVSET_ANYUNIT); 1519 DEVSET_DEL(devset, SBD_COMP_IO, DEVSET_ANYUNIT); 1520 break; 1521 } 1522 1523 *devsetp = devset; 1524 if (DEVSET_IS_NULL(devset)) { 1525 err = drerr_new(1, EX86_SUPPORT, NULL); 1526 } 1527 } 1528 1529 return (err); 1530 } 1531 1532 sbd_error_t * 1533 drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts, int rv) 1534 { 1535 _NOTE(ARGUNUSED(id, opts, rv)); 1536 1537 switch (cmd) { 1538 case SBD_CMD_STATUS: 1539 case SBD_CMD_GETNCM: 1540 break; 1541 1542 default: 1543 rw_exit(&drmach_cpr_rwlock); 1544 break; 1545 } 1546 1547 return (NULL); 1548 } 1549 1550 sbd_error_t * 1551 drmach_configure(drmachid_t id, int flags) 1552 { 1553 _NOTE(ARGUNUSED(flags)); 1554 1555 drmach_device_t *dp; 1556 sbd_error_t *err = NULL; 1557 dev_info_t *rdip; 1558 dev_info_t *fdip = NULL; 1559 1560 if (!DRMACH_IS_DEVICE_ID(id)) 1561 return (drerr_new(0, EX86_INAPPROP, NULL)); 1562 dp = id; 1563 1564 rdip = dp->node->getdip(dp->node); 1565 ASSERT(rdip); 1566 ASSERT(e_ddi_branch_held(rdip)); 1567 1568 /* allocate cpu id for the CPU device. */ 1569 if (DRMACH_IS_CPU_ID(id)) { 1570 DRMACH_HANDLE hdl = drmach_node_get_dnode(dp->node); 1571 ASSERT(hdl != NULL); 1572 if (ACPI_FAILURE(acpidev_dr_allocate_cpuid(hdl, NULL))) { 1573 err = drerr_new(1, EX86_ALLOC_CPUID, NULL); 1574 } 1575 return (err); 1576 } 1577 1578 if (DRMACH_IS_MEM_ID(id)) { 1579 err = drmach_mem_update_lgrp(id); 1580 if (err) 1581 return (err); 1582 } 1583 1584 if (e_ddi_branch_configure(rdip, &fdip, 0) != 0) { 1585 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 1586 dev_info_t *dip = (fdip != NULL) ? fdip : rdip; 1587 1588 (void) ddi_pathname(dip, path); 1589 err = drerr_new(1, EX86_DRVFAIL, path); 1590 kmem_free(path, MAXPATHLEN); 1591 1592 /* If non-NULL, fdip is returned held and must be released */ 1593 if (fdip != NULL) 1594 ddi_release_devi(fdip); 1595 } 1596 1597 return (err); 1598 } 1599 1600 sbd_error_t * 1601 drmach_unconfigure(drmachid_t id, int flags) 1602 { 1603 _NOTE(ARGUNUSED(flags)); 1604 1605 drmach_device_t *dp; 1606 sbd_error_t *err = NULL; 1607 dev_info_t *rdip, *fdip = NULL; 1608 1609 if (!DRMACH_IS_DEVICE_ID(id)) 1610 return (drerr_new(0, EX86_INAPPROP, NULL)); 1611 dp = id; 1612 1613 rdip = dp->node->getdip(dp->node); 1614 ASSERT(rdip); 1615 ASSERT(e_ddi_branch_held(rdip)); 1616 1617 if (DRMACH_IS_CPU_ID(id)) { 1618 DRMACH_HANDLE hdl = drmach_node_get_dnode(dp->node); 1619 ASSERT(hdl != NULL); 1620 if (ACPI_FAILURE(acpidev_dr_free_cpuid(hdl))) { 1621 err = drerr_new(1, EX86_FREE_CPUID, NULL); 1622 } 1623 return (err); 1624 } 1625 1626 /* 1627 * Note: FORCE flag is no longer necessary under devfs 1628 */ 1629 if (e_ddi_branch_unconfigure(rdip, &fdip, 0)) { 1630 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 1631 1632 /* 1633 * If non-NULL, fdip is returned held and must be released. 1634 */ 1635 if (fdip != NULL) { 1636 (void) ddi_pathname(fdip, path); 1637 ndi_rele_devi(fdip); 1638 } else { 1639 (void) ddi_pathname(rdip, path); 1640 } 1641 1642 err = drerr_new(1, EX86_DRVFAIL, path); 1643 1644 kmem_free(path, MAXPATHLEN); 1645 } 1646 1647 return (err); 1648 } 1649 1650 sbd_error_t * 1651 drmach_get_dip(drmachid_t id, dev_info_t **dip) 1652 { 1653 drmach_device_t *dp; 1654 1655 if (!DRMACH_IS_DEVICE_ID(id)) 1656 return (drerr_new(0, EX86_INAPPROP, NULL)); 1657 dp = id; 1658 1659 *dip = dp->node->getdip(dp->node); 1660 1661 return (NULL); 1662 } 1663 1664 sbd_error_t * 1665 drmach_release(drmachid_t id) 1666 { 1667 drmach_common_t *cp; 1668 1669 if (!DRMACH_IS_DEVICE_ID(id)) 1670 return (drerr_new(0, EX86_INAPPROP, NULL)); 1671 cp = id; 1672 1673 return (cp->release(id)); 1674 } 1675 1676 sbd_error_t * 1677 drmach_status(drmachid_t id, drmach_status_t *stat) 1678 { 1679 drmach_common_t *cp; 1680 sbd_error_t *err; 1681 1682 rw_enter(&drmach_boards_rwlock, RW_READER); 1683 if (!DRMACH_IS_ID(id)) { 1684 rw_exit(&drmach_boards_rwlock); 1685 return (drerr_new(0, EX86_NOTID, NULL)); 1686 } 1687 cp = (drmach_common_t *)id; 1688 err = cp->status(id, stat); 1689 rw_exit(&drmach_boards_rwlock); 1690 1691 return (err); 1692 } 1693 1694 static sbd_error_t * 1695 drmach_update_acpi_status(drmachid_t id, drmach_opts_t *opts) 1696 { 1697 char *copts; 1698 drmach_board_t *bp; 1699 DRMACH_HANDLE hdl; 1700 int event, code; 1701 boolean_t inprogress = B_FALSE; 1702 1703 if (DRMACH_NULL_ID(id) || !DRMACH_IS_BOARD_ID(id)) 1704 return (drerr_new(0, EX86_INAPPROP, NULL)); 1705 bp = (drmach_board_t *)id; 1706 hdl = drmach_node_get_dnode(bp->tree); 1707 ASSERT(hdl != NULL); 1708 if (hdl == NULL) 1709 return (drerr_new(0, EX86_INAPPROP, NULL)); 1710 1711 /* Get the status code. */ 1712 copts = opts->copts; 1713 if (strncmp(copts, ACPIDEV_CMD_OST_INPROGRESS, 1714 strlen(ACPIDEV_CMD_OST_INPROGRESS)) == 0) { 1715 inprogress = B_TRUE; 1716 code = ACPI_OST_STA_INSERT_IN_PROGRESS; 1717 copts += strlen(ACPIDEV_CMD_OST_INPROGRESS); 1718 } else if (strncmp(copts, ACPIDEV_CMD_OST_SUCCESS, 1719 strlen(ACPIDEV_CMD_OST_SUCCESS)) == 0) { 1720 code = ACPI_OST_STA_SUCCESS; 1721 copts += strlen(ACPIDEV_CMD_OST_SUCCESS); 1722 } else if (strncmp(copts, ACPIDEV_CMD_OST_FAILURE, 1723 strlen(ACPIDEV_CMD_OST_FAILURE)) == 0) { 1724 code = ACPI_OST_STA_FAILURE; 1725 copts += strlen(ACPIDEV_CMD_OST_FAILURE); 1726 } else if (strncmp(copts, ACPIDEV_CMD_OST_NOOP, 1727 strlen(ACPIDEV_CMD_OST_NOOP)) == 0) { 1728 return (NULL); 1729 } else { 1730 return (drerr_new(0, EX86_UNKPTCMD, opts->copts)); 1731 } 1732 1733 /* Get the event type. */ 1734 copts = strstr(copts, ACPIDEV_EVENT_TYPE_ATTR_NAME); 1735 if (copts == NULL) { 1736 return (drerr_new(0, EX86_UNKPTCMD, opts->copts)); 1737 } 1738 copts += strlen(ACPIDEV_EVENT_TYPE_ATTR_NAME); 1739 if (copts[0] != '=') { 1740 return (drerr_new(0, EX86_UNKPTCMD, opts->copts)); 1741 } 1742 copts += strlen("="); 1743 if (strncmp(copts, ACPIDEV_EVENT_TYPE_BUS_CHECK, 1744 strlen(ACPIDEV_EVENT_TYPE_BUS_CHECK)) == 0) { 1745 event = ACPI_NOTIFY_BUS_CHECK; 1746 } else if (strncmp(copts, ACPIDEV_EVENT_TYPE_DEVICE_CHECK, 1747 strlen(ACPIDEV_EVENT_TYPE_DEVICE_CHECK)) == 0) { 1748 event = ACPI_NOTIFY_DEVICE_CHECK; 1749 } else if (strncmp(copts, ACPIDEV_EVENT_TYPE_DEVICE_CHECK_LIGHT, 1750 strlen(ACPIDEV_EVENT_TYPE_DEVICE_CHECK_LIGHT)) == 0) { 1751 event = ACPI_NOTIFY_DEVICE_CHECK_LIGHT; 1752 } else if (strncmp(copts, ACPIDEV_EVENT_TYPE_EJECT_REQUEST, 1753 strlen(ACPIDEV_EVENT_TYPE_EJECT_REQUEST)) == 0) { 1754 event = ACPI_NOTIFY_EJECT_REQUEST; 1755 if (inprogress) { 1756 code = ACPI_OST_STA_EJECT_IN_PROGRESS; 1757 } 1758 } else { 1759 return (drerr_new(0, EX86_UNKPTCMD, opts->copts)); 1760 } 1761 1762 (void) acpidev_eval_ost(hdl, event, code, NULL, 0); 1763 1764 return (NULL); 1765 } 1766 1767 static struct { 1768 const char *name; 1769 sbd_error_t *(*handler)(drmachid_t id, drmach_opts_t *opts); 1770 } drmach_pt_arr[] = { 1771 { ACPIDEV_CMD_OST_PREFIX, &drmach_update_acpi_status }, 1772 /* the following line must always be last */ 1773 { NULL, NULL } 1774 }; 1775 1776 sbd_error_t * 1777 drmach_passthru(drmachid_t id, drmach_opts_t *opts) 1778 { 1779 int i; 1780 sbd_error_t *err; 1781 1782 i = 0; 1783 while (drmach_pt_arr[i].name != NULL) { 1784 int len = strlen(drmach_pt_arr[i].name); 1785 1786 if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0) 1787 break; 1788 1789 i += 1; 1790 } 1791 1792 if (drmach_pt_arr[i].name == NULL) 1793 err = drerr_new(0, EX86_UNKPTCMD, opts->copts); 1794 else 1795 err = (*drmach_pt_arr[i].handler)(id, opts); 1796 1797 return (err); 1798 } 1799 1800 /* 1801 * Board specific interfaces to support dr driver 1802 */ 1803 static int 1804 drmach_get_portid(drmach_node_t *np) 1805 { 1806 uint32_t portid; 1807 1808 if (np->getprop(np, ACPIDEV_DR_PROP_PORTID, 1809 &portid, sizeof (portid)) == 0) { 1810 /* 1811 * acpidev returns portid as uint32_t, validates it. 1812 */ 1813 if (portid > INT_MAX) { 1814 return (-1); 1815 } else { 1816 return (portid); 1817 } 1818 } 1819 1820 return (-1); 1821 } 1822 1823 /* 1824 * This is a helper function to determine if a given 1825 * node should be considered for a dr operation according 1826 * to predefined dr type nodes and the node's name. 1827 * Formal Parameter : The name of a device node. 1828 * Return Value: -1, name does not map to a valid dr type. 1829 * A value greater or equal to 0, name is a valid dr type. 1830 */ 1831 static int 1832 drmach_name2type_idx(char *name) 1833 { 1834 int index, ntypes; 1835 1836 if (name == NULL) 1837 return (-1); 1838 1839 /* 1840 * Determine how many possible types are currently supported 1841 * for dr. 1842 */ 1843 ntypes = sizeof (drmach_name2type) / sizeof (drmach_name2type[0]); 1844 1845 /* Determine if the node's name correspond to a predefined type. */ 1846 for (index = 0; index < ntypes; index++) { 1847 if (strcmp(drmach_name2type[index].name, name) == 0) 1848 /* The node is an allowed type for dr. */ 1849 return (index); 1850 } 1851 1852 /* 1853 * If the name of the node does not map to any of the 1854 * types in the array drmach_name2type then the node is not of 1855 * interest to dr. 1856 */ 1857 return (-1); 1858 } 1859 1860 static int 1861 drmach_board_find_devices_cb(drmach_node_walk_args_t *args) 1862 { 1863 drmach_node_t *node = args->node; 1864 drmach_board_cb_data_t *data = args->data; 1865 drmach_board_t *obj = data->obj; 1866 1867 int rv, portid; 1868 uint32_t bnum; 1869 drmachid_t id; 1870 drmach_device_t *device; 1871 char name[OBP_MAXDRVNAME]; 1872 1873 portid = drmach_get_portid(node); 1874 rv = node->getprop(node, ACPIDEV_DR_PROP_DEVNAME, 1875 name, OBP_MAXDRVNAME); 1876 if (rv) 1877 return (0); 1878 1879 rv = node->getprop(node, ACPIDEV_DR_PROP_BOARDNUM, 1880 &bnum, sizeof (bnum)); 1881 if (rv) { 1882 return (0); 1883 } 1884 if (bnum > INT_MAX) { 1885 return (0); 1886 } 1887 1888 if (bnum != obj->bnum) 1889 return (0); 1890 1891 if (drmach_name2type_idx(name) < 0) { 1892 return (0); 1893 } 1894 1895 /* 1896 * Create a device data structure from this node data. 1897 * The call may yield nothing if the node is not of interest 1898 * to drmach. 1899 */ 1900 data->err = drmach_device_new(node, obj, portid, &id); 1901 if (data->err) 1902 return (-1); 1903 else if (!id) { 1904 /* 1905 * drmach_device_new examined the node we passed in 1906 * and determined that it was one not of interest to 1907 * drmach. So, it is skipped. 1908 */ 1909 return (0); 1910 } 1911 1912 rv = drmach_array_set(obj->devices, data->ndevs++, id); 1913 if (rv) { 1914 data->err = DRMACH_INTERNAL_ERROR(); 1915 return (-1); 1916 } 1917 device = id; 1918 1919 data->err = (*data->found)(data->a, device->type, device->unum, id); 1920 1921 return (data->err == NULL ? 0 : -1); 1922 } 1923 1924 sbd_error_t * 1925 drmach_board_find_devices(drmachid_t id, void *a, 1926 sbd_error_t *(*found)(void *a, const char *, int, drmachid_t)) 1927 { 1928 drmach_board_t *bp = (drmach_board_t *)id; 1929 sbd_error_t *err; 1930 int max_devices; 1931 int rv; 1932 drmach_board_cb_data_t data; 1933 1934 if (!DRMACH_IS_BOARD_ID(id)) 1935 return (drerr_new(0, EX86_INAPPROP, NULL)); 1936 1937 max_devices = MAX_CPU_UNITS_PER_BOARD; 1938 max_devices += MAX_MEM_UNITS_PER_BOARD; 1939 max_devices += MAX_IO_UNITS_PER_BOARD; 1940 1941 if (bp->devices == NULL) 1942 bp->devices = drmach_array_new(0, max_devices); 1943 ASSERT(bp->tree != NULL); 1944 1945 data.obj = bp; 1946 data.ndevs = 0; 1947 data.found = found; 1948 data.a = a; 1949 data.err = NULL; 1950 1951 acpidev_dr_lock_all(); 1952 rv = drmach_node_walk(bp->tree, &data, drmach_board_find_devices_cb); 1953 acpidev_dr_unlock_all(); 1954 if (rv == 0) { 1955 err = NULL; 1956 } else { 1957 drmach_array_dispose(bp->devices, drmach_device_dispose); 1958 bp->devices = NULL; 1959 1960 if (data.err) 1961 err = data.err; 1962 else 1963 err = DRMACH_INTERNAL_ERROR(); 1964 } 1965 1966 return (err); 1967 } 1968 1969 int 1970 drmach_board_lookup(int bnum, drmachid_t *id) 1971 { 1972 int rv = 0; 1973 1974 if (bnum < 0) { 1975 *id = 0; 1976 return (-1); 1977 } 1978 1979 rw_enter(&drmach_boards_rwlock, RW_READER); 1980 if (drmach_array_get(drmach_boards, (uint_t)bnum, id)) { 1981 *id = 0; 1982 rv = -1; 1983 } 1984 rw_exit(&drmach_boards_rwlock); 1985 1986 return (rv); 1987 } 1988 1989 sbd_error_t * 1990 drmach_board_name(int bnum, char *buf, int buflen) 1991 { 1992 ACPI_HANDLE hdl; 1993 sbd_error_t *err = NULL; 1994 1995 if (bnum < 0) { 1996 return (drerr_new(1, EX86_BNUM, "%d", bnum)); 1997 } 1998 1999 acpidev_dr_lock_all(); 2000 if (ACPI_FAILURE(acpidev_dr_get_board_handle(bnum, &hdl))) { 2001 DRMACH_PR("!drmach_board_name: failed to lookup ACPI handle " 2002 "for board %d.", bnum); 2003 err = drerr_new(1, EX86_BNUM, "%d", bnum); 2004 } else if (ACPI_FAILURE(acpidev_dr_get_board_name(hdl, buf, buflen))) { 2005 DRMACH_PR("!drmach_board_name: failed to generate board name " 2006 "for board %d.", bnum); 2007 err = drerr_new(0, EX86_INVALID_ARG, 2008 ": buffer is too small for board name."); 2009 } 2010 acpidev_dr_unlock_all(); 2011 2012 return (err); 2013 } 2014 2015 int 2016 drmach_board_is_floating(drmachid_t id) 2017 { 2018 drmach_board_t *bp; 2019 2020 if (!DRMACH_IS_BOARD_ID(id)) 2021 return (0); 2022 2023 bp = (drmach_board_t *)id; 2024 2025 return ((drmach_domain.floating & (1ULL << bp->bnum)) ? 1 : 0); 2026 } 2027 2028 static ACPI_STATUS 2029 drmach_board_check_dependent_cb(ACPI_HANDLE hdl, UINT32 lvl, void *ctx, 2030 void **retval) 2031 { 2032 uint32_t bdnum; 2033 drmach_board_t *bp; 2034 ACPI_STATUS rc = AE_OK; 2035 int cmd = (int)(intptr_t)ctx; 2036 2037 ASSERT(hdl != NULL); 2038 ASSERT(lvl == UINT32_MAX); 2039 ASSERT(retval != NULL); 2040 2041 /* Skip non-board devices. */ 2042 if (!acpidev_dr_device_is_board(hdl)) { 2043 return (AE_OK); 2044 } else if (ACPI_FAILURE(acpidev_dr_get_board_number(hdl, &bdnum))) { 2045 DRMACH_PR("!drmach_board_check_dependent_cb: failed to get " 2046 "board number for object %p.\n", hdl); 2047 return (AE_ERROR); 2048 } else if (bdnum > MAX_BOARDS) { 2049 DRMACH_PR("!drmach_board_check_dependent_cb: board number %u " 2050 "is too big, max %u.", bdnum, MAX_BOARDS); 2051 return (AE_ERROR); 2052 } 2053 2054 bp = drmach_get_board_by_bnum(bdnum); 2055 switch (cmd) { 2056 case SBD_CMD_CONNECT: 2057 /* 2058 * Its parent board should be present, assigned, powered and 2059 * connected when connecting the child board. 2060 */ 2061 if (bp == NULL) { 2062 *retval = hdl; 2063 rc = AE_ERROR; 2064 } else { 2065 bp->powered = acpidev_dr_device_is_powered(hdl); 2066 if (!bp->connected || !bp->powered || !bp->assigned) { 2067 *retval = hdl; 2068 rc = AE_ERROR; 2069 } 2070 } 2071 break; 2072 2073 case SBD_CMD_POWERON: 2074 /* 2075 * Its parent board should be present, assigned and powered when 2076 * powering on the child board. 2077 */ 2078 if (bp == NULL) { 2079 *retval = hdl; 2080 rc = AE_ERROR; 2081 } else { 2082 bp->powered = acpidev_dr_device_is_powered(hdl); 2083 if (!bp->powered || !bp->assigned) { 2084 *retval = hdl; 2085 rc = AE_ERROR; 2086 } 2087 } 2088 break; 2089 2090 case SBD_CMD_ASSIGN: 2091 /* 2092 * Its parent board should be present and assigned when 2093 * assigning the child board. 2094 */ 2095 if (bp == NULL) { 2096 *retval = hdl; 2097 rc = AE_ERROR; 2098 } else if (!bp->assigned) { 2099 *retval = hdl; 2100 rc = AE_ERROR; 2101 } 2102 break; 2103 2104 case SBD_CMD_DISCONNECT: 2105 /* 2106 * The child board should be disconnected if present when 2107 * disconnecting its parent board. 2108 */ 2109 if (bp != NULL && bp->connected) { 2110 *retval = hdl; 2111 rc = AE_ERROR; 2112 } 2113 break; 2114 2115 case SBD_CMD_POWEROFF: 2116 /* 2117 * The child board should be disconnected and powered off if 2118 * present when powering off its parent board. 2119 */ 2120 if (bp != NULL) { 2121 bp->powered = acpidev_dr_device_is_powered(hdl); 2122 if (bp->connected || bp->powered) { 2123 *retval = hdl; 2124 rc = AE_ERROR; 2125 } 2126 } 2127 break; 2128 2129 case SBD_CMD_UNASSIGN: 2130 /* 2131 * The child board should be disconnected, powered off and 2132 * unassigned if present when unassigning its parent board. 2133 */ 2134 if (bp != NULL) { 2135 bp->powered = acpidev_dr_device_is_powered(hdl); 2136 if (bp->connected || bp->powered || bp->assigned) { 2137 *retval = hdl; 2138 rc = AE_ERROR; 2139 } 2140 } 2141 break; 2142 2143 default: 2144 /* Return success for all other commands. */ 2145 break; 2146 } 2147 2148 return (rc); 2149 } 2150 2151 sbd_error_t * 2152 drmach_board_check_dependent(int cmd, drmach_board_t *bp) 2153 { 2154 int reverse; 2155 char *name; 2156 sbd_error_t *err = NULL; 2157 DRMACH_HANDLE hdl; 2158 DRMACH_HANDLE dp = NULL; 2159 2160 ASSERT(bp != NULL); 2161 ASSERT(DRMACH_IS_BOARD_ID(bp)); 2162 ASSERT(RW_LOCK_HELD(&drmach_boards_rwlock)); 2163 2164 hdl = drmach_node_get_dnode(bp->tree); 2165 if (hdl == NULL) 2166 return (drerr_new(0, EX86_INAPPROP, NULL)); 2167 2168 switch (cmd) { 2169 case SBD_CMD_ASSIGN: 2170 case SBD_CMD_POWERON: 2171 case SBD_CMD_CONNECT: 2172 if (ACPI_SUCCESS(acpidev_dr_device_walk_ejd(hdl, 2173 &drmach_board_check_dependent_cb, 2174 (void *)(intptr_t)cmd, &dp))) { 2175 return (NULL); 2176 } 2177 reverse = 0; 2178 break; 2179 2180 case SBD_CMD_UNASSIGN: 2181 case SBD_CMD_POWEROFF: 2182 case SBD_CMD_DISCONNECT: 2183 if (ACPI_SUCCESS(acpidev_dr_device_walk_edl(hdl, 2184 &drmach_board_check_dependent_cb, 2185 (void *)(intptr_t)cmd, &dp))) { 2186 return (NULL); 2187 } 2188 reverse = 1; 2189 break; 2190 2191 default: 2192 return (drerr_new(0, EX86_INAPPROP, NULL)); 2193 } 2194 2195 if (dp == NULL) { 2196 return (drerr_new(1, EX86_WALK_DEPENDENCY, "%s", bp->cm.name)); 2197 } 2198 name = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 2199 if (ACPI_FAILURE(acpidev_dr_get_board_name(dp, name, MAXPATHLEN))) { 2200 err = drerr_new(1, EX86_WALK_DEPENDENCY, "%s", bp->cm.name); 2201 } else if (reverse == 0) { 2202 err = drerr_new(1, EX86_WALK_DEPENDENCY, 2203 "%s, depends on board %s", bp->cm.name, name); 2204 } else { 2205 err = drerr_new(1, EX86_WALK_DEPENDENCY, 2206 "board %s depends on %s", name, bp->cm.name); 2207 } 2208 kmem_free(name, MAXPATHLEN); 2209 2210 return (err); 2211 } 2212 2213 sbd_error_t * 2214 drmach_board_assign(int bnum, drmachid_t *id) 2215 { 2216 sbd_error_t *err = NULL; 2217 2218 if (bnum < 0) { 2219 return (drerr_new(1, EX86_BNUM, "%d", bnum)); 2220 } 2221 2222 rw_enter(&drmach_boards_rwlock, RW_WRITER); 2223 2224 if (drmach_array_get(drmach_boards, bnum, id) == -1) { 2225 err = drerr_new(1, EX86_BNUM, "%d", bnum); 2226 } else { 2227 drmach_board_t *bp; 2228 2229 /* 2230 * Board has already been created, downgrade to reader. 2231 */ 2232 if (*id) 2233 rw_downgrade(&drmach_boards_rwlock); 2234 2235 bp = *id; 2236 if (!(*id)) 2237 bp = *id = 2238 (drmachid_t)drmach_board_new(bnum, 0); 2239 2240 if (bp == NULL) { 2241 DRMACH_PR("!drmach_board_assign: failed to create " 2242 "object for board %d.", bnum); 2243 err = drerr_new(1, EX86_BNUM, "%d", bnum); 2244 } else { 2245 err = drmach_board_check_dependent(SBD_CMD_ASSIGN, bp); 2246 if (err == NULL) 2247 bp->assigned = 1; 2248 } 2249 } 2250 2251 rw_exit(&drmach_boards_rwlock); 2252 2253 return (err); 2254 } 2255 2256 sbd_error_t * 2257 drmach_board_unassign(drmachid_t id) 2258 { 2259 drmach_board_t *bp; 2260 sbd_error_t *err; 2261 drmach_status_t stat; 2262 2263 if (DRMACH_NULL_ID(id)) 2264 return (NULL); 2265 2266 if (!DRMACH_IS_BOARD_ID(id)) { 2267 return (drerr_new(0, EX86_INAPPROP, NULL)); 2268 } 2269 bp = id; 2270 2271 rw_enter(&drmach_boards_rwlock, RW_WRITER); 2272 2273 err = drmach_board_status(id, &stat); 2274 if (err) { 2275 rw_exit(&drmach_boards_rwlock); 2276 return (err); 2277 } 2278 2279 if (stat.configured || stat.busy) { 2280 err = drerr_new(0, EX86_CONFIGBUSY, bp->cm.name); 2281 } else if (bp->connected) { 2282 err = drerr_new(0, EX86_CONNECTBUSY, bp->cm.name); 2283 } else if (stat.powered) { 2284 err = drerr_new(0, EX86_POWERBUSY, bp->cm.name); 2285 } else { 2286 err = drmach_board_check_dependent(SBD_CMD_UNASSIGN, bp); 2287 if (err == NULL) { 2288 if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0) 2289 err = DRMACH_INTERNAL_ERROR(); 2290 else 2291 drmach_board_dispose(bp); 2292 } 2293 } 2294 2295 rw_exit(&drmach_boards_rwlock); 2296 2297 return (err); 2298 } 2299 2300 sbd_error_t * 2301 drmach_board_poweron(drmachid_t id) 2302 { 2303 drmach_board_t *bp; 2304 sbd_error_t *err = NULL; 2305 DRMACH_HANDLE hdl; 2306 2307 if (!DRMACH_IS_BOARD_ID(id)) 2308 return (drerr_new(0, EX86_INAPPROP, NULL)); 2309 bp = id; 2310 2311 hdl = drmach_node_get_dnode(bp->tree); 2312 if (hdl == NULL) 2313 return (drerr_new(0, EX86_INAPPROP, NULL)); 2314 2315 bp->powered = drmach_board_check_power(bp); 2316 if (bp->powered) { 2317 return (NULL); 2318 } 2319 2320 rw_enter(&drmach_boards_rwlock, RW_WRITER); 2321 err = drmach_board_check_dependent(SBD_CMD_POWERON, bp); 2322 if (err == NULL) { 2323 acpidev_dr_lock_all(); 2324 if (ACPI_FAILURE(acpidev_dr_device_poweron(hdl))) 2325 err = drerr_new(0, EX86_POWERON, NULL); 2326 acpidev_dr_unlock_all(); 2327 2328 /* Check whether the board is powered on. */ 2329 bp->powered = drmach_board_check_power(bp); 2330 if (err == NULL && bp->powered == 0) 2331 err = drerr_new(0, EX86_POWERON, NULL); 2332 } 2333 rw_exit(&drmach_boards_rwlock); 2334 2335 return (err); 2336 } 2337 2338 sbd_error_t * 2339 drmach_board_poweroff(drmachid_t id) 2340 { 2341 sbd_error_t *err = NULL; 2342 drmach_board_t *bp; 2343 drmach_status_t stat; 2344 DRMACH_HANDLE hdl; 2345 2346 if (DRMACH_NULL_ID(id)) 2347 return (NULL); 2348 2349 if (!DRMACH_IS_BOARD_ID(id)) 2350 return (drerr_new(0, EX86_INAPPROP, NULL)); 2351 bp = id; 2352 2353 hdl = drmach_node_get_dnode(bp->tree); 2354 if (hdl == NULL) 2355 return (drerr_new(0, EX86_INAPPROP, NULL)); 2356 2357 /* Check whether the board is busy, configured or connected. */ 2358 err = drmach_board_status(id, &stat); 2359 if (err != NULL) 2360 return (err); 2361 if (stat.configured || stat.busy) { 2362 return (drerr_new(0, EX86_CONFIGBUSY, bp->cm.name)); 2363 } else if (bp->connected) { 2364 return (drerr_new(0, EX86_CONNECTBUSY, bp->cm.name)); 2365 } 2366 2367 bp->powered = drmach_board_check_power(bp); 2368 if (bp->powered == 0) { 2369 return (NULL); 2370 } 2371 2372 rw_enter(&drmach_boards_rwlock, RW_WRITER); 2373 err = drmach_board_check_dependent(SBD_CMD_POWEROFF, bp); 2374 if (err == NULL) { 2375 acpidev_dr_lock_all(); 2376 if (ACPI_FAILURE(acpidev_dr_device_poweroff(hdl))) 2377 err = drerr_new(0, EX86_POWEROFF, NULL); 2378 acpidev_dr_unlock_all(); 2379 2380 bp->powered = drmach_board_check_power(bp); 2381 if (err == NULL && bp->powered != 0) 2382 err = drerr_new(0, EX86_POWEROFF, NULL); 2383 } 2384 rw_exit(&drmach_boards_rwlock); 2385 2386 return (err); 2387 } 2388 2389 sbd_error_t * 2390 drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force) 2391 { 2392 _NOTE(ARGUNUSED(opts, force)); 2393 2394 drmach_board_t *bp; 2395 DRMACH_HANDLE hdl; 2396 2397 if (DRMACH_NULL_ID(id)) 2398 return (NULL); 2399 2400 if (!DRMACH_IS_BOARD_ID(id)) 2401 return (drerr_new(0, EX86_INAPPROP, NULL)); 2402 bp = id; 2403 2404 hdl = drmach_node_get_dnode(bp->tree); 2405 if (hdl == NULL) 2406 return (drerr_new(0, EX86_INAPPROP, NULL)); 2407 2408 if (ACPI_FAILURE(acpidev_dr_device_check_status(hdl))) 2409 return (drerr_new(0, EX86_IN_FAILURE, NULL)); 2410 2411 return (NULL); 2412 } 2413 2414 sbd_error_t * 2415 drmach_board_connect(drmachid_t id, drmach_opts_t *opts) 2416 { 2417 _NOTE(ARGUNUSED(opts)); 2418 2419 sbd_error_t *err = NULL; 2420 drmach_board_t *bp = (drmach_board_t *)id; 2421 DRMACH_HANDLE hdl; 2422 2423 if (!DRMACH_IS_BOARD_ID(id)) 2424 return (drerr_new(0, EX86_INAPPROP, NULL)); 2425 bp = (drmach_board_t *)id; 2426 2427 hdl = drmach_node_get_dnode(bp->tree); 2428 if (hdl == NULL) 2429 return (drerr_new(0, EX86_INAPPROP, NULL)); 2430 2431 rw_enter(&drmach_boards_rwlock, RW_WRITER); 2432 err = drmach_board_check_dependent(SBD_CMD_CONNECT, bp); 2433 if (err == NULL) { 2434 acpidev_dr_lock_all(); 2435 if (ACPI_FAILURE(acpidev_dr_device_insert(hdl))) { 2436 (void) acpidev_dr_device_remove(hdl); 2437 err = drerr_new(1, EX86_PROBE, NULL); 2438 } else { 2439 bp->connected = 1; 2440 } 2441 acpidev_dr_unlock_all(); 2442 } 2443 rw_exit(&drmach_boards_rwlock); 2444 2445 return (err); 2446 } 2447 2448 sbd_error_t * 2449 drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts) 2450 { 2451 _NOTE(ARGUNUSED(opts)); 2452 2453 DRMACH_HANDLE hdl; 2454 drmach_board_t *bp; 2455 drmach_status_t stat; 2456 sbd_error_t *err = NULL; 2457 2458 if (DRMACH_NULL_ID(id)) 2459 return (NULL); 2460 if (!DRMACH_IS_BOARD_ID(id)) 2461 return (drerr_new(0, EX86_INAPPROP, NULL)); 2462 bp = (drmach_board_t *)id; 2463 2464 hdl = drmach_node_get_dnode(bp->tree); 2465 if (hdl == NULL) 2466 return (drerr_new(0, EX86_INAPPROP, NULL)); 2467 2468 /* Check whether the board is busy or configured. */ 2469 err = drmach_board_status(id, &stat); 2470 if (err != NULL) 2471 return (err); 2472 if (stat.configured || stat.busy) 2473 return (drerr_new(0, EX86_CONFIGBUSY, bp->cm.name)); 2474 2475 rw_enter(&drmach_boards_rwlock, RW_WRITER); 2476 err = drmach_board_check_dependent(SBD_CMD_DISCONNECT, bp); 2477 if (err == NULL) { 2478 acpidev_dr_lock_all(); 2479 if (ACPI_SUCCESS(acpidev_dr_device_remove(hdl))) { 2480 bp->connected = 0; 2481 } else { 2482 err = drerr_new(1, EX86_DEPROBE, bp->cm.name); 2483 } 2484 acpidev_dr_unlock_all(); 2485 } 2486 rw_exit(&drmach_boards_rwlock); 2487 2488 return (err); 2489 } 2490 2491 sbd_error_t * 2492 drmach_board_deprobe(drmachid_t id) 2493 { 2494 drmach_board_t *bp; 2495 2496 if (!DRMACH_IS_BOARD_ID(id)) 2497 return (drerr_new(0, EX86_INAPPROP, NULL)); 2498 bp = id; 2499 2500 cmn_err(CE_CONT, "DR: detach board %d\n", bp->bnum); 2501 2502 if (bp->devices) { 2503 drmach_array_dispose(bp->devices, drmach_device_dispose); 2504 bp->devices = NULL; 2505 } 2506 2507 bp->boot_board = 0; 2508 2509 return (NULL); 2510 } 2511 2512 /* 2513 * CPU specific interfaces to support dr driver 2514 */ 2515 sbd_error_t * 2516 drmach_cpu_disconnect(drmachid_t id) 2517 { 2518 if (!DRMACH_IS_CPU_ID(id)) 2519 return (drerr_new(0, EX86_INAPPROP, NULL)); 2520 2521 return (NULL); 2522 } 2523 2524 sbd_error_t * 2525 drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid) 2526 { 2527 drmach_cpu_t *cpu; 2528 2529 if (!DRMACH_IS_CPU_ID(id)) 2530 return (drerr_new(0, EX86_INAPPROP, NULL)); 2531 cpu = (drmach_cpu_t *)id; 2532 2533 if (cpu->cpuid == -1) { 2534 if (ACPI_SUCCESS(acpica_get_cpu_id_by_object( 2535 drmach_node_get_dnode(cpu->dev.node), cpuid))) { 2536 cpu->cpuid = *cpuid; 2537 } else { 2538 *cpuid = -1; 2539 } 2540 } else { 2541 *cpuid = cpu->cpuid; 2542 } 2543 2544 return (NULL); 2545 } 2546 2547 sbd_error_t * 2548 drmach_cpu_get_impl(drmachid_t id, int *ip) 2549 { 2550 if (!DRMACH_IS_CPU_ID(id)) 2551 return (drerr_new(0, EX86_INAPPROP, NULL)); 2552 2553 /* Assume all CPUs in system are homogeneous. */ 2554 *ip = X86_CPU_IMPL_UNKNOWN; 2555 2556 kpreempt_disable(); 2557 if (cpuid_getvendor(CPU) == X86_VENDOR_Intel) { 2558 /* NHM-EX CPU */ 2559 if (cpuid_getfamily(CPU) == 0x6 && 2560 cpuid_getmodel(CPU) == 0x2e) { 2561 *ip = X86_CPU_IMPL_NEHALEM_EX; 2562 } 2563 } 2564 kpreempt_enable(); 2565 2566 return (NULL); 2567 } 2568 2569 /* 2570 * Memory specific interfaces to support dr driver 2571 */ 2572 2573 /* 2574 * When drmach_mem_new() is called, the mp->base_pa field is set to the base 2575 * address of configured memory if there's configured memory on the board, 2576 * otherwise set to UINT64_MAX. For hot-added memory board, there's no 2577 * configured memory when drmach_mem_new() is called, so mp->base_pa is set 2578 * to UINT64_MAX and we need to set a correct value for it after memory 2579 * hot-add operations. 2580 * A hot-added memory board may contain multiple memory segments, 2581 * drmach_mem_add_span() will be called once for each segment, so we can't 2582 * rely on the basepa argument. And it's possible that only part of a memory 2583 * segment is added into OS, so need to intersect with phys_installed list 2584 * to get the real base address of configured memory on the board. 2585 */ 2586 sbd_error_t * 2587 drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size) 2588 { 2589 _NOTE(ARGUNUSED(basepa)); 2590 2591 uint64_t nbytes = 0; 2592 uint64_t endpa; 2593 drmach_mem_t *mp; 2594 struct memlist *ml2; 2595 struct memlist *p; 2596 2597 ASSERT(size != 0); 2598 2599 if (!DRMACH_IS_MEM_ID(id)) 2600 return (drerr_new(0, EX86_INAPPROP, NULL)); 2601 mp = (drmach_mem_t *)id; 2602 2603 /* Compute basepa and size of installed memory. */ 2604 endpa = _ptob64(physmax + 1); 2605 memlist_read_lock(); 2606 ml2 = memlist_dup(phys_install); 2607 memlist_read_unlock(); 2608 ml2 = memlist_del_span(ml2, 0ull, mp->slice_base); 2609 if (ml2 && endpa > mp->slice_top) { 2610 ml2 = memlist_del_span(ml2, mp->slice_top, 2611 endpa - mp->slice_top); 2612 } 2613 2614 ASSERT(ml2); 2615 if (ml2) { 2616 for (p = ml2; p; p = p->ml_next) { 2617 nbytes += p->ml_size; 2618 if (mp->base_pa > p->ml_address) 2619 mp->base_pa = p->ml_address; 2620 } 2621 ASSERT(nbytes > 0); 2622 mp->nbytes += nbytes; 2623 memlist_delete(ml2); 2624 } 2625 2626 return (NULL); 2627 } 2628 2629 static sbd_error_t * 2630 drmach_mem_update_lgrp(drmachid_t id) 2631 { 2632 ACPI_STATUS rc; 2633 DRMACH_HANDLE hdl; 2634 void *hdlp; 2635 drmach_mem_t *mp; 2636 update_membounds_t umb; 2637 2638 if (!DRMACH_IS_MEM_ID(id)) 2639 return (drerr_new(0, EX86_INAPPROP, NULL)); 2640 mp = (drmach_mem_t *)id; 2641 /* No need to update lgrp if memory is already installed. */ 2642 if (mp->nbytes != 0) 2643 return (NULL); 2644 /* No need to update lgrp if lgrp is disabled. */ 2645 if (max_mem_nodes == 1) 2646 return (NULL); 2647 2648 /* Add memory to lgroup */ 2649 hdl = mp->dev.node->get_dnode(mp->dev.node); 2650 rc = acpidev_dr_device_get_memory_index(hdl, &umb.u_device_id); 2651 ASSERT(ACPI_SUCCESS(rc)); 2652 if (ACPI_FAILURE(rc)) { 2653 cmn_err(CE_WARN, "drmach: failed to get device id of memory, " 2654 "can't update lgrp information."); 2655 return (drerr_new(0, EX86_INTERNAL, NULL)); 2656 } 2657 rc = acpidev_dr_get_mem_numa_info(hdl, mp->memlist, &hdlp, 2658 &umb.u_domain, &umb.u_sli_cnt, &umb.u_sli_ptr); 2659 ASSERT(ACPI_SUCCESS(rc)); 2660 if (ACPI_FAILURE(rc)) { 2661 cmn_err(CE_WARN, "drmach: failed to get lgrp info of memory, " 2662 "can't update lgrp information."); 2663 return (drerr_new(0, EX86_INTERNAL, NULL)); 2664 } 2665 umb.u_base = (uint64_t)mp->slice_base; 2666 umb.u_length = (uint64_t)(mp->slice_top - mp->slice_base); 2667 lgrp_plat_config(LGRP_CONFIG_MEM_ADD, (uintptr_t)&umb); 2668 acpidev_dr_free_mem_numa_info(hdlp); 2669 2670 return (NULL); 2671 } 2672 2673 sbd_error_t * 2674 drmach_mem_enable(drmachid_t id) 2675 { 2676 if (!DRMACH_IS_MEM_ID(id)) 2677 return (drerr_new(0, EX86_INAPPROP, NULL)); 2678 else 2679 return (NULL); 2680 } 2681 2682 sbd_error_t * 2683 drmach_mem_get_info(drmachid_t id, drmach_mem_info_t *mem) 2684 { 2685 drmach_mem_t *mp; 2686 2687 if (!DRMACH_IS_MEM_ID(id)) 2688 return (drerr_new(0, EX86_INAPPROP, NULL)); 2689 mp = (drmach_mem_t *)id; 2690 2691 /* 2692 * This is only used by dr to round up/down the memory 2693 * for copying. 2694 */ 2695 mem->mi_alignment_mask = mp->mem_alignment - 1; 2696 mem->mi_basepa = mp->base_pa; 2697 mem->mi_size = mp->nbytes; 2698 mem->mi_slice_base = mp->slice_base; 2699 mem->mi_slice_top = mp->slice_top; 2700 mem->mi_slice_size = mp->slice_size; 2701 2702 return (NULL); 2703 } 2704 2705 sbd_error_t * 2706 drmach_mem_get_slice_info(drmachid_t id, 2707 uint64_t *bp, uint64_t *ep, uint64_t *sp) 2708 { 2709 drmach_mem_t *mp; 2710 2711 if (!DRMACH_IS_MEM_ID(id)) 2712 return (drerr_new(0, EX86_INAPPROP, NULL)); 2713 mp = (drmach_mem_t *)id; 2714 2715 if (bp) 2716 *bp = mp->slice_base; 2717 if (ep) 2718 *ep = mp->slice_top; 2719 if (sp) 2720 *sp = mp->slice_size; 2721 2722 return (NULL); 2723 } 2724 2725 sbd_error_t * 2726 drmach_mem_get_memlist(drmachid_t id, struct memlist **ml) 2727 { 2728 #ifdef DEBUG 2729 int rv; 2730 #endif 2731 drmach_mem_t *mem; 2732 struct memlist *mlist; 2733 2734 if (!DRMACH_IS_MEM_ID(id)) 2735 return (drerr_new(0, EX86_INAPPROP, NULL)); 2736 mem = (drmach_mem_t *)id; 2737 2738 mlist = memlist_dup(mem->memlist); 2739 *ml = mlist; 2740 2741 #ifdef DEBUG 2742 /* 2743 * Make sure the incoming memlist doesn't already 2744 * intersect with what's present in the system (phys_install). 2745 */ 2746 memlist_read_lock(); 2747 rv = memlist_intersect(phys_install, mlist); 2748 memlist_read_unlock(); 2749 if (rv) { 2750 DRMACH_PR("Derived memlist intersects with phys_install\n"); 2751 memlist_dump(mlist); 2752 2753 DRMACH_PR("phys_install memlist:\n"); 2754 memlist_dump(phys_install); 2755 2756 memlist_delete(mlist); 2757 return (DRMACH_INTERNAL_ERROR()); 2758 } 2759 2760 DRMACH_PR("Derived memlist:"); 2761 memlist_dump(mlist); 2762 #endif 2763 2764 return (NULL); 2765 } 2766 2767 processorid_t 2768 drmach_mem_cpu_affinity(drmachid_t id) 2769 { 2770 _NOTE(ARGUNUSED(id)); 2771 2772 return (CPU_CURRENT); 2773 } 2774 2775 int 2776 drmach_copy_rename_need_suspend(drmachid_t id) 2777 { 2778 _NOTE(ARGUNUSED(id)); 2779 2780 return (0); 2781 } 2782 2783 /* 2784 * IO specific interfaces to support dr driver 2785 */ 2786 sbd_error_t * 2787 drmach_io_pre_release(drmachid_t id) 2788 { 2789 if (!DRMACH_IS_IO_ID(id)) 2790 return (drerr_new(0, EX86_INAPPROP, NULL)); 2791 2792 return (NULL); 2793 } 2794 2795 sbd_error_t * 2796 drmach_io_unrelease(drmachid_t id) 2797 { 2798 if (!DRMACH_IS_IO_ID(id)) 2799 return (drerr_new(0, EX86_INAPPROP, NULL)); 2800 2801 return (NULL); 2802 } 2803 2804 sbd_error_t * 2805 drmach_io_post_release(drmachid_t id) 2806 { 2807 _NOTE(ARGUNUSED(id)); 2808 2809 return (NULL); 2810 } 2811 2812 sbd_error_t * 2813 drmach_io_post_attach(drmachid_t id) 2814 { 2815 if (!DRMACH_IS_IO_ID(id)) 2816 return (drerr_new(0, EX86_INAPPROP, NULL)); 2817 2818 return (NULL); 2819 } 2820 2821 sbd_error_t * 2822 drmach_io_is_attached(drmachid_t id, int *yes) 2823 { 2824 drmach_device_t *dp; 2825 dev_info_t *dip; 2826 int state; 2827 2828 if (!DRMACH_IS_IO_ID(id)) 2829 return (drerr_new(0, EX86_INAPPROP, NULL)); 2830 dp = id; 2831 2832 dip = dp->node->getdip(dp->node); 2833 if (dip == NULL) { 2834 *yes = 0; 2835 return (NULL); 2836 } 2837 2838 state = ddi_get_devstate(dip); 2839 *yes = ((i_ddi_node_state(dip) >= DS_ATTACHED) || 2840 (state == DDI_DEVSTATE_UP)); 2841 2842 return (NULL); 2843 } 2844 2845 /* 2846 * Miscellaneous interfaces to support dr driver 2847 */ 2848 int 2849 drmach_verify_sr(dev_info_t *dip, int sflag) 2850 { 2851 _NOTE(ARGUNUSED(dip, sflag)); 2852 2853 return (0); 2854 } 2855 2856 void 2857 drmach_suspend_last(void) 2858 { 2859 } 2860 2861 void 2862 drmach_resume_first(void) 2863 { 2864 } 2865 2866 /* 2867 * Log a DR sysevent. 2868 * Return value: 0 success, non-zero failure. 2869 */ 2870 int 2871 drmach_log_sysevent(int board, char *hint, int flag, int verbose) 2872 { 2873 sysevent_t *ev = NULL; 2874 sysevent_id_t eid; 2875 int rv, km_flag; 2876 sysevent_value_t evnt_val; 2877 sysevent_attr_list_t *evnt_attr_list = NULL; 2878 sbd_error_t *err; 2879 char attach_pnt[MAXNAMELEN]; 2880 2881 km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP; 2882 attach_pnt[0] = '\0'; 2883 err = drmach_board_name(board, attach_pnt, MAXNAMELEN); 2884 if (err != NULL) { 2885 sbd_err_clear(&err); 2886 rv = -1; 2887 goto logexit; 2888 } 2889 if (verbose) { 2890 DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n", 2891 attach_pnt, hint, flag, verbose); 2892 } 2893 2894 if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, 2895 SUNW_KERN_PUB"dr", km_flag)) == NULL) { 2896 rv = -2; 2897 goto logexit; 2898 } 2899 evnt_val.value_type = SE_DATA_TYPE_STRING; 2900 evnt_val.value.sv_string = attach_pnt; 2901 if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val, 2902 km_flag)) != 0) 2903 goto logexit; 2904 2905 evnt_val.value_type = SE_DATA_TYPE_STRING; 2906 evnt_val.value.sv_string = hint; 2907 if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val, 2908 km_flag)) != 0) { 2909 sysevent_free_attr(evnt_attr_list); 2910 goto logexit; 2911 } 2912 2913 (void) sysevent_attach_attributes(ev, evnt_attr_list); 2914 2915 /* 2916 * Log the event but do not sleep waiting for its 2917 * delivery. This provides insulation from syseventd. 2918 */ 2919 rv = log_sysevent(ev, SE_NOSLEEP, &eid); 2920 2921 logexit: 2922 if (ev) 2923 sysevent_free(ev); 2924 if ((rv != 0) && verbose) 2925 cmn_err(CE_WARN, "!drmach_log_sysevent failed (rv %d) for %s " 2926 " %s\n", rv, attach_pnt, hint); 2927 2928 return (rv); 2929 } 2930