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