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 (c) 2010, Intel Corporation. 24 * All rights reserved. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/atomic.h> 29 #include <sys/cmn_err.h> 30 #include <sys/cpuvar.h> 31 #include <sys/memlist.h> 32 #include <sys/memlist_impl.h> 33 #include <sys/note.h> 34 #include <sys/obpdefs.h> 35 #include <sys/synch.h> 36 #include <sys/sysmacros.h> 37 #include <sys/sunddi.h> 38 #include <sys/sunndi.h> 39 #include <sys/x86_archext.h> 40 #include <sys/machsystm.h> 41 #include <sys/memnode.h> /* for lgrp_plat_node_cnt */ 42 #include <sys/psm_types.h> 43 #include <sys/acpi/acpi.h> 44 #include <sys/acpica.h> 45 #include <sys/acpidev.h> 46 #include <sys/acpidev_rsc.h> 47 #include <sys/acpidev_dr.h> 48 #include <sys/acpidev_impl.h> 49 50 struct acpidev_dr_set_prop_arg { 51 uint32_t level; 52 uint32_t bdnum; 53 uint32_t cpu_id; 54 uint32_t mem_id; 55 uint32_t io_id; 56 uint32_t mod_id; 57 }; 58 59 struct acpidev_dr_device_remove_arg { 60 uint32_t level; 61 }; 62 63 extern int acpidev_options; 64 65 /* User configurable option to enable/disable ACPI based DR operations. */ 66 int acpidev_dr_enable = 1; 67 int acpidev_dr_hierarchy_name = 1; 68 uint32_t acpidev_dr_max_segs_per_mem_device = ACPIDEV_DR_SEGS_PER_MEM_DEV; 69 uint32_t acpidev_dr_max_memlists_per_seg = ACPIDEV_DR_MEMLISTS_PER_SEG; 70 71 ACPI_TABLE_SRAT *acpidev_srat_tbl_ptr; 72 ACPI_TABLE_SLIT *acpidev_slit_tbl_ptr; 73 74 /* ACPI based DR operations are unsupported if zero. */ 75 static int acpidev_dr_supported = -1; 76 77 /* Failed to initialize support of DR operations if non-zero. */ 78 static int acpidev_dr_failed; 79 80 static volatile uint32_t acpidev_dr_boards; 81 static volatile uint32_t acpidev_dr_board_index; 82 static uint32_t acpidev_dr_max_cmp_per_board; 83 static uint32_t acpidev_dr_max_memory_per_board; 84 static uint32_t acpidev_dr_max_io_per_board; 85 static uint32_t acpidev_dr_memory_device_cnt; 86 87 static ACPI_HANDLE *acpidev_dr_board_handles[ACPIDEV_DR_MAX_BOARDS]; 88 89 /* Lock to protect/block DR operations at runtime. */ 90 static kmutex_t acpidev_dr_lock; 91 92 static acpidev_dr_capacity_t acpidev_dr_capacities[] = { 93 { /* Nehalem-EX */ 94 X86_VENDOR_Intel, 0x6, 0x2e, 0x2e, 0, UINT_MAX, 95 B_TRUE, /* Hotplug capable */ 96 1ULL << 30, /* Align on 1GB boundary */ 97 }, 98 { /* the last item is used to mark end of the table */ 99 UINT_MAX, UINT_MAX, UINT_MAX, 0, UINT_MAX, 0, 100 B_FALSE, 101 0, 102 }, 103 }; 104 105 static ACPI_STATUS acpidev_dr_scan_topo(ACPI_HANDLE hdl, UINT32 lvl, void *arg, 106 void **retval); 107 108 static acpidev_dr_capacity_t * 109 acpidev_dr_get_capacity(void) 110 { 111 acpidev_dr_capacity_t *cp, *cp1; 112 uint_t vendor, family, model, step; 113 static acpidev_dr_capacity_t *acpidev_dr_capacity_curr = NULL; 114 115 if (acpidev_dr_capacity_curr != NULL) { 116 return (acpidev_dr_capacity_curr); 117 } 118 119 kpreempt_disable(); 120 vendor = cpuid_getvendor(CPU); 121 family = cpuid_getfamily(CPU); 122 model = cpuid_getmodel(CPU); 123 step = cpuid_getstep(CPU); 124 kpreempt_enable(); 125 126 for (cp = acpidev_dr_capacities; ; cp++) { 127 ASSERT(cp < acpidev_dr_capacities + 128 sizeof (acpidev_dr_capacities) / sizeof (*cp)); 129 130 /* Check whether it reaches the last item of the table. */ 131 if (cp->cpu_vendor == UINT_MAX && cp->cpu_family == UINT_MAX && 132 cp->cpu_model_min == UINT_MAX && cp->cpu_model_max == 0 && 133 cp->cpu_step_min == UINT_MAX && cp->cpu_step_max == 0) { 134 break; 135 } 136 if (cp->cpu_vendor == vendor && cp->cpu_family == family && 137 model >= cp->cpu_model_min && model <= cp->cpu_model_max && 138 step >= cp->cpu_step_min && step <= cp->cpu_step_max) { 139 break; 140 } 141 } 142 143 /* Assume all CPUs in system are homogeneous. */ 144 cp1 = atomic_cas_ptr(&acpidev_dr_capacity_curr, NULL, cp); 145 ASSERT(cp1 == NULL || cp1 == cp); 146 if (cp1 != NULL && cp1 != cp) { 147 return (NULL); 148 } 149 150 return (cp); 151 } 152 153 int 154 acpidev_dr_capable(void) 155 { 156 uint64_t flags1, flags2; 157 acpidev_dr_capacity_t *cp; 158 159 /* 160 * Disable support of DR operations if: 161 * 1) acpidev fails to initialize DR interfaces. 162 * 2) ACPI based DR has been disabled by user. 163 * 3) No DR capable devices have been detected. 164 * 4) The system doesn't support DR operations. 165 * 5) Some acpidev features have been disabled by user. 166 */ 167 if (acpidev_dr_failed != 0 || acpidev_dr_enable == 0 || 168 acpidev_dr_supported == 0) { 169 return (0); 170 } 171 172 flags1 = ACPI_FEATURE_DEVCFG | ACPI_FEATURE_OSI_MODULE; 173 flags2 = ACPI_DEVCFG_CPU | ACPI_DEVCFG_MEMORY | 174 ACPI_DEVCFG_CONTAINER | ACPI_DEVCFG_PCI; 175 if (acpica_get_core_feature(flags1) != flags1 || 176 acpica_get_devcfg_feature(flags2) != flags2) { 177 cmn_err(CE_CONT, 178 "?acpidev: disable support of ACPI based DR because " 179 "some acpidev features have been disabled by user.\n"); 180 acpidev_dr_supported = 0; 181 return (0); 182 } 183 184 cp = acpidev_dr_get_capacity(); 185 if (cp == NULL || cp->hotplug_supported == B_FALSE) { 186 return (0); 187 } 188 189 return (1); 190 } 191 192 uint32_t 193 acpidev_dr_max_boards(void) 194 { 195 return (acpidev_dr_boards); 196 } 197 198 uint32_t 199 acpidev_dr_max_io_units_per_board(void) 200 { 201 return (acpidev_dr_max_io_per_board); 202 } 203 204 uint32_t 205 acpidev_dr_max_mem_units_per_board(void) 206 { 207 return (acpidev_dr_max_memory_per_board); 208 } 209 210 uint32_t 211 acpidev_dr_max_cmp_units_per_board(void) 212 { 213 return (acpidev_dr_max_cmp_per_board); 214 } 215 216 uint32_t 217 acpidev_dr_max_cpu_units_per_cmp(void) 218 { 219 static int max_cnt; 220 221 if (max_cnt == 0) { 222 kpreempt_disable(); 223 max_cnt = cpuid_get_ncpu_per_chip(CPU); 224 kpreempt_enable(); 225 } 226 227 return (max_cnt); 228 } 229 230 uint32_t 231 acpidev_dr_max_segments_per_mem_device(void) 232 { 233 if (acpidev_dr_max_segs_per_mem_device < 1) { 234 return (ACPIDEV_DR_SEGS_PER_MEM_DEV); 235 } else { 236 return (acpidev_dr_max_segs_per_mem_device); 237 } 238 } 239 240 uint32_t 241 acpidev_dr_max_memlists_per_segment(void) 242 { 243 if (acpidev_dr_max_memlists_per_seg < ACPIDEV_DR_MEMLISTS_PER_SEG) { 244 return (ACPIDEV_DR_MEMLISTS_PER_SEG); 245 } else { 246 return (acpidev_dr_max_memlists_per_seg); 247 } 248 } 249 250 void 251 acpidev_dr_init(void) 252 { 253 mutex_init(&acpidev_dr_lock, NULL, MUTEX_DRIVER, NULL); 254 } 255 256 static void 257 acpidev_dr_check_board_type(acpidev_data_handle_t dhdl, 258 struct acpidev_dr_set_prop_arg *ap, char *objname) 259 { 260 if (dhdl->aod_class_id == ACPIDEV_CLASS_ID_MEMORY) { 261 /* Memory board should have only one memory device. */ 262 ASSERT(ap->cpu_id == 0); 263 ASSERT(ap->mem_id == 1); 264 ASSERT(ap->io_id == 0); 265 ASSERT(ap->mod_id == 0); 266 dhdl->aod_bdtype = ACPIDEV_MEMORY_BOARD; 267 } else if (dhdl->aod_class_id == ACPIDEV_CLASS_ID_PCI || 268 dhdl->aod_class_id == ACPIDEV_CLASS_ID_PCIEX) { 269 /* IO board should have only one IO device. */ 270 ASSERT(ap->cpu_id == 0); 271 ASSERT(ap->mem_id == 0); 272 ASSERT(ap->io_id == 1); 273 ASSERT(ap->mod_id == 0); 274 dhdl->aod_bdtype = ACPIDEV_IO_BOARD; 275 } else if (dhdl->aod_class_id == ACPIDEV_CLASS_ID_CONTAINER) { 276 if (ap->mod_id == 1 && ap->mem_id == 0) { 277 dhdl->aod_bdtype = ACPIDEV_CPU_BOARD; 278 } else { 279 dhdl->aod_bdtype = ACPIDEV_SYSTEM_BOARD; 280 } 281 } else { 282 cmn_err(CE_WARN, 283 "!acpidev: unknown type of hotplug capable board %s.", 284 objname); 285 ASSERT(0); 286 } 287 } 288 289 /* 290 * Check for hotplug capable boards and create environment to support 291 * ACPI based DR operations. No need to acquire lock here, it's called 292 * from single-threaded context during boot. 293 */ 294 void 295 acpidev_dr_check(acpidev_walk_info_t *infop) 296 { 297 uint_t cmp; 298 boolean_t found = B_FALSE; 299 ACPI_HANDLE phdl; 300 acpidev_data_handle_t dhdl, pdhdl; 301 struct acpidev_dr_set_prop_arg arg; 302 303 if (infop == NULL || 304 infop->awi_op_type != ACPIDEV_OP_BOOT_PROBE) { 305 ACPIDEV_DEBUG(CE_WARN, 306 "!acpidev: invalid parameter to acpidev_dr_check()."); 307 return; 308 } 309 310 if (acpidev_dr_capable() == 0) { 311 return; 312 } 313 314 dhdl = infop->awi_data; 315 ASSERT(dhdl != NULL); 316 317 /* This device has already been handled before. */ 318 if (ACPIDEV_DR_IS_PROCESSED(dhdl)) { 319 return; 320 } 321 322 /* 323 * It implies that the device is hotplug capable if ACPI _EJ0 method 324 * is available. 325 */ 326 if (!ACPIDEV_DR_IS_BOARD(dhdl) && 327 acpidev_dr_device_hotplug_capable(infop->awi_hdl)) { 328 ACPIDEV_DR_SET_BOARD(dhdl); 329 } 330 331 /* All things are done if the device isn't hotplug capable. */ 332 if (!ACPIDEV_DR_IS_BOARD(dhdl)) { 333 return; 334 } 335 336 /* Check whether hardware topology is supported or not. */ 337 if (ACPI_FAILURE(acpidev_dr_scan_topo(infop->awi_hdl, 0, NULL, 338 NULL))) { 339 ACPIDEV_DR_SET_FAILED(dhdl); 340 ACPIDEV_DEBUG(CE_NOTE, "!acpidev: hardware topology under %s " 341 "is unsupported for DR operations.", infop->awi_name); 342 return; 343 } 344 345 /* Generate board/index/port number for the hotplug capable board. */ 346 dhdl->aod_bdnum = atomic_inc_32_nv(&acpidev_dr_boards) - 1; 347 dhdl->aod_portid = 0; 348 phdl = infop->awi_hdl; 349 while (ACPI_SUCCESS(AcpiGetParent(phdl, &phdl)) && 350 phdl != ACPI_ROOT_OBJECT) { 351 pdhdl = acpidev_data_get_handle(phdl); 352 if (pdhdl != NULL && ACPIDEV_DR_IS_BOARD(pdhdl)) { 353 dhdl->aod_bdidx = atomic_inc_32_nv(&pdhdl->aod_chidx); 354 found = B_TRUE; 355 break; 356 } 357 } 358 if (found == B_FALSE) { 359 dhdl->aod_bdidx = atomic_inc_32_nv(&acpidev_dr_board_index); 360 } 361 dhdl->aod_bdidx -= 1; 362 363 /* Found too many hotplug capable boards. */ 364 if (dhdl->aod_bdnum >= ACPIDEV_DR_MAX_BOARDS) { 365 ACPIDEV_DR_SET_FAILED(dhdl); 366 cmn_err(CE_WARN, "!acpidev: too many hotplug capable boards, " 367 "max %d, found %d.", 368 ACPIDEV_DR_MAX_BOARDS, dhdl->aod_bdnum + 1); 369 return; 370 } 371 372 /* Scan all descendant devices to prepare info for DR operations. */ 373 bzero(&arg, sizeof (arg)); 374 arg.bdnum = dhdl->aod_bdnum; 375 arg.level = infop->awi_level; 376 if (ACPI_FAILURE(acpidev_dr_scan_topo(infop->awi_hdl, 0, &arg, 377 NULL))) { 378 ACPIDEV_DR_SET_FAILED(dhdl); 379 ACPIDEV_DEBUG(CE_NOTE, "!acpidev: failed to set DR properties " 380 "for descendants of %s.", infop->awi_name); 381 return; 382 } 383 384 /* Get type of the hotplug capable board. */ 385 acpidev_dr_check_board_type(dhdl, &arg, infop->awi_name); 386 387 /* 388 * Save ACPI handle of the hotplug capable board to speed up lookup 389 * board handle if caching is enabled. 390 */ 391 if ((acpidev_options & ACPIDEV_OUSER_NO_CACHE) == 0) { 392 acpidev_dr_board_handles[dhdl->aod_bdnum] = infop->awi_hdl; 393 } 394 395 /* Update system maximum DR capabilities. */ 396 cmp = (arg.cpu_id + acpidev_dr_max_cpu_units_per_cmp() - 1); 397 cmp /= acpidev_dr_max_cpu_units_per_cmp(); 398 if (cmp > acpidev_dr_max_cmp_per_board) { 399 acpidev_dr_max_cmp_per_board = cmp; 400 } 401 if (arg.mem_id > acpidev_dr_max_memory_per_board) { 402 acpidev_dr_max_memory_per_board = arg.mem_id; 403 } 404 if (arg.io_id > acpidev_dr_max_io_per_board) { 405 acpidev_dr_max_io_per_board = arg.io_id; 406 } 407 } 408 409 static void 410 acpidev_dr_initialize_memory_hotplug(void) 411 { 412 caddr_t buf; 413 uint32_t cnt; 414 acpidev_dr_capacity_t *cp; 415 416 /* 417 * We have already checked that the platform supports DR operations. 418 */ 419 cp = acpidev_dr_get_capacity(); 420 ASSERT(cp != NULL && cp->hotplug_supported); 421 ASSERT(ISP2(cp->memory_alignment)); 422 ASSERT(cp->memory_alignment > MMU_PAGESIZE); 423 mem_node_physalign = cp->memory_alignment; 424 425 /* Pre-populate memlist cache. */ 426 cnt = acpidev_dr_memory_device_cnt; 427 cnt *= acpidev_dr_max_segments_per_mem_device(); 428 cnt *= acpidev_dr_max_memlists_per_segment(); 429 if (cnt > ACPIDEV_DR_MAX_MEMLIST_ENTRIES) { 430 cmn_err(CE_WARN, "!acpidev: attempted to reserve too many " 431 "memlist entries (%u), max %u. Falling back to %u and " 432 "some memory hot add operations may fail.", 433 cnt, ACPIDEV_DR_MAX_MEMLIST_ENTRIES, 434 ACPIDEV_DR_MAX_MEMLIST_ENTRIES); 435 cnt = ACPIDEV_DR_MAX_MEMLIST_ENTRIES; 436 } 437 cnt *= sizeof (struct memlist); 438 buf = kmem_zalloc(cnt, KM_SLEEP); 439 memlist_free_block(buf, cnt); 440 } 441 442 /* 443 * Create pseudo DR control device node if the system is hotplug capable. 444 * No need to acquire lock, it's called from single-threaded context 445 * during boot. pdip has been held by the caller. 446 */ 447 static ACPI_STATUS 448 acpidev_dr_create_node(dev_info_t *pdip) 449 { 450 dev_info_t *dip; 451 char unit[32]; 452 char *path; 453 char *comps[] = { 454 "acpidr_sbd", 455 }; 456 457 /* 458 * Disable support of DR operations if no hotplug capable board has 459 * been detected. 460 */ 461 if (acpidev_dr_boards == 0) { 462 acpidev_dr_supported = 0; 463 } else { 464 acpidev_dr_supported = 1; 465 } 466 467 /* 468 * Don't create control device node if the system isn't hotplug capable. 469 */ 470 if (acpidev_dr_capable() == 0) { 471 return (AE_SUPPORT); 472 } 473 474 /* Cache pointer to the ACPI SLIT table. */ 475 if (ACPI_FAILURE(AcpiGetTable(ACPI_SIG_SLIT, 1, 476 (ACPI_TABLE_HEADER **)&acpidev_slit_tbl_ptr))) { 477 acpidev_slit_tbl_ptr = NULL; 478 } 479 if (acpidev_srat_tbl_ptr == NULL || acpidev_slit_tbl_ptr == NULL) { 480 if (lgrp_plat_node_cnt != 1) { 481 /* 482 * Disable support of CPU/memory DR operations if lgrp 483 * is enabled but failed to cache SRAT/SLIT table 484 * pointers. 485 */ 486 cmn_err(CE_WARN, 487 "!acpidev: failed to get ACPI SRAT/SLIT table."); 488 plat_dr_disable_cpu(); 489 plat_dr_disable_memory(); 490 } 491 } 492 493 ndi_devi_alloc_sleep(pdip, ACPIDEV_NODE_NAME_ACPIDR, 494 (pnode_t)DEVI_PSEUDO_NODEID, &dip); 495 496 /* Set "unit-address" device property. */ 497 (void) snprintf(unit, sizeof (unit), "%u", 0); 498 if (ndi_prop_update_string(DDI_DEV_T_NONE, dip, 499 ACPIDEV_PROP_NAME_UNIT_ADDR, unit) != NDI_SUCCESS) { 500 path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 501 cmn_err(CE_CONT, 502 "?acpidev: failed to set unit-address property for %s.\n", 503 ddi_pathname(dip, path)); 504 kmem_free(path, MAXPATHLEN); 505 (void) ddi_remove_child(dip, 0); 506 acpidev_dr_failed = 1; 507 return (AE_ERROR); 508 } 509 510 /* Set "compatible" device property. */ 511 if (ndi_prop_update_string_array(DDI_DEV_T_NONE, dip, OBP_COMPATIBLE, 512 comps, sizeof (comps) / sizeof (comps[0])) != NDI_SUCCESS) { 513 path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 514 cmn_err(CE_CONT, "?acpidev: failed to set compatible " 515 "property for %s.\n", ddi_pathname(dip, path)); 516 kmem_free(path, MAXPATHLEN); 517 (void) ddi_remove_child(dip, 0); 518 acpidev_dr_failed = 1; 519 return (AE_ERROR); 520 } 521 522 (void) ndi_devi_bind_driver(dip, 0); 523 524 return (AE_OK); 525 } 526 527 ACPI_STATUS 528 acpidev_dr_initialize(dev_info_t *pdip) 529 { 530 ACPI_STATUS rc; 531 532 rc = acpidev_dr_create_node(pdip); 533 if (ACPI_FAILURE(rc)) { 534 return (rc); 535 } 536 537 /* Initialize support of memory DR operations. */ 538 if (plat_dr_support_memory()) { 539 acpidev_dr_initialize_memory_hotplug(); 540 } 541 542 /* Mark the DR subsystem is ready for use. */ 543 plat_dr_enable(); 544 545 return (AE_OK); 546 } 547 548 static ACPI_STATUS 549 acpidev_dr_find_board(ACPI_HANDLE hdl, uint_t lvl, void *ctx, void **retval) 550 { 551 _NOTE(ARGUNUSED(lvl)); 552 553 acpidev_data_handle_t dhdl; 554 555 ASSERT(hdl != NULL); 556 dhdl = acpidev_data_get_handle(hdl); 557 if (dhdl == NULL) { 558 /* No data handle available, not ready for DR operations. */ 559 return (AE_CTRL_DEPTH); 560 } else if (ACPIDEV_DR_IS_BOARD(dhdl) && ACPIDEV_DR_IS_WORKING(dhdl) && 561 dhdl->aod_bdnum == (intptr_t)ctx) { 562 ASSERT(retval != NULL); 563 *(ACPI_HANDLE *)retval = hdl; 564 return (AE_CTRL_TERMINATE); 565 } 566 567 return (AE_OK); 568 } 569 570 ACPI_STATUS 571 acpidev_dr_get_board_handle(uint_t board, ACPI_HANDLE *hdlp) 572 { 573 ACPI_STATUS rc = AE_OK; 574 ACPI_HANDLE hdl; 575 576 ASSERT(hdlp != NULL); 577 if (hdlp == NULL) { 578 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 579 "acpidev_dr_get_board_handle()."); 580 return (AE_BAD_PARAMETER); 581 } 582 583 if (board >= acpidev_dr_boards) { 584 ACPIDEV_DEBUG(CE_NOTE, 585 "!acpidev: board number %d is out of range, max %d.", 586 board, acpidev_dr_boards); 587 return (AE_NOT_FOUND); 588 } 589 590 /* Use cached handles if caching is enabled. */ 591 if ((acpidev_options & ACPIDEV_OUSER_NO_CACHE) == 0) { 592 if (acpidev_dr_board_handles[board] != NULL) { 593 hdl = acpidev_dr_board_handles[board]; 594 if (ACPI_FAILURE(acpidev_dr_find_board(hdl, 1, 595 (void *)(intptr_t)board, (void **)hdlp)) && 596 *hdlp != NULL) { 597 return (AE_OK); 598 } 599 } 600 ACPIDEV_DEBUG(CE_NOTE, 601 "!acpidev: board %d doesn't exist.", board); 602 *hdlp = NULL; 603 return (AE_NOT_FOUND); 604 } 605 606 /* All hotplug capable boards should exist under \_SB_. */ 607 if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, 608 ACPIDEV_OBJECT_NAME_SB, &hdl))) { 609 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to get handle of %s.", 610 ACPIDEV_OBJECT_NAME_SB); 611 return (AE_ERROR); 612 } 613 614 *hdlp = NULL; 615 if (ACPI_FAILURE(AcpiWalkNamespace(ACPI_TYPE_DEVICE, hdl, 616 ACPIDEV_MAX_ENUM_LEVELS - 1, acpidev_dr_find_board, NULL, 617 (void *)(intptr_t)board, (void **)hdlp))) { 618 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to find ACPI handle " 619 "for board %d.", board); 620 rc = AE_NOT_FOUND; 621 } else if (*hdlp == NULL) { 622 ACPIDEV_DEBUG(CE_NOTE, 623 "!acpidev: board %d doesn't exist.", board); 624 rc = AE_NOT_FOUND; 625 } 626 627 return (rc); 628 } 629 630 acpidev_board_type_t 631 acpidev_dr_get_board_type(ACPI_HANDLE hdl) 632 { 633 acpidev_data_handle_t dhdl; 634 acpidev_board_type_t type = ACPIDEV_INVALID_BOARD; 635 636 if (hdl == NULL) { 637 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 638 "acpidev_dr_get_board_type()."); 639 return (type); 640 } 641 642 dhdl = acpidev_data_get_handle(hdl); 643 if (dhdl == NULL) { 644 ACPIDEV_DEBUG(CE_WARN, 645 "!acpidev: failed to get data associated with %p.", hdl); 646 } else { 647 type = dhdl->aod_bdtype; 648 } 649 650 return (type); 651 } 652 653 ACPI_STATUS 654 acpidev_dr_get_board_number(ACPI_HANDLE hdl, uint32_t *bnump) 655 { 656 acpidev_data_handle_t dhdl; 657 658 if (hdl == NULL || bnump == NULL) { 659 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 660 "acpidev_dr_get_board_number()."); 661 return (AE_BAD_PARAMETER); 662 } 663 664 dhdl = acpidev_data_get_handle(hdl); 665 if (dhdl == NULL) { 666 ACPIDEV_DEBUG(CE_WARN, 667 "!acpidev: failed to get data associated with %p.", hdl); 668 return (AE_ERROR); 669 } 670 *bnump = dhdl->aod_bdnum; 671 672 return (AE_OK); 673 } 674 675 ACPI_STATUS 676 acpidev_dr_get_board_name(ACPI_HANDLE hdl, char *buf, size_t len) 677 { 678 char *fmt; 679 int count = 0; 680 size_t rlen = 0; 681 ACPI_HANDLE thdl; 682 acpidev_data_handle_t dhdl; 683 acpidev_data_handle_t dhdls[ACPIDEV_MAX_ENUM_LEVELS]; 684 685 if (hdl == NULL || buf == NULL) { 686 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 687 "acpidev_dr_get_board_name()."); 688 return (AE_BAD_PARAMETER); 689 } 690 691 /* Find ancestors of the device which are hotplug capable. */ 692 for (thdl = hdl; thdl != NULL; ) { 693 dhdl = acpidev_data_get_handle(thdl); 694 if (dhdl == NULL) { 695 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to get data " 696 "associated with %p.", thdl); 697 return (AE_ERROR); 698 } 699 700 if (!ACPIDEV_DR_IS_BOARD(dhdl)) { 701 /* The board itself should be hotplug capable. */ 702 if (count == 0) { 703 ACPIDEV_DEBUG(CE_WARN, "!acpidev: object %p is " 704 "not hotplug capable.", thdl); 705 return (AE_ERROR); 706 } 707 } else { 708 if (ACPIDEV_DR_IS_FAILED(dhdl)) { 709 ACPIDEV_DEBUG(CE_WARN, "!acpidev: object %p is " 710 "in the FAILED state.", thdl); 711 } 712 713 if (count >= ACPIDEV_MAX_ENUM_LEVELS) { 714 ACPIDEV_DEBUG(CE_WARN, 715 "!acpidev: recursive level for hotplug " 716 "capable board is too deep."); 717 return (AE_ERROR); 718 } 719 720 dhdls[count] = dhdl; 721 count++; 722 } 723 724 if (acpidev_dr_hierarchy_name == 0) { 725 thdl = NULL; 726 } else if (ACPI_FAILURE(AcpiGetParent(thdl, &thdl))) { 727 thdl = NULL; 728 } 729 } 730 731 /* Generate hierarchy board name for the board. */ 732 ASSERT(count > 0); 733 for (count--; count >= 0 && rlen < len; count--) { 734 dhdl = dhdls[count]; 735 switch (dhdl->aod_bdtype) { 736 case ACPIDEV_CPU_BOARD: 737 fmt = ACPIDEV_DR_CPU_BD_FMT; 738 break; 739 case ACPIDEV_MEMORY_BOARD: 740 fmt = ACPIDEV_DR_MEMORY_BD_FMT; 741 break; 742 case ACPIDEV_IO_BOARD: 743 fmt = ACPIDEV_DR_IO_BD_FMT; 744 break; 745 case ACPIDEV_SYSTEM_BOARD: 746 fmt = ACPIDEV_DR_SYSTEM_BD_FMT; 747 break; 748 case ACPIDEV_INVALID_BOARD: 749 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid board type."); 750 return (AE_ERROR); 751 default: 752 ACPIDEV_DEBUG(CE_WARN, 753 "!acpidev: unknown board type %u.", 754 dhdl->aod_bdtype); 755 return (AE_ERROR); 756 } 757 758 /* Add "." before component name except first item. */ 759 if (rlen != 0) { 760 rlen += snprintf(buf + rlen, len - rlen, "."); 761 } 762 if (rlen < len) { 763 rlen += snprintf(buf + rlen, len - rlen, fmt, 764 dhdl->aod_bdidx); 765 } 766 } 767 768 /* Check whether the buffer is sufficient. */ 769 if (rlen >= len) { 770 ACPIDEV_DEBUG(CE_WARN, "!acpidev: buffer length to " 771 "acpidev_dr_get_board_name() is too small."); 772 return (AE_NO_MEMORY); 773 } 774 775 return (AE_OK); 776 } 777 778 ACPI_STATUS 779 acpidev_dr_get_attachment_point(ACPI_HANDLE hdl, char *buf, size_t len) 780 { 781 size_t rlen; 782 783 if (hdl == NULL || buf == NULL) { 784 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 785 "acpidev_dr_get_attachment_point()."); 786 return (AE_BAD_PARAMETER); 787 } 788 789 rlen = snprintf(buf, len, "/devices/%s/%s@%u:", 790 ACPIDEV_NODE_NAME_ROOT, ACPIDEV_NODE_NAME_ACPIDR, 0); 791 if (rlen >= len) { 792 ACPIDEV_DEBUG(CE_WARN, "!acpidev: buffer to " 793 "acpidev_dr_get_attachment_point() is too small."); 794 return (AE_NO_MEMORY); 795 } 796 797 return (acpidev_dr_get_board_name(hdl, buf + rlen, len - rlen)); 798 } 799 800 /* 801 * Existence of ACPI _EJ0 method implies that the device is hotplug capable. 802 */ 803 int 804 acpidev_dr_device_hotplug_capable(ACPI_HANDLE hdl) 805 { 806 ACPI_HANDLE ej0; 807 808 ASSERT(hdl != NULL); 809 if (ACPI_FAILURE(AcpiGetHandle(hdl, ACPIDEV_METHOD_NAME_EJ0, &ej0))) { 810 return (0); 811 } 812 813 return (1); 814 } 815 816 int 817 acpidev_dr_device_has_edl(ACPI_HANDLE hdl) 818 { 819 ACPI_HANDLE edl; 820 821 ASSERT(hdl != NULL); 822 if (ACPI_FAILURE(AcpiGetHandle(hdl, ACPIDEV_METHOD_NAME_EDL, &edl))) { 823 return (0); 824 } 825 826 return (1); 827 } 828 829 int 830 acpidev_dr_device_is_present(ACPI_HANDLE hdl) 831 { 832 int status; 833 834 ASSERT(hdl != NULL); 835 836 status = acpidev_query_device_status(hdl); 837 if (acpidev_check_device_present(status)) { 838 return (1); 839 } 840 841 return (0); 842 } 843 844 int 845 acpidev_dr_device_is_powered(ACPI_HANDLE hdl) 846 { 847 int status; 848 849 ASSERT(hdl != NULL); 850 851 /* 852 * Check device status returned by ACPI _STA method. 853 * It implies that the device is powered if status is both PRESENT 854 * and ENABLED. 855 */ 856 status = acpidev_query_device_status(hdl); 857 if (acpidev_check_device_enabled(status)) { 858 return (1); 859 } 860 861 return (0); 862 } 863 864 ACPI_STATUS 865 acpidev_dr_get_mem_alignment(ACPI_HANDLE hdl, uint64_t *ap) 866 { 867 acpidev_dr_capacity_t *cp; 868 869 ASSERT(hdl != NULL); 870 ASSERT(ap != NULL); 871 if (ap == NULL || hdl == NULL) { 872 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 873 "acpidev_dr_get_mem_alignment()."); 874 return (AE_BAD_PARAMETER); 875 } 876 877 cp = acpidev_dr_get_capacity(); 878 if (cp == NULL || cp->hotplug_supported == B_FALSE) { 879 ACPIDEV_DEBUG(CE_WARN, 880 "!acpidev: failed to get memory alignment."); 881 return (AE_SUPPORT); 882 } 883 *ap = cp->memory_alignment; 884 885 return (AE_OK); 886 } 887 888 /* 889 * Get the device property for the given name and store it into buf. 890 * Returns the amount of data copied to buf if len is large enough to 891 * hold all of the data. If len is not large enough, then the required 892 * len would be returned and buf would not be modified. On any errors, 893 * -1 is returned and buf is not modified. 894 */ 895 ACPI_STATUS 896 acpidev_dr_device_get_regspec(ACPI_HANDLE hdl, boolean_t assigned, 897 acpidev_regspec_t **regpp, uint_t *cntp) 898 { 899 int *valp; 900 uint_t count; 901 char *propname; 902 dev_info_t *dip; 903 acpidev_data_handle_t dhdl; 904 905 ASSERT(hdl != NULL); 906 ASSERT(regpp != NULL && cntp != NULL); 907 if (hdl == NULL || regpp == NULL || cntp == NULL) { 908 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameters to " 909 "acpidev_dr_device_get_regspec()."); 910 return (AE_BAD_PARAMETER); 911 } 912 913 /* Set default return value. */ 914 *regpp = NULL; 915 *cntp = 0; 916 917 dhdl = acpidev_data_get_handle(hdl); 918 if (dhdl == NULL) { 919 ACPIDEV_DEBUG(CE_WARN, 920 "!acpidev: failed to get data associated with %p.", hdl); 921 return (AE_ERROR); 922 } else if ((dip = acpidev_data_get_devinfo(dhdl)) == NULL) { 923 ACPIDEV_DEBUG(CE_WARN, 924 "!acpidev: failed to get dip associated with %p.", hdl); 925 return (AE_NOT_FOUND); 926 } 927 928 propname = assigned ? "assigned-addresses" : "reg"; 929 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 930 propname, &valp, &count) != DDI_PROP_SUCCESS) { 931 ACPIDEV_DEBUG(CE_WARN, 932 "!acpidev: failed to lookup device property %s.", propname); 933 return (AE_NOT_FOUND); 934 } 935 936 if (count % (sizeof (**regpp) / sizeof (int)) != 0) { 937 ACPIDEV_DEBUG(CE_WARN, 938 "!acpidev: device property %s is invalid.", propname); 939 ddi_prop_free(valp); 940 return (AE_ERROR); 941 } 942 943 *regpp = (acpidev_regspec_t *)valp; 944 *cntp = count / (sizeof (**regpp) / sizeof (int)); 945 946 return (AE_OK); 947 } 948 949 void 950 acpidev_dr_device_free_regspec(acpidev_regspec_t *regp, uint_t count) 951 { 952 _NOTE(ARGUNUSED(count)); 953 954 if (regp != NULL) { 955 ddi_prop_free(regp); 956 } 957 } 958 959 /* 960 * Return values 961 * . negative values on error 962 * . size of data copied to buffer if it's bigger enough 963 * . size of buffer needed if buffer is too small 964 */ 965 int 966 acpidev_dr_device_getprop(ACPI_HANDLE hdl, char *name, caddr_t buf, size_t len) 967 { 968 int rlen = -1; 969 acpidev_data_handle_t dhdl; 970 971 if (hdl == NULL) { 972 return (-1); 973 } 974 975 dhdl = acpidev_data_get_handle(hdl); 976 if (dhdl == NULL) { 977 return (-1); 978 } else if (!ACPIDEV_DR_IS_WORKING(dhdl)) { 979 return (-1); 980 } 981 982 if (strcmp(name, ACPIDEV_DR_PROP_PORTID) == 0) { 983 if (len >= sizeof (uint32_t)) { 984 *(uint32_t *)(void *)buf = dhdl->aod_portid; 985 } 986 rlen = sizeof (uint32_t); 987 } else if (strcmp(name, ACPIDEV_DR_PROP_BOARDNUM) == 0) { 988 if (len >= sizeof (uint32_t)) { 989 *(uint32_t *)(void *)buf = dhdl->aod_bdnum; 990 } 991 rlen = sizeof (uint32_t); 992 } else if (strcmp(name, ACPIDEV_DR_PROP_DEVNAME) == 0) { 993 switch (dhdl->aod_class_id) { 994 case ACPIDEV_CLASS_ID_CPU: 995 if (len >= sizeof (ACPIDEV_NODE_NAME_CPU)) { 996 (void) strlcpy((char *)buf, 997 ACPIDEV_NODE_NAME_CPU, len); 998 } 999 rlen = sizeof (ACPIDEV_NODE_NAME_CPU); 1000 break; 1001 1002 case ACPIDEV_CLASS_ID_MEMORY: 1003 if (len >= sizeof (ACPIDEV_NODE_NAME_MEMORY)) { 1004 (void) strlcpy((char *)buf, 1005 ACPIDEV_NODE_NAME_MEMORY, len); 1006 } 1007 rlen = sizeof (ACPIDEV_NODE_NAME_MEMORY); 1008 break; 1009 1010 case ACPIDEV_CLASS_ID_PCI: 1011 case ACPIDEV_CLASS_ID_PCIEX: 1012 if (len >= sizeof (ACPIDEV_NODE_NAME_PCI)) { 1013 (void) strlcpy((char *)buf, 1014 ACPIDEV_NODE_NAME_PCI, len); 1015 } 1016 rlen = sizeof (ACPIDEV_NODE_NAME_PCI); 1017 break; 1018 1019 default: 1020 break; 1021 } 1022 } 1023 1024 return (rlen); 1025 } 1026 1027 /* 1028 * Figure out device class of the device. 1029 * It only supports device classes which may be involved in DR operations. 1030 */ 1031 acpidev_class_id_t 1032 acpidev_dr_device_get_class(ACPI_HANDLE hdl) 1033 { 1034 ACPI_OBJECT_TYPE type; 1035 ACPI_DEVICE_INFO *infop; 1036 acpidev_class_id_t id = ACPIDEV_CLASS_ID_INVALID; 1037 1038 static char *acpidev_id_cpu[] = { 1039 ACPIDEV_HID_CPU, 1040 }; 1041 static char *acpidev_id_mem[] = { 1042 ACPIDEV_HID_MEMORY, 1043 }; 1044 static char *acpidev_id_mod[] = { 1045 ACPIDEV_HID_MODULE, 1046 }; 1047 static char *acpidev_id_pci[] = { 1048 ACPIDEV_HID_PCI_HOSTBRIDGE, 1049 }; 1050 static char *acpidev_id_pciex[] = { 1051 ACPIDEV_HID_PCIEX_HOSTBRIDGE, 1052 }; 1053 1054 /* Figure out device type by checking ACPI object type. */ 1055 if (ACPI_FAILURE(AcpiGetType(hdl, &type))) { 1056 return (ACPIDEV_CLASS_ID_INVALID); 1057 } else if (type == ACPI_TYPE_PROCESSOR) { 1058 return (ACPIDEV_CLASS_ID_CPU); 1059 } else if (type != ACPI_TYPE_DEVICE) { 1060 return (ACPIDEV_CLASS_ID_INVALID); 1061 } 1062 1063 if (ACPI_FAILURE(AcpiGetObjectInfo(hdl, &infop))) { 1064 return (ACPIDEV_CLASS_ID_INVALID); 1065 } 1066 1067 /* Figure out device type by checking _HID and _CID. */ 1068 if (acpidev_match_device_id(infop, 1069 ACPIDEV_ARRAY_PARAM(acpidev_id_cpu))) { 1070 id = ACPIDEV_CLASS_ID_CPU; 1071 } else if (acpidev_match_device_id(infop, 1072 ACPIDEV_ARRAY_PARAM(acpidev_id_mem))) { 1073 id = ACPIDEV_CLASS_ID_MEMORY; 1074 } else if (acpidev_match_device_id(infop, 1075 ACPIDEV_ARRAY_PARAM(acpidev_id_mod))) { 1076 id = ACPIDEV_CLASS_ID_CONTAINER; 1077 } else if (acpidev_match_device_id(infop, 1078 ACPIDEV_ARRAY_PARAM(acpidev_id_pciex))) { 1079 id = ACPIDEV_CLASS_ID_PCIEX; 1080 } else if (acpidev_match_device_id(infop, 1081 ACPIDEV_ARRAY_PARAM(acpidev_id_pci))) { 1082 id = ACPIDEV_CLASS_ID_PCI; 1083 } 1084 1085 AcpiOsFree(infop); 1086 1087 return (id); 1088 } 1089 1090 ACPI_STATUS 1091 acpidev_dr_device_get_memory_index(ACPI_HANDLE hdl, uint32_t *idxp) 1092 { 1093 acpidev_data_handle_t dhdl; 1094 1095 ASSERT(idxp != NULL); 1096 ASSERT(hdl != NULL); 1097 1098 dhdl = acpidev_data_get_handle(hdl); 1099 if (dhdl == NULL) { 1100 ACPIDEV_DEBUG(CE_WARN, 1101 "!acpidev: failed to get data handle for %p.", hdl); 1102 return (AE_ERROR); 1103 } else if (dhdl->aod_class_id != ACPIDEV_CLASS_ID_MEMORY) { 1104 ACPIDEV_DEBUG(CE_WARN, 1105 "!acpidev: object %p is not a memory device.", hdl); 1106 return (AE_ERROR); 1107 } else { 1108 *idxp = dhdl->aod_memidx; 1109 } 1110 1111 return (AE_OK); 1112 } 1113 1114 int 1115 acpidev_dr_device_is_board(ACPI_HANDLE hdl) 1116 { 1117 acpidev_data_handle_t dhdl; 1118 1119 ASSERT(hdl != NULL); 1120 if (hdl == NULL) { 1121 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 1122 "acpidev_dr_is_board()."); 1123 return (0); 1124 } 1125 1126 dhdl = acpidev_data_get_handle(hdl); 1127 if (dhdl == NULL) { 1128 return (0); 1129 } else if (!ACPIDEV_DR_IS_BOARD(dhdl)) { 1130 return (0); 1131 } 1132 1133 return (1); 1134 } 1135 1136 ACPI_STATUS 1137 acpidev_dr_device_walk_edl(ACPI_HANDLE hdl, 1138 ACPI_WALK_CALLBACK cb, void *arg, void **retval) 1139 { 1140 ACPI_STATUS rc = AE_OK; 1141 int i; 1142 char *objname; 1143 ACPI_OBJECT *obj; 1144 ACPI_BUFFER buf; 1145 char *method = ACPIDEV_METHOD_NAME_EDL; 1146 1147 ASSERT(hdl != NULL); 1148 ASSERT(cb != NULL); 1149 if (hdl == NULL || cb == NULL) { 1150 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 1151 "acpidev_dr_device_walk_edl()."); 1152 return (AE_BAD_PARAMETER); 1153 } 1154 1155 objname = acpidev_get_object_name(hdl); 1156 buf.Length = ACPI_ALLOCATE_BUFFER; 1157 rc = AcpiEvaluateObjectTyped(hdl, method, NULL, &buf, 1158 ACPI_TYPE_PACKAGE); 1159 if (rc == AE_NOT_FOUND) { 1160 acpidev_free_object_name(objname); 1161 return (AE_OK); 1162 } else if (ACPI_FAILURE(rc)) { 1163 cmn_err(CE_WARN, 1164 "!acpidev: failed to evaluate method %s under %s.", 1165 method, objname); 1166 acpidev_free_object_name(objname); 1167 return (AE_ERROR); 1168 } 1169 1170 /* Validate the package structure. */ 1171 obj = buf.Pointer; 1172 for (i = 0; i < obj->Package.Count; i++) { 1173 if (obj->Package.Elements[i].Type != 1174 ACPI_TYPE_LOCAL_REFERENCE) { 1175 cmn_err(CE_WARN, "!acpidev: element %d in package " 1176 "returned by %s of %s is not local reference.", 1177 i, method, objname); 1178 AcpiOsFree(buf.Pointer); 1179 acpidev_free_object_name(objname); 1180 return (AE_ERROR); 1181 } else if (obj->Package.Elements[i].Reference.ActualType != 1182 ACPI_TYPE_DEVICE) { 1183 cmn_err(CE_WARN, "!acpidev: element %d in package " 1184 "returned by %s of %s doesn't refer to device.", 1185 i, method, objname); 1186 AcpiOsFree(buf.Pointer); 1187 acpidev_free_object_name(objname); 1188 return (AE_ERROR); 1189 } 1190 } 1191 1192 for (i = 0; i < obj->Package.Count; i++) { 1193 if (obj->Package.Elements[i].Reference.Handle == NULL) { 1194 cmn_err(CE_WARN, "!acpidev: handle of element %d in " 1195 "package returned by %s of %s is NULL.", 1196 i, method, objname); 1197 continue; 1198 } 1199 rc = (*cb)(obj->Package.Elements[i].Reference.Handle, 1200 UINT32_MAX, arg, retval); 1201 if (rc == AE_CTRL_DEPTH || rc == AE_CTRL_TERMINATE) { 1202 rc = AE_OK; 1203 } 1204 if (ACPI_FAILURE(rc)) { 1205 break; 1206 } 1207 } 1208 1209 AcpiOsFree(buf.Pointer); 1210 acpidev_free_object_name(objname); 1211 1212 return (rc); 1213 } 1214 1215 ACPI_STATUS 1216 acpidev_dr_device_walk_ejd(ACPI_HANDLE hdl, 1217 ACPI_WALK_CALLBACK cb, void *arg, void **retval) 1218 { 1219 ACPI_STATUS rc = AE_OK; 1220 char *objname; 1221 ACPI_OBJECT *obj; 1222 ACPI_BUFFER buf; 1223 ACPI_HANDLE chdl; 1224 char *method = ACPIDEV_METHOD_NAME_EJD; 1225 1226 ASSERT(hdl != NULL); 1227 ASSERT(cb != NULL); 1228 if (hdl == NULL || cb == NULL) { 1229 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 1230 "acpidev_dr_device_walk_ejd()."); 1231 return (AE_BAD_PARAMETER); 1232 } 1233 1234 objname = acpidev_get_object_name(hdl); 1235 buf.Length = ACPI_ALLOCATE_BUFFER; 1236 rc = AcpiEvaluateObjectTyped(hdl, method, NULL, &buf, 1237 ACPI_TYPE_STRING); 1238 if (rc == AE_NOT_FOUND) { 1239 acpidev_free_object_name(objname); 1240 return (AE_OK); 1241 } else if (ACPI_FAILURE(rc)) { 1242 cmn_err(CE_WARN, 1243 "!acpidev: failed to evaluate method %s under %s.", 1244 method, objname); 1245 acpidev_free_object_name(objname); 1246 return (AE_ERROR); 1247 } 1248 1249 obj = buf.Pointer; 1250 ASSERT(obj->String.Pointer); 1251 if (ACPI_FAILURE(AcpiGetHandle(NULL, obj->String.Pointer, &chdl))) { 1252 cmn_err(CE_WARN, "!acpidev: failed to get handle for %s.", 1253 obj->String.Pointer); 1254 rc = AE_ERROR; 1255 } else { 1256 rc = (*cb)(chdl, UINT32_MAX, arg, retval); 1257 if (rc == AE_CTRL_DEPTH || rc == AE_CTRL_TERMINATE) { 1258 rc = AE_OK; 1259 } 1260 } 1261 1262 AcpiOsFree(buf.Pointer); 1263 acpidev_free_object_name(objname); 1264 1265 return (rc); 1266 } 1267 1268 /* 1269 * Walk all child devices and special devices in the eject device list. 1270 */ 1271 static ACPI_STATUS 1272 acpidev_dr_device_walk_child(ACPI_HANDLE hdl, boolean_t init, uint_t max_lvl, 1273 ACPI_WALK_CALLBACK cb, void *arg, void **retval) 1274 { 1275 ACPI_STATUS rc = AE_OK; 1276 1277 ASSERT(hdl != NULL); 1278 ASSERT(cb != NULL); 1279 if (hdl == NULL || cb == NULL) { 1280 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 1281 "acpidev_dr_device_walk_child()."); 1282 return (AE_BAD_PARAMETER); 1283 } 1284 1285 /* 1286 * Walk the eject device list first when destroying. 1287 * According to ACPI spec, devices in _EDL list must be handled first 1288 * when the ejecting device. 1289 */ 1290 if (init == B_FALSE) { 1291 rc = acpidev_dr_device_walk_edl(hdl, cb, arg, retval); 1292 if (ACPI_FAILURE(rc)) { 1293 ACPIDEV_DEBUG(CE_NOTE, 1294 "!acpidev: failed to walk eject device list in " 1295 "acpidev_dr_device_walk_child()."); 1296 } 1297 } 1298 1299 /* Walk all child ACPI DEVICE objects. */ 1300 if (ACPI_SUCCESS(rc)) { 1301 rc = AcpiWalkNamespace(ACPI_TYPE_DEVICE, hdl, 1302 max_lvl, cb, NULL, arg, retval); 1303 if (ACPI_FAILURE(rc)) { 1304 ACPIDEV_DEBUG(CE_NOTE, 1305 "!acpidev: failed to walk DEVICE objects in " 1306 "acpidev_dr_device_walk_child()."); 1307 } 1308 } 1309 1310 /* Walk all child ACPI PROCESSOR objects. */ 1311 if (ACPI_SUCCESS(rc)) { 1312 rc = AcpiWalkNamespace(ACPI_TYPE_PROCESSOR, hdl, 1313 max_lvl, cb, NULL, arg, retval); 1314 if (ACPI_FAILURE(rc)) { 1315 ACPIDEV_DEBUG(CE_NOTE, 1316 "!acpidev: failed to walk PROCESSOR objects in " 1317 "acpidev_dr_device_walk_child()."); 1318 } 1319 } 1320 1321 /* 1322 * Walk the eject device list last when initializing. 1323 */ 1324 if (init == B_TRUE && ACPI_SUCCESS(rc)) { 1325 rc = acpidev_dr_device_walk_edl(hdl, cb, arg, retval); 1326 if (ACPI_FAILURE(rc)) { 1327 ACPIDEV_DEBUG(CE_NOTE, 1328 "!acpidev: failed to walk eject device list in " 1329 "acpidev_dr_device_walk_child()."); 1330 } 1331 } 1332 1333 return (rc); 1334 } 1335 1336 ACPI_STATUS 1337 acpidev_dr_device_walk_device(ACPI_HANDLE hdl, uint_t max_lvl, 1338 ACPI_WALK_CALLBACK cb, void *arg, void **retval) 1339 { 1340 ACPI_STATUS rc = AE_OK; 1341 char *objname; 1342 1343 ASSERT(hdl != NULL); 1344 ASSERT(cb != NULL); 1345 if (hdl == NULL || cb == NULL) { 1346 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 1347 "acpidev_dr_walk_device()."); 1348 return (AE_BAD_PARAMETER); 1349 } 1350 1351 /* Walk the top object itself first. */ 1352 rc = (*cb)(hdl, 0, arg, retval); 1353 if (rc == AE_CTRL_DEPTH || rc == AE_CTRL_TERMINATE) { 1354 rc = AE_OK; 1355 } else if (ACPI_FAILURE(rc)) { 1356 objname = acpidev_get_object_name(hdl); 1357 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to handle top node %s " 1358 "in acpidev_dr_walk_device().", objname); 1359 acpidev_free_object_name(objname); 1360 } else { 1361 rc = acpidev_dr_device_walk_child(hdl, B_TRUE, max_lvl, 1362 cb, arg, retval); 1363 if (ACPI_FAILURE(rc)) { 1364 objname = acpidev_get_object_name(hdl); 1365 ACPIDEV_DEBUG(CE_WARN, 1366 "!acpidev: failed to handle descendant nodes of %s " 1367 "in acpidev_dr_walk_device().", objname); 1368 acpidev_free_object_name(objname); 1369 } 1370 } 1371 1372 return (rc); 1373 } 1374 1375 static ACPI_STATUS 1376 acpidev_dr_no_support(ACPI_HANDLE hdl, UINT32 lvl, void *arg, void **retval) 1377 { 1378 _NOTE(ARGUNUSED(arg, retval)); 1379 1380 char *objname; 1381 1382 ASSERT(hdl != NULL); 1383 1384 objname = acpidev_get_object_name(hdl); 1385 ACPIDEV_DEBUG(CE_NOTE, 1386 "!acpidev: device %s at level 0x%x is unsupported.", 1387 objname, lvl); 1388 acpidev_free_object_name(objname); 1389 1390 return (AE_SUPPORT); 1391 } 1392 1393 static ACPI_STATUS 1394 acpidev_dr_set_prop(ACPI_HANDLE hdl, char *objname, 1395 struct acpidev_dr_set_prop_arg *ap, uint32_t lvl, 1396 acpidev_class_id_t clsid, uint_t *devid) 1397 { 1398 acpidev_data_handle_t dhdl; 1399 1400 /* Create data handle first if it doesn't exist yet. */ 1401 dhdl = acpidev_data_get_handle(hdl); 1402 if (dhdl == NULL) { 1403 uint32_t rlvl; 1404 ACPI_HANDLE phdl; 1405 1406 /* 1407 * Compute level by walking ACPI namespace if it's a device 1408 * from the eject device list. 1409 */ 1410 if (lvl == UINT32_MAX) { 1411 /* 1412 * AcpiGetParent() fails when it tries to get 1413 * the parent of the ACPI namespace root node. 1414 */ 1415 for (rlvl = 0, phdl = hdl; 1416 ACPI_SUCCESS(AcpiGetParent(phdl, &phdl)); 1417 rlvl++) { 1418 if (phdl == ACPI_ROOT_OBJECT) { 1419 break; 1420 } 1421 } 1422 if (rlvl == 0) { 1423 ACPIDEV_DEBUG(CE_WARN, 1424 "!acpidev: failed to get level of %s.", 1425 objname); 1426 return (AE_BAD_PARAMETER); 1427 } 1428 } else { 1429 rlvl = ap->level; 1430 } 1431 if (rlvl >= ACPIDEV_MAX_ENUM_LEVELS) { 1432 ACPIDEV_DEBUG(CE_WARN, 1433 "!acpidev: recursive level of %s is too deep.", 1434 objname); 1435 return (AE_SUPPORT); 1436 } 1437 1438 dhdl = acpidev_data_create_handle(hdl); 1439 if (dhdl != NULL) { 1440 dhdl->aod_hdl = hdl; 1441 dhdl->aod_level = rlvl; 1442 } 1443 } 1444 1445 if (dhdl == NULL) { 1446 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to create data handle " 1447 "for device %s.", objname); 1448 return (AE_NO_MEMORY); 1449 } 1450 1451 if (ACPIDEV_DR_IS_READY(dhdl)) { 1452 /* 1453 * The same device may be enumerated twice at most. Once as 1454 * child devices, another time from the eject device list. 1455 */ 1456 if (dhdl->aod_bdnum == ap->bdnum) { 1457 return (AE_OK); 1458 } else { 1459 /* 1460 * A device has been enumerated more than once from 1461 * different paths. It's dangerous to support such 1462 * a topology. Disable support of DR operations. 1463 */ 1464 ACPIDEV_DEBUG(CE_WARN, "!acpidev: device %s has been " 1465 "enumerated more than once for DR.", objname); 1466 acpidev_dr_failed = 1; 1467 return (AE_SUPPORT); 1468 } 1469 } 1470 1471 /* Set properties for DR operations. */ 1472 dhdl->aod_class_id = clsid; 1473 dhdl->aod_bdnum = ap->bdnum; 1474 dhdl->aod_portid = atomic_inc_32_nv(devid) - 1; 1475 if (clsid == ACPIDEV_CLASS_ID_MEMORY) { 1476 dhdl->aod_memidx = acpidev_dr_memory_device_cnt; 1477 ASSERT(dhdl->aod_memidx < ACPI_MEMNODE_DEVID_BOOT); 1478 } 1479 ACPIDEV_DR_SET_READY(dhdl); 1480 1481 return (AE_OK); 1482 } 1483 1484 /* 1485 * Verify whether the hardware topology is supported by the DR driver. 1486 * The ACPI specification is so flexible that for safety reasons, only 1487 * a few well defined topologies are supported. 1488 * Possible values of parameter lvl: 1489 * 0: the device is the board itself. 1490 * UINT32_MAX: the device is from the _EDL list of the board. 1491 * other: the device is a descendant of the board. 1492 * Return values: 1493 * AE_OK: the topology is supported 1494 * AE_SUPPORT: the topology is unsupported 1495 * AE_ERROR: other errors 1496 */ 1497 static ACPI_STATUS 1498 acpidev_dr_scan_topo(ACPI_HANDLE hdl, UINT32 lvl, void *arg, void **retval) 1499 { 1500 _NOTE(ARGUNUSED(retval)); 1501 1502 ACPI_STATUS rc = AE_OK; 1503 char *objname; 1504 acpidev_class_id_t cid; 1505 struct acpidev_dr_set_prop_arg *ap = arg; 1506 1507 ASSERT(hdl != NULL); 1508 ASSERT(lvl == 0 || lvl == 1 || lvl == UINT32_MAX); 1509 objname = acpidev_get_object_name(hdl); 1510 1511 /* 1512 * Validate descendants of the hotplug capable board. 1513 * lvl is zero if it's the hotplug capable board itself, otherwise 1514 * non-zero for descendants. 1515 */ 1516 if (lvl != 0) { 1517 /* 1518 * Skip subtree if the device is hotplug capable. 1519 * It will be treated as another hotplug capable board. 1520 */ 1521 if (acpidev_dr_device_hotplug_capable(hdl)) { 1522 acpidev_free_object_name(objname); 1523 return (AE_CTRL_DEPTH); 1524 } 1525 1526 /* 1527 * Don't support the _EDL list of a non-hotplug-capable device. 1528 */ 1529 if (acpidev_dr_device_has_edl(hdl)) { 1530 ACPIDEV_DEBUG(CE_NOTE, "!acpidev: non-hotplug-capable " 1531 "object %s has _EDL method.", objname); 1532 acpidev_free_object_name(objname); 1533 return (AE_SUPPORT); 1534 } 1535 } 1536 1537 cid = acpidev_dr_device_get_class(hdl); 1538 switch (cid) { 1539 case ACPIDEV_CLASS_ID_CPU: 1540 /* Don't support logical CPUs in the _EDL list. */ 1541 if (lvl == UINT32_MAX) { 1542 ACPIDEV_DEBUG(CE_WARN, "!acpidev: logical CPU %s in " 1543 "_EDL is unsupported.", objname); 1544 rc = AE_SUPPORT; 1545 break; 1546 } 1547 1548 /* Don't support logical CPUs with children. */ 1549 ap->level++; 1550 rc = acpidev_dr_device_walk_child(hdl, B_TRUE, 1, 1551 acpidev_dr_no_support, arg, NULL); 1552 ap->level--; 1553 if (rc == AE_SUPPORT) { 1554 ACPIDEV_DEBUG(CE_NOTE, "!acpidev: logical CPU %s has " 1555 "child or dependent devices.", objname); 1556 break; 1557 } else if (ACPI_FAILURE(rc)) { 1558 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to scan " 1559 "children of logical CPU %s.", objname); 1560 rc = AE_ERROR; 1561 break; 1562 } else if (ap != NULL) { 1563 rc = acpidev_dr_set_prop(hdl, objname, ap, lvl, 1564 ACPIDEV_CLASS_ID_CPU, &ap->cpu_id); 1565 } 1566 break; 1567 1568 case ACPIDEV_CLASS_ID_MEMORY: 1569 /* Don't support memory devices with children. */ 1570 ap->level++; 1571 rc = acpidev_dr_device_walk_child(hdl, B_TRUE, 1, 1572 acpidev_dr_no_support, arg, NULL); 1573 ap->level--; 1574 if (rc == AE_SUPPORT) { 1575 ACPIDEV_DEBUG(CE_NOTE, 1576 "!acpidev: memory device %s has child or " 1577 "dependent devices.", objname); 1578 } else if (ACPI_FAILURE(rc)) { 1579 ACPIDEV_DEBUG(CE_WARN, 1580 "!acpidev: failed to scan children of " 1581 "memory device %s.", objname); 1582 rc = AE_ERROR; 1583 } else if (ap != NULL) { 1584 acpidev_dr_memory_device_cnt++; 1585 rc = acpidev_dr_set_prop(hdl, objname, ap, lvl, 1586 ACPIDEV_CLASS_ID_MEMORY, &ap->mem_id); 1587 } 1588 break; 1589 1590 case ACPIDEV_CLASS_ID_PCI: 1591 case ACPIDEV_CLASS_ID_PCIEX: 1592 /* Don't scan child/descendant devices of PCI/PCIex devices. */ 1593 if (ap != NULL) { 1594 rc = acpidev_dr_set_prop(hdl, objname, ap, lvl, 1595 cid, &ap->io_id); 1596 } 1597 break; 1598 1599 case ACPIDEV_CLASS_ID_CONTAINER: 1600 /* Don't support module devices in the _EDL list. */ 1601 if (lvl == UINT32_MAX) { 1602 ACPIDEV_DEBUG(CE_WARN, "!acpidev: module device %s in " 1603 "_EDL is unsupported.", objname); 1604 rc = AE_SUPPORT; 1605 break; 1606 } 1607 1608 /* Don't support recurrence of module devices. */ 1609 if (lvl > 0) { 1610 ACPIDEV_DEBUG(CE_NOTE, "!acpidev: recursion level of " 1611 "module device %s is too deep.", objname); 1612 rc = AE_SUPPORT; 1613 break; 1614 } 1615 1616 ap->level++; 1617 rc = acpidev_dr_device_walk_child(hdl, B_TRUE, 1, 1618 acpidev_dr_scan_topo, arg, NULL); 1619 ap->level--; 1620 if (ACPI_SUCCESS(rc) && ap != NULL) { 1621 rc = acpidev_dr_set_prop(hdl, objname, ap, lvl, 1622 ACPIDEV_CLASS_ID_CONTAINER, &ap->mod_id); 1623 } 1624 break; 1625 1626 case ACPIDEV_CLASS_ID_INVALID: 1627 /*FALLTHROUGH*/ 1628 default: 1629 ACPIDEV_DEBUG(CE_NOTE, 1630 "!acpidev: device %s is unsupported.", objname); 1631 rc = AE_SUPPORT; 1632 break; 1633 } 1634 1635 acpidev_free_object_name(objname); 1636 1637 return (rc); 1638 } 1639 1640 /* Create walk information structures. */ 1641 static ACPI_STATUS 1642 acpidev_dr_create_walk_info(ACPI_HANDLE hdl, acpidev_data_handle_t dhdl, 1643 char *objname, acpidev_walk_info_t **infopp, acpidev_walk_info_t **cinfopp) 1644 { 1645 ACPI_HANDLE phdl = NULL; 1646 dev_info_t *pdip = NULL; 1647 acpidev_data_handle_t pdhdl, tdhdl; 1648 acpidev_walk_info_t *infop = NULL, *cinfop = NULL; 1649 1650 ASSERT(hdl != NULL); 1651 ASSERT(dhdl != NULL); 1652 ASSERT(dhdl->aod_class_list != NULL); 1653 ASSERT(objname != NULL); 1654 ASSERT(infopp != NULL); 1655 ASSERT(cinfopp != NULL); 1656 1657 if (ACPI_FAILURE(AcpiGetParent(hdl, &phdl))) { 1658 cmn_err(CE_WARN, 1659 "!acpidev: failed to get parent object of %s.", objname); 1660 return (AE_ERROR); 1661 } 1662 1663 pdhdl = acpidev_data_get_handle(phdl); 1664 if (pdhdl == NULL) { 1665 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to get data " 1666 "associated with parent of %s.", objname); 1667 return (AE_ERROR); 1668 } 1669 if (pdhdl->aod_level >= ACPIDEV_MAX_ENUM_LEVELS - 1) { 1670 ACPIDEV_DEBUG(CE_WARN, 1671 "!acpidev: recursion level (%d) of %s is too deep.", 1672 pdhdl->aod_level, objname); 1673 return (AE_ERROR); 1674 } 1675 ASSERT(pdhdl->aod_class_list != NULL); 1676 if (pdhdl->aod_class_list == NULL) { 1677 ACPIDEV_DEBUG(CE_WARN, 1678 "!acpidev: class list for parent of %s is NULL.", objname); 1679 return (AE_ERROR); 1680 } 1681 1682 /* Allocate a walk info structure for its parent. */ 1683 infop = acpidev_alloc_walk_info(ACPIDEV_OP_HOTPLUG_PROBE, 1684 pdhdl->aod_level, phdl, dhdl->aod_class_list, NULL); 1685 if (infop == NULL) { 1686 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to allocate walk info " 1687 "structure for parent of %s.", objname); 1688 return (AE_ERROR); 1689 } 1690 1691 /* Get the parent dip if it's not ready yet. */ 1692 while (infop->awi_dip == NULL) { 1693 if (ACPI_FAILURE(AcpiGetParent(phdl, &phdl))) { 1694 ACPIDEV_DEBUG(CE_WARN, 1695 "!acpidev: failed to get parent of object %p.", 1696 phdl); 1697 break; 1698 } 1699 tdhdl = acpidev_data_get_handle(phdl); 1700 if (tdhdl == NULL) { 1701 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to get data " 1702 "associated with object %p.", phdl); 1703 break; 1704 } 1705 pdip = acpidev_data_get_devinfo(tdhdl); 1706 if (pdip != NULL) { 1707 infop->awi_dip = pdip; 1708 break; 1709 } 1710 /* Give up if reaches the ACPI namespace root node. */ 1711 if (phdl == ACPI_ROOT_OBJECT) { 1712 break; 1713 } 1714 } 1715 if (infop->awi_dip == NULL) { 1716 ACPIDEV_DEBUG(CE_WARN, 1717 "!acpidev: failed to get parent dip of %s.", objname); 1718 acpidev_free_walk_info(infop); 1719 return (AE_ERROR); 1720 } 1721 1722 /* Allocate a walk info for the child. */ 1723 cinfop = acpidev_alloc_walk_info(ACPIDEV_OP_HOTPLUG_PROBE, 1724 infop->awi_level + 1, hdl, NULL, infop); 1725 if (cinfop == NULL) { 1726 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to allocate walk info " 1727 "structure for %s.", objname); 1728 acpidev_free_walk_info(infop); 1729 return (AE_ERROR); 1730 } 1731 1732 *infopp = infop; 1733 *cinfopp = cinfop; 1734 1735 return (AE_OK); 1736 } 1737 1738 static ACPI_STATUS 1739 acpidev_dr_probe_object(ACPI_HANDLE hdl, acpidev_data_handle_t dhdl) 1740 { 1741 ACPI_STATUS rc = AE_OK; 1742 int circ; 1743 char *objname; 1744 dev_info_t *pdip; 1745 ACPI_STATUS res; 1746 ACPI_OBJECT_TYPE type; 1747 acpidev_class_list_t *it; 1748 acpidev_walk_info_t *infop, *cinfop; 1749 1750 ASSERT(hdl != NULL); 1751 ASSERT(dhdl != NULL); 1752 if (hdl == NULL || dhdl == NULL) { 1753 ACPIDEV_DEBUG(CE_WARN, "!acpidev: hdl or dhdl is NULL in " 1754 "acpidev_dr_probe_object()."); 1755 return (AE_BAD_PARAMETER); 1756 } 1757 objname = acpidev_get_object_name(hdl); 1758 1759 /* Check whether the device is of interest. */ 1760 if (ACPI_FAILURE(AcpiGetType(hdl, &type)) || 1761 type > ACPI_TYPE_NS_NODE_MAX || 1762 BT_TEST(acpidev_object_type_mask, type) == 0) { 1763 ACPIDEV_DEBUG(CE_WARN, 1764 "!acpidev: ACPI object %s is unsupported.", objname); 1765 acpidev_free_object_name(objname); 1766 return (AE_SUPPORT); 1767 } 1768 1769 if (dhdl->aod_class_list == NULL) { 1770 ACPIDEV_DEBUG(CE_WARN, 1771 "!acpidev: class list is NULL in data associated with %s.", 1772 objname); 1773 acpidev_free_object_name(objname); 1774 return (AE_ERROR); 1775 } 1776 1777 pdip = NULL; 1778 infop = NULL; 1779 cinfop = NULL; 1780 rc = acpidev_dr_create_walk_info(hdl, dhdl, objname, &infop, &cinfop); 1781 if (ACPI_FAILURE(rc)) { 1782 ACPIDEV_DEBUG(CE_WARN, 1783 "!acpidev: failed to create walk info structures for %s.", 1784 objname); 1785 acpidev_free_object_name(objname); 1786 return (rc); 1787 } 1788 ASSERT(infop != NULL); 1789 ASSERT(infop->awi_dip != NULL); 1790 ASSERT(infop->awi_class_list != NULL); 1791 ASSERT(cinfop != NULL); 1792 ASSERT(cinfop->awi_data == dhdl); 1793 1794 /* Lock the parent dip before touching children. */ 1795 pdip = infop->awi_dip; 1796 ndi_devi_enter(pdip, &circ); 1797 rw_enter(&acpidev_class_lock, RW_READER); 1798 1799 /* Call pre-probe callback functions to prepare for probing. */ 1800 for (it = *(infop->awi_class_list); it != NULL; it = it->acl_next) { 1801 if (it->acl_class->adc_pre_probe == NULL) { 1802 continue; 1803 } 1804 infop->awi_class_curr = it->acl_class; 1805 if (ACPI_FAILURE(it->acl_class->adc_pre_probe(infop))) { 1806 ACPIDEV_DEBUG(CE_NOTE, "!acpidev: failed to pre-probe " 1807 "device of type %s under %s.", 1808 it->acl_class->adc_class_name, infop->awi_name); 1809 } 1810 } 1811 1812 /* Call registered probe callback functions to probe devices. */ 1813 for (it = *(infop->awi_class_list); it != NULL; it = it->acl_next) { 1814 if (it->acl_class->adc_probe == NULL) { 1815 continue; 1816 } 1817 cinfop->awi_class_curr = it->acl_class; 1818 res = it->acl_class->adc_probe(cinfop); 1819 if (ACPI_FAILURE(res)) { 1820 rc = res; 1821 ACPIDEV_DEBUG(CE_NOTE, 1822 "!acpidev: failed to process object %s under %s.", 1823 objname, infop->awi_name); 1824 } 1825 } 1826 1827 /* Call post-probe callback functions to clean up. */ 1828 for (it = *(infop->awi_class_list); it != NULL; it = it->acl_next) { 1829 if (it->acl_class->adc_post_probe == NULL) { 1830 continue; 1831 } 1832 infop->awi_class_curr = it->acl_class; 1833 if (ACPI_FAILURE(it->acl_class->adc_post_probe(infop))) { 1834 ACPIDEV_DEBUG(CE_NOTE, "!acpidev: failed to post-probe " 1835 "device of type %s under %s.", 1836 it->acl_class->adc_class_name, infop->awi_name); 1837 } 1838 } 1839 1840 rw_exit(&acpidev_class_lock); 1841 ndi_devi_exit(pdip, circ); 1842 1843 acpidev_free_walk_info(cinfop); 1844 acpidev_free_walk_info(infop); 1845 acpidev_free_object_name(objname); 1846 1847 return (rc); 1848 } 1849 1850 /* 1851 * Some PCI/PCIex buses embedded in physical processors may be presented in 1852 * the eject device list instead of being presented as child devices. 1853 * This function figures out such devices and create device nodes for them. 1854 */ 1855 static ACPI_STATUS 1856 acpidev_dr_probe_dependent(ACPI_HANDLE hdl, UINT32 lvl, void *ctx, 1857 void **retval) 1858 { 1859 _NOTE(ARGUNUSED(retval)); 1860 1861 ACPI_STATUS rc = AE_OK; 1862 int status; 1863 char *objname; 1864 ACPI_HANDLE phdl, thdl; 1865 acpidev_data_handle_t dhdl; 1866 1867 ASSERT(lvl == UINT32_MAX); 1868 ASSERT(hdl != NULL); 1869 ASSERT(ctx != NULL); 1870 phdl = ctx; 1871 objname = acpidev_get_object_name(hdl); 1872 1873 dhdl = acpidev_data_get_handle(hdl); 1874 if (dhdl == NULL) { 1875 ACPIDEV_DEBUG(CE_WARN, 1876 "!acpidev: failed to get data associated with %s.", 1877 objname); 1878 acpidev_free_object_name(objname); 1879 return (AE_ERROR); 1880 } 1881 1882 /* 1883 * It should be treated as another board if device is hotplug capable. 1884 */ 1885 if (ACPIDEV_DR_IS_BOARD(dhdl)) { 1886 acpidev_free_object_name(objname); 1887 return (AE_OK); 1888 } else if (!ACPIDEV_DR_IS_WORKING(dhdl)) { 1889 ACPIDEV_DEBUG(CE_WARN, 1890 "!acpidev: %s is unusable for DR operations.", objname); 1891 acpidev_free_object_name(objname); 1892 return (AE_SUPPORT); 1893 } 1894 1895 /* 1896 * Skip hdl if it's a descendant of phdl because it should have 1897 * already been handled when handling phdl itself. 1898 */ 1899 for (thdl = hdl; ACPI_SUCCESS(AcpiGetParent(thdl, &thdl)); ) { 1900 /* Return when reaches the phdl. */ 1901 if (thdl == phdl) { 1902 acpidev_free_object_name(objname); 1903 return (AE_OK); 1904 } 1905 /* Break out when reaches the ACPI namespace root node. */ 1906 if (thdl == ACPI_ROOT_OBJECT) { 1907 break; 1908 } 1909 } 1910 1911 /* 1912 * No support of enumerating PCI/PCIex Host Bridge devices yet. 1913 * It will be enabled when PCI/PCIex Host Bridge hotplug is ready. 1914 */ 1915 if (dhdl->aod_class_id == ACPIDEV_CLASS_ID_PCI || 1916 dhdl->aod_class_id == ACPIDEV_CLASS_ID_PCIEX) { 1917 ACPIDEV_DEBUG(CE_WARN, "!acpidev: PCI/PCIEX host bridge %s is " 1918 "unsupported, skip it.", objname); 1919 acpidev_free_object_name(objname); 1920 return (AE_OK); 1921 } 1922 1923 /* Check whether the device exists and has been enabled. */ 1924 status = acpidev_query_device_status(hdl); 1925 if (!acpidev_check_device_enabled(status)) { 1926 ACPIDEV_DEBUG(CE_NOTE, "!acpidev: object %s is disabled/absent " 1927 "when trying to connect it.", objname); 1928 acpidev_free_object_name(objname); 1929 return (AE_OK); 1930 } 1931 1932 /* Probe the device and its children. */ 1933 rc = acpidev_dr_probe_object(hdl, dhdl); 1934 if (ACPI_FAILURE(rc)) { 1935 ACPIDEV_DEBUG(CE_WARN, 1936 "!acpidev: failed to probe object %s in eject device list.", 1937 objname); 1938 return (rc); 1939 } 1940 1941 return (AE_OK); 1942 } 1943 1944 ACPI_STATUS 1945 acpidev_dr_device_insert(ACPI_HANDLE hdl) 1946 { 1947 ACPI_STATUS rc = AE_OK; 1948 int status, circ; 1949 char *objname; 1950 dev_info_t *dip; 1951 acpidev_data_handle_t dhdl; 1952 1953 ASSERT(acpidev_root_node() != NULL); 1954 ASSERT(hdl != NULL); 1955 if (hdl == NULL) { 1956 ACPIDEV_DEBUG(CE_WARN, "!acpidev: parameter hdl to " 1957 "acpidev_dr_insert_insert() is NULL."); 1958 return (AE_BAD_PARAMETER); 1959 } 1960 1961 objname = acpidev_get_object_name(hdl); 1962 dhdl = acpidev_data_get_handle(hdl); 1963 if (dhdl == NULL) { 1964 ACPIDEV_DEBUG(CE_WARN, 1965 "!acpidev: failed to get data handle associated with %s.", 1966 objname); 1967 acpidev_free_object_name(objname); 1968 return (AE_ERROR); 1969 } 1970 1971 /* Validate that the object is hotplug capable. */ 1972 if (!ACPIDEV_DR_BOARD_READY(dhdl)) { 1973 ACPIDEV_DEBUG(CE_WARN, 1974 "!acpidev: object %s is not hotplug capable.", objname); 1975 acpidev_free_object_name(objname); 1976 return (AE_SUPPORT); 1977 } else if (ACPIDEV_DR_IS_FAILED(dhdl)) { 1978 ACPIDEV_DEBUG(CE_WARN, "!acpidev: object %s is in the FAILED " 1979 "state, unusable for DR.", objname); 1980 acpidev_free_object_name(objname); 1981 return (AE_ERROR); 1982 } 1983 1984 /* Check whether the device exists and has been enabled. */ 1985 status = acpidev_query_device_status(hdl); 1986 if (!acpidev_check_device_enabled(status)) { 1987 ACPIDEV_DEBUG(CE_WARN, "!acpidev: object %s is disabled/absent " 1988 "when trying to connect it.", objname); 1989 acpidev_free_object_name(objname); 1990 return (AE_NOT_EXIST); 1991 } 1992 1993 /* Check that there's no device node created for object yet. */ 1994 dip = acpidev_data_get_devinfo(dhdl); 1995 if (dip != NULL) { 1996 ACPIDEV_DEBUG(CE_WARN, "!acpidev: device node for object %s " 1997 "already exists when trying to connect it.", objname); 1998 acpidev_free_object_name(objname); 1999 return (AE_ALREADY_EXISTS); 2000 } 2001 2002 /* 2003 * Solaris has a limitation that all device nodes for PCI/PCIex host 2004 * bridges must exist directly under /devices. 2005 * Special care is needed here to deal with hot-adding PCI/PCIex host 2006 * bridges to avoid dead lock caused by ndi_devi_enter(). 2007 * Here the lock on ddi_root_node() is held first, which will break 2008 * the dead lock loop. 2009 */ 2010 ndi_devi_enter(ddi_root_node(), &circ); 2011 2012 rc = acpidev_dr_probe_object(hdl, dhdl); 2013 if (ACPI_SUCCESS(rc)) { 2014 rc = acpidev_dr_device_walk_edl(hdl, 2015 &acpidev_dr_probe_dependent, hdl, NULL); 2016 } 2017 if (ACPI_FAILURE(rc)) { 2018 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to create device " 2019 "nodes for children of %s.", objname); 2020 cmn_err(CE_WARN, "!acpidev: disable DR support for object %s " 2021 "due to failure when creating device nodes for it.", 2022 objname); 2023 ACPIDEV_DR_SET_FAILED(dhdl); 2024 } 2025 2026 ndi_devi_exit(ddi_root_node(), circ); 2027 acpidev_free_object_name(objname); 2028 2029 return (rc); 2030 } 2031 2032 static ACPI_STATUS 2033 acpidev_dr_device_remove_cb(ACPI_HANDLE hdl, UINT32 lvl, void *ctx, 2034 void **retval) 2035 { 2036 _NOTE(ARGUNUSED(lvl)); 2037 2038 ACPI_STATUS rc = AE_OK; 2039 int status; 2040 char *objname; 2041 dev_info_t *dip; 2042 acpidev_data_handle_t dhdl; 2043 struct acpidev_dr_device_remove_arg *argp; 2044 2045 ASSERT(hdl != NULL && ctx != NULL); 2046 if (hdl == NULL || ctx == NULL) { 2047 ACPIDEV_DEBUG(CE_WARN, "!acpidev: parameter to " 2048 "acpidev_dr_device_remove_cb() is NULL."); 2049 return (AE_BAD_PARAMETER); 2050 } 2051 2052 argp = (struct acpidev_dr_device_remove_arg *)ctx; 2053 objname = acpidev_get_object_name(hdl); 2054 dhdl = acpidev_data_get_handle(hdl); 2055 if (dhdl == NULL) { 2056 ACPIDEV_DEBUG(CE_WARN, 2057 "!acpidev: failed to get data handle associated with %s.", 2058 objname); 2059 acpidev_free_object_name(objname); 2060 return (AE_ERROR); 2061 } 2062 2063 /* Validate that the object is hotplug capable. */ 2064 /* It's the hotplug capable board itself if level is zero. */ 2065 if (argp->level == 0) { 2066 if (!ACPIDEV_DR_BOARD_READY(dhdl)) { 2067 ACPIDEV_DEBUG(CE_WARN, 2068 "!acpidev: object %s is not hotplug capable.", 2069 objname); 2070 acpidev_free_object_name(objname); 2071 return (AE_SUPPORT); 2072 } else if (ACPIDEV_DR_IS_FAILED(dhdl)) { 2073 ACPIDEV_DEBUG(CE_WARN, 2074 "!acpidev: object %s is unusable for DR.", objname); 2075 acpidev_free_object_name(objname); 2076 return (AE_SUPPORT); 2077 } 2078 } else { 2079 /* It's a device under the hotplug capable board. */ 2080 /* 2081 * Skip it if device itself is hotplug capable. 2082 * It will be treated as another hotplug capable board. 2083 */ 2084 if (ACPIDEV_DR_IS_BOARD(dhdl)) { 2085 acpidev_free_object_name(objname); 2086 return (AE_OK); 2087 } 2088 2089 if (!ACPIDEV_DR_IS_READY(dhdl)) { 2090 ACPIDEV_DEBUG(CE_WARN, 2091 "!acpidev: object %s is not hotplug capable.", 2092 objname); 2093 acpidev_free_object_name(objname); 2094 return (AE_SUPPORT); 2095 } else if (ACPIDEV_DR_IS_FAILED(dhdl)) { 2096 ACPIDEV_DEBUG(CE_WARN, 2097 "!acpidev: object %s is unusable for DR.", objname); 2098 acpidev_free_object_name(objname); 2099 return (AE_SUPPORT); 2100 } 2101 } 2102 2103 /* Skip the device if it hasn't been enabled at all. */ 2104 status = acpidev_data_get_status(dhdl); 2105 if (!acpidev_check_device_enabled(status)) { 2106 acpidev_free_object_name(objname); 2107 return (AE_OK); 2108 } 2109 2110 dip = acpidev_data_get_devinfo(dhdl); 2111 if (dip == NULL) { 2112 ACPIDEV_DEBUG(CE_WARN, 2113 "!acpidev: failed to get dev_info associated with %s.", 2114 objname); 2115 acpidev_free_object_name(objname); 2116 return (AE_SUPPORT); 2117 } 2118 2119 /* For safety, only handle supported device types when unconfiguring. */ 2120 switch (dhdl->aod_class_id) { 2121 case ACPIDEV_CLASS_ID_CONTAINER: 2122 /*FALLTHROUGH*/ 2123 case ACPIDEV_CLASS_ID_CPU: 2124 /*FALLTHROUGH*/ 2125 case ACPIDEV_CLASS_ID_MEMORY: 2126 break; 2127 2128 default: 2129 ACPIDEV_DEBUG(CE_WARN, "!acpidev: object %s (type %d) doesn't " 2130 "support unconfiguration.", objname, dhdl->aod_class_id); 2131 acpidev_free_object_name(objname); 2132 return (AE_SUPPORT); 2133 } 2134 2135 /* Destroy descendants first. */ 2136 argp->level++; 2137 rc = acpidev_dr_device_walk_child(hdl, B_FALSE, 1, 2138 acpidev_dr_device_remove_cb, ctx, retval); 2139 argp->level--; 2140 if (ACPI_FAILURE(rc)) { 2141 ACPIDEV_DEBUG(CE_WARN, 2142 "!acpidev: failed to destroy descendants of %s.", objname); 2143 acpidev_free_object_name(objname); 2144 return (rc); 2145 } 2146 2147 /* Untag dip and ACPI object before destroying the dip. */ 2148 if ((dhdl->aod_iflag & ACPIDEV_ODF_DEVINFO_TAGGED) && 2149 ACPI_FAILURE(acpica_untag_devinfo(dip, hdl))) { 2150 ACPIDEV_DEBUG(CE_WARN, 2151 "!acpidev: failed to untag object %s.", objname); 2152 /* Mark the node as unusable. */ 2153 ACPIDEV_DR_SET_FAILED(dhdl); 2154 acpidev_free_object_name(objname); 2155 return (AE_ERROR); 2156 } 2157 2158 /* Destroy the node itself. */ 2159 if (e_ddi_branch_destroy(dip, NULL, 0) != 0) { 2160 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 2161 2162 if ((dhdl->aod_iflag & ACPIDEV_ODF_DEVINFO_TAGGED) && 2163 ACPI_FAILURE(acpica_tag_devinfo(dip, hdl))) { 2164 ACPIDEV_DEBUG(CE_WARN, 2165 "!acpidev: failed to retag object %s.", objname); 2166 } 2167 2168 /* Mark the node as unusable. */ 2169 ACPIDEV_DR_SET_FAILED(dhdl); 2170 2171 (void) ddi_pathname(dip, path); 2172 cmn_err(CE_WARN, 2173 "acpidev: failed to remove node %s (%s).", path, objname); 2174 kmem_free(path, MAXPATHLEN); 2175 acpidev_free_object_name(objname); 2176 2177 return (AE_ERROR); 2178 } 2179 2180 /* Update status and information associated with the device. */ 2181 dhdl->aod_dip = NULL; 2182 dhdl->aod_iflag &= ~ACPIDEV_ODF_DEVINFO_CREATED; 2183 dhdl->aod_iflag &= ~ACPIDEV_ODF_DEVINFO_TAGGED; 2184 if (dhdl->aod_class != NULL) { 2185 if (dhdl->aod_class->adc_fini != NULL) { 2186 (*(dhdl->aod_class->adc_fini))(hdl, dhdl, 2187 dhdl->aod_class); 2188 } 2189 atomic_dec_32(&(dhdl->aod_class->adc_refcnt)); 2190 dhdl->aod_class = NULL; 2191 } 2192 dhdl->aod_iflag &= ~ACPIDEV_ODF_STATUS_VALID; 2193 dhdl->aod_status = 0; 2194 2195 acpidev_free_object_name(objname); 2196 2197 return (AE_OK); 2198 } 2199 2200 ACPI_STATUS 2201 acpidev_dr_device_remove(ACPI_HANDLE hdl) 2202 { 2203 ACPI_STATUS rc = AE_OK; 2204 int circ; 2205 char *objname; 2206 acpidev_data_handle_t dhdl; 2207 struct acpidev_dr_device_remove_arg arg; 2208 2209 ASSERT(acpidev_root_node() != NULL); 2210 ASSERT(hdl != NULL); 2211 if (hdl == NULL) { 2212 ACPIDEV_DEBUG(CE_WARN, "!acpidev: parameter hdl to " 2213 "acpidev_dr_device_remove() is NULL."); 2214 return (AE_BAD_PARAMETER); 2215 } 2216 2217 objname = acpidev_get_object_name(hdl); 2218 dhdl = acpidev_data_get_handle(hdl); 2219 if (dhdl == NULL) { 2220 ACPIDEV_DEBUG(CE_WARN, 2221 "!acpidev: failed to get data handle associated with %s.", 2222 objname); 2223 acpidev_free_object_name(objname); 2224 return (AE_ERROR); 2225 } 2226 2227 /* Validate that the device is hotplug capable. */ 2228 if (!ACPIDEV_DR_BOARD_READY(dhdl)) { 2229 ACPIDEV_DEBUG(CE_WARN, 2230 "!acpidev: object %s is not hotplug capable.", objname); 2231 acpidev_free_object_name(objname); 2232 return (AE_SUPPORT); 2233 } else if (ACPIDEV_DR_IS_FAILED(dhdl)) { 2234 ACPIDEV_DEBUG(CE_WARN, "!acpidev: object %s is in the FAILED " 2235 "state, unusable for DR.", objname); 2236 acpidev_free_object_name(objname); 2237 return (AE_ERROR); 2238 } 2239 2240 /* 2241 * Recursively destroy descendants under the top node. 2242 * No need to undo what has been done if error happens, it will be 2243 * handled by DR driver. 2244 */ 2245 /* 2246 * Lock ddi_root_node() to avoid deadlock. 2247 */ 2248 ndi_devi_enter(ddi_root_node(), &circ); 2249 2250 arg.level = 0; 2251 rc = acpidev_dr_device_remove_cb(hdl, 0, &arg, NULL); 2252 ASSERT(arg.level == 0); 2253 if (ACPI_FAILURE(rc)) { 2254 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to destroy device " 2255 "nodes for children of %s.", objname); 2256 cmn_err(CE_WARN, "!acpidev: disable DR support for object %s " 2257 "due to failure when destroying device nodes for it.", 2258 objname); 2259 ACPIDEV_DR_SET_FAILED(dhdl); 2260 } 2261 2262 ndi_devi_exit(ddi_root_node(), circ); 2263 acpidev_free_object_name(objname); 2264 2265 return (rc); 2266 } 2267 2268 ACPI_STATUS 2269 acpidev_dr_device_poweron(ACPI_HANDLE hdl) 2270 { 2271 acpidev_data_handle_t dhdl; 2272 2273 dhdl = acpidev_data_get_handle(hdl); 2274 if (dhdl == NULL) { 2275 ACPIDEV_DEBUG(CE_WARN, 2276 "!acpidev: failed to get data handle associated with %p.", 2277 hdl); 2278 return (AE_ERROR); 2279 } 2280 2281 /* Check whether the device is hotplug capable. */ 2282 if (!ACPIDEV_DR_BOARD_READY(dhdl)) { 2283 ACPIDEV_DEBUG(CE_WARN, 2284 "!acpidev: object %p is not hotplug capable.", hdl); 2285 return (AE_SUPPORT); 2286 } else if (ACPIDEV_DR_IS_FAILED(dhdl)) { 2287 ACPIDEV_DEBUG(CE_WARN, "!acpidev: object %p is in the FAILED " 2288 "state, unusable for DR.", hdl); 2289 return (AE_ERROR); 2290 } 2291 2292 return (AE_OK); 2293 } 2294 2295 ACPI_STATUS 2296 acpidev_dr_device_poweroff(ACPI_HANDLE hdl) 2297 { 2298 ACPI_STATUS rc; 2299 acpidev_data_handle_t dhdl; 2300 2301 dhdl = acpidev_data_get_handle(hdl); 2302 if (dhdl == NULL) { 2303 ACPIDEV_DEBUG(CE_WARN, 2304 "!acpidev: failed to get data handle associated with %p.", 2305 hdl); 2306 return (AE_ERROR); 2307 } 2308 2309 /* Check whether the device is hotplug capable. */ 2310 if (!ACPIDEV_DR_BOARD_READY(dhdl)) { 2311 ACPIDEV_DEBUG(CE_WARN, 2312 "!acpidev: object %p is not hotplug capable.", hdl); 2313 return (AE_SUPPORT); 2314 } else if (ACPIDEV_DR_IS_FAILED(dhdl)) { 2315 ACPIDEV_DEBUG(CE_WARN, "!acpidev: object %p is in the FAILED " 2316 "state, unusable for DR.", hdl); 2317 return (AE_ERROR); 2318 } 2319 2320 rc = acpidev_eval_ej0(hdl); 2321 if (ACPI_FAILURE(rc)) { 2322 ACPIDEV_DEBUG(CE_WARN, 2323 "!acpidev: failed to evaluate _EJ0 for object %p.", hdl); 2324 } 2325 2326 return (rc); 2327 } 2328 2329 ACPI_STATUS 2330 acpidev_dr_device_check_status(ACPI_HANDLE hdl) 2331 { 2332 acpidev_data_handle_t dhdl; 2333 2334 dhdl = acpidev_data_get_handle(hdl); 2335 if (dhdl == NULL) { 2336 ACPIDEV_DEBUG(CE_WARN, 2337 "!acpidev: failed to get data handle associated with %p.", 2338 hdl); 2339 return (AE_ERROR); 2340 } 2341 2342 /* Check whether the device is hotplug capable. */ 2343 if (!ACPIDEV_DR_BOARD_READY(dhdl)) { 2344 ACPIDEV_DEBUG(CE_WARN, 2345 "!acpidev: object %p is not hotplug capable.", hdl); 2346 return (AE_SUPPORT); 2347 } else if (ACPIDEV_DR_IS_FAILED(dhdl)) { 2348 ACPIDEV_DEBUG(CE_WARN, "!acpidev: object %p is in the FAILED " 2349 "state, unusable for DR.", hdl); 2350 return (AE_ERROR); 2351 } 2352 2353 return (AE_OK); 2354 } 2355 2356 void 2357 acpidev_dr_lock_all(void) 2358 { 2359 mutex_enter(&acpidev_dr_lock); 2360 } 2361 2362 void 2363 acpidev_dr_unlock_all(void) 2364 { 2365 mutex_exit(&acpidev_dr_lock); 2366 } 2367 2368 ACPI_STATUS 2369 acpidev_dr_allocate_cpuid(ACPI_HANDLE hdl, processorid_t *idp) 2370 { 2371 int rv; 2372 processorid_t cpuid; 2373 uint32_t procid, apicid; 2374 mach_cpu_add_arg_t arg; 2375 acpidev_data_handle_t dhdl; 2376 dev_info_t *dip = NULL; 2377 2378 ASSERT(MUTEX_HELD(&cpu_lock)); 2379 ASSERT(hdl != NULL); 2380 if (hdl == NULL) { 2381 ACPIDEV_DEBUG(CE_WARN, "!acpidev: parameter hdl to " 2382 "acpidev_dr_allocate_cpuid() is NULL."); 2383 return (AE_BAD_PARAMETER); 2384 } 2385 2386 /* Validate that the device is ready for hotplug. */ 2387 if (ACPI_FAILURE(acpica_get_devinfo(hdl, &dip))) { 2388 ACPIDEV_DEBUG(CE_WARN, 2389 "!acpidev: failed to get devinfo for object %p.", hdl); 2390 return (AE_ERROR); 2391 } 2392 ASSERT(dip != NULL); 2393 dhdl = acpidev_data_get_handle(hdl); 2394 if (dhdl == NULL) { 2395 ACPIDEV_DEBUG(CE_WARN, 2396 "!acpidev: failed to get data associated with object %p", 2397 hdl); 2398 return (AE_SUPPORT); 2399 } 2400 if (!ACPIDEV_DR_IS_READY(dhdl)) { 2401 ACPIDEV_DEBUG(CE_WARN, 2402 "!acpidev: dip %p is not hotplug ready.", (void *)dip); 2403 return (AE_SUPPORT); 2404 } 2405 if (ACPIDEV_DR_IS_FAILED(dhdl)) { 2406 ACPIDEV_DEBUG(CE_NOTE, 2407 "!acpidev: dip %p is in the FAILED state.", (void *)dip); 2408 return (AE_SUPPORT); 2409 } 2410 2411 /* Query CPU relative information */ 2412 apicid = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip, 2413 DDI_PROP_DONTPASS, ACPIDEV_PROP_NAME_LOCALAPIC_ID, UINT32_MAX); 2414 procid = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip, 2415 DDI_PROP_DONTPASS, ACPIDEV_PROP_NAME_PROCESSOR_ID, UINT32_MAX); 2416 if (procid == UINT32_MAX || apicid == UINT32_MAX || apicid == 255) { 2417 ACPIDEV_DEBUG(CE_WARN, "!acpidev: dip %p is malformed, " 2418 "procid(0x%x) or apicid(0x%x) is invalid.", 2419 (void *)dip, procid, apicid); 2420 return (AE_ERROR); 2421 } 2422 2423 /* Check whether the CPU device is in offline state. */ 2424 mutex_enter(&(DEVI(dip)->devi_lock)); 2425 if (!DEVI_IS_DEVICE_OFFLINE(dip)) { 2426 mutex_exit(&DEVI(dip)->devi_lock); 2427 ACPIDEV_DEBUG(CE_WARN, 2428 "!acpidev: dip %p isn't in offline state.", (void *)dip); 2429 return (AE_ERROR); 2430 } 2431 mutex_exit(&DEVI(dip)->devi_lock); 2432 2433 /* Check whether the CPU already exists. */ 2434 if (ACPI_SUCCESS(acpica_get_cpu_id_by_object(hdl, &cpuid))) { 2435 ACPIDEV_DEBUG(CE_WARN, 2436 "!acpidev: dip %p already has CPU id(%d) assigned.", 2437 (void *)dip, cpuid); 2438 return (AE_ALREADY_EXISTS); 2439 } 2440 2441 /* Allocate cpuid for the CPU */ 2442 arg.arg.apic.apic_id = apicid; 2443 arg.arg.apic.proc_id = procid; 2444 if (apicid >= 255) { 2445 arg.type = MACH_CPU_ARG_LOCAL_X2APIC; 2446 } else { 2447 arg.type = MACH_CPU_ARG_LOCAL_APIC; 2448 } 2449 rv = mach_cpu_add(&arg, &cpuid); 2450 if (rv != PSM_SUCCESS) { 2451 ACPIDEV_DEBUG(CE_WARN, 2452 "!acpidev: failed to allocate cpu id for dip %p.", 2453 (void *)dip); 2454 return (AE_NOT_EXIST); 2455 } 2456 2457 ASSERT(cpuid >= 0 && cpuid < NCPU && cpuid < max_ncpus); 2458 if (idp != NULL) { 2459 *idp = cpuid; 2460 } 2461 2462 return (AE_OK); 2463 } 2464 2465 ACPI_STATUS 2466 acpidev_dr_free_cpuid(ACPI_HANDLE hdl) 2467 { 2468 ACPI_STATUS rv = AE_OK; 2469 processorid_t cpuid; 2470 2471 ASSERT(MUTEX_HELD(&cpu_lock)); 2472 ASSERT(hdl != NULL); 2473 if (hdl == NULL) { 2474 ACPIDEV_DEBUG(CE_WARN, "!acpidev: parameter hdl to " 2475 "acpidev_dr_free_cpuid() is NULL."); 2476 return (AE_BAD_PARAMETER); 2477 } 2478 2479 if (ACPI_FAILURE(acpica_get_cpu_id_by_object(hdl, &cpuid))) { 2480 ACPIDEV_DEBUG(CE_WARN, 2481 "!acpidev: failed to get cpuid for object %p.", hdl); 2482 rv = AE_NOT_EXIST; 2483 } else if (cpuid < 0 || cpuid > max_ncpus) { 2484 ACPIDEV_DEBUG(CE_WARN, 2485 "!acpidev: cpuid(%d) of object %p is invalid.", 2486 cpuid, hdl); 2487 rv = AE_ERROR; 2488 } else if (mach_cpu_remove(cpuid) != PSM_SUCCESS) { 2489 ACPIDEV_DEBUG(CE_WARN, 2490 "!acpidev: failed to free cpuid(%d) for object %p.", 2491 cpuid, hdl); 2492 rv = AE_ERROR; 2493 } 2494 2495 return (rv); 2496 } 2497 2498 static ACPI_STATUS 2499 acpidev_dr_get_latency(ACPI_HANDLE hdl, void **hdlpp, 2500 uint32_t pxmid, uint32_t *slicntp, uchar_t **slipp) 2501 { 2502 ACPI_STATUS rc; 2503 ACPI_BUFFER buf; 2504 uint32_t i, pxmcnt; 2505 uchar_t *valp, *sp, *ep; 2506 2507 /* Evaluate the ACPI _SLI method under the object. */ 2508 buf.Length = ACPI_ALLOCATE_BUFFER; 2509 rc = AcpiEvaluateObjectTyped(hdl, ACPIDEV_METHOD_NAME_SLI, NULL, &buf, 2510 ACPI_TYPE_BUFFER); 2511 if (ACPI_SUCCESS(rc)) { 2512 valp = (uchar_t *)buf.Pointer; 2513 if (acpidev_slit_tbl_ptr->LocalityCount > pxmid) { 2514 pxmcnt = acpidev_slit_tbl_ptr->LocalityCount; 2515 } else { 2516 pxmcnt = pxmid + 1; 2517 } 2518 2519 /* 2520 * Validate data returned by the ACPI _SLI method. 2521 * Please refer to 6.2.14 "_SLI (System Locality Information)" 2522 * in ACPI4.0 for data format returned by _SLI method. 2523 */ 2524 if (buf.Length != pxmcnt * 2 * sizeof (uchar_t)) { 2525 ACPIDEV_DEBUG(CE_WARN, 2526 "!acpidev: buffer length returned by _SLI method " 2527 "under %p is invalid.", hdl); 2528 AcpiOsFree(buf.Pointer); 2529 } else if (valp[pxmid] != ACPI_SLIT_SELF_LATENCY || 2530 valp[pxmid + pxmcnt] != ACPI_SLIT_SELF_LATENCY) { 2531 ACPIDEV_DEBUG(CE_WARN, 2532 "!acpidev: local latency returned by _SLI method " 2533 "under %p is not %u.", hdl, ACPI_SLIT_SELF_LATENCY); 2534 AcpiOsFree(buf.Pointer); 2535 } else { 2536 *slicntp = pxmcnt; 2537 *slipp = (uchar_t *)buf.Pointer; 2538 *hdlpp = buf.Pointer; 2539 return (AE_OK); 2540 } 2541 } else if (rc != AE_NOT_FOUND) { 2542 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to evaluate " 2543 "_SLI method under object %p.", hdl); 2544 } 2545 2546 /* Return data from the ACPI SLIT table. */ 2547 ASSERT(acpidev_slit_tbl_ptr != NULL); 2548 pxmcnt = acpidev_slit_tbl_ptr->LocalityCount; 2549 if (pxmid >= pxmcnt) { 2550 ACPIDEV_DEBUG(CE_WARN, "!acpidev: proximity domain id " 2551 "(%u) is too big, max %u.", pxmid, pxmcnt - 1); 2552 *slicntp = 0; 2553 *slipp = NULL; 2554 return (AE_ERROR); 2555 } else { 2556 sp = AcpiOsAllocate(pxmcnt * 2 * sizeof (uchar_t)); 2557 ep = acpidev_slit_tbl_ptr->Entry; 2558 for (i = 0; i < pxmcnt; i++) { 2559 sp[i] = ep[pxmcnt * pxmid + i]; 2560 sp[i + pxmcnt] = ep[pxmcnt * i + pxmid]; 2561 } 2562 *slicntp = pxmcnt; 2563 *slipp = sp; 2564 *hdlpp = sp; 2565 return (AE_OK); 2566 } 2567 } 2568 2569 /* 2570 * Query NUMA information for the CPU device. 2571 * It returns APIC id, Proximity id and latency information of the CPU device. 2572 */ 2573 int 2574 acpidev_dr_get_cpu_numa_info(cpu_t *cp, void **hdlpp, uint32_t *apicidp, 2575 uint32_t *pxmidp, uint32_t *slicntp, uchar_t **slipp) 2576 { 2577 dev_info_t *dip = NULL; 2578 ACPI_HANDLE hdl = NULL; 2579 2580 ASSERT(cp != NULL); 2581 ASSERT(hdlpp != NULL); 2582 ASSERT(apicidp != NULL); 2583 ASSERT(pxmidp != NULL); 2584 if (cp == NULL || hdlpp == NULL || apicidp == NULL || pxmidp == NULL) { 2585 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameters to " 2586 "acpidev_dr_get_cpu_numa_info()."); 2587 return (-1); 2588 } 2589 2590 *hdlpp = NULL; 2591 *apicidp = UINT32_MAX; 2592 *pxmidp = UINT32_MAX; 2593 if (lgrp_plat_node_cnt == 1) { 2594 return (-1); 2595 } 2596 ASSERT(acpidev_slit_tbl_ptr != NULL); 2597 2598 /* Query APIC id and Proximity id from device properties. */ 2599 if (ACPI_FAILURE(acpica_get_cpu_object_by_cpuid(cp->cpu_id, &hdl))) { 2600 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to get ACPI object " 2601 "for CPU(%d).", cp->cpu_id); 2602 return (-1); 2603 } 2604 if (ACPI_FAILURE(acpica_get_devinfo(hdl, &dip))) { 2605 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to get device node " 2606 "for CPU(%d).", cp->cpu_id); 2607 return (-1); 2608 } 2609 *apicidp = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 2610 ACPIDEV_PROP_NAME_LOCALAPIC_ID, UINT32_MAX); 2611 *pxmidp = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 2612 ACPIDEV_PROP_NAME_PROXIMITY_ID, UINT32_MAX); 2613 if (*apicidp == UINT32_MAX || *pxmidp == UINT32_MAX) { 2614 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to get local APIC id " 2615 "or proximity id for CPU(%d).", cp->cpu_id); 2616 return (-1); 2617 } 2618 2619 ASSERT((slicntp && slipp) || (!slicntp && !slipp)); 2620 if (slicntp != NULL && slipp != NULL) { 2621 if (ACPI_FAILURE(acpidev_dr_get_latency(hdl, hdlpp, *pxmidp, 2622 slicntp, slipp))) { 2623 return (-1); 2624 } 2625 } 2626 2627 return (0); 2628 } 2629 2630 void 2631 acpidev_dr_free_cpu_numa_info(void *hdlp) 2632 { 2633 if (hdlp != NULL) { 2634 AcpiOsFree(hdlp); 2635 } 2636 } 2637 2638 static ACPI_STATUS 2639 acpidev_dr_mem_search_srat(struct memlist *ml, uint32_t *pxmidp) 2640 { 2641 int len, off; 2642 uint64_t start, end; 2643 boolean_t found = B_FALSE; 2644 ACPI_SUBTABLE_HEADER *sp; 2645 ACPI_SRAT_MEM_AFFINITY *mp; 2646 2647 ASSERT(ml != NULL); 2648 ASSERT(pxmidp != NULL); 2649 ASSERT(acpidev_srat_tbl_ptr != NULL); 2650 2651 /* Search the static ACPI SRAT table for proximity domain. */ 2652 sp = (ACPI_SUBTABLE_HEADER *)(acpidev_srat_tbl_ptr + 1); 2653 len = acpidev_srat_tbl_ptr->Header.Length; 2654 off = sizeof (*acpidev_srat_tbl_ptr); 2655 while (off < len) { 2656 if (sp->Type == ACPI_SRAT_TYPE_MEMORY_AFFINITY) { 2657 mp = (ACPI_SRAT_MEM_AFFINITY *)sp; 2658 if ((mp->Flags & ACPI_SRAT_MEM_ENABLED) && 2659 (mp->Flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && 2660 ml->ml_address >= mp->BaseAddress && 2661 ml->ml_address <= mp->BaseAddress + mp->Length) { 2662 found = B_TRUE; 2663 break; 2664 } 2665 } 2666 off += sp->Length; 2667 sp = (ACPI_SUBTABLE_HEADER *)(((char *)sp) + sp->Length); 2668 } 2669 if (!found) 2670 return (AE_NOT_FOUND); 2671 2672 /* 2673 * Verify that all memory regions in the list belong to the same domain. 2674 */ 2675 start = mp->BaseAddress; 2676 end = mp->BaseAddress + mp->Length; 2677 while (ml) { 2678 if (ml->ml_address < start || 2679 ml->ml_address + ml->ml_size > end) { 2680 ACPIDEV_DEBUG(CE_WARN, 2681 "!acpidev: memory for hot-adding doesn't belong " 2682 "to the same proximity domain."); 2683 return (AE_ERROR); 2684 } 2685 ml = ml->ml_next; 2686 } 2687 2688 return (AE_OK); 2689 } 2690 2691 /* 2692 * Query lgrp information for a memory device. 2693 * It returns proximity domain id and latency information of the memory device. 2694 */ 2695 ACPI_STATUS 2696 acpidev_dr_get_mem_numa_info(ACPI_HANDLE hdl, struct memlist *ml, 2697 void **hdlpp, uint32_t *pxmidp, uint32_t *slicntp, uchar_t **slipp) 2698 { 2699 ASSERT(ml != NULL); 2700 ASSERT(hdlpp != NULL); 2701 ASSERT(pxmidp != NULL); 2702 if (ml == NULL || hdlpp == NULL || pxmidp == NULL) { 2703 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameters to " 2704 "acpidev_dr_get_mem_numa_info()."); 2705 return (AE_BAD_PARAMETER); 2706 } 2707 2708 *pxmidp = UINT32_MAX; 2709 if (lgrp_plat_node_cnt == 1) { 2710 return (AE_SUPPORT); 2711 } 2712 2713 if (ACPI_FAILURE(acpidev_eval_pxm(hdl, pxmidp))) { 2714 /* 2715 * Try to get proximity domain id from SRAT table if failed to 2716 * evaluate ACPI _PXM method for memory device. 2717 */ 2718 if (ACPI_FAILURE(acpidev_dr_mem_search_srat(ml, pxmidp))) { 2719 ACPIDEV_DEBUG(CE_WARN, 2720 "!acpidev: failed to get proximity domain id for " 2721 "memory device %p.", hdl); 2722 return (AE_ERROR); 2723 } 2724 } 2725 2726 ASSERT((slicntp && slipp) || (!slicntp && !slipp)); 2727 if (slicntp != NULL && slipp != NULL) { 2728 if (ACPI_FAILURE(acpidev_dr_get_latency(hdl, hdlpp, *pxmidp, 2729 slicntp, slipp))) { 2730 return (AE_ERROR); 2731 } 2732 } 2733 2734 return (AE_OK); 2735 } 2736 2737 void 2738 acpidev_dr_free_mem_numa_info(void *hdlp) 2739 { 2740 if (hdlp != NULL) { 2741 AcpiOsFree(hdlp); 2742 } 2743 } 2744