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