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