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