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