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 <acpica/include/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 }