1b72d5b75SMichael Corcoran /*
2b72d5b75SMichael Corcoran  * CDDL HEADER START
3b72d5b75SMichael Corcoran  *
4b72d5b75SMichael Corcoran  * The contents of this file are subject to the terms of the
5b72d5b75SMichael Corcoran  * Common Development and Distribution License (the "License").
6b72d5b75SMichael Corcoran  * You may not use this file except in compliance with the License.
7b72d5b75SMichael Corcoran  *
8b72d5b75SMichael Corcoran  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9b72d5b75SMichael Corcoran  * or http://www.opensolaris.org/os/licensing.
10b72d5b75SMichael Corcoran  * See the License for the specific language governing permissions
11b72d5b75SMichael Corcoran  * and limitations under the License.
12b72d5b75SMichael Corcoran  *
13b72d5b75SMichael Corcoran  * When distributing Covered Code, include this CDDL HEADER in each
14b72d5b75SMichael Corcoran  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15b72d5b75SMichael Corcoran  * If applicable, add the following below this CDDL HEADER, with the
16b72d5b75SMichael Corcoran  * fields enclosed by brackets "[]" replaced with your own identifying
17b72d5b75SMichael Corcoran  * information: Portions Copyright [yyyy] [name of copyright owner]
18b72d5b75SMichael Corcoran  *
19b72d5b75SMichael Corcoran  * CDDL HEADER END
20b72d5b75SMichael Corcoran  */
21b72d5b75SMichael Corcoran 
22b72d5b75SMichael Corcoran /*
23b72d5b75SMichael Corcoran  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24b72d5b75SMichael Corcoran  * Use is subject to license terms.
25*35786f68SRobert Mustacchi  * Copyright (c) 2018, Joyent, Inc.
26b72d5b75SMichael Corcoran  */
27b72d5b75SMichael Corcoran /*
28a3114836SGerry Liu  * Copyright (c) 2009-2010, Intel Corporation.
29b72d5b75SMichael Corcoran  * All rights reserved.
30b72d5b75SMichael Corcoran  */
31b72d5b75SMichael Corcoran 
32b72d5b75SMichael Corcoran #include <sys/types.h>
33b72d5b75SMichael Corcoran #include <sys/cmn_err.h>
34a3114836SGerry Liu #include <sys/note.h>
35b72d5b75SMichael Corcoran #include <sys/sysmacros.h>
36b72d5b75SMichael Corcoran #include <sys/sunddi.h>
37b72d5b75SMichael Corcoran #include <sys/sunndi.h>
38b72d5b75SMichael Corcoran #include <sys/acpi/acpi.h>
39b72d5b75SMichael Corcoran #include <sys/acpica.h>
40b72d5b75SMichael Corcoran #include <sys/acpidev.h>
41b72d5b75SMichael Corcoran #include <sys/acpidev_impl.h>
42b72d5b75SMichael Corcoran #include <util/sscanf.h>
43b72d5b75SMichael Corcoran 
44b72d5b75SMichael Corcoran /* Data structures used to extract the numeric unit address from string _UID. */
45b72d5b75SMichael Corcoran static acpidev_pseudo_uid_head_t acpidev_uid_heads[ACPIDEV_CLASS_ID_MAX];
46b72d5b75SMichael Corcoran static char *acpidev_uid_formats[] = {
47b72d5b75SMichael Corcoran 	"%u",
48b72d5b75SMichael Corcoran };
49b72d5b75SMichael Corcoran 
50b72d5b75SMichael Corcoran static char *acpidev_unknown_object_name = "<unknown>";
51b72d5b75SMichael Corcoran 
52b72d5b75SMichael Corcoran int
acpidev_query_device_status(ACPI_HANDLE hdl)53b72d5b75SMichael Corcoran acpidev_query_device_status(ACPI_HANDLE hdl)
54b72d5b75SMichael Corcoran {
55b72d5b75SMichael Corcoran 	int status;
56b72d5b75SMichael Corcoran 
57b72d5b75SMichael Corcoran 	ASSERT(hdl != NULL);
58b72d5b75SMichael Corcoran 	if (hdl == NULL) {
59b72d5b75SMichael Corcoran 		ACPIDEV_DEBUG(CE_WARN,
60a3114836SGerry Liu 		    "!acpidev: hdl is NULL in acpidev_query_device_status().");
61b72d5b75SMichael Corcoran 		return (0);
62b72d5b75SMichael Corcoran 	}
63b72d5b75SMichael Corcoran 
64*35786f68SRobert Mustacchi 	if (ACPI_FAILURE(acpica_get_object_status(hdl, &status))) {
65b72d5b75SMichael Corcoran 		/*
66*35786f68SRobert Mustacchi 		 * When the object status is not present, it will generally be
67*35786f68SRobert Mustacchi 		 * set to the default value as per the ACPI specification (6.3.7
68*35786f68SRobert Mustacchi 		 * _STA (Status)).  However, there are other possible cases of
69*35786f68SRobert Mustacchi 		 * ACPI failures. As this code is not aware of them and has
70*35786f68SRobert Mustacchi 		 * always treated all failures like the not-present set. Do the
71*35786f68SRobert Mustacchi 		 * same for the time being.
72b72d5b75SMichael Corcoran 		 */
73b72d5b75SMichael Corcoran 		status = 0xF;
74b72d5b75SMichael Corcoran 	}
75b72d5b75SMichael Corcoran 
76b72d5b75SMichael Corcoran 	return (status);
77b72d5b75SMichael Corcoran }
78b72d5b75SMichael Corcoran 
79b72d5b75SMichael Corcoran boolean_t
acpidev_check_device_present(int status)80b72d5b75SMichael Corcoran acpidev_check_device_present(int status)
81b72d5b75SMichael Corcoran {
82b72d5b75SMichael Corcoran 	/*
83b72d5b75SMichael Corcoran 	 * According to ACPI3.0 Spec, if either the ACPI_STA_DEVICE_PRESENT bit
84b72d5b75SMichael Corcoran 	 * or the ACPI_STA_DEVICE_FUNCTIONING bit is set, the device exists.
85b72d5b75SMichael Corcoran 	 */
86b72d5b75SMichael Corcoran 	if (status & (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_FUNCTIONING)) {
87b72d5b75SMichael Corcoran 		return (B_TRUE);
88b72d5b75SMichael Corcoran 	}
89b72d5b75SMichael Corcoran 
90b72d5b75SMichael Corcoran 	return (B_FALSE);
91b72d5b75SMichael Corcoran }
92b72d5b75SMichael Corcoran 
93b72d5b75SMichael Corcoran boolean_t
acpidev_check_device_enabled(int stat)94b72d5b75SMichael Corcoran acpidev_check_device_enabled(int stat)
95b72d5b75SMichael Corcoran {
96b72d5b75SMichael Corcoran 	/*
97b72d5b75SMichael Corcoran 	 * According to ACPI3.0 Spec, if either the ACPI_STA_DEVICE_PRESENT bit
98b72d5b75SMichael Corcoran 	 * or the ACPI_STA_DEVICE_FUNCTIONING bit is set, the device exists.
99b72d5b75SMichael Corcoran 	 * Return true if device exists and has been enabled.
100b72d5b75SMichael Corcoran 	 */
101b72d5b75SMichael Corcoran 	if ((stat & (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_FUNCTIONING)) &&
102b72d5b75SMichael Corcoran 	    (stat & ACPI_STA_DEVICE_ENABLED)) {
103b72d5b75SMichael Corcoran 		return (B_TRUE);
104b72d5b75SMichael Corcoran 	}
105b72d5b75SMichael Corcoran 
106b72d5b75SMichael Corcoran 	return (B_FALSE);
107b72d5b75SMichael Corcoran }
108b72d5b75SMichael Corcoran 
109b72d5b75SMichael Corcoran boolean_t
acpidev_match_device_id(ACPI_DEVICE_INFO * infop,char ** ids,int count)110b72d5b75SMichael Corcoran acpidev_match_device_id(ACPI_DEVICE_INFO *infop, char **ids, int count)
111b72d5b75SMichael Corcoran {
112b72d5b75SMichael Corcoran 	int i, j;
113b72d5b75SMichael Corcoran 
114b72d5b75SMichael Corcoran 	ASSERT(infop != NULL);
115b72d5b75SMichael Corcoran 	ASSERT(ids != NULL || count == 0);
116b72d5b75SMichael Corcoran 	/* Special case to match all devices if count is 0. */
117b72d5b75SMichael Corcoran 	if (count == 0) {
118b72d5b75SMichael Corcoran 		return (B_TRUE);
119b72d5b75SMichael Corcoran 	} else if (infop == NULL || ids == NULL) {
120a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameters in "
121b72d5b75SMichael Corcoran 		    "acpidev_match_device_id().");
122b72d5b75SMichael Corcoran 		return (B_FALSE);
123b72d5b75SMichael Corcoran 	}
124b72d5b75SMichael Corcoran 
125b72d5b75SMichael Corcoran 	/* Match _HID first. */
126b72d5b75SMichael Corcoran 	if (infop->Valid & ACPI_VALID_HID) {
127b72d5b75SMichael Corcoran 		for (i = 0; i < count; i++) {
12857190917SDana Myers 			if (strncmp(ids[i], infop->HardwareId.String,
12957190917SDana Myers 			    infop->HardwareId.Length) == 0) {
130b72d5b75SMichael Corcoran 				return (B_TRUE);
131b72d5b75SMichael Corcoran 			}
132b72d5b75SMichael Corcoran 		}
133b72d5b75SMichael Corcoran 	}
134b72d5b75SMichael Corcoran 
135b72d5b75SMichael Corcoran 	/* Match _CID next. */
136b72d5b75SMichael Corcoran 	if (infop->Valid & ACPI_VALID_CID) {
137b72d5b75SMichael Corcoran 		for (i = 0; i < count; i++) {
13857190917SDana Myers 			for (j = 0; j < infop->CompatibleIdList.Count; j++) {
139b72d5b75SMichael Corcoran 				if (strncmp(ids[i],
14057190917SDana Myers 				    infop->CompatibleIdList.Ids[j].String,
14157190917SDana Myers 				    infop->CompatibleIdList.Ids[j].Length)
14257190917SDana Myers 				    == 0) {
143b72d5b75SMichael Corcoran 					return (B_TRUE);
144b72d5b75SMichael Corcoran 				}
145b72d5b75SMichael Corcoran 			}
146b72d5b75SMichael Corcoran 		}
147b72d5b75SMichael Corcoran 	}
148b72d5b75SMichael Corcoran 
149b72d5b75SMichael Corcoran 	return (B_FALSE);
150b72d5b75SMichael Corcoran }
151b72d5b75SMichael Corcoran 
152b72d5b75SMichael Corcoran struct acpidev_get_device_arg {
153b72d5b75SMichael Corcoran 	boolean_t		skip_non_exist;
154b72d5b75SMichael Corcoran 	int			id_count;
155b72d5b75SMichael Corcoran 	char 			**device_ids;
156b72d5b75SMichael Corcoran 	void			*user_arg;
157b72d5b75SMichael Corcoran 	ACPI_WALK_CALLBACK	user_func;
158b72d5b75SMichael Corcoran };
159b72d5b75SMichael Corcoran 
160b72d5b75SMichael Corcoran static ACPI_STATUS
acpidev_get_device_callback(ACPI_HANDLE hdl,UINT32 level,void * arg,void ** retval)161b72d5b75SMichael Corcoran acpidev_get_device_callback(ACPI_HANDLE hdl, UINT32 level, void *arg,
162b72d5b75SMichael Corcoran     void **retval)
163b72d5b75SMichael Corcoran {
164b72d5b75SMichael Corcoran 	ACPI_STATUS rc;
165b72d5b75SMichael Corcoran 	ACPI_DEVICE_INFO *infop;
166b72d5b75SMichael Corcoran 	struct acpidev_get_device_arg *argp;
167*35786f68SRobert Mustacchi 	int status;
168b72d5b75SMichael Corcoran 
169b72d5b75SMichael Corcoran 	argp = (struct acpidev_get_device_arg *)arg;
170b72d5b75SMichael Corcoran 	ASSERT(argp != NULL);
171b72d5b75SMichael Corcoran 	ASSERT(hdl != NULL);
172b72d5b75SMichael Corcoran 
173b72d5b75SMichael Corcoran 	/* Query object information. */
17457190917SDana Myers 	rc = AcpiGetObjectInfo(hdl, &infop);
175b72d5b75SMichael Corcoran 	if (ACPI_FAILURE(rc)) {
176b72d5b75SMichael Corcoran 		cmn_err(CE_WARN, "!acpidev: failed to get ACPI object info "
177b72d5b75SMichael Corcoran 		    "in acpidev_get_device_callback().");
178b72d5b75SMichael Corcoran 		return (AE_CTRL_DEPTH);
179b72d5b75SMichael Corcoran 	}
180b72d5b75SMichael Corcoran 
181*35786f68SRobert Mustacchi 	rc = acpica_get_object_status(hdl, &status);
182*35786f68SRobert Mustacchi 
183b72d5b75SMichael Corcoran 	/*
184b72d5b75SMichael Corcoran 	 * Skip scanning of children if the device is neither PRESENT nor
185b72d5b75SMichael Corcoran 	 * FUNCTIONING.
186b72d5b75SMichael Corcoran 	 * Please refer to ACPI Spec3.0b Sec 6.3.1 and 6.5.1.
187b72d5b75SMichael Corcoran 	 */
188*35786f68SRobert Mustacchi 	if (argp->skip_non_exist && rc == AE_OK &&
189*35786f68SRobert Mustacchi 	    !acpidev_check_device_present(status)) {
190b72d5b75SMichael Corcoran 		rc = AE_CTRL_DEPTH;
191b72d5b75SMichael Corcoran 	/* Call user callback if matched. */
192b72d5b75SMichael Corcoran 	} else if (acpidev_match_device_id(infop, argp->device_ids,
193b72d5b75SMichael Corcoran 	    argp->id_count)) {
194b72d5b75SMichael Corcoran 		rc = argp->user_func(hdl, level, argp->user_arg, retval);
195b72d5b75SMichael Corcoran 	} else {
196b72d5b75SMichael Corcoran 		rc = AE_OK;
197b72d5b75SMichael Corcoran 	}
198b72d5b75SMichael Corcoran 
199b72d5b75SMichael Corcoran 	/* Free ACPI object info buffer. */
200b72d5b75SMichael Corcoran 	AcpiOsFree(infop);
201b72d5b75SMichael Corcoran 
202b72d5b75SMichael Corcoran 	return (rc);
203b72d5b75SMichael Corcoran }
204b72d5b75SMichael Corcoran 
205b72d5b75SMichael Corcoran ACPI_STATUS
acpidev_get_device_by_id(ACPI_HANDLE hdl,char ** ids,int count,int maxdepth,boolean_t skip_non_exist,ACPI_WALK_CALLBACK userfunc,void * userarg,void ** retval)206b72d5b75SMichael Corcoran acpidev_get_device_by_id(ACPI_HANDLE hdl, char **ids, int count,
207b72d5b75SMichael Corcoran     int maxdepth, boolean_t skip_non_exist,
208b72d5b75SMichael Corcoran     ACPI_WALK_CALLBACK userfunc, void *userarg, void **retval)
209b72d5b75SMichael Corcoran {
210b72d5b75SMichael Corcoran 	ACPI_STATUS rc;
211b72d5b75SMichael Corcoran 	struct acpidev_get_device_arg arg;
212b72d5b75SMichael Corcoran 
213b72d5b75SMichael Corcoran 	ASSERT(userfunc != NULL);
214b72d5b75SMichael Corcoran 	if (hdl == NULL || userfunc == NULL || (ids == NULL && count != 0)) {
215a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameters "
216b72d5b75SMichael Corcoran 		    "in acpidev_get_device_by_id().");
217b72d5b75SMichael Corcoran 		return (AE_BAD_PARAMETER);
218b72d5b75SMichael Corcoran 	}
219b72d5b75SMichael Corcoran 
220b72d5b75SMichael Corcoran 	/* Enumerate all descendant objects. */
221b72d5b75SMichael Corcoran 	arg.skip_non_exist = skip_non_exist;
222b72d5b75SMichael Corcoran 	arg.device_ids = ids;
223b72d5b75SMichael Corcoran 	arg.id_count = count;
224b72d5b75SMichael Corcoran 	arg.user_arg = userarg;
225b72d5b75SMichael Corcoran 	arg.user_func = userfunc;
226b72d5b75SMichael Corcoran 	rc = AcpiWalkNamespace(ACPI_TYPE_DEVICE, hdl, maxdepth,
22757190917SDana Myers 	    &acpidev_get_device_callback, NULL, &arg, retval);
228b72d5b75SMichael Corcoran 
229b72d5b75SMichael Corcoran 	return (rc);
230b72d5b75SMichael Corcoran }
231b72d5b75SMichael Corcoran 
232b72d5b75SMichael Corcoran ACPI_STATUS
acpidev_walk_apic(ACPI_BUFFER * bufp,ACPI_HANDLE hdl,char * method,acpidev_apic_walker_t func,void * context)233b72d5b75SMichael Corcoran acpidev_walk_apic(ACPI_BUFFER *bufp, ACPI_HANDLE hdl, char *method,
234b72d5b75SMichael Corcoran     acpidev_apic_walker_t func, void *context)
235b72d5b75SMichael Corcoran {
236b72d5b75SMichael Corcoran 	ACPI_STATUS rc;
237b72d5b75SMichael Corcoran 	ssize_t len;
238b72d5b75SMichael Corcoran 	ACPI_BUFFER buf;
2393b17bf65SMichael Corcoran 	ACPI_OBJECT *obj;
240b72d5b75SMichael Corcoran 	ACPI_SUBTABLE_HEADER *ap;
241b72d5b75SMichael Corcoran 	ACPI_TABLE_MADT *mp = NULL;
242b72d5b75SMichael Corcoran 
243b72d5b75SMichael Corcoran 	ASSERT(func != NULL);
244b72d5b75SMichael Corcoran 	if (func == NULL) {
245b72d5b75SMichael Corcoran 		ACPIDEV_DEBUG(CE_WARN,
246a3114836SGerry Liu 		    "!acpidev: invalid parameters for acpidev_walk_apic().");
247b72d5b75SMichael Corcoran 		return (AE_BAD_PARAMETER);
248b72d5b75SMichael Corcoran 	}
249b72d5b75SMichael Corcoran 
250b72d5b75SMichael Corcoran 	buf.Pointer = NULL;
251b72d5b75SMichael Corcoran 	buf.Length = ACPI_ALLOCATE_BUFFER;
252b72d5b75SMichael Corcoran 
253b72d5b75SMichael Corcoran 	/* A walk buffer was passed in if bufp isn't NULL. */
254b72d5b75SMichael Corcoran 	if (bufp != NULL) {
255b72d5b75SMichael Corcoran 		ap = (ACPI_SUBTABLE_HEADER *)(bufp->Pointer);
256b72d5b75SMichael Corcoran 		len = bufp->Length;
257b72d5b75SMichael Corcoran 	} else if (method != NULL) {
258b72d5b75SMichael Corcoran 		/*
259b72d5b75SMichael Corcoran 		 * Otherwise, if we have an evaluate method, we get the walk
2603b17bf65SMichael Corcoran 		 * buffer from a successful invocation of
2613b17bf65SMichael Corcoran 		 * AcpiEvaluateObjectTyped().
262b72d5b75SMichael Corcoran 		 */
263b72d5b75SMichael Corcoran 		ASSERT(hdl != NULL);
2643b17bf65SMichael Corcoran 		rc = AcpiEvaluateObjectTyped(hdl, method, NULL, &buf,
2653b17bf65SMichael Corcoran 		    ACPI_TYPE_BUFFER);
2663b17bf65SMichael Corcoran 		if (ACPI_SUCCESS(rc)) {
2673b17bf65SMichael Corcoran 			ASSERT(buf.Length >= sizeof (*obj));
2683b17bf65SMichael Corcoran 			obj = buf.Pointer;
2693b17bf65SMichael Corcoran 			ap = (ACPI_SUBTABLE_HEADER *)obj->Buffer.Pointer;
2703b17bf65SMichael Corcoran 			len = obj->Buffer.Length;
2713b17bf65SMichael Corcoran 		} else {
2723b17bf65SMichael Corcoran 			if (rc != AE_NOT_FOUND)
2733b17bf65SMichael Corcoran 				cmn_err(CE_WARN, "!acpidev: failed to evaluate "
2743b17bf65SMichael Corcoran 				    "%s in acpidev_walk_apic().", method);
275b72d5b75SMichael Corcoran 			return (rc);
276b72d5b75SMichael Corcoran 		}
277b72d5b75SMichael Corcoran 	} else {
278b72d5b75SMichael Corcoran 		/* As a last resort, walk the MADT table. */
279b72d5b75SMichael Corcoran 		rc = AcpiGetTable(ACPI_SIG_MADT, 1, (ACPI_TABLE_HEADER **)&mp);
280b72d5b75SMichael Corcoran 		if (ACPI_FAILURE(rc)) {
281b72d5b75SMichael Corcoran 			cmn_err(CE_WARN, "!acpidev: failed to get MADT table "
282b72d5b75SMichael Corcoran 			    "in acpidev_walk_apic().");
283b72d5b75SMichael Corcoran 			return (rc);
284b72d5b75SMichael Corcoran 		}
285b72d5b75SMichael Corcoran 		ap = (ACPI_SUBTABLE_HEADER *)(mp + 1);
286b72d5b75SMichael Corcoran 		len = mp->Header.Length - sizeof (*mp);
287b72d5b75SMichael Corcoran 	}
288b72d5b75SMichael Corcoran 
289b72d5b75SMichael Corcoran 	ASSERT(len >= 0);
290b72d5b75SMichael Corcoran 	for (rc = AE_OK; len > 0 && ACPI_SUCCESS(rc); len -= ap->Length,
291b72d5b75SMichael Corcoran 	    ap = (ACPI_SUBTABLE_HEADER *)(((char *)ap) + ap->Length)) {
292b72d5b75SMichael Corcoran 		ASSERT(len >= sizeof (ACPI_SUBTABLE_HEADER));
293b72d5b75SMichael Corcoran 		if (len <= sizeof (ACPI_SUBTABLE_HEADER) ||
294b72d5b75SMichael Corcoran 		    ap->Length <= sizeof (ACPI_SUBTABLE_HEADER) ||
295b72d5b75SMichael Corcoran 		    len < ap->Length) {
296b72d5b75SMichael Corcoran 			cmn_err(CE_WARN,
297b72d5b75SMichael Corcoran 			    "!acpidev: invalid APIC entry in MADT/_MAT.");
298b72d5b75SMichael Corcoran 			break;
299b72d5b75SMichael Corcoran 		}
300b72d5b75SMichael Corcoran 		rc = (*func)(ap, context);
301b72d5b75SMichael Corcoran 	}
302b72d5b75SMichael Corcoran 
303b72d5b75SMichael Corcoran 	if (buf.Pointer != NULL) {
304b72d5b75SMichael Corcoran 		AcpiOsFree(buf.Pointer);
305b72d5b75SMichael Corcoran 	}
306b72d5b75SMichael Corcoran 
307b72d5b75SMichael Corcoran 	return (rc);
308b72d5b75SMichael Corcoran }
309b72d5b75SMichael Corcoran 
310b72d5b75SMichael Corcoran char *
acpidev_get_object_name(ACPI_HANDLE hdl)311b72d5b75SMichael Corcoran acpidev_get_object_name(ACPI_HANDLE hdl)
312b72d5b75SMichael Corcoran {
313b72d5b75SMichael Corcoran 	ACPI_BUFFER buf;
314b72d5b75SMichael Corcoran 	char *objname = acpidev_unknown_object_name;
315b72d5b75SMichael Corcoran 
316b72d5b75SMichael Corcoran 	buf.Length = ACPI_ALLOCATE_BUFFER;
317b72d5b75SMichael Corcoran 	buf.Pointer = NULL;
318b72d5b75SMichael Corcoran 	if (ACPI_SUCCESS(AcpiGetName(hdl, ACPI_FULL_PATHNAME, &buf))) {
319b72d5b75SMichael Corcoran 		ASSERT(buf.Pointer != NULL);
320b72d5b75SMichael Corcoran 		objname = (char *)buf.Pointer;
321b72d5b75SMichael Corcoran 	}
322b72d5b75SMichael Corcoran 
323b72d5b75SMichael Corcoran 	return (objname);
324b72d5b75SMichael Corcoran }
325b72d5b75SMichael Corcoran 
326b72d5b75SMichael Corcoran void
acpidev_free_object_name(char * objname)327b72d5b75SMichael Corcoran acpidev_free_object_name(char *objname)
328b72d5b75SMichael Corcoran {
329b72d5b75SMichael Corcoran 	if (objname != acpidev_unknown_object_name && objname != NULL) {
330b72d5b75SMichael Corcoran 		AcpiOsFree(objname);
331b72d5b75SMichael Corcoran 	}
332b72d5b75SMichael Corcoran }
333b72d5b75SMichael Corcoran 
334b72d5b75SMichael Corcoran acpidev_walk_info_t *
acpidev_alloc_walk_info(acpidev_op_type_t op_type,int lvl,ACPI_HANDLE hdl,acpidev_class_list_t ** listpp,acpidev_walk_info_t * pinfop)335b72d5b75SMichael Corcoran acpidev_alloc_walk_info(acpidev_op_type_t op_type, int lvl, ACPI_HANDLE hdl,
336b72d5b75SMichael Corcoran     acpidev_class_list_t **listpp, acpidev_walk_info_t *pinfop)
337b72d5b75SMichael Corcoran {
338b72d5b75SMichael Corcoran 	acpidev_walk_info_t *infop = NULL;
339b72d5b75SMichael Corcoran 	acpidev_data_handle_t datap = NULL;
340b72d5b75SMichael Corcoran 
341b72d5b75SMichael Corcoran 	ASSERT(0 <= lvl && lvl < ACPIDEV_MAX_ENUM_LEVELS);
342b72d5b75SMichael Corcoran 	infop = kmem_zalloc(sizeof (*infop), KM_SLEEP);
343b72d5b75SMichael Corcoran 	infop->awi_op_type = op_type;
344b72d5b75SMichael Corcoran 	infop->awi_level = lvl;
345b72d5b75SMichael Corcoran 	infop->awi_parent = pinfop;
346b72d5b75SMichael Corcoran 	infop->awi_class_list = listpp;
347b72d5b75SMichael Corcoran 	infop->awi_hdl = hdl;
348b72d5b75SMichael Corcoran 	infop->awi_name = acpidev_get_object_name(hdl);
349b72d5b75SMichael Corcoran 
350b72d5b75SMichael Corcoran 	/* Cache ACPI device information. */
35157190917SDana Myers 	if (ACPI_FAILURE(AcpiGetObjectInfo(hdl, &infop->awi_info))) {
352b72d5b75SMichael Corcoran 		cmn_err(CE_WARN, "!acpidev: failed to get object info for %s "
353b72d5b75SMichael Corcoran 		    "in acpidev_alloc_walk_info().", infop->awi_name);
354b72d5b75SMichael Corcoran 		acpidev_free_object_name(infop->awi_name);
355b72d5b75SMichael Corcoran 		kmem_free(infop, sizeof (*infop));
356b72d5b75SMichael Corcoran 		return (NULL);
357b72d5b75SMichael Corcoran 	}
358b72d5b75SMichael Corcoran 
359b72d5b75SMichael Corcoran 	/*
360b72d5b75SMichael Corcoran 	 * Get or create an ACPI object data handle, which will be used to
361b72d5b75SMichael Corcoran 	 * maintain object status information.
362b72d5b75SMichael Corcoran 	 */
363b72d5b75SMichael Corcoran 	if ((datap = acpidev_data_get_handle(hdl)) != NULL) {
364b72d5b75SMichael Corcoran 		ASSERT(datap->aod_hdl == hdl);
365b72d5b75SMichael Corcoran 		ASSERT(datap->aod_level == lvl);
366b72d5b75SMichael Corcoran 	} else if ((datap = acpidev_data_create_handle(hdl)) != NULL) {
367b72d5b75SMichael Corcoran 		datap->aod_level = lvl;
368b72d5b75SMichael Corcoran 		datap->aod_hdl = hdl;
369b72d5b75SMichael Corcoran 	} else {
370a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to create object "
371b72d5b75SMichael Corcoran 		    "handle for %s in acpidev_alloc_walk_info().",
372b72d5b75SMichael Corcoran 		    infop->awi_name);
373b72d5b75SMichael Corcoran 		AcpiOsFree(infop->awi_info);
374b72d5b75SMichael Corcoran 		acpidev_free_object_name(infop->awi_name);
375b72d5b75SMichael Corcoran 		kmem_free(infop, sizeof (*infop));
376b72d5b75SMichael Corcoran 		return (NULL);
377b72d5b75SMichael Corcoran 	}
378b72d5b75SMichael Corcoran 	infop->awi_data = datap;
379b72d5b75SMichael Corcoran 	/* Sync DEVICE_CREATED flag. */
380b72d5b75SMichael Corcoran 	if (datap->aod_iflag & ACPIDEV_ODF_DEVINFO_CREATED) {
381b72d5b75SMichael Corcoran 		ASSERT(datap->aod_dip != NULL);
382b72d5b75SMichael Corcoran 		ASSERT(datap->aod_class != NULL);
383b72d5b75SMichael Corcoran 		infop->awi_dip = datap->aod_dip;
384b72d5b75SMichael Corcoran 		infop->awi_flags |= ACPIDEV_WI_DEVICE_CREATED;
385b72d5b75SMichael Corcoran 	}
386b72d5b75SMichael Corcoran 
387b72d5b75SMichael Corcoran 	return (infop);
388b72d5b75SMichael Corcoran }
389b72d5b75SMichael Corcoran 
390b72d5b75SMichael Corcoran void
acpidev_free_walk_info(acpidev_walk_info_t * infop)391b72d5b75SMichael Corcoran acpidev_free_walk_info(acpidev_walk_info_t *infop)
392b72d5b75SMichael Corcoran {
393b72d5b75SMichael Corcoran 	/*
394b72d5b75SMichael Corcoran 	 * The ACPI object data handle will only be released when the
395b72d5b75SMichael Corcoran 	 * corresponding object is going to be destroyed.
396b72d5b75SMichael Corcoran 	 */
397b72d5b75SMichael Corcoran 	if (infop != NULL) {
398b72d5b75SMichael Corcoran 		if (infop->awi_info != NULL) {
399b72d5b75SMichael Corcoran 			AcpiOsFree(infop->awi_info);
400b72d5b75SMichael Corcoran 		}
401b72d5b75SMichael Corcoran 		if (infop->awi_name != NULL) {
402b72d5b75SMichael Corcoran 			acpidev_free_object_name(infop->awi_name);
403b72d5b75SMichael Corcoran 		}
404b72d5b75SMichael Corcoran 		kmem_free(infop, sizeof (*infop));
405b72d5b75SMichael Corcoran 	}
406b72d5b75SMichael Corcoran }
407b72d5b75SMichael Corcoran 
408b72d5b75SMichael Corcoran dev_info_t *
acpidev_walk_info_get_pdip(acpidev_walk_info_t * infop)409b72d5b75SMichael Corcoran acpidev_walk_info_get_pdip(acpidev_walk_info_t *infop)
410b72d5b75SMichael Corcoran {
411b72d5b75SMichael Corcoran 	while (infop != NULL) {
412b72d5b75SMichael Corcoran 		if (infop->awi_dip != NULL) {
413b72d5b75SMichael Corcoran 			return (infop->awi_dip);
414b72d5b75SMichael Corcoran 		}
415b72d5b75SMichael Corcoran 		infop = infop->awi_parent;
416b72d5b75SMichael Corcoran 	}
417b72d5b75SMichael Corcoran 
418b72d5b75SMichael Corcoran 	return (NULL);
419b72d5b75SMichael Corcoran }
420b72d5b75SMichael Corcoran 
421b72d5b75SMichael Corcoran /*
422b72d5b75SMichael Corcoran  * Called to release resources when the corresponding object is going
423b72d5b75SMichael Corcoran  * to be destroyed.
424b72d5b75SMichael Corcoran  */
425b72d5b75SMichael Corcoran static void
acpidev_free_object_handler(ACPI_HANDLE hdl,void * data)426a3114836SGerry Liu acpidev_free_object_handler(ACPI_HANDLE hdl, void *data)
427b72d5b75SMichael Corcoran {
428a3114836SGerry Liu 	_NOTE(ARGUNUSED(hdl));
429a3114836SGerry Liu 
430b72d5b75SMichael Corcoran 	acpidev_data_handle_t objhdl = data;
431b72d5b75SMichael Corcoran 
432a3114836SGerry Liu 	if (objhdl->aod_class != NULL) {
433a3114836SGerry Liu 		atomic_dec_32(&objhdl->aod_class->adc_refcnt);
434a3114836SGerry Liu 		objhdl->aod_class = NULL;
435a3114836SGerry Liu 	}
436b72d5b75SMichael Corcoran 	kmem_free(objhdl, sizeof (acpidev_data_handle_t));
437b72d5b75SMichael Corcoran }
438b72d5b75SMichael Corcoran 
439b72d5b75SMichael Corcoran acpidev_data_handle_t
acpidev_data_get_handle(ACPI_HANDLE hdl)440b72d5b75SMichael Corcoran acpidev_data_get_handle(ACPI_HANDLE hdl)
441b72d5b75SMichael Corcoran {
442b72d5b75SMichael Corcoran 	void *ptr;
443b72d5b75SMichael Corcoran 	acpidev_data_handle_t objhdl = NULL;
444b72d5b75SMichael Corcoran 
445a3114836SGerry Liu 	if (ACPI_SUCCESS(AcpiGetData(hdl, acpidev_free_object_handler, &ptr))) {
446b72d5b75SMichael Corcoran 		objhdl = (acpidev_data_handle_t)ptr;
447b72d5b75SMichael Corcoran 	}
448b72d5b75SMichael Corcoran 
449b72d5b75SMichael Corcoran 	return (objhdl);
450b72d5b75SMichael Corcoran }
451b72d5b75SMichael Corcoran 
452b72d5b75SMichael Corcoran acpidev_data_handle_t
acpidev_data_create_handle(ACPI_HANDLE hdl)453b72d5b75SMichael Corcoran acpidev_data_create_handle(ACPI_HANDLE hdl)
454b72d5b75SMichael Corcoran {
455b72d5b75SMichael Corcoran 	acpidev_data_handle_t objhdl;
456b72d5b75SMichael Corcoran 
457b72d5b75SMichael Corcoran 	objhdl = kmem_zalloc(sizeof (*objhdl), KM_SLEEP);
458a3114836SGerry Liu 	objhdl->aod_bdtype = ACPIDEV_INVALID_BOARD;
459a3114836SGerry Liu 	objhdl->aod_bdnum = UINT32_MAX;
460a3114836SGerry Liu 	objhdl->aod_portid = UINT32_MAX;
461a3114836SGerry Liu 	objhdl->aod_class_id = ACPIDEV_CLASS_ID_INVALID;
462a3114836SGerry Liu 
463a3114836SGerry Liu 	if (ACPI_FAILURE(AcpiAttachData(hdl, acpidev_free_object_handler,
464b72d5b75SMichael Corcoran 	    (void *)objhdl))) {
465b72d5b75SMichael Corcoran 		cmn_err(CE_WARN,
466b72d5b75SMichael Corcoran 		    "!acpidev: failed to attach handle data to object.");
467b72d5b75SMichael Corcoran 		kmem_free(objhdl, sizeof (*objhdl));
468b72d5b75SMichael Corcoran 		return (NULL);
469b72d5b75SMichael Corcoran 	}
470b72d5b75SMichael Corcoran 
471b72d5b75SMichael Corcoran 	return (objhdl);
472b72d5b75SMichael Corcoran }
473b72d5b75SMichael Corcoran 
474b72d5b75SMichael Corcoran void
acpidev_data_destroy_handle(ACPI_HANDLE hdl)475b72d5b75SMichael Corcoran acpidev_data_destroy_handle(ACPI_HANDLE hdl)
476b72d5b75SMichael Corcoran {
477b72d5b75SMichael Corcoran 	void *ptr;
478a3114836SGerry Liu 	acpidev_data_handle_t objhdl = NULL;
479b72d5b75SMichael Corcoran 
480a3114836SGerry Liu 	if (ACPI_SUCCESS(AcpiGetData(hdl, acpidev_free_object_handler, &ptr)) &&
481a3114836SGerry Liu 	    ACPI_SUCCESS(AcpiDetachData(hdl, acpidev_free_object_handler))) {
482a3114836SGerry Liu 		objhdl = ptr;
483a3114836SGerry Liu 		if (objhdl->aod_class != NULL) {
484a3114836SGerry Liu 			atomic_dec_32(&objhdl->aod_class->adc_refcnt);
485a3114836SGerry Liu 			objhdl->aod_class = NULL;
486a3114836SGerry Liu 		}
487b72d5b75SMichael Corcoran 		kmem_free(ptr, sizeof (acpidev_data_handle_t));
488b72d5b75SMichael Corcoran 	}
489b72d5b75SMichael Corcoran }
490b72d5b75SMichael Corcoran 
491b72d5b75SMichael Corcoran ACPI_HANDLE
acpidev_data_get_object(acpidev_data_handle_t hdl)492b72d5b75SMichael Corcoran acpidev_data_get_object(acpidev_data_handle_t hdl)
493b72d5b75SMichael Corcoran {
494b72d5b75SMichael Corcoran 	ASSERT(hdl != NULL);
495b72d5b75SMichael Corcoran 	return ((hdl != NULL) ? hdl->aod_hdl : NULL);
496b72d5b75SMichael Corcoran }
497b72d5b75SMichael Corcoran 
498b72d5b75SMichael Corcoran dev_info_t *
acpidev_data_get_devinfo(acpidev_data_handle_t hdl)499b72d5b75SMichael Corcoran acpidev_data_get_devinfo(acpidev_data_handle_t hdl)
500b72d5b75SMichael Corcoran {
501b72d5b75SMichael Corcoran 	ASSERT(hdl != NULL);
502b72d5b75SMichael Corcoran 	if (hdl == NULL ||
503b72d5b75SMichael Corcoran 	    (hdl->aod_iflag & ACPIDEV_ODF_DEVINFO_CREATED) == 0) {
504b72d5b75SMichael Corcoran 		return (NULL);
505b72d5b75SMichael Corcoran 	} else {
506b72d5b75SMichael Corcoran 		ASSERT(hdl->aod_dip != NULL);
507b72d5b75SMichael Corcoran 		return (hdl->aod_dip);
508b72d5b75SMichael Corcoran 	}
509b72d5b75SMichael Corcoran }
510b72d5b75SMichael Corcoran 
511b72d5b75SMichael Corcoran int
acpidev_data_get_status(acpidev_data_handle_t hdl)512b72d5b75SMichael Corcoran acpidev_data_get_status(acpidev_data_handle_t hdl)
513b72d5b75SMichael Corcoran {
514b72d5b75SMichael Corcoran 	ASSERT(hdl != NULL);
515b72d5b75SMichael Corcoran 	if (hdl == NULL ||
516b72d5b75SMichael Corcoran 	    (hdl->aod_iflag & ACPIDEV_ODF_STATUS_VALID) == 0) {
517b72d5b75SMichael Corcoran 		return (0);
518b72d5b75SMichael Corcoran 	} else {
519b72d5b75SMichael Corcoran 		return (hdl->aod_status);
520b72d5b75SMichael Corcoran 	}
521b72d5b75SMichael Corcoran }
522b72d5b75SMichael Corcoran 
523b72d5b75SMichael Corcoran void
acpidev_data_set_flag(acpidev_data_handle_t hdl,uint32_t flag)524b72d5b75SMichael Corcoran acpidev_data_set_flag(acpidev_data_handle_t hdl, uint32_t flag)
525b72d5b75SMichael Corcoran {
526b72d5b75SMichael Corcoran 	ASSERT(hdl != NULL);
527a3114836SGerry Liu 	atomic_or_32(&hdl->aod_eflag, flag);
528b72d5b75SMichael Corcoran }
529b72d5b75SMichael Corcoran 
530b72d5b75SMichael Corcoran void
acpidev_data_clear_flag(acpidev_data_handle_t hdl,uint32_t flag)531b72d5b75SMichael Corcoran acpidev_data_clear_flag(acpidev_data_handle_t hdl, uint32_t flag)
532b72d5b75SMichael Corcoran {
533b72d5b75SMichael Corcoran 	ASSERT(hdl != NULL);
534a3114836SGerry Liu 	atomic_and_32(&hdl->aod_eflag, ~flag);
535b72d5b75SMichael Corcoran }
536b72d5b75SMichael Corcoran 
537b72d5b75SMichael Corcoran uint32_t
acpidev_data_get_flag(acpidev_data_handle_t hdl,uint32_t flag)538b72d5b75SMichael Corcoran acpidev_data_get_flag(acpidev_data_handle_t hdl, uint32_t flag)
539b72d5b75SMichael Corcoran {
540b72d5b75SMichael Corcoran 	ASSERT(hdl != NULL);
541b72d5b75SMichael Corcoran 	return (hdl->aod_eflag & flag);
542b72d5b75SMichael Corcoran }
543b72d5b75SMichael Corcoran 
544a3114836SGerry Liu boolean_t
acpidev_data_dr_capable(acpidev_data_handle_t hdl)545a3114836SGerry Liu acpidev_data_dr_capable(acpidev_data_handle_t hdl)
546a3114836SGerry Liu {
547a3114836SGerry Liu 	ASSERT(hdl != NULL);
548a3114836SGerry Liu 	return (hdl->aod_iflag & ACPIDEV_ODF_HOTPLUG_CAPABLE);
549a3114836SGerry Liu }
550a3114836SGerry Liu 
551a3114836SGerry Liu boolean_t
acpidev_data_dr_ready(acpidev_data_handle_t hdl)552a3114836SGerry Liu acpidev_data_dr_ready(acpidev_data_handle_t hdl)
553a3114836SGerry Liu {
554a3114836SGerry Liu 	ASSERT(hdl != NULL);
555a3114836SGerry Liu 	return (hdl->aod_iflag & ACPIDEV_ODF_HOTPLUG_READY);
556a3114836SGerry Liu }
557a3114836SGerry Liu 
558a3114836SGerry Liu boolean_t
acpidev_data_dr_failed(acpidev_data_handle_t hdl)559a3114836SGerry Liu acpidev_data_dr_failed(acpidev_data_handle_t hdl)
560a3114836SGerry Liu {
561a3114836SGerry Liu 	ASSERT(hdl != NULL);
562a3114836SGerry Liu 	return (hdl->aod_iflag & ACPIDEV_ODF_HOTPLUG_FAILED);
563a3114836SGerry Liu }
564a3114836SGerry Liu 
565b72d5b75SMichael Corcoran static char *
acpidev_generate_pseudo_unitaddr(char * uid,acpidev_class_id_t cid,char * buf,size_t len)566b72d5b75SMichael Corcoran acpidev_generate_pseudo_unitaddr(char *uid, acpidev_class_id_t cid,
567b72d5b75SMichael Corcoran     char *buf, size_t len)
568b72d5b75SMichael Corcoran {
569b72d5b75SMichael Corcoran 	acpidev_pseudo_uid_t *up, **pp;
570b72d5b75SMichael Corcoran 
571b72d5b75SMichael Corcoran 	ASSERT(len >= 64);
572b72d5b75SMichael Corcoran 	ASSERT(cid >= 0 && cid < ACPIDEV_CLASS_ID_MAX);
573b72d5b75SMichael Corcoran 	if (cid < 0 || cid >= ACPIDEV_CLASS_ID_MAX) {
574b72d5b75SMichael Corcoran 		return (NULL);
575b72d5b75SMichael Corcoran 	}
576b72d5b75SMichael Corcoran 
577b72d5b75SMichael Corcoran 	mutex_enter(&acpidev_uid_heads[cid].apuh_lock);
578b72d5b75SMichael Corcoran 	for (pp = &acpidev_uid_heads[cid].apuh_first; *pp != NULL;
579b72d5b75SMichael Corcoran 	    pp = &(*pp)->apu_next) {
580b72d5b75SMichael Corcoran 		if (strcmp(uid, (*pp)->apu_uid) == 0 &&
581b72d5b75SMichael Corcoran 		    (*pp)->apu_cid == cid) {
582b72d5b75SMichael Corcoran 			break;
583b72d5b75SMichael Corcoran 		}
584b72d5b75SMichael Corcoran 	}
585b72d5b75SMichael Corcoran 	/* uid doesn't exist, create one and insert it into the list. */
586b72d5b75SMichael Corcoran 	if (*pp == NULL) {
587b72d5b75SMichael Corcoran 		up = kmem_zalloc(sizeof (*up), KM_SLEEP);
588b72d5b75SMichael Corcoran 		up->apu_uid = ddi_strdup(uid, KM_SLEEP);
589b72d5b75SMichael Corcoran 		up->apu_cid = cid;
590b72d5b75SMichael Corcoran 		up->apu_nid = acpidev_uid_heads[cid].apuh_id++;
591b72d5b75SMichael Corcoran 		*pp = up;
592b72d5b75SMichael Corcoran 	}
593b72d5b75SMichael Corcoran 	ASSERT(*pp != NULL);
594b72d5b75SMichael Corcoran 	mutex_exit(&acpidev_uid_heads[cid].apuh_lock);
595b72d5b75SMichael Corcoran 
596b72d5b75SMichael Corcoran 	/*
597b72d5b75SMichael Corcoran 	 * Generate a special format unit address with three fields to
598b72d5b75SMichael Corcoran 	 * guarantee uniqueness. Normal unit addresses for ACPI devices have
599b72d5b75SMichael Corcoran 	 * either one or two fields.
600b72d5b75SMichael Corcoran 	 */
601b72d5b75SMichael Corcoran 	if (snprintf(buf, len, "%u,%u,0", (*pp)->apu_nid, cid) > len) {
602b72d5b75SMichael Corcoran 		return (NULL);
603b72d5b75SMichael Corcoran 	}
604b72d5b75SMichael Corcoran 
605b72d5b75SMichael Corcoran 	return (buf);
606b72d5b75SMichael Corcoran }
607b72d5b75SMichael Corcoran 
608b72d5b75SMichael Corcoran static char *
acpidev_gen_unitaddr(char * uid,char * fmt,char * buf,size_t len)609b72d5b75SMichael Corcoran acpidev_gen_unitaddr(char *uid, char *fmt, char *buf, size_t len)
610b72d5b75SMichael Corcoran {
611b72d5b75SMichael Corcoran 	size_t i, cnt;
612b72d5b75SMichael Corcoran 	uint_t id1, id2;
613b72d5b75SMichael Corcoran 
614b72d5b75SMichael Corcoran 	ASSERT(len >= 64);
615b72d5b75SMichael Corcoran 	if (fmt == NULL || strlen(fmt) == 0) {
616b72d5b75SMichael Corcoran 		return (NULL);
617b72d5b75SMichael Corcoran 	}
618b72d5b75SMichael Corcoran 
619b72d5b75SMichael Corcoran 	/*
620b72d5b75SMichael Corcoran 	 * Count '%' in format string to protect sscanf().
621b72d5b75SMichael Corcoran 	 * Only support '%u' and '%x', and maximum 2 conversions.
622b72d5b75SMichael Corcoran 	 */
623b72d5b75SMichael Corcoran 	for (cnt = 0, i = 0; fmt[i] != 0 && cnt <= 2; i++) {
624b72d5b75SMichael Corcoran 		if (fmt[i] != '%') {
625b72d5b75SMichael Corcoran 			continue;
626b72d5b75SMichael Corcoran 		} else if (fmt[i + 1] == 'u' || fmt[i + 1] == 'x') {
627b72d5b75SMichael Corcoran 			/* Skip next character. */
628b72d5b75SMichael Corcoran 			i++;
629b72d5b75SMichael Corcoran 			cnt++;
630b72d5b75SMichael Corcoran 		} else {
631b72d5b75SMichael Corcoran 			/* Invalid conversion, stop walking. */
632b72d5b75SMichael Corcoran 			cnt = SIZE_MAX;
633b72d5b75SMichael Corcoran 		}
634b72d5b75SMichael Corcoran 	}
635b72d5b75SMichael Corcoran 	if (cnt != 1 && cnt != 2) {
636b72d5b75SMichael Corcoran 		ACPIDEV_DEBUG(CE_WARN,
637a3114836SGerry Liu 		    "!acpidev: invalid uid format string '%s'.", fmt);
638b72d5b75SMichael Corcoran 		return (NULL);
639b72d5b75SMichael Corcoran 	}
640b72d5b75SMichael Corcoran 
641b72d5b75SMichael Corcoran 	/* Scan uid and generate unitaddr. */
642b72d5b75SMichael Corcoran 	if (sscanf(uid, fmt, &id1, &id2) != cnt) {
643b72d5b75SMichael Corcoran 		return (NULL);
644b72d5b75SMichael Corcoran 	}
645b72d5b75SMichael Corcoran 	/*
646b72d5b75SMichael Corcoran 	 * Reverse the order of the two IDs to match the requirements of the
647b72d5b75SMichael Corcoran 	 * hotplug driver.
648b72d5b75SMichael Corcoran 	 */
649b72d5b75SMichael Corcoran 	if (cnt == 2 && snprintf(buf, len, "%u,%u", id2, id1) >= len) {
650b72d5b75SMichael Corcoran 		ACPIDEV_DEBUG(CE_WARN,
651a3114836SGerry Liu 		    "!acpidev: generated unitaddr is too long.");
652b72d5b75SMichael Corcoran 		return (NULL);
653b72d5b75SMichael Corcoran 	} else if (cnt == 1 && snprintf(buf, len, "%u", id1) >= len) {
654b72d5b75SMichael Corcoran 		ACPIDEV_DEBUG(CE_WARN,
655a3114836SGerry Liu 		    "!acpidev: generated unitaddr is too long.");
656b72d5b75SMichael Corcoran 		return (NULL);
657b72d5b75SMichael Corcoran 	}
658b72d5b75SMichael Corcoran 
659b72d5b75SMichael Corcoran 	return (buf);
660b72d5b75SMichael Corcoran }
661b72d5b75SMichael Corcoran 
662b72d5b75SMichael Corcoran char *
acpidev_generate_unitaddr(char * uid,char ** fmts,size_t nfmt,char * buf,size_t len)663b72d5b75SMichael Corcoran acpidev_generate_unitaddr(char *uid, char **fmts, size_t nfmt,
664b72d5b75SMichael Corcoran     char *buf, size_t len)
665b72d5b75SMichael Corcoran {
666b72d5b75SMichael Corcoran 	size_t i;
667b72d5b75SMichael Corcoran 	uint_t count = 0;
668b72d5b75SMichael Corcoran 	ulong_t val;
669b72d5b75SMichael Corcoran 	char **formats = NULL;
670b72d5b75SMichael Corcoran 	char *rbuf = NULL;
671b72d5b75SMichael Corcoran 	char *endp = NULL;
672b72d5b75SMichael Corcoran 
673b72d5b75SMichael Corcoran 	ASSERT(len >= 64);
674b72d5b75SMichael Corcoran 
675b72d5b75SMichael Corcoran 	/* Use _UID as unit address if it's a decimal integer. */
676b72d5b75SMichael Corcoran 	if (ddi_strtoul(uid, &endp, 10, &val) == 0 &&
677b72d5b75SMichael Corcoran 	    (endp == NULL || *endp == 0)) {
678b72d5b75SMichael Corcoran 		if (snprintf(buf, len, "%s", uid) >= len) {
679b72d5b75SMichael Corcoran 			return (NULL);
680b72d5b75SMichael Corcoran 		} else {
681b72d5b75SMichael Corcoran 			return (buf);
682b72d5b75SMichael Corcoran 		}
683b72d5b75SMichael Corcoran 	}
684b72d5b75SMichael Corcoran 
685b72d5b75SMichael Corcoran 	/* First handle uid format strings from device property. */
686b72d5b75SMichael Corcoran 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, ddi_root_node(),
687b72d5b75SMichael Corcoran 	    DDI_PROP_DONTPASS,
688b72d5b75SMichael Corcoran 	    ACPIDEV_PROP_NAME_UID_FORMAT, &formats, &count) == DDI_SUCCESS) {
689b72d5b75SMichael Corcoran 		/* Walk through format strings and try to generate unitaddr. */
690b72d5b75SMichael Corcoran 		for (i = 0; i < count && rbuf == NULL; i++) {
691b72d5b75SMichael Corcoran 			rbuf = acpidev_gen_unitaddr(uid, formats[i], buf, len);
692b72d5b75SMichael Corcoran 		}
693b72d5b75SMichael Corcoran 		ddi_prop_free(formats);
694b72d5b75SMichael Corcoran 	}
695b72d5b75SMichael Corcoran 
696b72d5b75SMichael Corcoran 	/* Then handle embedded uid format strings. */
697b72d5b75SMichael Corcoran 	if (fmts != NULL) {
698b72d5b75SMichael Corcoran 		for (i = 0; i < nfmt && rbuf == NULL; i++) {
699b72d5b75SMichael Corcoran 			rbuf = acpidev_gen_unitaddr(uid, fmts[i], buf, len);
700b72d5b75SMichael Corcoran 		}
701b72d5b75SMichael Corcoran 	}
702b72d5b75SMichael Corcoran 
703b72d5b75SMichael Corcoran 	return (rbuf);
704b72d5b75SMichael Corcoran }
705b72d5b75SMichael Corcoran 
706b72d5b75SMichael Corcoran /*
707b72d5b75SMichael Corcoran  * The Solaris device "unit-address" property is composed of a comma-delimited
708b72d5b75SMichael Corcoran  * list of hexadecimal values. According to the ACPI spec, the ACPI _UID method
709b72d5b75SMichael Corcoran  * could return an integer or a string. If it returns an integer, it is used
710b72d5b75SMichael Corcoran  * as the unit-address as is. If _UID returns a string, we try to extract some
711b72d5b75SMichael Corcoran  * meaningful integers to compose the unit-address property. If we fail to
712b72d5b75SMichael Corcoran  * extract any integers, a pseudo-sequential number will be generated for the
713b72d5b75SMichael Corcoran  * unit-address.
714b72d5b75SMichael Corcoran  */
715b72d5b75SMichael Corcoran ACPI_STATUS
acpidev_set_unitaddr(acpidev_walk_info_t * infop,char ** fmts,size_t nfmt,char * unitaddr)716b72d5b75SMichael Corcoran acpidev_set_unitaddr(acpidev_walk_info_t *infop, char **fmts, size_t nfmt,
717b72d5b75SMichael Corcoran     char *unitaddr)
718b72d5b75SMichael Corcoran {
719b72d5b75SMichael Corcoran 	char unit[64];
720b72d5b75SMichael Corcoran 
721b72d5b75SMichael Corcoran 	ASSERT(infop != NULL);
722b72d5b75SMichael Corcoran 	ASSERT(infop->awi_dip != NULL);
723b72d5b75SMichael Corcoran 	ASSERT(infop->awi_info != NULL);
724b72d5b75SMichael Corcoran 	if (infop == NULL || infop->awi_dip == NULL ||
725b72d5b75SMichael Corcoran 	    infop->awi_info == NULL) {
726b72d5b75SMichael Corcoran 		ACPIDEV_DEBUG(CE_WARN,
727a3114836SGerry Liu 		    "!acpidev: invalid parameters in acpidev_set_unitaddr().");
728b72d5b75SMichael Corcoran 		return (AE_BAD_PARAMETER);
729b72d5b75SMichael Corcoran 	}
730b72d5b75SMichael Corcoran 
731b72d5b75SMichael Corcoran 	if (infop->awi_info->Valid & ACPI_VALID_UID) {
732b72d5b75SMichael Corcoran 		if (ndi_prop_update_string(DDI_DEV_T_NONE, infop->awi_dip,
73357190917SDana Myers 		    ACPIDEV_PROP_NAME_ACPI_UID,
73457190917SDana Myers 		    infop->awi_info->UniqueId.String) != NDI_SUCCESS) {
735b72d5b75SMichael Corcoran 			cmn_err(CE_WARN,
736b72d5b75SMichael Corcoran 			    "!acpidev: failed to set UID property for %s.",
737b72d5b75SMichael Corcoran 			    infop->awi_name);
738b72d5b75SMichael Corcoran 			return (AE_ERROR);
739b72d5b75SMichael Corcoran 		}
740b72d5b75SMichael Corcoran 	}
741b72d5b75SMichael Corcoran 
742b72d5b75SMichael Corcoran 	if (unitaddr == NULL && (infop->awi_info->Valid & ACPI_VALID_UID)) {
743b72d5b75SMichael Corcoran 		/* Try to generate unit address from _UID. */
744b72d5b75SMichael Corcoran 		if (fmts == NULL) {
745b72d5b75SMichael Corcoran 			fmts = acpidev_uid_formats;
746b72d5b75SMichael Corcoran 			nfmt = sizeof (acpidev_uid_formats) / sizeof (char *);
747b72d5b75SMichael Corcoran 		}
748b72d5b75SMichael Corcoran 		unitaddr = acpidev_generate_unitaddr(
74957190917SDana Myers 		    infop->awi_info->UniqueId.String, fmts, nfmt,
750b72d5b75SMichael Corcoran 		    unit, sizeof (unit));
751b72d5b75SMichael Corcoran 		/* Generate pseudo sequential unit address. */
752b72d5b75SMichael Corcoran 		if (unitaddr == NULL) {
753b72d5b75SMichael Corcoran 			unitaddr = acpidev_generate_pseudo_unitaddr(
75457190917SDana Myers 			    infop->awi_info->UniqueId.String,
755b72d5b75SMichael Corcoran 			    infop->awi_class_curr->adc_class_id,
756b72d5b75SMichael Corcoran 			    unit, sizeof (unit));
757b72d5b75SMichael Corcoran 		}
758b72d5b75SMichael Corcoran 		if (unitaddr == NULL) {
759b72d5b75SMichael Corcoran 			cmn_err(CE_WARN, "!acpidev: failed to generate unit "
760b72d5b75SMichael Corcoran 			    "address from %s.",
76157190917SDana Myers 			    infop->awi_info->UniqueId.String);
762b72d5b75SMichael Corcoran 			return (AE_ERROR);
763b72d5b75SMichael Corcoran 		}
764b72d5b75SMichael Corcoran 	}
765b72d5b75SMichael Corcoran 	if (unitaddr == NULL) {
766b72d5b75SMichael Corcoran 		/*
767b72d5b75SMichael Corcoran 		 * Some ACPI objects may have no _UID method available, so we
768b72d5b75SMichael Corcoran 		 * can't generate the "unit-address" property for them.
769b72d5b75SMichael Corcoran 		 * On the other hand, it's legal to support such a device
770b72d5b75SMichael Corcoran 		 * without a unit address, so return success here.
771b72d5b75SMichael Corcoran 		 */
772b72d5b75SMichael Corcoran 		return (AE_OK);
773b72d5b75SMichael Corcoran 	}
774b72d5b75SMichael Corcoran 
775b72d5b75SMichael Corcoran 	if (ndi_prop_update_string(DDI_DEV_T_NONE, infop->awi_dip,
776b72d5b75SMichael Corcoran 	    ACPIDEV_PROP_NAME_UNIT_ADDR, unitaddr) != NDI_SUCCESS) {
777b72d5b75SMichael Corcoran 		cmn_err(CE_WARN, "!acpidev: failed to set unitaddr for %s.",
778b72d5b75SMichael Corcoran 		    infop->awi_name);
779b72d5b75SMichael Corcoran 		return (AE_ERROR);
780b72d5b75SMichael Corcoran 	}
781b72d5b75SMichael Corcoran 
782b72d5b75SMichael Corcoran 	return (AE_OK);
783b72d5b75SMichael Corcoran }
784b72d5b75SMichael Corcoran 
785b72d5b75SMichael Corcoran ACPI_STATUS
acpidev_set_compatible(acpidev_walk_info_t * infop,char ** compat,int acount)786b72d5b75SMichael Corcoran acpidev_set_compatible(acpidev_walk_info_t *infop, char **compat, int acount)
787b72d5b75SMichael Corcoran {
788b72d5b75SMichael Corcoran 	int count, i, j;
789b72d5b75SMichael Corcoran 	char **compatible = NULL;
790b72d5b75SMichael Corcoran 	ACPI_DEVICE_INFO *di;
791b72d5b75SMichael Corcoran 
792b72d5b75SMichael Corcoran 	/*
793b72d5b75SMichael Corcoran 	 * Generate compatible list for device based on:
794b72d5b75SMichael Corcoran 	 *	* Device HID if available
795b72d5b75SMichael Corcoran 	 *	* Device CIDs if available
796b72d5b75SMichael Corcoran 	 *	* property array passed in
797b72d5b75SMichael Corcoran 	 */
798b72d5b75SMichael Corcoran 	ASSERT(infop != NULL);
799b72d5b75SMichael Corcoran 	ASSERT(infop->awi_dip != NULL);
800b72d5b75SMichael Corcoran 	ASSERT(infop->awi_info != NULL);
801b72d5b75SMichael Corcoran 	ASSERT(compat != NULL || acount == 0);
802b72d5b75SMichael Corcoran 	if (infop == NULL || infop->awi_dip == NULL ||
803b72d5b75SMichael Corcoran 	    infop->awi_info == NULL || (compat == NULL && acount != 0)) {
804a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameters "
805b72d5b75SMichael Corcoran 		    "in acpidev_set_compatible().");
806b72d5b75SMichael Corcoran 		return (AE_BAD_PARAMETER);
807b72d5b75SMichael Corcoran 	}
808b72d5b75SMichael Corcoran 
809b72d5b75SMichael Corcoran 	/* Compute string count. */
810b72d5b75SMichael Corcoran 	count = acount;
811b72d5b75SMichael Corcoran 	di = infop->awi_info;
812b72d5b75SMichael Corcoran 	if (di->Valid & ACPI_VALID_HID) {
813b72d5b75SMichael Corcoran 		count++;
814b72d5b75SMichael Corcoran 	}
815b72d5b75SMichael Corcoran 	if (di->Valid & ACPI_VALID_CID) {
81657190917SDana Myers 		count += di->CompatibleIdList.Count;
817b72d5b75SMichael Corcoran 	}
818b72d5b75SMichael Corcoran 	compatible = kmem_zalloc(sizeof (char *) * count, KM_SLEEP);
819b72d5b75SMichael Corcoran 
820b72d5b75SMichael Corcoran 	/* Generate string array. */
821b72d5b75SMichael Corcoran 	i = 0;
822b72d5b75SMichael Corcoran 	if (di->Valid & ACPI_VALID_HID) {
82357190917SDana Myers 		compatible[i++] = di->HardwareId.String;
824b72d5b75SMichael Corcoran 	}
825b72d5b75SMichael Corcoran 	if (di->Valid & ACPI_VALID_CID) {
82657190917SDana Myers 		for (j = 0; j < di->CompatibleIdList.Count; j++) {
82757190917SDana Myers 			compatible[i++] = di->CompatibleIdList.Ids[j].String;
828b72d5b75SMichael Corcoran 		}
829b72d5b75SMichael Corcoran 	}
830b72d5b75SMichael Corcoran 	for (j = 0; j < acount; j++) {
831b72d5b75SMichael Corcoran 		compatible[i++] = compat[j];
832b72d5b75SMichael Corcoran 	}
833b72d5b75SMichael Corcoran 	ASSERT(i == count);
834b72d5b75SMichael Corcoran 
835b72d5b75SMichael Corcoran 	/* Set "compatible" property. */
836b72d5b75SMichael Corcoran 	if (ndi_prop_update_string_array(DDI_DEV_T_NONE, infop->awi_dip,
837b72d5b75SMichael Corcoran 	    OBP_COMPATIBLE, compatible, count) != NDI_SUCCESS) {
838b72d5b75SMichael Corcoran 		cmn_err(CE_WARN, "!acpidev: failed to set compatible "
839b72d5b75SMichael Corcoran 		    "property for %s in acpidev_set_compatible().",
840b72d5b75SMichael Corcoran 		    infop->awi_name);
841b72d5b75SMichael Corcoran 		kmem_free(compatible, count * sizeof (char *));
842b72d5b75SMichael Corcoran 		return (AE_ERROR);
843b72d5b75SMichael Corcoran 	}
844b72d5b75SMichael Corcoran 	kmem_free(compatible, count * sizeof (char *));
845b72d5b75SMichael Corcoran 
846b72d5b75SMichael Corcoran 	return (AE_OK);
847b72d5b75SMichael Corcoran }
848a3114836SGerry Liu 
849a3114836SGerry Liu /* Evaluate _OST method under object, which is used to support hotplug event. */
850a3114836SGerry Liu ACPI_STATUS
acpidev_eval_ost(ACPI_HANDLE hdl,uint32_t code,uint32_t status,char * bufp,size_t len)851a3114836SGerry Liu acpidev_eval_ost(ACPI_HANDLE hdl, uint32_t code, uint32_t status,
852a3114836SGerry Liu     char *bufp, size_t len)
853a3114836SGerry Liu {
854a3114836SGerry Liu 	ACPI_STATUS rc;
855a3114836SGerry Liu 	ACPI_OBJECT args[3];
856a3114836SGerry Liu 	ACPI_OBJECT_LIST arglist;
857a3114836SGerry Liu 
858a3114836SGerry Liu 	args[0].Type = ACPI_TYPE_INTEGER;
859a3114836SGerry Liu 	args[0].Integer.Value = code;
860a3114836SGerry Liu 	args[1].Type = ACPI_TYPE_INTEGER;
861a3114836SGerry Liu 	args[1].Integer.Value = status;
862a3114836SGerry Liu 	args[2].Type = ACPI_TYPE_BUFFER;
863a3114836SGerry Liu 	args[2].Buffer.Pointer = (UINT8 *)bufp;
864a3114836SGerry Liu 	args[2].Buffer.Length = (UINT32)len;
865a3114836SGerry Liu 	if (bufp == NULL || len == 0) {
866a3114836SGerry Liu 		arglist.Count = 2;
867a3114836SGerry Liu 	} else {
868a3114836SGerry Liu 		arglist.Count = 3;
869a3114836SGerry Liu 	}
870a3114836SGerry Liu 	arglist.Pointer = args;
871a3114836SGerry Liu 	rc = AcpiEvaluateObject(hdl, ACPIDEV_METHOD_NAME_OST, &arglist, NULL);
872a3114836SGerry Liu 	if (rc != AE_OK && rc != AE_NOT_FOUND) {
873a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_WARN,
874a3114836SGerry Liu 		    "!acpidev: failed to evaluate _OST method, code 0x%x.", rc);
875a3114836SGerry Liu 	}
876a3114836SGerry Liu 
877a3114836SGerry Liu 	return (rc);
878a3114836SGerry Liu }
879a3114836SGerry Liu 
880a3114836SGerry Liu ACPI_STATUS
acpidev_eval_ej0(ACPI_HANDLE hdl)881a3114836SGerry Liu acpidev_eval_ej0(ACPI_HANDLE hdl)
882a3114836SGerry Liu {
883a3114836SGerry Liu 	ACPI_STATUS rc;
884a3114836SGerry Liu 	ACPI_OBJECT args[1];
885a3114836SGerry Liu 	ACPI_OBJECT_LIST arglist;
886a3114836SGerry Liu 
887a3114836SGerry Liu 	/*
888a3114836SGerry Liu 	 * Quotation from ACPI spec 4.0 section 6.3.3.
889a3114836SGerry Liu 	 * Arg0 An Integer containing a device ejection control
890a3114836SGerry Liu 	 * 	0  Cancel a mark for ejection request (EJ0 will never be called
891a3114836SGerry Liu 	 *	   with this value)
892a3114836SGerry Liu 	 * 	1  Hot eject or mark for ejection
893a3114836SGerry Liu 	 */
894a3114836SGerry Liu 	args[0].Type = ACPI_TYPE_INTEGER;
895a3114836SGerry Liu 	args[0].Integer.Value = 1;
896a3114836SGerry Liu 	arglist.Count = 1;
897a3114836SGerry Liu 	arglist.Pointer = args;
898a3114836SGerry Liu 	rc = AcpiEvaluateObject(hdl, ACPIDEV_METHOD_NAME_EJ0, &arglist, NULL);
899a3114836SGerry Liu 	if (rc != AE_OK) {
900a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_WARN,
901a3114836SGerry Liu 		    "!acpidev: failed to evaluate _EJ0 method, code 0x%x.", rc);
902a3114836SGerry Liu 	}
903a3114836SGerry Liu 
904a3114836SGerry Liu 	return (rc);
905a3114836SGerry Liu }
906a3114836SGerry Liu 
907a3114836SGerry Liu ACPI_STATUS
acpidev_eval_pxm(ACPI_HANDLE hdl,uint32_t * idp)908a3114836SGerry Liu acpidev_eval_pxm(ACPI_HANDLE hdl, uint32_t *idp)
909a3114836SGerry Liu {
910a3114836SGerry Liu 	int pxmid;
911a3114836SGerry Liu 
912a3114836SGerry Liu 	ASSERT(idp != NULL);
913a3114836SGerry Liu 
914a3114836SGerry Liu 	/*
915a3114836SGerry Liu 	 * Try to evaluate ACPI _PXM method to get proximity doamin id.
916a3114836SGerry Liu 	 * Quotation from ACPI4.0:
917a3114836SGerry Liu 	 * If the Local APIC ID / Local SAPIC ID / Local x2APIC ID of a
918a3114836SGerry Liu 	 * dynamically added processor is not present in the System Resource
919a3114836SGerry Liu 	 * Affinity Table (SRAT), a _PXM object must exist for the processor's
920a3114836SGerry Liu 	 * device or one of its ancestors in the ACPI Namespace.
921a3114836SGerry Liu 	 */
922a3114836SGerry Liu 	while (hdl != NULL) {
923a3114836SGerry Liu 		if (ACPI_SUCCESS(acpica_eval_int(hdl,
924a3114836SGerry Liu 		    ACPIDEV_METHOD_NAME_PXM, &pxmid))) {
925a3114836SGerry Liu 			*idp = (uint32_t)pxmid;
926a3114836SGerry Liu 			return (AE_OK);
927a3114836SGerry Liu 		}
928a3114836SGerry Liu 		if (ACPI_FAILURE(AcpiGetParent(hdl, &hdl))) {
929a3114836SGerry Liu 			break;
930a3114836SGerry Liu 		}
931a3114836SGerry Liu 	}
932a3114836SGerry Liu 
933a3114836SGerry Liu 	return (AE_NOT_FOUND);
934a3114836SGerry Liu }
935