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