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