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