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  */
26 /*
27  * Copyright (c) 2009, Intel Corporation.
28  * All rights reserved.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/cmn_err.h>
33 #include <sys/sysmacros.h>
34 #include <sys/sunddi.h>
35 #include <sys/sunndi.h>
36 #include <sys/acpi/acpi.h>
37 #include <sys/acpica.h>
38 #include <sys/acpidev.h>
39 #include <sys/acpidev_impl.h>
40 #include <util/sscanf.h>
41 
42 /* Data structures used to extract the numeric unit address from string _UID. */
43 static acpidev_pseudo_uid_head_t acpidev_uid_heads[ACPIDEV_CLASS_ID_MAX];
44 static char *acpidev_uid_formats[] = {
45 	"%u",
46 };
47 
48 static char *acpidev_unknown_object_name = "<unknown>";
49 
50 int
51 acpidev_query_device_status(ACPI_HANDLE hdl)
52 {
53 	int status;
54 
55 	ASSERT(hdl != NULL);
56 	if (hdl == NULL) {
57 		ACPIDEV_DEBUG(CE_WARN,
58 		    "acpidev: hdl is NULL in acpidev_query_device_status().");
59 		return (0);
60 	}
61 
62 	if (ACPI_FAILURE(acpica_eval_int(hdl, METHOD_NAME__STA, &status))) {
63 		/*
64 		 * Set the default value according to ACPI3.0b sec 6.3.7:
65 		 * If a device object (including the processor object) does
66 		 * not have an _STA object, then OSPM assumes that all of the
67 		 * above bits are set (in other words, the device is present,
68 		 * enabled, shown in the UI, and functioning).
69 		 */
70 		status = 0xF;
71 	}
72 
73 	return (status);
74 }
75 
76 boolean_t
77 acpidev_check_device_present(int status)
78 {
79 	/*
80 	 * According to ACPI3.0 Spec, if either the ACPI_STA_DEVICE_PRESENT bit
81 	 * or the ACPI_STA_DEVICE_FUNCTIONING bit is set, the device exists.
82 	 */
83 	if (status & (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_FUNCTIONING)) {
84 		return (B_TRUE);
85 	}
86 
87 	return (B_FALSE);
88 }
89 
90 boolean_t
91 acpidev_check_device_enabled(int stat)
92 {
93 	/*
94 	 * According to ACPI3.0 Spec, if either the ACPI_STA_DEVICE_PRESENT bit
95 	 * or the ACPI_STA_DEVICE_FUNCTIONING bit is set, the device exists.
96 	 * Return true if device exists and has been enabled.
97 	 */
98 	if ((stat & (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_FUNCTIONING)) &&
99 	    (stat & ACPI_STA_DEVICE_ENABLED)) {
100 		return (B_TRUE);
101 	}
102 
103 	return (B_FALSE);
104 }
105 
106 boolean_t
107 acpidev_match_device_id(ACPI_DEVICE_INFO *infop, char **ids, int count)
108 {
109 	int i, j;
110 
111 	ASSERT(infop != NULL);
112 	ASSERT(ids != NULL || count == 0);
113 	/* Special case to match all devices if count is 0. */
114 	if (count == 0) {
115 		return (B_TRUE);
116 	} else if (infop == NULL || ids == NULL) {
117 		ACPIDEV_DEBUG(CE_WARN, "acpidev: invalid parameters in "
118 		    "acpidev_match_device_id().");
119 		return (B_FALSE);
120 	}
121 
122 	/* Match _HID first. */
123 	if (infop->Valid & ACPI_VALID_HID) {
124 		for (i = 0; i < count; i++) {
125 			if (strncmp(ids[i], infop->HardwareId.Value,
126 			    sizeof (infop->HardwareId.Value)) == 0) {
127 				return (B_TRUE);
128 			}
129 		}
130 	}
131 
132 	/* Match _CID next. */
133 	if (infop->Valid & ACPI_VALID_CID) {
134 		for (i = 0; i < count; i++) {
135 			for (j = 0; j < infop->CompatibilityId.Count; j++) {
136 				if (strncmp(ids[i],
137 				    infop->CompatibilityId.Id[j].Value,
138 				    sizeof (ACPI_COMPATIBLE_ID)) == 0) {
139 					return (B_TRUE);
140 				}
141 			}
142 		}
143 	}
144 
145 	return (B_FALSE);
146 }
147 
148 struct acpidev_get_device_arg {
149 	boolean_t		skip_non_exist;
150 	int			id_count;
151 	char 			**device_ids;
152 	void			*user_arg;
153 	ACPI_WALK_CALLBACK	user_func;
154 };
155 
156 static ACPI_STATUS
157 acpidev_get_device_callback(ACPI_HANDLE hdl, UINT32 level, void *arg,
158     void **retval)
159 {
160 	ACPI_STATUS rc;
161 	ACPI_BUFFER buf;
162 	ACPI_DEVICE_INFO *infop;
163 	struct acpidev_get_device_arg *argp;
164 
165 	argp = (struct acpidev_get_device_arg *)arg;
166 	ASSERT(argp != NULL);
167 	ASSERT(hdl != NULL);
168 
169 	/* Query object information. */
170 	buf.Length = ACPI_ALLOCATE_BUFFER;
171 	rc = AcpiGetObjectInfo(hdl, &buf);
172 	if (ACPI_FAILURE(rc)) {
173 		cmn_err(CE_WARN, "!acpidev: failed to get ACPI object info "
174 		    "in acpidev_get_device_callback().");
175 		return (AE_CTRL_DEPTH);
176 	}
177 	infop = buf.Pointer;
178 
179 	/*
180 	 * Skip scanning of children if the device is neither PRESENT nor
181 	 * FUNCTIONING.
182 	 * Please refer to ACPI Spec3.0b Sec 6.3.1 and 6.5.1.
183 	 */
184 	if (argp->skip_non_exist && (infop->Valid & ACPI_VALID_STA) &&
185 	    !acpidev_check_device_present(infop->CurrentStatus)) {
186 		rc = AE_CTRL_DEPTH;
187 	/* Call user callback if matched. */
188 	} else if (acpidev_match_device_id(infop, argp->device_ids,
189 	    argp->id_count)) {
190 		rc = argp->user_func(hdl, level, argp->user_arg, retval);
191 	} else {
192 		rc = AE_OK;
193 	}
194 
195 	/* Free ACPI object info buffer. */
196 	AcpiOsFree(infop);
197 
198 	return (rc);
199 }
200 
201 ACPI_STATUS
202 acpidev_get_device_by_id(ACPI_HANDLE hdl, char **ids, int count,
203     int maxdepth, boolean_t skip_non_exist,
204     ACPI_WALK_CALLBACK userfunc, void *userarg, void **retval)
205 {
206 	ACPI_STATUS rc;
207 	struct acpidev_get_device_arg arg;
208 
209 	ASSERT(userfunc != NULL);
210 	if (hdl == NULL || userfunc == NULL || (ids == NULL && count != 0)) {
211 		ACPIDEV_DEBUG(CE_WARN, "acpidev: invalid parameters "
212 		    "in acpidev_get_device_by_id().");
213 		return (AE_BAD_PARAMETER);
214 	}
215 
216 	/* Enumerate all descendant objects. */
217 	arg.skip_non_exist = skip_non_exist;
218 	arg.device_ids = ids;
219 	arg.id_count = count;
220 	arg.user_arg = userarg;
221 	arg.user_func = userfunc;
222 	rc = AcpiWalkNamespace(ACPI_TYPE_DEVICE, hdl, maxdepth,
223 	    &acpidev_get_device_callback, &arg, retval);
224 
225 	return (rc);
226 }
227 
228 ACPI_STATUS
229 acpidev_walk_apic(ACPI_BUFFER *bufp, ACPI_HANDLE hdl, char *method,
230     acpidev_apic_walker_t func, void *context)
231 {
232 	ACPI_STATUS rc;
233 	ssize_t len;
234 	ACPI_BUFFER buf;
235 	ACPI_OBJECT *obj;
236 	ACPI_SUBTABLE_HEADER *ap;
237 	ACPI_TABLE_MADT *mp = NULL;
238 
239 	ASSERT(func != NULL);
240 	if (func == NULL) {
241 		ACPIDEV_DEBUG(CE_WARN,
242 		    "acpidev: invalid parameters for acpidev_walk_apic().");
243 		return (AE_BAD_PARAMETER);
244 	}
245 
246 	buf.Pointer = NULL;
247 	buf.Length = ACPI_ALLOCATE_BUFFER;
248 
249 	/* A walk buffer was passed in if bufp isn't NULL. */
250 	if (bufp != NULL) {
251 		ap = (ACPI_SUBTABLE_HEADER *)(bufp->Pointer);
252 		len = bufp->Length;
253 	} else if (method != NULL) {
254 		/*
255 		 * Otherwise, if we have an evaluate method, we get the walk
256 		 * buffer from a successful invocation of
257 		 * AcpiEvaluateObjectTyped().
258 		 */
259 		ASSERT(hdl != NULL);
260 		rc = AcpiEvaluateObjectTyped(hdl, method, NULL, &buf,
261 		    ACPI_TYPE_BUFFER);
262 		if (ACPI_SUCCESS(rc)) {
263 			ASSERT(buf.Length >= sizeof (*obj));
264 			obj = buf.Pointer;
265 			ap = (ACPI_SUBTABLE_HEADER *)obj->Buffer.Pointer;
266 			len = obj->Buffer.Length;
267 		} else {
268 			if (rc != AE_NOT_FOUND)
269 				cmn_err(CE_WARN, "!acpidev: failed to evaluate "
270 				    "%s in acpidev_walk_apic().", method);
271 			return (rc);
272 		}
273 	} else {
274 		/* As a last resort, walk the MADT table. */
275 		rc = AcpiGetTable(ACPI_SIG_MADT, 1, (ACPI_TABLE_HEADER **)&mp);
276 		if (ACPI_FAILURE(rc)) {
277 			cmn_err(CE_WARN, "!acpidev: failed to get MADT table "
278 			    "in acpidev_walk_apic().");
279 			return (rc);
280 		}
281 		ap = (ACPI_SUBTABLE_HEADER *)(mp + 1);
282 		len = mp->Header.Length - sizeof (*mp);
283 	}
284 
285 	ASSERT(len >= 0);
286 	for (rc = AE_OK; len > 0 && ACPI_SUCCESS(rc); len -= ap->Length,
287 	    ap = (ACPI_SUBTABLE_HEADER *)(((char *)ap) + ap->Length)) {
288 		ASSERT(len >= sizeof (ACPI_SUBTABLE_HEADER));
289 		if (len <= sizeof (ACPI_SUBTABLE_HEADER) ||
290 		    ap->Length <= sizeof (ACPI_SUBTABLE_HEADER) ||
291 		    len < ap->Length) {
292 			cmn_err(CE_WARN,
293 			    "!acpidev: invalid APIC entry in MADT/_MAT.");
294 			break;
295 		}
296 		rc = (*func)(ap, context);
297 	}
298 
299 	if (buf.Pointer != NULL) {
300 		AcpiOsFree(buf.Pointer);
301 	}
302 
303 	return (rc);
304 }
305 
306 char *
307 acpidev_get_object_name(ACPI_HANDLE hdl)
308 {
309 	ACPI_BUFFER buf;
310 	char *objname = acpidev_unknown_object_name;
311 
312 	buf.Length = ACPI_ALLOCATE_BUFFER;
313 	buf.Pointer = NULL;
314 	if (ACPI_SUCCESS(AcpiGetName(hdl, ACPI_FULL_PATHNAME, &buf))) {
315 		ASSERT(buf.Pointer != NULL);
316 		objname = (char *)buf.Pointer;
317 	}
318 
319 	return (objname);
320 }
321 
322 void
323 acpidev_free_object_name(char *objname)
324 {
325 	if (objname != acpidev_unknown_object_name && objname != NULL) {
326 		AcpiOsFree(objname);
327 	}
328 }
329 
330 acpidev_walk_info_t *
331 acpidev_alloc_walk_info(acpidev_op_type_t op_type, int lvl, ACPI_HANDLE hdl,
332     acpidev_class_list_t **listpp, acpidev_walk_info_t *pinfop)
333 {
334 	ACPI_BUFFER buf;
335 	acpidev_walk_info_t *infop = NULL;
336 	acpidev_data_handle_t datap = NULL;
337 
338 	ASSERT(0 <= lvl && lvl < ACPIDEV_MAX_ENUM_LEVELS);
339 	infop = kmem_zalloc(sizeof (*infop), KM_SLEEP);
340 	infop->awi_op_type = op_type;
341 	infop->awi_level = lvl;
342 	infop->awi_parent = pinfop;
343 	infop->awi_class_list = listpp;
344 	infop->awi_hdl = hdl;
345 	infop->awi_name = acpidev_get_object_name(hdl);
346 
347 	/* Cache ACPI device information. */
348 	buf.Length = ACPI_ALLOCATE_BUFFER;
349 	if (ACPI_FAILURE(AcpiGetObjectInfo(hdl, &buf))) {
350 		cmn_err(CE_WARN, "!acpidev: failed to get object info for %s "
351 		    "in acpidev_alloc_walk_info().", infop->awi_name);
352 		acpidev_free_object_name(infop->awi_name);
353 		kmem_free(infop, sizeof (*infop));
354 		return (NULL);
355 	}
356 	infop->awi_info = buf.Pointer;
357 
358 	/*
359 	 * Get or create an ACPI object data handle, which will be used to
360 	 * maintain object status information.
361 	 */
362 	if ((datap = acpidev_data_get_handle(hdl)) != NULL) {
363 		ASSERT(datap->aod_hdl == hdl);
364 		ASSERT(datap->aod_level == lvl);
365 	} else if ((datap = acpidev_data_create_handle(hdl)) != NULL) {
366 		datap->aod_level = lvl;
367 		datap->aod_hdl = hdl;
368 	} else {
369 		ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to create object "
370 		    "handle for %s in acpidev_alloc_walk_info().",
371 		    infop->awi_name);
372 		AcpiOsFree(infop->awi_info);
373 		acpidev_free_object_name(infop->awi_name);
374 		kmem_free(infop, sizeof (*infop));
375 		return (NULL);
376 	}
377 	infop->awi_data = datap;
378 	/* Sync DEVICE_CREATED flag. */
379 	if (datap->aod_iflag & ACPIDEV_ODF_DEVINFO_CREATED) {
380 		ASSERT(datap->aod_dip != NULL);
381 		ASSERT(datap->aod_class != NULL);
382 		infop->awi_dip = datap->aod_dip;
383 		infop->awi_flags |= ACPIDEV_WI_DEVICE_CREATED;
384 	}
385 
386 	return (infop);
387 }
388 
389 void
390 acpidev_free_walk_info(acpidev_walk_info_t *infop)
391 {
392 	/*
393 	 * The ACPI object data handle will only be released when the
394 	 * corresponding object is going to be destroyed.
395 	 */
396 	if (infop != NULL) {
397 		if (infop->awi_info != NULL) {
398 			AcpiOsFree(infop->awi_info);
399 		}
400 		if (infop->awi_name != NULL) {
401 			acpidev_free_object_name(infop->awi_name);
402 		}
403 		kmem_free(infop, sizeof (*infop));
404 	}
405 }
406 
407 dev_info_t *
408 acpidev_walk_info_get_pdip(acpidev_walk_info_t *infop)
409 {
410 	while (infop != NULL) {
411 		if (infop->awi_dip != NULL) {
412 			return (infop->awi_dip);
413 		}
414 		infop = infop->awi_parent;
415 	}
416 
417 	return (NULL);
418 }
419 
420 /*
421  * Called to release resources when the corresponding object is going
422  * to be destroyed.
423  */
424 /*ARGSUSED*/
425 static void
426 acpidev_get_object_handler(ACPI_HANDLE hdl, UINT32 func, void *data)
427 {
428 	acpidev_data_handle_t objhdl = data;
429 
430 	kmem_free(objhdl, sizeof (acpidev_data_handle_t));
431 }
432 
433 acpidev_data_handle_t
434 acpidev_data_get_handle(ACPI_HANDLE hdl)
435 {
436 	void *ptr;
437 	acpidev_data_handle_t objhdl = NULL;
438 
439 	if (ACPI_SUCCESS(AcpiGetData(hdl, acpidev_get_object_handler, &ptr))) {
440 		objhdl = (acpidev_data_handle_t)ptr;
441 	}
442 
443 	return (objhdl);
444 }
445 
446 acpidev_data_handle_t
447 acpidev_data_create_handle(ACPI_HANDLE hdl)
448 {
449 	acpidev_data_handle_t objhdl;
450 
451 	objhdl = kmem_zalloc(sizeof (*objhdl), KM_SLEEP);
452 	if (ACPI_FAILURE(AcpiAttachData(hdl, acpidev_get_object_handler,
453 	    (void *)objhdl))) {
454 		cmn_err(CE_WARN,
455 		    "!acpidev: failed to attach handle data to object.");
456 		kmem_free(objhdl, sizeof (*objhdl));
457 		return (NULL);
458 	}
459 
460 	return (objhdl);
461 }
462 
463 void
464 acpidev_data_destroy_handle(ACPI_HANDLE hdl)
465 {
466 	void *ptr;
467 
468 	if (ACPI_SUCCESS(AcpiGetData(hdl, acpidev_get_object_handler, &ptr)) &&
469 	    ACPI_SUCCESS(AcpiDetachData(hdl, acpidev_get_object_handler))) {
470 		kmem_free(ptr, sizeof (acpidev_data_handle_t));
471 	}
472 }
473 
474 ACPI_HANDLE
475 acpidev_data_get_object(acpidev_data_handle_t hdl)
476 {
477 	ASSERT(hdl != NULL);
478 	return ((hdl != NULL) ? hdl->aod_hdl : NULL);
479 }
480 
481 dev_info_t *
482 acpidev_data_get_devinfo(acpidev_data_handle_t hdl)
483 {
484 	ASSERT(hdl != NULL);
485 	if (hdl == NULL ||
486 	    (hdl->aod_iflag & ACPIDEV_ODF_DEVINFO_CREATED) == 0) {
487 		return (NULL);
488 	} else {
489 		ASSERT(hdl->aod_dip != NULL);
490 		return (hdl->aod_dip);
491 	}
492 }
493 
494 int
495 acpidev_data_get_status(acpidev_data_handle_t hdl)
496 {
497 	ASSERT(hdl != NULL);
498 	if (hdl == NULL ||
499 	    (hdl->aod_iflag & ACPIDEV_ODF_STATUS_VALID) == 0) {
500 		return (0);
501 	} else {
502 		return (hdl->aod_status);
503 	}
504 }
505 
506 void
507 acpidev_data_set_flag(acpidev_data_handle_t hdl, uint32_t flag)
508 {
509 	ASSERT(hdl != NULL);
510 	hdl->aod_eflag |= flag;
511 }
512 
513 void
514 acpidev_data_clear_flag(acpidev_data_handle_t hdl, uint32_t flag)
515 {
516 	ASSERT(hdl != NULL);
517 	hdl->aod_eflag &= ~flag;
518 }
519 
520 uint32_t
521 acpidev_data_get_flag(acpidev_data_handle_t hdl, uint32_t flag)
522 {
523 	ASSERT(hdl != NULL);
524 	return (hdl->aod_eflag & flag);
525 }
526 
527 static char *
528 acpidev_generate_pseudo_unitaddr(char *uid, acpidev_class_id_t cid,
529     char *buf, size_t len)
530 {
531 	acpidev_pseudo_uid_t *up, **pp;
532 
533 	ASSERT(len >= 64);
534 	ASSERT(cid >= 0 && cid < ACPIDEV_CLASS_ID_MAX);
535 	if (cid < 0 || cid >= ACPIDEV_CLASS_ID_MAX) {
536 		return (NULL);
537 	}
538 
539 	mutex_enter(&acpidev_uid_heads[cid].apuh_lock);
540 	for (pp = &acpidev_uid_heads[cid].apuh_first; *pp != NULL;
541 	    pp = &(*pp)->apu_next) {
542 		if (strcmp(uid, (*pp)->apu_uid) == 0 &&
543 		    (*pp)->apu_cid == cid) {
544 			break;
545 		}
546 	}
547 	/* uid doesn't exist, create one and insert it into the list. */
548 	if (*pp == NULL) {
549 		up = kmem_zalloc(sizeof (*up), KM_SLEEP);
550 		up->apu_uid = ddi_strdup(uid, KM_SLEEP);
551 		up->apu_cid = cid;
552 		up->apu_nid = acpidev_uid_heads[cid].apuh_id++;
553 		*pp = up;
554 	}
555 	ASSERT(*pp != NULL);
556 	mutex_exit(&acpidev_uid_heads[cid].apuh_lock);
557 
558 	/*
559 	 * Generate a special format unit address with three fields to
560 	 * guarantee uniqueness. Normal unit addresses for ACPI devices have
561 	 * either one or two fields.
562 	 */
563 	if (snprintf(buf, len, "%u,%u,0", (*pp)->apu_nid, cid) > len) {
564 		return (NULL);
565 	}
566 
567 	return (buf);
568 }
569 
570 static char *
571 acpidev_gen_unitaddr(char *uid, char *fmt, char *buf, size_t len)
572 {
573 	size_t i, cnt;
574 	uint_t id1, id2;
575 
576 	ASSERT(len >= 64);
577 	if (fmt == NULL || strlen(fmt) == 0) {
578 		return (NULL);
579 	}
580 
581 	/*
582 	 * Count '%' in format string to protect sscanf().
583 	 * Only support '%u' and '%x', and maximum 2 conversions.
584 	 */
585 	for (cnt = 0, i = 0; fmt[i] != 0 && cnt <= 2; i++) {
586 		if (fmt[i] != '%') {
587 			continue;
588 		} else if (fmt[i + 1] == 'u' || fmt[i + 1] == 'x') {
589 			/* Skip next character. */
590 			i++;
591 			cnt++;
592 		} else {
593 			/* Invalid conversion, stop walking. */
594 			cnt = SIZE_MAX;
595 		}
596 	}
597 	if (cnt != 1 && cnt != 2) {
598 		ACPIDEV_DEBUG(CE_WARN,
599 		    "acpidev: invalid uid format string '%s'.", fmt);
600 		return (NULL);
601 	}
602 
603 	/* Scan uid and generate unitaddr. */
604 	if (sscanf(uid, fmt, &id1, &id2) != cnt) {
605 		return (NULL);
606 	}
607 	/*
608 	 * Reverse the order of the two IDs to match the requirements of the
609 	 * hotplug driver.
610 	 */
611 	if (cnt == 2 && snprintf(buf, len, "%u,%u", id2, id1) >= len) {
612 		ACPIDEV_DEBUG(CE_WARN,
613 		    "acpidev: generated unitaddr is too long.");
614 		return (NULL);
615 	} else if (cnt == 1 && snprintf(buf, len, "%u", id1) >= len) {
616 		ACPIDEV_DEBUG(CE_WARN,
617 		    "acpidev: generated unitaddr is too long.");
618 		return (NULL);
619 	}
620 
621 	return (buf);
622 }
623 
624 char *
625 acpidev_generate_unitaddr(char *uid, char **fmts, size_t nfmt,
626     char *buf, size_t len)
627 {
628 	size_t i;
629 	uint_t count = 0;
630 	ulong_t val;
631 	char **formats = NULL;
632 	char *rbuf = NULL;
633 	char *endp = NULL;
634 
635 	ASSERT(len >= 64);
636 
637 	/* Use _UID as unit address if it's a decimal integer. */
638 	if (ddi_strtoul(uid, &endp, 10, &val) == 0 &&
639 	    (endp == NULL || *endp == 0)) {
640 		if (snprintf(buf, len, "%s", uid) >= len) {
641 			return (NULL);
642 		} else {
643 			return (buf);
644 		}
645 	}
646 
647 	/* First handle uid format strings from device property. */
648 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, ddi_root_node(),
649 	    DDI_PROP_DONTPASS,
650 	    ACPIDEV_PROP_NAME_UID_FORMAT, &formats, &count) == DDI_SUCCESS) {
651 		/* Walk through format strings and try to generate unitaddr. */
652 		for (i = 0; i < count && rbuf == NULL; i++) {
653 			rbuf = acpidev_gen_unitaddr(uid, formats[i], buf, len);
654 		}
655 		ddi_prop_free(formats);
656 	}
657 
658 	/* Then handle embedded uid format strings. */
659 	if (fmts != NULL) {
660 		for (i = 0; i < nfmt && rbuf == NULL; i++) {
661 			rbuf = acpidev_gen_unitaddr(uid, fmts[i], buf, len);
662 		}
663 	}
664 
665 	return (rbuf);
666 }
667 
668 /*
669  * The Solaris device "unit-address" property is composed of a comma-delimited
670  * list of hexadecimal values. According to the ACPI spec, the ACPI _UID method
671  * could return an integer or a string. If it returns an integer, it is used
672  * as the unit-address as is. If _UID returns a string, we try to extract some
673  * meaningful integers to compose the unit-address property. If we fail to
674  * extract any integers, a pseudo-sequential number will be generated for the
675  * unit-address.
676  */
677 ACPI_STATUS
678 acpidev_set_unitaddr(acpidev_walk_info_t *infop, char **fmts, size_t nfmt,
679     char *unitaddr)
680 {
681 	char unit[64];
682 
683 	ASSERT(infop != NULL);
684 	ASSERT(infop->awi_dip != NULL);
685 	ASSERT(infop->awi_info != NULL);
686 	if (infop == NULL || infop->awi_dip == NULL ||
687 	    infop->awi_info == NULL) {
688 		ACPIDEV_DEBUG(CE_WARN,
689 		    "acpidev: invalid parameters in acpidev_set_unitaddr().");
690 		return (AE_BAD_PARAMETER);
691 	}
692 
693 	if (infop->awi_info->Valid & ACPI_VALID_UID) {
694 		if (ndi_prop_update_string(DDI_DEV_T_NONE, infop->awi_dip,
695 		    ACPIDEV_PROP_NAME_ACPI_UID, infop->awi_info->UniqueId.Value)
696 		    != NDI_SUCCESS) {
697 			cmn_err(CE_WARN,
698 			    "!acpidev: failed to set UID property for %s.",
699 			    infop->awi_name);
700 			return (AE_ERROR);
701 		}
702 	}
703 
704 	if (unitaddr == NULL && (infop->awi_info->Valid & ACPI_VALID_UID)) {
705 		/* Try to generate unit address from _UID. */
706 		if (fmts == NULL) {
707 			fmts = acpidev_uid_formats;
708 			nfmt = sizeof (acpidev_uid_formats) / sizeof (char *);
709 		}
710 		unitaddr = acpidev_generate_unitaddr(
711 		    infop->awi_info->UniqueId.Value, fmts, nfmt,
712 		    unit, sizeof (unit));
713 		/* Generate pseudo sequential unit address. */
714 		if (unitaddr == NULL) {
715 			unitaddr = acpidev_generate_pseudo_unitaddr(
716 			    infop->awi_info->UniqueId.Value,
717 			    infop->awi_class_curr->adc_class_id,
718 			    unit, sizeof (unit));
719 		}
720 		if (unitaddr == NULL) {
721 			cmn_err(CE_WARN, "!acpidev: failed to generate unit "
722 			    "address from %s.",
723 			    infop->awi_info->UniqueId.Value);
724 			return (AE_ERROR);
725 		}
726 	}
727 	if (unitaddr == NULL) {
728 		/*
729 		 * Some ACPI objects may have no _UID method available, so we
730 		 * can't generate the "unit-address" property for them.
731 		 * On the other hand, it's legal to support such a device
732 		 * without a unit address, so return success here.
733 		 */
734 		return (AE_OK);
735 	}
736 
737 	if (ndi_prop_update_string(DDI_DEV_T_NONE, infop->awi_dip,
738 	    ACPIDEV_PROP_NAME_UNIT_ADDR, unitaddr) != NDI_SUCCESS) {
739 		cmn_err(CE_WARN, "!acpidev: failed to set unitaddr for %s.",
740 		    infop->awi_name);
741 		return (AE_ERROR);
742 	}
743 
744 	return (AE_OK);
745 }
746 
747 ACPI_STATUS
748 acpidev_set_compatible(acpidev_walk_info_t *infop, char **compat, int acount)
749 {
750 	int count, i, j;
751 	char **compatible = NULL;
752 	ACPI_DEVICE_INFO *di;
753 
754 	/*
755 	 * Generate compatible list for device based on:
756 	 *	* Device HID if available
757 	 *	* Device CIDs if available
758 	 *	* property array passed in
759 	 */
760 	ASSERT(infop != NULL);
761 	ASSERT(infop->awi_dip != NULL);
762 	ASSERT(infop->awi_info != NULL);
763 	ASSERT(compat != NULL || acount == 0);
764 	if (infop == NULL || infop->awi_dip == NULL ||
765 	    infop->awi_info == NULL || (compat == NULL && acount != 0)) {
766 		ACPIDEV_DEBUG(CE_WARN, "acpidev: invalid parameters "
767 		    "in acpidev_set_compatible().");
768 		return (AE_BAD_PARAMETER);
769 	}
770 
771 	/* Compute string count. */
772 	count = acount;
773 	di = infop->awi_info;
774 	if (di->Valid & ACPI_VALID_HID) {
775 		count++;
776 	}
777 	if (di->Valid & ACPI_VALID_CID) {
778 		count += di->CompatibilityId.Count;
779 	}
780 	compatible = kmem_zalloc(sizeof (char *) * count, KM_SLEEP);
781 
782 	/* Generate string array. */
783 	i = 0;
784 	if (di->Valid & ACPI_VALID_HID) {
785 		compatible[i++] = di->HardwareId.Value;
786 	}
787 	if (di->Valid & ACPI_VALID_CID) {
788 		for (j = 0; j < di->CompatibilityId.Count; j++) {
789 			compatible[i++] = di->CompatibilityId.Id[j].Value;
790 		}
791 	}
792 	for (j = 0; j < acount; j++) {
793 		compatible[i++] = compat[j];
794 	}
795 	ASSERT(i == count);
796 
797 	/* Set "compatible" property. */
798 	if (ndi_prop_update_string_array(DDI_DEV_T_NONE, infop->awi_dip,
799 	    OBP_COMPATIBLE, compatible, count) != NDI_SUCCESS) {
800 		cmn_err(CE_WARN, "!acpidev: failed to set compatible "
801 		    "property for %s in acpidev_set_compatible().",
802 		    infop->awi_name);
803 		kmem_free(compatible, count * sizeof (char *));
804 		return (AE_ERROR);
805 	}
806 	kmem_free(compatible, count * sizeof (char *));
807 
808 	return (AE_OK);
809 }
810