xref: /illumos-gate/usr/src/uts/intel/io/acpica/acpica.c (revision 72888e72b624f86b06ea05b1de2287dcf9f40d23)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 /*
26  * Solaris x86 ACPI CA services
27  */
28 
29 #include <sys/file.h>
30 #include <sys/errno.h>
31 #include <sys/conf.h>
32 #include <sys/modctl.h>
33 #include <sys/open.h>
34 #include <sys/stat.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37 #include <sys/esunddi.h>
38 #include <sys/kstat.h>
39 
40 #include <sys/acpi/acpi.h>
41 #include <sys/acpica.h>
42 
43 /*
44  *
45  */
46 static	struct modlmisc modlmisc = {
47 	&mod_miscops,
48 	"ACPI interpreter",
49 };
50 
51 static	struct modlinkage modlinkage = {
52 	MODREV_1,		/* MODREV_1 manual */
53 	(void *)&modlmisc,	/* module linkage */
54 	NULL,			/* list terminator */
55 };
56 
57 /*
58  * Local prototypes
59  */
60 
61 static void	acpica_init_kstats(void);
62 
63 /*
64  * Local data
65  */
66 
67 static kmutex_t	acpica_module_lock;
68 static kstat_t	*acpica_ksp;
69 
70 /*
71  * State of acpica subsystem
72  * After successful initialization, will be ACPICA_INITIALIZED
73  */
74 int acpica_init_state = ACPICA_NOT_INITIALIZED;
75 
76 /*
77  * Following are set by acpica_process_user_options()
78  *
79  * acpica_enable = FALSE prevents initialization of ACPI CA
80  * completely
81  *
82  * acpi_init_level determines level of ACPI CA functionality
83  * enabled in acpica_init()
84  */
85 int	acpica_enable;
86 UINT32	acpi_init_level;
87 
88 /*
89  * Non-zero enables lax behavior with respect to some
90  * common ACPI BIOS issues; see ACPI CA documentation
91  * Setting this to zero causes ACPI CA to enforce strict
92  * compliance with ACPI specification
93  */
94 int acpica_enable_interpreter_slack = 1;
95 
96 /*
97  * For non-DEBUG builds, set the ACPI CA debug level to 0
98  * to quiet chatty BIOS output into /var/adm/messages
99  * Field-patchable for diagnostic use.
100  */
101 #ifdef  DEBUG
102 int acpica_muzzle_debug_output = 0;
103 #else
104 int acpica_muzzle_debug_output = 1;
105 #endif
106 
107 /*
108  * ACPI DDI hooks
109  */
110 static int acpica_ddi_setwake(dev_info_t *dip, int level);
111 
112 int
113 _init(void)
114 {
115 	int error = EBUSY;
116 	int	status;
117 	extern int (*acpi_fp_setwake)();
118 
119 	mutex_init(&acpica_module_lock, NULL, MUTEX_DRIVER, NULL);
120 
121 	if ((error = mod_install(&modlinkage)) != 0) {
122 		mutex_destroy(&acpica_module_lock);
123 		goto load_error;
124 	}
125 
126 	AcpiGbl_EnableInterpreterSlack = (acpica_enable_interpreter_slack != 0);
127 
128 	if ((status = AcpiInitializeSubsystem()) != AE_OK) {
129 		cmn_err(CE_WARN, "!acpica: error pre-init:1:%d", status);
130 	}
131 
132 	acpi_fp_setwake = acpica_ddi_setwake;
133 
134 load_error:
135 	return (error);
136 }
137 
138 int
139 _info(struct modinfo *modinfop)
140 {
141 	return (mod_info(&modlinkage, modinfop));
142 }
143 
144 int
145 _fini(void)
146 {
147 	/*
148 	 * acpica module is never unloaded at run-time; there's always
149 	 * a PSM depending on it, at the very least
150 	 */
151 	return (EBUSY);
152 }
153 
154 /*
155  * Install acpica-provided address-space handlers
156  */
157 static int
158 acpica_install_handlers()
159 {
160 	ACPI_STATUS	rv = AE_OK;
161 
162 	/*
163 	 * Install ACPI CA default handlers
164 	 */
165 	if (AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
166 	    ACPI_ADR_SPACE_SYSTEM_MEMORY,
167 	    ACPI_DEFAULT_HANDLER, NULL, NULL) != AE_OK) {
168 		cmn_err(CE_WARN, "!acpica: no default handler for"
169 		    " system memory");
170 		rv = AE_ERROR;
171 	}
172 
173 	if (AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
174 	    ACPI_ADR_SPACE_SYSTEM_IO,
175 	    ACPI_DEFAULT_HANDLER, NULL, NULL) != AE_OK) {
176 		cmn_err(CE_WARN, "!acpica: no default handler for"
177 		    " system I/O");
178 		rv = AE_ERROR;
179 	}
180 
181 	if (AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
182 	    ACPI_ADR_SPACE_PCI_CONFIG,
183 	    ACPI_DEFAULT_HANDLER, NULL, NULL) != AE_OK) {
184 		cmn_err(CE_WARN, "!acpica: no default handler for"
185 		    " PCI Config");
186 		rv = AE_ERROR;
187 	}
188 
189 
190 	return (rv);
191 }
192 
193 /*
194  * Find the BIOS date, and return TRUE if supplied
195  * date is same or later than the BIOS date, or FALSE
196  * if the BIOS date can't be fetched for any reason
197  */
198 static int
199 acpica_check_bios_date(int yy, int mm, int dd)
200 {
201 
202 	char *datep;
203 	int bios_year, bios_month, bios_day;
204 
205 	/* If firmware has no bios, skip the check */
206 	if (ddi_prop_exists(DDI_DEV_T_ANY, ddi_root_node(), 0, "bios-free"))
207 		return (TRUE);
208 
209 	/*
210 	 * PC BIOSes contain a string in the form of
211 	 * "mm/dd/yy" at absolute address 0xffff5,
212 	 * where mm, dd and yy are all ASCII digits.
213 	 * We map the string, pluck out the values,
214 	 * and accept all BIOSes from 1 Jan 1999 on
215 	 * as valid.
216 	 */
217 
218 	if ((datep = (char *)AcpiOsMapMemory(0xffff5, 8)) == NULL)
219 		return (FALSE);
220 
221 	/* year */
222 	bios_year = ((int)(*(datep + 6) - '0') * 10) + (*(datep + 7) - '0');
223 	/* month */
224 	bios_month = ((int)(*datep - '0') * 10) + (*(datep + 1) - '0');
225 	/* day */
226 	bios_day = ((int)(*(datep + 3) - '0') * 10) + (*(datep + 4) - '0');
227 
228 	AcpiOsUnmapMemory((void *) datep, 8);
229 
230 	if (bios_year < 0 || bios_year > 99 || bios_month < 0 ||
231 	    bios_month > 99 || bios_day < 0 || bios_day > 99) {
232 		/* non-digit chars in BIOS date */
233 		return (FALSE);
234 	}
235 
236 	/*
237 	 * Adjust for 2-digit year; note to grand-children:
238 	 * need a new scheme before 2080 rolls around
239 	 */
240 	bios_year += (bios_year >= 80 && bios_year <= 99) ?
241 	    1900 : 2000;
242 
243 	if (bios_year < yy)
244 		return (FALSE);
245 	else if (bios_year > yy)
246 		return (TRUE);
247 
248 	if (bios_month < mm)
249 		return (FALSE);
250 	else if (bios_month > mm)
251 		return (TRUE);
252 
253 	if (bios_day < dd)
254 		return (FALSE);
255 
256 	return (TRUE);
257 }
258 
259 /*
260  * Check for Metropolis systems with BIOSes older than 10/12/04
261  * return TRUE if BIOS requires legacy mode, FALSE otherwise
262  */
263 static int
264 acpica_metro_old_bios()
265 {
266 	ACPI_TABLE_HEADER *fadt;
267 
268 	/* get the FADT */
269 	if (AcpiGetTable(ACPI_SIG_FADT, 1, (ACPI_TABLE_HEADER **)&fadt) !=
270 	    AE_OK)
271 		return (FALSE);
272 
273 	/* compare OEM Table ID to "SUNmetro" - no match, return false */
274 	if (strncmp("SUNmetro", fadt->OemTableId, 8))
275 		return (FALSE);
276 
277 	/* On a Metro - return FALSE if later than 10/12/04 */
278 	return (!acpica_check_bios_date(2004, 10, 12));
279 }
280 
281 
282 /*
283  * Process acpi-user-options property  if present
284  */
285 static void
286 acpica_process_user_options()
287 {
288 	static int processed = 0;
289 	int acpi_user_options;
290 	char *acpi_prop;
291 
292 	/*
293 	 * return if acpi-user-options has already been processed
294 	 */
295 	if (processed)
296 		return;
297 	else
298 		processed = 1;
299 
300 	/* converts acpi-user-options from type string to int, if any */
301 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
302 	    DDI_PROP_DONTPASS, "acpi-user-options", &acpi_prop) ==
303 	    DDI_PROP_SUCCESS) {
304 		long data;
305 		int ret;
306 		ret = ddi_strtol(acpi_prop, NULL, 0, &data);
307 		if (ret == 0) {
308 			e_ddi_prop_remove(DDI_DEV_T_NONE, ddi_root_node(),
309 			    "acpi-user-options");
310 			e_ddi_prop_update_int(DDI_DEV_T_NONE, ddi_root_node(),
311 			    "acpi-user-options", data);
312 		}
313 		ddi_prop_free(acpi_prop);
314 	}
315 
316 	/*
317 	 * fetch the optional options property
318 	 */
319 	acpi_user_options = ddi_prop_get_int(DDI_DEV_T_ANY, ddi_root_node(), 0,
320 	    "acpi-user-options", 0);
321 
322 	/*
323 	 * Note that 'off' has precedence over 'on'
324 	 * Also note - all cases of ACPI_OUSER_MASK
325 	 * provided here, no default: case is present
326 	 */
327 	switch (acpi_user_options & ACPI_OUSER_MASK) {
328 	case ACPI_OUSER_DFLT:
329 		acpica_enable = acpica_check_bios_date(1999, 1, 1);
330 		break;
331 	case ACPI_OUSER_ON:
332 		acpica_enable = TRUE;
333 		break;
334 	case ACPI_OUSER_OFF:
335 	case ACPI_OUSER_OFF | ACPI_OUSER_ON:
336 		acpica_enable = FALSE;
337 		break;
338 	}
339 
340 	acpi_init_level = ACPI_FULL_INITIALIZATION;
341 
342 	/*
343 	 * special test here; may be generalized in the
344 	 * future - test for a machines that are known to
345 	 * work only in legacy mode, and set OUSER_LEGACY if
346 	 * we're on one
347 	 */
348 	if (acpica_metro_old_bios())
349 		acpi_user_options |= ACPI_OUSER_LEGACY;
350 
351 	/*
352 	 * If legacy mode is specified, set initialization
353 	 * options to avoid entering ACPI mode and hooking SCI
354 	 * - basically try to act like legacy acpi_intp
355 	 */
356 	if ((acpi_user_options & ACPI_OUSER_LEGACY) != 0)
357 		acpi_init_level |= (ACPI_NO_ACPI_ENABLE | ACPI_NO_HANDLER_INIT);
358 
359 	/*
360 	 * modify default ACPI CA debug output level for non-DEBUG builds
361 	 * (to avoid BIOS debug chatter in /var/adm/messages)
362 	 */
363 	if (acpica_muzzle_debug_output)
364 		AcpiDbgLevel = 0;
365 }
366 
367 /*
368  * Initialize the CA subsystem if it hasn't been done already
369  */
370 int
371 acpica_init()
372 {
373 	ACPI_STATUS status;
374 
375 	/*
376 	 * Make sure user options are processed,
377 	 * then fail to initialize if ACPI CA has been
378 	 * disabled
379 	 */
380 	acpica_process_user_options();
381 	if (!acpica_enable)
382 		return (AE_ERROR);
383 
384 	mutex_enter(&acpica_module_lock);
385 
386 	if (acpica_init_state == ACPICA_NOT_INITIALIZED) {
387 		if (ACPI_FAILURE(status = AcpiInitializeTables(NULL, 0, 0)))
388 			goto error;
389 
390 		if (ACPI_FAILURE(status = AcpiLoadTables()))
391 			goto error;
392 
393 		if (ACPI_FAILURE(status = acpica_install_handlers()))
394 			goto error;
395 
396 		if (ACPI_FAILURE(status = AcpiEnableSubsystem(
397 		    acpi_init_level)))
398 			goto error;
399 
400 		if (ACPI_FAILURE(status = AcpiInitializeObjects(0)))
401 			goto error;
402 
403 		/*
404 		 * Initialize EC
405 		 */
406 		acpica_ec_init();
407 
408 		acpica_init_state = ACPICA_INITIALIZED;
409 		acpica_init_kstats();
410 error:
411 		if (acpica_init_state != ACPICA_INITIALIZED) {
412 			cmn_err(CE_NOTE, "!failed to initialize"
413 			    " ACPI services");
414 		}
415 	} else
416 		status = AE_OK;
417 
418 	/*
419 	 * Set acpi-status to 13 if acpica has been initialized successfully.
420 	 * This indicates that acpica is up and running.  This variable name
421 	 * and value were chosen in order to remain compatible with acpi_intp.
422 	 */
423 	e_ddi_prop_update_int(DDI_DEV_T_NONE, ddi_root_node(), "acpi-status",
424 	    (status == AE_OK) ? (ACPI_BOOT_INIT | ACPI_BOOT_ENABLE |
425 	    ACPI_BOOT_BOOTCONF) : 0);
426 
427 	mutex_exit(&acpica_module_lock);
428 	return (status);
429 }
430 
431 /*
432  * SCI handling
433  */
434 
435 ACPI_STATUS
436 acpica_get_sci(int *sci_irq, iflag_t *sci_flags)
437 {
438 	ACPI_SUBTABLE_HEADER		*ap;
439 	ACPI_TABLE_MADT			*mat;
440 	ACPI_MADT_INTERRUPT_OVERRIDE	*mio;
441 	ACPI_TABLE_FADT			*fadt;
442 	int			madt_seen, madt_size;
443 
444 
445 	/*
446 	 * Make sure user options are processed,
447 	 * then return error if ACPI CA has been
448 	 * disabled or system is not running in ACPI
449 	 * and won't need/understand SCI
450 	 */
451 	acpica_process_user_options();
452 	if ((!acpica_enable) || (acpi_init_level & ACPI_NO_ACPI_ENABLE))
453 		return (AE_ERROR);
454 
455 	/*
456 	 * according to Intel ACPI developers, SCI
457 	 * conforms to PCI bus conventions; level/low
458 	 * unless otherwise directed by overrides.
459 	 */
460 	sci_flags->intr_el = INTR_EL_LEVEL;
461 	sci_flags->intr_po = INTR_PO_ACTIVE_LOW;
462 	sci_flags->bustype = BUS_PCI;	/*  we *do* conform to PCI */
463 
464 	/* get the SCI from the FADT */
465 	if (AcpiGetTable(ACPI_SIG_FADT, 1, (ACPI_TABLE_HEADER **)&fadt) !=
466 	    AE_OK)
467 		return (AE_ERROR);
468 
469 	*sci_irq = fadt->SciInterrupt;
470 
471 	/* search for ISOs that modify it */
472 	/* if we don't find a MADT, that's OK; no ISOs then */
473 	if (AcpiGetTable(ACPI_SIG_MADT, 1, (ACPI_TABLE_HEADER **) &mat) !=
474 	    AE_OK)
475 		return (AE_OK);
476 
477 	ap = (ACPI_SUBTABLE_HEADER *) (mat + 1);
478 	madt_size = mat->Header.Length;
479 	madt_seen = sizeof (*mat);
480 
481 	while (madt_seen < madt_size) {
482 		switch (ap->Type) {
483 		case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE:
484 			mio = (ACPI_MADT_INTERRUPT_OVERRIDE *) ap;
485 			if (mio->SourceIrq == *sci_irq) {
486 				*sci_irq = mio->GlobalIrq;
487 				sci_flags->intr_el = (mio->IntiFlags &
488 				    ACPI_MADT_TRIGGER_MASK) >> 2;
489 				sci_flags->intr_po = mio->IntiFlags &
490 				    ACPI_MADT_POLARITY_MASK;
491 			}
492 			break;
493 		}
494 
495 		/* advance to next entry */
496 		madt_seen += ap->Length;
497 		ap = (ACPI_SUBTABLE_HEADER *)(((char *)ap) + ap->Length);
498 	}
499 
500 	/*
501 	 * One more check; if ISO said "conform", revert to default
502 	 */
503 	if (sci_flags->intr_el == INTR_EL_CONFORM)
504 		sci_flags->intr_el = INTR_EL_LEVEL;
505 	if (sci_flags->intr_po == INTR_PO_CONFORM)
506 		sci_flags->intr_po = INTR_PO_ACTIVE_LOW;
507 
508 	return (AE_OK);
509 }
510 
511 /*
512  * Sets ACPI wake state for device referenced by dip.
513  * If level is S0 (0), disables wake event; otherwise,
514  * enables wake event which will wake system from level.
515  */
516 static int
517 acpica_ddi_setwake(dev_info_t *dip, int level)
518 {
519 	ACPI_STATUS	status;
520 	ACPI_HANDLE	devobj, gpeobj;
521 	ACPI_OBJECT	*prw, *gpe;
522 	ACPI_BUFFER	prw_buf;
523 	int		gpebit, pwr_res_count, prw_level, rv;
524 
525 	/*
526 	 * initialize these early so we can use a common
527 	 * exit point below
528 	 */
529 	prw_buf.Pointer = NULL;
530 	prw_buf.Length = ACPI_ALLOCATE_BUFFER;
531 	rv = 0;
532 
533 	/*
534 	 * Attempt to get a handle to a corresponding ACPI object.
535 	 * If no object is found, return quietly, since not all
536 	 * devices have corresponding ACPI objects.
537 	 */
538 	status = acpica_get_handle(dip, &devobj);
539 	if (ACPI_FAILURE(status)) {
540 		char pathbuf[MAXPATHLEN];
541 		ddi_pathname(dip, pathbuf);
542 #ifdef DEBUG
543 		cmn_err(CE_NOTE, "!acpica_ddi_setwake: could not get"
544 		    " handle for %s, %s:%d", pathbuf, ddi_driver_name(dip),
545 		    ddi_get_instance(dip));
546 #endif
547 		goto done;
548 	}
549 
550 	/*
551 	 * Attempt to evaluate _PRW object.
552 	 * If no valid object is found, return quietly, since not all
553 	 * devices have _PRW objects.
554 	 */
555 	status = AcpiEvaluateObject(devobj, "_PRW", NULL, &prw_buf);
556 	prw = prw_buf.Pointer;
557 	if (ACPI_FAILURE(status) || prw_buf.Length == 0 || prw == NULL ||
558 	    prw->Type != ACPI_TYPE_PACKAGE || prw->Package.Count < 2 ||
559 	    prw->Package.Elements[1].Type != ACPI_TYPE_INTEGER) {
560 		cmn_err(CE_NOTE, "acpica_ddi_setwake: could not "
561 		    " evaluate _PRW");
562 		goto done;
563 	}
564 
565 	/* fetch the lowest wake level from the _PRW */
566 	prw_level = prw->Package.Elements[1].Integer.Value;
567 
568 	/*
569 	 * process the GPE description
570 	 */
571 	switch (prw->Package.Elements[0].Type) {
572 	case ACPI_TYPE_INTEGER:
573 		gpeobj = NULL;
574 		gpebit = prw->Package.Elements[0].Integer.Value;
575 		break;
576 	case ACPI_TYPE_PACKAGE:
577 		gpe = &prw->Package.Elements[0];
578 		if (gpe->Package.Count != 2 ||
579 		    gpe->Package.Elements[1].Type != ACPI_TYPE_INTEGER)
580 			goto done;
581 		gpeobj = gpe->Package.Elements[0].Reference.Handle;
582 		gpebit = gpe->Package.Elements[1].Integer.Value;
583 		if (gpeobj == NULL)
584 			goto done;
585 	default:
586 		goto done;
587 	}
588 
589 	rv = -1;
590 	if (level == 0) {
591 		if (ACPI_FAILURE(AcpiDisableGpe(gpeobj, gpebit, ACPI_NOT_ISR)))
592 			goto done;
593 	} else if (prw_level <= level) {
594 		if (ACPI_SUCCESS(
595 		    AcpiSetGpeType(gpeobj, gpebit, ACPI_GPE_TYPE_WAKE)))
596 			if (ACPI_FAILURE(
597 			    AcpiEnableGpe(gpeobj, gpebit, ACPI_NOT_ISR)))
598 				goto done;
599 	}
600 	rv = 0;
601 done:
602 	if (prw_buf.Pointer != NULL)
603 		AcpiOsFree(prw_buf.Pointer);
604 	return (rv);
605 }
606 
607 /*
608  * kstat access to a limited set of ACPI propertis
609  */
610 static void
611 acpica_init_kstats()
612 {
613 	ACPI_HANDLE	s3handle;
614 	ACPI_STATUS	status;
615 	ACPI_TABLE_FADT	*fadt;
616 	kstat_named_t *knp;
617 
618 	/*
619 	 * Create a small set of named kstats; just return in the rare
620 	 * case of a failure, * in which case, the kstats won't be present.
621 	 */
622 	if ((acpica_ksp = kstat_create("acpi", 0, "acpi", "misc",
623 	    KSTAT_TYPE_NAMED, 2, 0)) == NULL)
624 		return;
625 
626 	/*
627 	 * initialize kstat 'S3' to reflect the presence of \_S3 in
628 	 * the ACPI namespace (1 = present, 0 = not present)
629 	 */
630 	knp = acpica_ksp->ks_data;
631 	knp->value.l = (AcpiGetHandle(NULL, "\\_S3", &s3handle) == AE_OK);
632 	kstat_named_init(knp, "S3", KSTAT_DATA_LONG);
633 	knp++;		/* advance to next named kstat */
634 
635 	/*
636 	 * initialize kstat 'preferred_pm_profile' to the value
637 	 * contained in the (always present) FADT
638 	 */
639 	status = AcpiGetTable(ACPI_SIG_FADT, 1, (ACPI_TABLE_HEADER **)&fadt);
640 	knp->value.l = (status == AE_OK) ? fadt->PreferredProfile : -1;
641 	kstat_named_init(knp, "preferred_pm_profile", KSTAT_DATA_LONG);
642 
643 	/*
644 	 * install the named kstats
645 	 */
646 	kstat_install(acpica_ksp);
647 }
648 
649 /*
650  * Attempt to save the current ACPI settings (_CRS) for the device
651  * which corresponds to the supplied devinfo node.  The settings are
652  * saved as a property on the dip.  If no ACPI object is found to be
653  * associated with the devinfo node, no action is taken and no error
654  * is reported.
655  */
656 void
657 acpica_ddi_save_resources(dev_info_t *dip)
658 {
659 	ACPI_HANDLE	devobj;
660 	ACPI_BUFFER	resbuf;
661 	int		ret;
662 
663 	resbuf.Length = ACPI_ALLOCATE_BUFFER;
664 	if (ACPI_FAILURE(acpica_get_handle(dip, &devobj)) ||
665 	    ACPI_FAILURE(AcpiGetCurrentResources(devobj, &resbuf)))
666 		return;
667 
668 	ret = ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
669 	    "acpi-crs", resbuf.Pointer, resbuf.Length);
670 
671 	ASSERT(ret == DDI_PROP_SUCCESS);
672 
673 	AcpiOsFree(resbuf.Pointer);
674 }
675 
676 /*
677  * If the supplied devinfo node has an ACPI settings property attached,
678  * restore them to the associated ACPI device using _SRS.  The property
679  * is deleted from the devinfo node afterward.
680  */
681 void
682 acpica_ddi_restore_resources(dev_info_t *dip)
683 {
684 	ACPI_HANDLE	devobj;
685 	ACPI_BUFFER	resbuf;
686 	uchar_t		*propdata;
687 	uint_t		proplen;
688 
689 	if (ACPI_FAILURE(acpica_get_handle(dip, &devobj)))
690 		return;
691 
692 	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
693 	    "acpi-crs", &propdata, &proplen) != DDI_PROP_SUCCESS)
694 		return;
695 
696 	resbuf.Pointer = propdata;
697 	resbuf.Length = proplen;
698 	(void) AcpiSetCurrentResources(devobj, &resbuf);
699 	ddi_prop_free(propdata);
700 	(void) ddi_prop_remove(DDI_DEV_T_NONE, dip, "acpi-crs");
701 }
702