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