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