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