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