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