xref: /illumos-gate/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_drv.c (revision 201ceb75ab95f9bf1f42ea1dc9ab363b43ba47cf)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2009-2010, Intel Corporation.
23  * All rights reserved.
24  * Copyright (c) 2018, Joyent, Inc.
25  * Copyright 2023 Oxide Computer Company
26  */
27 
28 /*
29  * Platform specific device enumerator for ACPI specific devices.
30  * "x86 system devices" refers to the suite of hardware components which are
31  * common to the x86 platform and play important roles in the system
32  * architecture but can't be enumerated/discovered through industry-standard
33  * bus specifications. Examples of these x86 system devices include:
34  *   * Logical processor/CPU
35  *   * Memory device
36  *   * Non-PCI discoverable IOMMU or DMA Remapping Engine
37  *   * Non-PCI discoverable IOxAPIC
38  *   * Non-PCI discoverable HPET (High Precision Event Timer)
39  *   * ACPI defined devices, including power button, sleep button, battery etc.
40  *
41  * X86 system devices may be discovered through BIOS/Firmware interfaces, such
42  * as SMBIOS tables, MPS tables and ACPI tables since their discovery isn't
43  * covered by any industry-standard bus specifications.
44  *
45  * In order to aid Solaris in flexibly managing x86 system devices,
46  * x86 system devices are placed into a specific firmware device
47  * subtree whose device path is '/devices/fw'.
48  *
49  * This driver populates the firmware device subtree with ACPI-discoverable
50  * system devices if possible. To achieve that, the ACPI object
51  * namespace is abstracted as ACPI virtual buses which host system devices.
52  * Another nexus driver for the ACPI virtual bus will manage all devices
53  * connected to it.
54  *
55  * For more detailed information, please refer to PSARC/2009/104.
56  */
57 
58 #include <sys/types.h>
59 #include <sys/bitmap.h>
60 #include <sys/cmn_err.h>
61 #include <sys/ddi_subrdefs.h>
62 #include <sys/errno.h>
63 #include <sys/modctl.h>
64 #include <sys/mutex.h>
65 #include <sys/note.h>
66 #include <sys/obpdefs.h>
67 #include <sys/sunddi.h>
68 #include <sys/sunndi.h>
69 #include <sys/acpi/acpi.h>
70 #include <sys/acpica.h>
71 #include <sys/acpidev.h>
72 #include <sys/acpidev_dr.h>
73 #include <sys/acpidev_impl.h>
74 
75 /* Patchable through /etc/system */
76 int acpidev_options = 0;
77 int acpidev_debug = 0;
78 
79 krwlock_t acpidev_class_lock;
80 acpidev_class_list_t *acpidev_class_list_root = NULL;
81 ulong_t acpidev_object_type_mask[BT_BITOUL(ACPI_TYPE_NS_NODE_MAX + 1)];
82 
83 /* ACPI device autoconfig global status */
84 typedef enum acpidev_status {
85 	ACPIDEV_STATUS_FAILED = -2,	/* ACPI device autoconfig failed */
86 	ACPIDEV_STATUS_DISABLED = -1,	/* ACPI device autoconfig disabled */
87 	ACPIDEV_STATUS_UNKNOWN = 0,	/* initial status */
88 	ACPIDEV_STATUS_INITIALIZED,	/* ACPI device autoconfig initialized */
89 	ACPIDEV_STATUS_FIRST_PASS,	/* first probing finished */
90 	ACPIDEV_STATUS_READY		/* second probing finished */
91 } acpidev_status_t;
92 
93 static acpidev_status_t acpidev_status = ACPIDEV_STATUS_UNKNOWN;
94 static kmutex_t	acpidev_drv_lock;
95 static dev_info_t *acpidev_root_dip = NULL;
96 
97 /* Boot time ACPI device enumerator. */
98 static void acpidev_boot_probe(int type);
99 
100 /* DDI module auto configuration interface */
101 extern struct mod_ops mod_miscops;
102 
103 static struct modlmisc modlmisc = {
104 	&mod_miscops,
105 	"ACPI device enumerator"
106 };
107 
108 static struct modlinkage modlinkage = {
109 	MODREV_1,
110 	(void *)&modlmisc,
111 	NULL
112 };
113 
114 int
115 _init(void)
116 {
117 	int err;
118 
119 	if ((err = mod_install(&modlinkage)) == 0) {
120 		bzero(acpidev_object_type_mask,
121 		    sizeof (acpidev_object_type_mask));
122 		mutex_init(&acpidev_drv_lock, NULL, MUTEX_DRIVER, NULL);
123 		rw_init(&acpidev_class_lock, NULL, RW_DEFAULT, NULL);
124 		acpidev_dr_init();
125 		impl_bus_add_probe(acpidev_boot_probe);
126 	} else {
127 		cmn_err(CE_WARN, "!acpidev: failed to install driver.");
128 	}
129 
130 	return (err);
131 }
132 
133 int
134 _fini(void)
135 {
136 	/* No support for module unload. */
137 	return (EBUSY);
138 }
139 
140 int
141 _info(struct modinfo *modinfop)
142 {
143 	return (mod_info(&modlinkage, modinfop));
144 }
145 
146 /* Check blacklists and load platform specific driver modules. */
147 static ACPI_STATUS
148 acpidev_load_plat_modules(void)
149 {
150 	return (AE_OK);
151 }
152 
153 /* Unload platform specific driver modules. */
154 static void
155 acpidev_unload_plat_modules(void)
156 {
157 }
158 
159 /* Unregister all device class drivers from the device driver lists. */
160 static void
161 acpidev_class_list_fini(void)
162 {
163 	acpidev_unload_plat_modules();
164 
165 	(void) acpidev_unregister_class(&acpidev_class_list_usbport,
166 	    &acpidev_class_usbport);
167 
168 	if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) {
169 		(void) acpidev_unregister_class(&acpidev_class_list_scope,
170 		    &acpidev_class_pci);
171 		(void) acpidev_unregister_class(&acpidev_class_list_device,
172 		    &acpidev_class_pci);
173 	}
174 
175 	if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) {
176 		(void) acpidev_unregister_class(&acpidev_class_list_device,
177 		    &acpidev_class_memory);
178 	}
179 
180 	if (acpidev_options & ACPIDEV_OUSER_NO_CPU) {
181 		(void) acpidev_unregister_class(&acpidev_class_list_device,
182 		    &acpidev_class_cpu);
183 		(void) acpidev_unregister_class(&acpidev_class_list_scope,
184 		    &acpidev_class_cpu);
185 		(void) acpidev_unregister_class(&acpidev_class_list_root,
186 		    &acpidev_class_cpu);
187 	}
188 
189 	if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) {
190 		(void) acpidev_unregister_class(&acpidev_class_list_device,
191 		    &acpidev_class_container);
192 	}
193 
194 	(void) acpidev_unregister_class(&acpidev_class_list_device,
195 	    &acpidev_class_device);
196 	(void) acpidev_unregister_class(&acpidev_class_list_root,
197 	    &acpidev_class_device);
198 
199 	(void) acpidev_unregister_class(&acpidev_class_list_root,
200 	    &acpidev_class_scope);
201 }
202 
203 /* Register all device class drivers onto the driver lists. */
204 static ACPI_STATUS
205 acpidev_class_list_init(uint64_t *fp)
206 {
207 	ACPI_STATUS rc = AE_OK;
208 
209 	/* Set bit in mask for supported object types. */
210 	BT_SET(acpidev_object_type_mask, ACPI_TYPE_LOCAL_SCOPE);
211 	BT_SET(acpidev_object_type_mask, ACPI_TYPE_DEVICE);
212 
213 	/*
214 	 * Register the ACPI scope class driver onto the class driver lists.
215 	 * Currently only ACPI scope objects under ACPI root node, such as _PR,
216 	 * _SB, _TZ etc, need to be handled, so only register the scope class
217 	 * driver onto the root list.
218 	 */
219 	if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_root,
220 	    &acpidev_class_scope, B_FALSE))) {
221 		goto error_out;
222 	}
223 
224 	/*
225 	 * Register the ACPI device class driver onto the class driver lists.
226 	 * The ACPI device class driver should be registered at the tail to
227 	 * handle all device objects which haven't been handled by other
228 	 * HID/CID specific device class drivers.
229 	 */
230 	if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_root,
231 	    &acpidev_class_device, B_TRUE))) {
232 		goto error_root_device;
233 	}
234 	if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_device,
235 	    &acpidev_class_device, B_TRUE))) {
236 		goto error_device_device;
237 	}
238 
239 	/* Check and register support for ACPI container device. */
240 	if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) {
241 		if (ACPI_FAILURE(acpidev_register_class(
242 		    &acpidev_class_list_device, &acpidev_class_container,
243 		    B_FALSE))) {
244 			goto error_device_container;
245 		}
246 		*fp |= ACPI_DEVCFG_CONTAINER;
247 	}
248 
249 	/* Check and register support for ACPI CPU device. */
250 	if ((acpidev_options & ACPIDEV_OUSER_NO_CPU) == 0) {
251 		/* Handle ACPI CPU Device */
252 		if (ACPI_FAILURE(acpidev_register_class(
253 		    &acpidev_class_list_device, &acpidev_class_cpu, B_FALSE))) {
254 			goto error_device_cpu;
255 		}
256 		/* Handle ACPI Processor under _PR */
257 		if (ACPI_FAILURE(acpidev_register_class(
258 		    &acpidev_class_list_scope, &acpidev_class_cpu, B_FALSE))) {
259 			goto error_scope_cpu;
260 		}
261 		/* House-keeping for CPU scan */
262 		if (ACPI_FAILURE(acpidev_register_class(
263 		    &acpidev_class_list_root, &acpidev_class_cpu, B_FALSE))) {
264 			goto error_root_cpu;
265 		}
266 		BT_SET(acpidev_object_type_mask, ACPI_TYPE_PROCESSOR);
267 		*fp |= ACPI_DEVCFG_CPU;
268 	}
269 
270 	/* Check support of ACPI memory devices. */
271 	if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) {
272 		/*
273 		 * Register the ACPI memory class driver onto the
274 		 * acpidev_class_list_device list because ACPI module
275 		 * class driver uses that list.
276 		 */
277 		if (ACPI_FAILURE(acpidev_register_class(
278 		    &acpidev_class_list_device, &acpidev_class_memory,
279 		    B_FALSE))) {
280 			goto error_device_memory;
281 		}
282 		*fp |= ACPI_DEVCFG_MEMORY;
283 	}
284 
285 	/* Check support of PCI/PCIex Host Bridge devices. */
286 	if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) {
287 		/*
288 		 * Register pci/pciex class drivers onto
289 		 * the acpidev_class_list_device class list because ACPI
290 		 * module class driver uses that list.
291 		 */
292 		if (ACPI_FAILURE(acpidev_register_class(
293 		    &acpidev_class_list_device, &acpidev_class_pci,
294 		    B_FALSE))) {
295 			goto error_device_pci;
296 		}
297 
298 		/*
299 		 * Register pci/pciex class drivers onto the
300 		 * acpidev_class_list_scope class list.
301 		 */
302 		if (ACPI_FAILURE(acpidev_register_class(
303 		    &acpidev_class_list_scope, &acpidev_class_pci,
304 		    B_FALSE))) {
305 			goto error_scope_pci;
306 		}
307 
308 		*fp |= ACPI_DEVCFG_PCI;
309 	}
310 
311 	/* Check support of USB port enumeration */
312 	if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_usbport,
313 	    &acpidev_class_usbport, B_TRUE))) {
314 		goto error_usbport;
315 	}
316 
317 
318 	/* Check blacklist and load platform specific modules. */
319 	rc = acpidev_load_plat_modules();
320 	if (ACPI_FAILURE(rc)) {
321 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to check blacklist "
322 		    "or load pratform modules.");
323 		goto error_plat;
324 	}
325 
326 	return (AE_OK);
327 
328 error_plat:
329 	if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) {
330 		(void) acpidev_unregister_class(&acpidev_class_list_scope,
331 		    &acpidev_class_pci);
332 	}
333 
334 error_usbport:
335 	(void) acpidev_unregister_class(&acpidev_class_list_usbport,
336 	    &acpidev_class_usbport);
337 
338 error_scope_pci:
339 	if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) {
340 		(void) acpidev_unregister_class(&acpidev_class_list_device,
341 		    &acpidev_class_pci);
342 	}
343 error_device_pci:
344 	if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) {
345 		(void) acpidev_unregister_class(&acpidev_class_list_device,
346 		    &acpidev_class_memory);
347 	}
348 error_device_memory:
349 	if (acpidev_options & ACPIDEV_OUSER_NO_CPU) {
350 		(void) acpidev_unregister_class(&acpidev_class_list_root,
351 		    &acpidev_class_cpu);
352 	}
353 error_root_cpu:
354 	if (acpidev_options & ACPIDEV_OUSER_NO_CPU) {
355 		(void) acpidev_unregister_class(&acpidev_class_list_scope,
356 		    &acpidev_class_cpu);
357 	}
358 error_scope_cpu:
359 	if (acpidev_options & ACPIDEV_OUSER_NO_CPU) {
360 		(void) acpidev_unregister_class(&acpidev_class_list_device,
361 		    &acpidev_class_cpu);
362 	}
363 error_device_cpu:
364 	if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) {
365 		(void) acpidev_unregister_class(&acpidev_class_list_device,
366 		    &acpidev_class_container);
367 	}
368 error_device_container:
369 	(void) acpidev_unregister_class(&acpidev_class_list_device,
370 	    &acpidev_class_device);
371 error_device_device:
372 	(void) acpidev_unregister_class(&acpidev_class_list_root,
373 	    &acpidev_class_device);
374 error_root_device:
375 	(void) acpidev_unregister_class(&acpidev_class_list_root,
376 	    &acpidev_class_scope);
377 error_out:
378 	ACPIDEV_DEBUG(CE_WARN,
379 	    "!acpidev: failed to register built-in class drivers.");
380 	*fp = 0;
381 
382 	return (AE_ERROR);
383 }
384 
385 /*
386  * Called in single threaded context during boot, no protection for
387  * reentrance.
388  */
389 static ACPI_STATUS
390 acpidev_create_root_node(void)
391 {
392 	int rv = AE_OK;
393 	dev_info_t *dip = NULL;
394 	acpidev_data_handle_t objhdl;
395 	char *compatibles[] = {
396 		ACPIDEV_HID_ROOTNEX,
397 		ACPIDEV_TYPE_ROOTNEX,
398 		ACPIDEV_HID_VIRTNEX,
399 		ACPIDEV_TYPE_VIRTNEX,
400 	};
401 
402 	ndi_devi_enter(ddi_root_node());
403 	ASSERT(acpidev_root_dip == NULL);
404 
405 	/* Query whether device node already exists. */
406 	dip = ddi_find_devinfo(ACPIDEV_NODE_NAME_ROOT, -1, 0);
407 	if (dip != NULL && ddi_get_parent(dip) == ddi_root_node()) {
408 		ndi_devi_exit(ddi_root_node());
409 		cmn_err(CE_WARN, "!acpidev: node /devices/%s already exists, "
410 		    "disable driver.", ACPIDEV_NODE_NAME_ROOT);
411 		return (AE_ALREADY_EXISTS);
412 	}
413 
414 	/* Create the device node if it doesn't exist. */
415 	rv = ndi_devi_alloc(ddi_root_node(), ACPIDEV_NODE_NAME_ROOT,
416 	    (pnode_t)DEVI_SID_NODEID, &dip);
417 	if (rv != NDI_SUCCESS) {
418 		ndi_devi_exit(ddi_root_node());
419 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to create device node "
420 		    "for ACPI root with errcode %d.", rv);
421 		return (AE_ERROR);
422 	}
423 
424 	/* Build cross reference between dip and ACPI object. */
425 	if (ACPI_FAILURE(acpica_tag_devinfo(dip, ACPI_ROOT_OBJECT))) {
426 		(void) ddi_remove_child(dip, 0);
427 		ndi_devi_exit(ddi_root_node());
428 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to tag object %s.",
429 		    ACPIDEV_OBJECT_NAME_SB);
430 		return (AE_ERROR);
431 	}
432 
433 	/* Set device properties. */
434 	rv = ndi_prop_update_string_array(DDI_DEV_T_NONE, dip,
435 	    OBP_COMPATIBLE, ACPIDEV_ARRAY_PARAM(compatibles));
436 	if (rv == NDI_SUCCESS) {
437 		rv = ndi_prop_update_string(DDI_DEV_T_NONE, dip,
438 		    OBP_DEVICETYPE, ACPIDEV_TYPE_ROOTNEX);
439 	}
440 	if (rv != DDI_SUCCESS) {
441 		ACPIDEV_DEBUG(CE_WARN,
442 		    "!acpidev: failed to set device property for /devices/%s.",
443 		    ACPIDEV_NODE_NAME_ROOT);
444 		goto error_out;
445 	}
446 
447 	/* Manually create an object handle for the root node */
448 	objhdl = acpidev_data_create_handle(ACPI_ROOT_OBJECT);
449 	if (objhdl == NULL) {
450 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to create object "
451 		    "handle for the root node.");
452 		goto error_out;
453 	}
454 	objhdl->aod_level = 0;
455 	objhdl->aod_hdl = ACPI_ROOT_OBJECT;
456 	objhdl->aod_dip = dip;
457 	objhdl->aod_class = &acpidev_class_scope;
458 	objhdl->aod_status = acpidev_query_device_status(ACPI_ROOT_OBJECT);
459 	objhdl->aod_iflag = ACPIDEV_ODF_STATUS_VALID |
460 	    ACPIDEV_ODF_DEVINFO_CREATED | ACPIDEV_ODF_DEVINFO_TAGGED;
461 
462 	/* Bind device driver. */
463 	(void) ndi_devi_bind_driver(dip, 0);
464 
465 	acpidev_root_dip = dip;
466 	ndi_devi_exit(ddi_root_node());
467 
468 	return (AE_OK);
469 
470 error_out:
471 	(void) acpica_untag_devinfo(dip, ACPI_ROOT_OBJECT);
472 	(void) ddi_remove_child(dip, 0);
473 	ndi_devi_exit(ddi_root_node());
474 	return (AE_ERROR);
475 }
476 
477 static void
478 acpidev_initialize(void)
479 {
480 	int rc;
481 	char *str = NULL;
482 	uint64_t features = 0;
483 
484 	/* Check whether it has already been initialized. */
485 	if (acpidev_status == ACPIDEV_STATUS_DISABLED) {
486 		cmn_err(CE_CONT, "?acpidev: ACPI device autoconfig "
487 		    "disabled by user.\n");
488 		return;
489 	} else if (acpidev_status != ACPIDEV_STATUS_UNKNOWN) {
490 		ACPIDEV_DEBUG(CE_NOTE,
491 		    "!acpidev: initialization called more than once.");
492 		return;
493 	}
494 
495 	/* Check whether ACPI device autoconfig has been disabled by user. */
496 	rc = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
497 	    DDI_PROP_DONTPASS, "acpidev-autoconfig", &str);
498 	if (rc == DDI_SUCCESS) {
499 		if (strcasecmp(str, "off") == 0 || strcasecmp(str, "no") == 0) {
500 			cmn_err(CE_CONT, "?acpidev: ACPI device autoconfig "
501 			    "disabled by user.\n");
502 			ddi_prop_free(str);
503 			acpidev_status = ACPIDEV_STATUS_DISABLED;
504 			return;
505 		}
506 		ddi_prop_free(str);
507 	}
508 
509 	/* Initialize acpica subsystem. */
510 	if (ACPI_FAILURE(acpica_init())) {
511 		cmn_err(CE_WARN,
512 		    "!acpidev: failed to initialize acpica subsystem.");
513 		acpidev_status = ACPIDEV_STATUS_FAILED;
514 		return;
515 	}
516 
517 	/* Check ACPICA subsystem status. */
518 	if (!acpica_get_core_feature(ACPI_FEATURE_FULL_INIT)) {
519 		cmn_err(CE_WARN, "!acpidev: ACPICA hasn't been fully "
520 		    "initialized, ACPI device autoconfig will be disabled.");
521 		acpidev_status = ACPIDEV_STATUS_DISABLED;
522 		return;
523 	}
524 
525 	/* Converts acpidev-options from type string to int, if any */
526 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
527 	    DDI_PROP_DONTPASS, "acpidev-options", &str) == DDI_PROP_SUCCESS) {
528 		long data;
529 		rc = ddi_strtol(str, NULL, 0, &data);
530 		if (rc == 0) {
531 			(void) e_ddi_prop_remove(DDI_DEV_T_NONE,
532 			    ddi_root_node(), "acpidev-options");
533 			(void) e_ddi_prop_update_int(DDI_DEV_T_NONE,
534 			    ddi_root_node(), "acpidev-options", data);
535 		}
536 		ddi_prop_free(str);
537 	}
538 	/* Get acpidev_options user options. */
539 	acpidev_options = ddi_prop_get_int(DDI_DEV_T_ANY, ddi_root_node(),
540 	    DDI_PROP_DONTPASS, "acpidev-options", acpidev_options);
541 
542 	/* Check whether ACPI based DR has been disabled by user. */
543 	rc = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
544 	    DDI_PROP_DONTPASS, "acpidev-dr", &str);
545 	if (rc == DDI_SUCCESS) {
546 		if (strcasecmp(str, "off") == 0 || strcasecmp(str, "no") == 0) {
547 			cmn_err(CE_CONT, "?acpidev: ACPI based DR has been "
548 			    "disabled by user.\n");
549 			acpidev_dr_enable = 0;
550 		}
551 		ddi_prop_free(str);
552 	}
553 
554 	/* Register all device class drivers. */
555 	if (ACPI_FAILURE(acpidev_class_list_init(&features))) {
556 		cmn_err(CE_WARN,
557 		    "!acpidev: failed to initalize class driver lists.");
558 		acpidev_status = ACPIDEV_STATUS_FAILED;
559 		return;
560 	}
561 
562 	/* Create root node for ACPI/firmware device subtree. */
563 	if (ACPI_FAILURE(acpidev_create_root_node())) {
564 		cmn_err(CE_WARN, "!acpidev: failed to create root node "
565 		    "for acpi device tree.");
566 		acpidev_class_list_fini();
567 		acpidev_status = ACPIDEV_STATUS_FAILED;
568 		return;
569 	}
570 
571 	/* Notify acpica to enable ACPI device auto configuration. */
572 	acpica_set_core_feature(ACPI_FEATURE_DEVCFG);
573 	acpica_set_devcfg_feature(features);
574 
575 	ACPIDEV_DEBUG(CE_NOTE, "!acpidev: ACPI device autoconfig initialized.");
576 	acpidev_status = ACPIDEV_STATUS_INITIALIZED;
577 }
578 
579 /*
580  * Probe devices in ACPI namespace which can't be enumerated by other methods
581  * at boot time.
582  */
583 static ACPI_STATUS
584 acpidev_boot_probe_device(acpidev_op_type_t op_type)
585 {
586 	ACPI_STATUS rc = AE_OK;
587 	acpidev_walk_info_t *infop;
588 
589 	ASSERT(acpidev_root_dip != NULL);
590 	ASSERT(op_type == ACPIDEV_OP_BOOT_PROBE ||
591 	    op_type == ACPIDEV_OP_BOOT_REPROBE);
592 
593 	infop = acpidev_alloc_walk_info(op_type, 0, ACPI_ROOT_OBJECT,
594 	    &acpidev_class_list_root, NULL);
595 	if (infop == NULL) {
596 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to allocate walk info "
597 		    "object in acpi_boot_probe_device().");
598 		return (AE_ERROR);
599 	}
600 	/* Enumerate ACPI devices. */
601 	rc = acpidev_probe_child(infop);
602 	if (ACPI_FAILURE(rc)) {
603 		cmn_err(CE_WARN, "!acpidev: failed to probe child object "
604 		    "under ACPI root node.");
605 	}
606 	acpidev_free_walk_info(infop);
607 
608 	return (rc);
609 }
610 
611 /*
612  * Platform specific device prober for ACPI virtual bus.
613  * It will be called in single-threaded environment to enumerate devices in
614  * ACPI namespace at boot time.
615  */
616 static void
617 acpidev_boot_probe(int type)
618 {
619 	ACPI_STATUS rc;
620 
621 	/* Initialize subsystem on first pass. */
622 	mutex_enter(&acpidev_drv_lock);
623 	if (type == 0) {
624 		acpidev_initialize();
625 		if (acpidev_status != ACPIDEV_STATUS_INITIALIZED &&
626 		    acpidev_status != ACPIDEV_STATUS_DISABLED) {
627 			cmn_err(CE_WARN, "!acpidev: driver disabled due to "
628 			    "initalization failure.");
629 		}
630 	}
631 
632 	/* Probe ACPI devices */
633 	if (type == 0 && acpidev_status == ACPIDEV_STATUS_INITIALIZED) {
634 		rc = acpidev_boot_probe_device(ACPIDEV_OP_BOOT_PROBE);
635 		if (ACPI_SUCCESS(rc)) {
636 			/*
637 			 * Support of DR operations will be disabled
638 			 * if failed to initialize DR subsystem.
639 			 */
640 			rc = acpidev_dr_initialize(acpidev_root_dip);
641 			if (ACPI_FAILURE(rc) && rc != AE_SUPPORT) {
642 				cmn_err(CE_CONT, "?acpidev: failed to "
643 				    "initialize DR subsystem.");
644 			}
645 			acpidev_status = ACPIDEV_STATUS_FIRST_PASS;
646 		} else {
647 			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to probe ACPI "
648 			    "devices during boot.");
649 			acpidev_status = ACPIDEV_STATUS_FAILED;
650 		}
651 	} else if (type != 0 && acpidev_status == ACPIDEV_STATUS_FIRST_PASS) {
652 		rc = acpidev_boot_probe_device(ACPIDEV_OP_BOOT_REPROBE);
653 		if (ACPI_SUCCESS(rc)) {
654 			acpidev_status = ACPIDEV_STATUS_READY;
655 		} else {
656 			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to reprobe "
657 			    "ACPI devices during boot.");
658 			acpidev_status = ACPIDEV_STATUS_FAILED;
659 		}
660 	} else if (acpidev_status != ACPIDEV_STATUS_FAILED &&
661 	    acpidev_status != ACPIDEV_STATUS_DISABLED &&
662 	    acpidev_status != ACPIDEV_STATUS_READY) {
663 		cmn_err(CE_WARN,
664 		    "!acpidev: invalid ACPI device autoconfig global status.");
665 	}
666 	mutex_exit(&acpidev_drv_lock);
667 }
668 
669 ACPI_STATUS
670 acpidev_probe_child(acpidev_walk_info_t *infop)
671 {
672 	dev_info_t *pdip;
673 	ACPI_STATUS res, rc = AE_OK;
674 	ACPI_HANDLE child;
675 	ACPI_OBJECT_TYPE type;
676 	acpidev_class_list_t *it;
677 	acpidev_walk_info_t *cinfop;
678 	acpidev_data_handle_t datap;
679 
680 	/* Validate parameter first. */
681 	ASSERT(infop != NULL);
682 	if (infop == NULL) {
683 		ACPIDEV_DEBUG(CE_WARN,
684 		    "!acpidev: infop is NULL in acpidev_probe_child().");
685 		return (AE_BAD_PARAMETER);
686 	}
687 	ASSERT(infop->awi_level < ACPIDEV_MAX_ENUM_LEVELS - 1);
688 	if (infop->awi_level >= ACPIDEV_MAX_ENUM_LEVELS - 1) {
689 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: recursive level is too deep "
690 		    "in acpidev_probe_child().");
691 		return (AE_BAD_PARAMETER);
692 	}
693 	ASSERT(infop->awi_class_list != NULL);
694 	ASSERT(infop->awi_hdl != NULL);
695 	ASSERT(infop->awi_info != NULL);
696 	ASSERT(infop->awi_name != NULL);
697 	ASSERT(infop->awi_data != NULL);
698 	if (infop->awi_class_list == NULL || infop->awi_hdl == NULL ||
699 	    infop->awi_info == NULL || infop->awi_name == NULL ||
700 	    infop->awi_data == NULL) {
701 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: infop has NULL fields in "
702 		    "acpidev_probe_child().");
703 		return (AE_BAD_PARAMETER);
704 	}
705 	pdip = acpidev_walk_info_get_pdip(infop);
706 	if (pdip == NULL) {
707 		ACPIDEV_DEBUG(CE_WARN,
708 		    "!acpidev: pdip is NULL in acpidev_probe_child().");
709 		return (AE_BAD_PARAMETER);
710 	}
711 
712 	ndi_devi_enter(pdip);
713 	rw_enter(&acpidev_class_lock, RW_READER);
714 
715 	/* Call pre-probe callback functions. */
716 	for (it = *(infop->awi_class_list); it != NULL; it = it->acl_next) {
717 		if (it->acl_class->adc_pre_probe == NULL) {
718 			continue;
719 		}
720 		infop->awi_class_curr = it->acl_class;
721 		if (ACPI_FAILURE(it->acl_class->adc_pre_probe(infop))) {
722 			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to pre-probe "
723 			    "device of type %s under %s.",
724 			    it->acl_class->adc_class_name, infop->awi_name);
725 		}
726 	}
727 
728 	/* Walk child objects. */
729 	child = NULL;
730 	while (ACPI_SUCCESS(AcpiGetNextObject(ACPI_TYPE_ANY,
731 	    infop->awi_hdl, child, &child))) {
732 		/* Skip object if we're not interested in it. */
733 		if (ACPI_FAILURE(AcpiGetType(child, &type)) ||
734 		    type > ACPI_TYPE_NS_NODE_MAX ||
735 		    BT_TEST(acpidev_object_type_mask, type) == 0) {
736 			continue;
737 		}
738 
739 		/* It's another hotplug-capable board, skip it. */
740 		if (infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE &&
741 		    acpidev_dr_device_is_board(child)) {
742 			continue;
743 		}
744 
745 		/* Allocate the walk info structure. */
746 		cinfop = acpidev_alloc_walk_info(infop->awi_op_type,
747 		    infop->awi_level + 1, child, NULL, infop);
748 		if (cinfop == NULL) {
749 			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to allocate "
750 			    "walk info child object of %s.",
751 			    infop->awi_name);
752 			/* Mark error and continue to handle next child. */
753 			rc = AE_ERROR;
754 			continue;
755 		}
756 
757 		/*
758 		 * Remember the class list used to handle this object.
759 		 * It should be the same list for different passes of scans.
760 		 */
761 		ASSERT(cinfop->awi_data != NULL);
762 		datap = cinfop->awi_data;
763 		if (cinfop->awi_op_type == ACPIDEV_OP_BOOT_PROBE) {
764 			datap->aod_class_list = infop->awi_class_list;
765 		}
766 
767 		/* Call registered process callbacks. */
768 		for (it = *(infop->awi_class_list); it != NULL;
769 		    it = it->acl_next) {
770 			if (it->acl_class->adc_probe == NULL) {
771 				continue;
772 			}
773 			cinfop->awi_class_curr = it->acl_class;
774 			res = it->acl_class->adc_probe(cinfop);
775 			if (ACPI_FAILURE(res)) {
776 				rc = res;
777 				ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to "
778 				    "process object of type %s under %s.",
779 				    it->acl_class->adc_class_name,
780 				    infop->awi_name);
781 			}
782 		}
783 
784 		/* Free resources. */
785 		acpidev_free_walk_info(cinfop);
786 	}
787 
788 	/* Call post-probe callback functions. */
789 	for (it = *(infop->awi_class_list); it != NULL; it = it->acl_next) {
790 		if (it->acl_class->adc_post_probe == NULL) {
791 			continue;
792 		}
793 		infop->awi_class_curr = it->acl_class;
794 		if (ACPI_FAILURE(it->acl_class->adc_post_probe(infop))) {
795 			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to post-probe "
796 			    "device of type %s under %s.",
797 			    it->acl_class->adc_class_name, infop->awi_name);
798 		}
799 	}
800 
801 	rw_exit(&acpidev_class_lock);
802 	ndi_devi_exit(pdip);
803 
804 	return (rc);
805 }
806 
807 ACPI_STATUS
808 acpidev_process_object(acpidev_walk_info_t *infop, int flags)
809 {
810 	ACPI_STATUS rc = AE_OK;
811 	char *devname;
812 	dev_info_t *dip, *pdip;
813 	ACPI_HANDLE hdl;
814 	ACPI_DEVICE_INFO *adip;
815 	acpidev_class_t *clsp;
816 	acpidev_data_handle_t datap;
817 	acpidev_filter_result_t res;
818 
819 	/* Validate parameters first. */
820 	ASSERT(infop != NULL);
821 	if (infop == NULL) {
822 		ACPIDEV_DEBUG(CE_WARN,
823 		    "!acpidev: infop is NULL in acpidev_process_object().");
824 		return (AE_BAD_PARAMETER);
825 	}
826 	ASSERT(infop->awi_hdl != NULL);
827 	ASSERT(infop->awi_info != NULL);
828 	ASSERT(infop->awi_data != NULL);
829 	ASSERT(infop->awi_class_curr != NULL);
830 	ASSERT(infop->awi_class_curr->adc_filter != NULL);
831 	hdl = infop->awi_hdl;
832 	adip = infop->awi_info;
833 	datap = infop->awi_data;
834 	clsp = infop->awi_class_curr;
835 	if (hdl == NULL || datap == NULL || adip == NULL || clsp == NULL ||
836 	    clsp->adc_filter == NULL) {
837 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: infop has NULL pointer in "
838 		    "acpidev_process_object().");
839 		return (AE_BAD_PARAMETER);
840 	}
841 	pdip = acpidev_walk_info_get_pdip(infop);
842 	if (pdip == NULL) {
843 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to get pdip for %s "
844 		    "in acpidev_process_object().", infop->awi_name);
845 		return (AE_BAD_PARAMETER);
846 	}
847 
848 	/*
849 	 * Check whether the object has already been handled.
850 	 * Tag and child dip pointer are used to indicate the object has been
851 	 * handled by the ACPI auto configure driver. It has the
852 	 * following usages:
853 	 * 1) Prevent creating dip for objects which already have a dip
854 	 *    when reloading the ACPI auto configure driver.
855 	 * 2) Prevent creating multiple dips for ACPI objects with ACPI
856 	 *    aliases. Currently ACPICA framework has no way to tell whether
857 	 *    an object is an alias or not for some types of object. So tag
858 	 *    is used to indicate that the object has been handled.
859 	 * 3) Prevent multiple class drivers from creating multiple devices for
860 	 *    the same ACPI object.
861 	 */
862 	if ((flags & ACPIDEV_PROCESS_FLAG_CREATE) &&
863 	    (flags & ACPIDEV_PROCESS_FLAG_CHECK) &&
864 	    !(infop->awi_flags & ACPIDEV_WI_DISABLE_CREATE) &&
865 	    (infop->awi_flags & ACPIDEV_WI_DEVICE_CREATED)) {
866 		ASSERT(infop->awi_dip != NULL);
867 		ACPIDEV_DEBUG(CE_NOTE,
868 		    "!acpidev: device has already been created for object %s.",
869 		    infop->awi_name);
870 		return (AE_ALREADY_EXISTS);
871 	}
872 
873 	/*
874 	 * Determine action according to following rules based on device
875 	 * status returned by _STA method. Please refer to ACPI3.0b section
876 	 * 6.3.1 and 6.5.1.
877 	 * present functioning enabled	Action
878 	 *	0	0	x	Do nothing
879 	 *	1	x	0	Do nothing
880 	 *	1	x	1	Create node and scan child
881 	 *	x	1	0	Do nothing
882 	 *	x	1	1	Create node and scan child
883 	 */
884 	if ((datap->aod_iflag & ACPIDEV_ODF_STATUS_VALID) == 0 ||
885 	    (flags & ACPIDEV_PROCESS_FLAG_SYNCSTATUS)) {
886 		datap->aod_status = acpidev_query_device_status(hdl);
887 		datap->aod_iflag |= ACPIDEV_ODF_STATUS_VALID;
888 	}
889 	if (!acpidev_check_device_enabled(datap->aod_status)) {
890 		ACPIDEV_DEBUG(CE_NOTE, "!acpidev: object %s doesn't exist.",
891 		    infop->awi_name);
892 		/*
893 		 * Need to scan for hotplug-capable boards even if object
894 		 * doesn't exist or has been disabled during the first pass.
895 		 * So just disable creating device node and keep on scanning.
896 		 */
897 		if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE) {
898 			flags &= ~ACPIDEV_PROCESS_FLAG_CREATE;
899 		} else {
900 			return (AE_NOT_EXIST);
901 		}
902 	}
903 
904 	ASSERT(infop->awi_data != NULL);
905 	ASSERT(infop->awi_parent != NULL);
906 	ASSERT(infop->awi_parent->awi_data != NULL);
907 	if (flags & ACPIDEV_PROCESS_FLAG_CREATE) {
908 		mutex_enter(&(DEVI(pdip)->devi_lock));
909 		/*
910 		 * Put the device into offline state if its parent is in
911 		 * offline state.
912 		 */
913 		if (DEVI_IS_DEVICE_OFFLINE(pdip)) {
914 			flags |= ACPIDEV_PROCESS_FLAG_OFFLINE;
915 		}
916 		mutex_exit(&(DEVI(pdip)->devi_lock));
917 	}
918 
919 	/* Evaluate filtering rules and generate device name. */
920 	devname = kmem_zalloc(ACPIDEV_MAX_NAMELEN + 1, KM_SLEEP);
921 	(void) memcpy(devname, (char *)&adip->Name, sizeof (adip->Name));
922 	if (flags & ACPIDEV_PROCESS_FLAG_CREATE) {
923 		res = clsp->adc_filter(infop, devname, ACPIDEV_MAX_NAMELEN);
924 	} else {
925 		res = clsp->adc_filter(infop, NULL, 0);
926 	}
927 
928 	/* Create device if requested. */
929 	if ((flags & ACPIDEV_PROCESS_FLAG_CREATE) &&
930 	    !(infop->awi_flags & ACPIDEV_WI_DISABLE_CREATE) &&
931 	    !(infop->awi_flags & ACPIDEV_WI_DEVICE_CREATED) &&
932 	    (res == ACPIDEV_FILTER_DEFAULT || res == ACPIDEV_FILTER_CREATE)) {
933 		int ret;
934 
935 		/*
936 		 * Allocate dip and set default properties.
937 		 * Properties can be overriden in class specific init routines.
938 		 */
939 		ASSERT(infop->awi_dip == NULL);
940 		ndi_devi_alloc_sleep(pdip, devname, (pnode_t)DEVI_SID_NODEID,
941 		    &dip);
942 		infop->awi_dip = dip;
943 		ret = ndi_prop_update_string(DDI_DEV_T_NONE, dip,
944 		    OBP_DEVICETYPE, clsp->adc_dev_type);
945 		if (ret != NDI_SUCCESS) {
946 			ACPIDEV_DEBUG(CE_WARN,
947 			    "!acpidev: failed to set device property for %s.",
948 			    infop->awi_name);
949 			(void) ddi_remove_child(dip, 0);
950 			infop->awi_dip = NULL;
951 			kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1);
952 			return (AE_ERROR);
953 		}
954 
955 		/* Build cross reference between dip and ACPI object. */
956 		if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0 &&
957 		    ACPI_FAILURE(acpica_tag_devinfo(dip, hdl))) {
958 			cmn_err(CE_WARN,
959 			    "!acpidev: failed to tag object %s.",
960 			    infop->awi_name);
961 			(void) ddi_remove_child(dip, 0);
962 			infop->awi_dip = NULL;
963 			kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1);
964 			return (AE_ERROR);
965 		}
966 
967 		/* Call class specific initialization callback. */
968 		if (clsp->adc_init != NULL &&
969 		    ACPI_FAILURE(clsp->adc_init(infop))) {
970 			ACPIDEV_DEBUG(CE_WARN,
971 			    "!acpidev: failed to initialize device %s.",
972 			    infop->awi_name);
973 			if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0) {
974 				(void) acpica_untag_devinfo(dip, hdl);
975 			}
976 			(void) ddi_remove_child(dip, 0);
977 			infop->awi_dip = NULL;
978 			kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1);
979 			return (AE_ERROR);
980 		}
981 
982 		/* Set device into offline state if requested. */
983 		if (flags & ACPIDEV_PROCESS_FLAG_OFFLINE) {
984 			mutex_enter(&(DEVI(dip)->devi_lock));
985 			DEVI_SET_DEVICE_OFFLINE(dip);
986 			mutex_exit(&(DEVI(dip)->devi_lock));
987 		}
988 
989 		/* Mark status */
990 		infop->awi_flags |= ACPIDEV_WI_DEVICE_CREATED;
991 		datap->aod_iflag |= ACPIDEV_ODF_DEVINFO_CREATED;
992 		datap->aod_dip = dip;
993 		datap->aod_class = clsp;
994 		/* Hold reference count on class driver. */
995 		atomic_inc_32(&clsp->adc_refcnt);
996 		if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0) {
997 			datap->aod_iflag |= ACPIDEV_ODF_DEVINFO_TAGGED;
998 		}
999 
1000 		/* Bind device driver. */
1001 		if ((flags & ACPIDEV_PROCESS_FLAG_NOBIND) != 0) {
1002 			mutex_enter(&(DEVI(dip)->devi_lock));
1003 			DEVI(dip)->devi_flags |= DEVI_NO_BIND;
1004 			mutex_exit(&(DEVI(dip)->devi_lock));
1005 		} else {
1006 			(void) ndi_devi_bind_driver(dip, 0);
1007 		}
1008 
1009 		/* Hold reference on branch when hot-adding devices. */
1010 		if (flags & ACPIDEV_PROCESS_FLAG_HOLDBRANCH) {
1011 			e_ddi_branch_hold(dip);
1012 		}
1013 	}
1014 
1015 	/* Free resources */
1016 	kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1);
1017 	rc = AE_OK;
1018 
1019 	/* Recursively scan child objects if requested. */
1020 	switch (res) {
1021 	case ACPIDEV_FILTER_DEFAULT:
1022 		/* FALLTHROUGH */
1023 	case ACPIDEV_FILTER_SCAN:
1024 		/* Check if we need to scan child. */
1025 		if ((flags & ACPIDEV_PROCESS_FLAG_SCAN) &&
1026 		    !(infop->awi_flags & ACPIDEV_WI_DISABLE_SCAN) &&
1027 		    !(infop->awi_flags & ACPIDEV_WI_CHILD_SCANNED)) {
1028 			/* probe child object. */
1029 			rc = acpidev_probe_child(infop);
1030 			if (ACPI_FAILURE(rc)) {
1031 				ACPIDEV_DEBUG(CE_WARN,
1032 				    "!acpidev: failed to probe subtree of %s.",
1033 				    infop->awi_name);
1034 				rc = AE_ERROR;
1035 			}
1036 			/* Mark object as scanned. */
1037 			infop->awi_flags |= ACPIDEV_WI_CHILD_SCANNED;
1038 		}
1039 		break;
1040 
1041 	case ACPIDEV_FILTER_CREATE:
1042 		/* FALLTHROUGH */
1043 	case ACPIDEV_FILTER_CONTINUE:
1044 		/* FALLTHROUGH */
1045 	case ACPIDEV_FILTER_SKIP:
1046 		break;
1047 
1048 	case ACPIDEV_FILTER_FAILED:
1049 		ACPIDEV_DEBUG(CE_WARN,
1050 		    "!acpidev: failed to probe device for %s.",
1051 		    infop->awi_name);
1052 		rc = AE_ERROR;
1053 		break;
1054 
1055 	default:
1056 		cmn_err(CE_WARN,
1057 		    "!acpidev: unknown filter result code %d.", res);
1058 		rc = AE_ERROR;
1059 		break;
1060 	}
1061 
1062 	return (rc);
1063 }
1064 
1065 acpidev_filter_result_t
1066 acpidev_filter_default(acpidev_walk_info_t *infop, ACPI_HANDLE hdl,
1067     acpidev_filter_rule_t *afrp, char *devname, int len)
1068 {
1069 	_NOTE(ARGUNUSED(hdl));
1070 
1071 	ASSERT(afrp != NULL);
1072 	ASSERT(devname == NULL || len >= ACPIDEV_MAX_NAMELEN);
1073 	if (infop->awi_level < afrp->adf_minlvl ||
1074 	    infop->awi_level > afrp->adf_maxlvl) {
1075 		return (ACPIDEV_FILTER_CONTINUE);
1076 	} else if (afrp->adf_pattern != NULL &&
1077 	    strncmp(afrp->adf_pattern,
1078 	    (char *)&infop->awi_info->Name,
1079 	    sizeof (infop->awi_info->Name))) {
1080 		return (ACPIDEV_FILTER_CONTINUE);
1081 	}
1082 	if (afrp->adf_replace != NULL && devname != NULL) {
1083 		(void) strlcpy(devname, afrp->adf_replace, len);
1084 	}
1085 
1086 	return (afrp->adf_retcode);
1087 }
1088 
1089 acpidev_filter_result_t
1090 acpidev_filter_device(acpidev_walk_info_t *infop, ACPI_HANDLE hdl,
1091     acpidev_filter_rule_t *afrp, int entries, char *devname, int len)
1092 {
1093 	acpidev_filter_result_t res;
1094 
1095 	res = ACPIDEV_FILTER_FAILED;
1096 	/* Evaluate filtering rules. */
1097 	for (; entries > 0; entries--, afrp++) {
1098 		if (afrp->adf_filter_func != NULL) {
1099 			res = afrp->adf_filter_func(infop, hdl, afrp,
1100 			    devname, len);
1101 		} else {
1102 			res = acpidev_filter_default(infop, hdl, afrp,
1103 			    devname, len);
1104 		}
1105 		if (res == ACPIDEV_FILTER_DEFAULT ||
1106 		    res == ACPIDEV_FILTER_SCAN) {
1107 			infop->awi_class_list = afrp->adf_class_list;
1108 			break;
1109 		}
1110 	}
1111 
1112 	return (res);
1113 }
1114 
1115 dev_info_t *
1116 acpidev_root_node(void)
1117 {
1118 	return (acpidev_root_dip);
1119 }
1120 
1121 ACPI_STATUS
1122 acpidev_register_class(acpidev_class_list_t **listpp, acpidev_class_t *clsp,
1123     boolean_t tail)
1124 {
1125 	ACPI_STATUS rc;
1126 	acpidev_class_list_t *item;
1127 	acpidev_class_list_t *temp;
1128 
1129 	ASSERT(clsp != NULL);
1130 	ASSERT(listpp != NULL);
1131 	if (listpp == NULL || clsp == NULL) {
1132 		ACPIDEV_DEBUG(CE_WARN,
1133 		    "!acpidev: invalid parameter in acpidev_register_class().");
1134 		return (AE_BAD_PARAMETER);
1135 	} else if (clsp->adc_version != ACPIDEV_CLASS_REV) {
1136 		cmn_err(CE_WARN,
1137 		    "!acpidev: class driver %s version mismatch.",
1138 		    clsp->adc_class_name);
1139 		return (AE_BAD_DATA);
1140 	}
1141 
1142 	rc = AE_OK;
1143 	item = kmem_zalloc(sizeof (*item), KM_SLEEP);
1144 	item->acl_class = clsp;
1145 	rw_enter(&acpidev_class_lock, RW_WRITER);
1146 	/* Check for duplicated item. */
1147 	for (temp = *listpp; temp != NULL; temp = temp->acl_next) {
1148 		if (temp->acl_class == clsp) {
1149 			cmn_err(CE_WARN,
1150 			    "!acpidev: register duplicate class driver %s.",
1151 			    clsp->adc_class_name);
1152 			rc = AE_ALREADY_EXISTS;
1153 			break;
1154 		}
1155 	}
1156 	if (ACPI_SUCCESS(rc)) {
1157 		if (tail) {
1158 			while (*listpp) {
1159 				listpp = &(*listpp)->acl_next;
1160 			}
1161 		}
1162 		item->acl_next = *listpp;
1163 		*listpp = item;
1164 	}
1165 	rw_exit(&acpidev_class_lock);
1166 	if (ACPI_FAILURE(rc)) {
1167 		kmem_free(item, sizeof (*item));
1168 	}
1169 
1170 	return (rc);
1171 }
1172 
1173 ACPI_STATUS
1174 acpidev_unregister_class(acpidev_class_list_t **listpp,
1175     acpidev_class_t *clsp)
1176 {
1177 	ACPI_STATUS rc = AE_NOT_FOUND;
1178 	acpidev_class_list_t *temp;
1179 
1180 	ASSERT(clsp != NULL);
1181 	ASSERT(listpp != NULL);
1182 	if (listpp == NULL || clsp == NULL) {
1183 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter "
1184 		    "in acpidev_unregister_class().");
1185 		return (AE_BAD_PARAMETER);
1186 	}
1187 
1188 	rw_enter(&acpidev_class_lock, RW_WRITER);
1189 	for (temp = NULL; *listpp; listpp = &(*listpp)->acl_next) {
1190 		if ((*listpp)->acl_class == clsp) {
1191 			temp = *listpp;
1192 			*listpp = (*listpp)->acl_next;
1193 			break;
1194 		}
1195 	}
1196 	if (temp == NULL) {
1197 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: class %p(%s) doesn't exist "
1198 		    "in acpidev_unregister_class().",
1199 		    (void *)clsp, clsp->adc_class_name);
1200 		rc = AE_NOT_FOUND;
1201 	} else if (temp->acl_class->adc_refcnt != 0) {
1202 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: class %p(%s) is still in use "
1203 		    "in acpidev_unregister_class()..",
1204 		    (void *)clsp, clsp->adc_class_name);
1205 		rc = AE_ERROR;
1206 	} else {
1207 		kmem_free(temp, sizeof (*temp));
1208 		rc = AE_OK;
1209 	}
1210 	rw_exit(&acpidev_class_lock);
1211 
1212 	return (rc);
1213 }
1214