You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
957 lines
24 KiB
957 lines
24 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
acpiosnt.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the OS specific functions for the Windows NT
|
|
version of the ACPI driver
|
|
|
|
Author:
|
|
|
|
Jason Clark (jasoncl)
|
|
Stephane Plante (splante)
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Revision History:
|
|
|
|
09-Oct-96 Initial Revision
|
|
20-Nov-96 Interrupt Vector support
|
|
31-Mar-97 Cleanup
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
#include "amlihook.h"
|
|
|
|
// from shared\acpiinit.c
|
|
extern PACPIInformation AcpiInformation;
|
|
|
|
// from irqarb.c
|
|
extern ULONG InterruptModel;
|
|
|
|
NTSTATUS
|
|
DriverEntry (
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
);
|
|
|
|
PPM_DISPATCH_TABLE PmHalDispatchTable;
|
|
FAST_IO_DISPATCH ACPIFastIoDispatch;
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT,DriverEntry)
|
|
#pragma alloc_text(PAGE,OSInterruptVector)
|
|
#pragma alloc_text(PAGE,NotifyHalWithMachineStates)
|
|
#endif
|
|
|
|
ACPI_HAL_DISPATCH_TABLE AcpiHalDispatchTable;
|
|
|
|
NTSTATUS
|
|
DriverEntry (
|
|
PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The ACPI driver's entry point
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to the object that represents this driver
|
|
|
|
Return Value:
|
|
|
|
N/A
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
ULONG i;
|
|
ULONG argSize;
|
|
|
|
//
|
|
// If the AMLIHOOK interface is enabled
|
|
// hook it.
|
|
//
|
|
|
|
AmliHook_InitTestHookInterface();
|
|
|
|
//
|
|
// Remember the name of the driver object
|
|
//
|
|
AcpiDriverObject = DriverObject;
|
|
|
|
//
|
|
// Save registry path for use by WMI registration code
|
|
//
|
|
AcpiRegistryPath.Length = 0;
|
|
AcpiRegistryPath.MaximumLength = RegistryPath->Length + sizeof(WCHAR);
|
|
AcpiRegistryPath.Buffer = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
RegistryPath->Length+sizeof(WCHAR),
|
|
ACPI_MISC_POOLTAG
|
|
);
|
|
if (AcpiRegistryPath.Buffer != NULL) {
|
|
RtlCopyUnicodeString(&AcpiRegistryPath, RegistryPath);
|
|
|
|
} else {
|
|
|
|
AcpiRegistryPath.MaximumLength = 0;
|
|
|
|
}
|
|
|
|
//
|
|
// Read the keys that we need to operate this driver from the
|
|
// registry
|
|
//
|
|
ACPIInitReadRegistryKeys();
|
|
|
|
//
|
|
// This flag may be set, when its not required, nor desired
|
|
// so take the opportunity to clean it up now
|
|
//
|
|
if (AcpiOverrideAttributes & ACPI_OVERRIDE_MP_SLEEP) {
|
|
|
|
KAFFINITY processors = KeQueryActiveProcessors();
|
|
|
|
//
|
|
// If this is a UP system, then turn off this override
|
|
//
|
|
if (processors == 1) {
|
|
|
|
AcpiOverrideAttributes &= ~ACPI_OVERRIDE_MP_SLEEP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Initialize the DPCs
|
|
//
|
|
KeInitializeDpc( &AcpiPowerDpc, ACPIDevicePowerDpc, NULL );
|
|
KeInitializeDpc( &AcpiBuildDpc, ACPIBuildDeviceDpc, NULL );
|
|
KeInitializeDpc( &AcpiGpeDpc, ACPIInterruptDispatchEventDpc, NULL );
|
|
|
|
//
|
|
// Initialize the timer
|
|
//
|
|
KeInitializeTimer( &AcpiGpeTimer );
|
|
|
|
//
|
|
// Initialize the SpinLocks
|
|
//
|
|
KeInitializeSpinLock( &AcpiDeviceTreeLock );
|
|
KeInitializeSpinLock( &AcpiPowerLock );
|
|
KeInitializeSpinLock( &AcpiPowerQueueLock );
|
|
KeInitializeSpinLock( &AcpiBuildQueueLock );
|
|
KeInitializeSpinLock( &AcpiThermalLock );
|
|
KeInitializeSpinLock( &AcpiButtonLock );
|
|
KeInitializeSpinLock( &AcpiFatalLock );
|
|
KeInitializeSpinLock( &AcpiUpdateFlagsLock );
|
|
KeInitializeSpinLock( &AcpiGetLock );
|
|
|
|
//
|
|
// Initialize the List Entry's
|
|
//
|
|
InitializeListHead( &AcpiPowerDelayedQueueList );
|
|
InitializeListHead( &AcpiPowerQueueList );
|
|
InitializeListHead( &AcpiPowerPhase0List );
|
|
InitializeListHead( &AcpiPowerPhase1List );
|
|
InitializeListHead( &AcpiPowerPhase2List );
|
|
InitializeListHead( &AcpiPowerPhase3List );
|
|
InitializeListHead( &AcpiPowerPhase4List );
|
|
InitializeListHead( &AcpiPowerPhase5List );
|
|
InitializeListHead( &AcpiPowerWaitWakeList );
|
|
InitializeListHead( &AcpiPowerSynchronizeList );
|
|
InitializeListHead( &AcpiPowerNodeList );
|
|
InitializeListHead( &AcpiBuildQueueList );
|
|
InitializeListHead( &AcpiBuildDeviceList );
|
|
InitializeListHead( &AcpiBuildOperationRegionList );
|
|
InitializeListHead( &AcpiBuildPowerResourceList );
|
|
InitializeListHead( &AcpiBuildRunMethodList );
|
|
InitializeListHead( &AcpiBuildSynchronizationList );
|
|
InitializeListHead( &AcpiBuildThermalZoneList );
|
|
InitializeListHead( &AcpiUnresolvedEjectList );
|
|
InitializeListHead( &AcpiThermalList );
|
|
InitializeListHead( &AcpiButtonList );
|
|
InitializeListHead( &AcpiGetListEntry );
|
|
|
|
//
|
|
// Initialize the variables/booleans
|
|
//
|
|
AcpiPowerDpcRunning = FALSE;
|
|
AcpiPowerWorkDone = FALSE;
|
|
AcpiBuildDpcRunning = FALSE;
|
|
AcpiBuildFixedButtonEnumerated = FALSE;
|
|
AcpiBuildWorkDone = FALSE;
|
|
AcpiFatalOutstanding = FALSE;
|
|
AcpiGpeDpcRunning = FALSE;
|
|
AcpiGpeDpcScheduled = FALSE;
|
|
AcpiGpeWorkDone = FALSE;
|
|
|
|
//
|
|
// Initialize the LookAside lists.
|
|
//
|
|
ExInitializeNPagedLookasideList(
|
|
&BuildRequestLookAsideList,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
sizeof(ACPI_BUILD_REQUEST),
|
|
ACPI_DEVICE_POOLTAG,
|
|
(PAGE_SIZE / sizeof(ACPI_BUILD_REQUEST) )
|
|
);
|
|
ExInitializeNPagedLookasideList(
|
|
&RequestLookAsideList,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
sizeof(ACPI_POWER_REQUEST),
|
|
ACPI_POWER_POOLTAG,
|
|
(PAGE_SIZE * 4 / sizeof(ACPI_POWER_REQUEST) )
|
|
);
|
|
ExInitializeNPagedLookasideList(
|
|
&DeviceExtensionLookAsideList,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
sizeof(DEVICE_EXTENSION),
|
|
ACPI_DEVICE_POOLTAG,
|
|
64
|
|
);
|
|
ExInitializeNPagedLookasideList(
|
|
&ObjectDataLookAsideList,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
sizeof(OBJDATA),
|
|
ACPI_OBJECT_POOLTAG,
|
|
(PAGE_SIZE / sizeof(OBJDATA) )
|
|
);
|
|
ExInitializeNPagedLookasideList(
|
|
&PswContextLookAsideList,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
sizeof(ACPI_WAKE_PSW_CONTEXT),
|
|
ACPI_POWER_POOLTAG,
|
|
16
|
|
);
|
|
|
|
//
|
|
// Initialize internal worker
|
|
//
|
|
ACPIInitializeWorker ();
|
|
|
|
//
|
|
// Make sure that we have an AddDevice function that will create
|
|
// the basic FDO for this device when it is called
|
|
//
|
|
DriverObject->DriverExtension->AddDevice = ACPIDispatchAddDevice;
|
|
|
|
//
|
|
// All irps will be sent through a single dispatch point
|
|
//
|
|
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
|
|
|
|
DriverObject->MajorFunction[ i ] = ACPIDispatchIrp;
|
|
|
|
}
|
|
DriverObject->DriverUnload = ACPIUnload;
|
|
|
|
//
|
|
// Fill out the Fast Io Detach callback for our bus filter
|
|
//
|
|
RtlZeroMemory(&ACPIFastIoDispatch, sizeof(FAST_IO_DISPATCH)) ;
|
|
ACPIFastIoDispatch.SizeOfFastIoDispatch = sizeof(FAST_IO_DISPATCH) ;
|
|
ACPIFastIoDispatch.FastIoDetachDevice = ACPIFilterFastIoDetachCallback ;
|
|
DriverObject->FastIoDispatch = &ACPIFastIoDispatch ;
|
|
|
|
//
|
|
// Initialize some HAL stuff
|
|
//
|
|
AcpiHalDispatchTable.Signature = ACPI_HAL_DISPATCH_SIGNATURE;
|
|
AcpiHalDispatchTable.Version = ACPI_HAL_DISPATCH_VERSION;
|
|
AcpiHalDispatchTable.AcpipEnableDisableGPEvents =
|
|
&ACPIGpeHalEnableDisableEvents;
|
|
AcpiHalDispatchTable.AcpipInitEnableAcpi =
|
|
&ACPIEnableInitializeACPI;
|
|
AcpiHalDispatchTable.AcpipGpeEnableWakeEvents =
|
|
&ACPIWakeEnableWakeEvents;
|
|
HalInitPowerManagement(
|
|
(PPM_DISPATCH_TABLE)(&AcpiHalDispatchTable),
|
|
&PmHalDispatchTable
|
|
);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
OSInitializeCallbacks(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called right after the interper has been initialized,
|
|
but before AML code has actually been executed.
|
|
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
POPREGIONHANDLER dummy;
|
|
#if DBG
|
|
NTSTATUS status;
|
|
|
|
status =
|
|
#endif
|
|
AMLIRegEventHandler(
|
|
EVTYPE_OPCODE_EX,
|
|
OP_LOAD,
|
|
ACPICallBackLoad,
|
|
0
|
|
);
|
|
#if DBG
|
|
if (!NT_SUCCESS(status)) {
|
|
ACPIBreakPoint();
|
|
}
|
|
|
|
status =
|
|
#endif
|
|
AMLIRegEventHandler(
|
|
EVTYPE_OPCODE_EX,
|
|
OP_UNLOAD,
|
|
ACPICallBackUnload,
|
|
0
|
|
);
|
|
#if DBG
|
|
if (!NT_SUCCESS(status)) {
|
|
ACPIBreakPoint();
|
|
}
|
|
|
|
status =
|
|
#endif
|
|
AMLIRegEventHandler(
|
|
EVTYPE_DESTROYOBJ,
|
|
0,
|
|
(PFNHND)ACPITableNotifyFreeObject,
|
|
0
|
|
);
|
|
#if DBG
|
|
if (!NT_SUCCESS(status)) {
|
|
ACPIBreakPoint();
|
|
}
|
|
|
|
status =
|
|
#endif
|
|
AMLIRegEventHandler(
|
|
EVTYPE_NOTIFY,
|
|
0,
|
|
NotifyHandler,
|
|
0
|
|
);
|
|
#if DBG
|
|
if (!NT_SUCCESS(status)) {
|
|
ACPIBreakPoint();
|
|
}
|
|
|
|
status =
|
|
#endif
|
|
AMLIRegEventHandler(
|
|
EVTYPE_ACQREL_GLOBALLOCK,
|
|
0,
|
|
GlobalLockEventHandler,
|
|
0
|
|
);
|
|
#if DBG
|
|
if (!NT_SUCCESS(status)) {
|
|
ACPIBreakPoint();
|
|
}
|
|
|
|
status =
|
|
#endif
|
|
AMLIRegEventHandler(
|
|
EVTYPE_CREATE,
|
|
0,
|
|
OSNotifyCreate,
|
|
0
|
|
);
|
|
#if DBG
|
|
if (!NT_SUCCESS(status)) {
|
|
ACPIBreakPoint();
|
|
}
|
|
|
|
status =
|
|
#endif
|
|
AMLIRegEventHandler(
|
|
EVTYPE_FATAL,
|
|
0,
|
|
OSNotifyFatalError,
|
|
0
|
|
);
|
|
#if DBG
|
|
if (!NT_SUCCESS(status)) {
|
|
ACPIBreakPoint();
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Register internal support of PCI operational regions. Note that
|
|
// we don't specify a region object here because we haven't yet created it
|
|
//
|
|
RegisterOperationRegionHandler(
|
|
NULL,
|
|
EVTYPE_RS_COOKACCESS,
|
|
REGSPACE_PCICFG, // PCI config space
|
|
(PFNHND)PciConfigSpaceHandler,
|
|
0,
|
|
&dummy);
|
|
}
|
|
|
|
BOOLEAN
|
|
OSInterruptVector(
|
|
PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is charged with claiming an Interrupt for the device
|
|
|
|
Arguments:
|
|
|
|
Context - Context Pointer (points to FDO currently)
|
|
|
|
Return
|
|
|
|
TRUE for success
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR InterruptDesc;
|
|
ULONG Count;
|
|
|
|
PAGED_CODE();
|
|
|
|
deviceExtension = ACPIInternalGetDeviceExtension( (PDEVICE_OBJECT) Context );
|
|
|
|
//
|
|
// Grab the translated interrupt vector from our resource list
|
|
//
|
|
Count = 0;
|
|
InterruptDesc = RtlUnpackPartialDesc(
|
|
CmResourceTypeInterrupt,
|
|
deviceExtension->ResourceList,
|
|
&Count
|
|
);
|
|
if (InterruptDesc == NULL) {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_CRITICAL,
|
|
deviceExtension,
|
|
" - Could not find interrupt descriptor\n"
|
|
) );
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_ROOT_RESOURCES_FAILURE,
|
|
(ULONG_PTR) deviceExtension,
|
|
(ULONG_PTR) deviceExtension->ResourceList,
|
|
1
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Initialize our DPC object
|
|
//
|
|
KeInitializeDpc(
|
|
&(deviceExtension->Fdo.InterruptDpc),
|
|
ACPIInterruptServiceRoutineDPC,
|
|
deviceExtension
|
|
);
|
|
|
|
//
|
|
// Now, lets connect ourselves to the interrupt
|
|
//
|
|
status = IoConnectInterrupt(
|
|
&(deviceExtension->Fdo.InterruptObject),
|
|
(PKSERVICE_ROUTINE) ACPIInterruptServiceRoutine,
|
|
deviceExtension,
|
|
NULL,
|
|
InterruptDesc->u.Interrupt.Vector,
|
|
(KIRQL)InterruptDesc->u.Interrupt.Level,
|
|
(KIRQL)InterruptDesc->u.Interrupt.Level,
|
|
LevelSensitive,
|
|
CmResourceShareShared,
|
|
InterruptDesc->u.Interrupt.Affinity,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
ACPIPrint( (
|
|
ACPI_PRINT_CRITICAL,
|
|
"OSInterruptVector: Could not connected to interrupt - %#08lx\n",
|
|
status
|
|
) );
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Tell the HAL directly that we are done with the interrupt init
|
|
// stuff and it can start using the ACPI timer.
|
|
//
|
|
HalAcpiTimerInit(0,0);
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return (TRUE);
|
|
}
|
|
|
|
VOID
|
|
ACPIAssert (
|
|
ULONG Condition,
|
|
ULONG ErrorCode,
|
|
PCHAR ReplacementText,
|
|
PCHAR SupplementalText,
|
|
ULONG Flags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is called to allow OS specific code to perform some additional OS
|
|
specific processing for Asserts. After this function returns, the normal
|
|
ASSERT macro will be called
|
|
|
|
Arguments:
|
|
|
|
Condition
|
|
ErrorCode
|
|
ReplacementText
|
|
SupplementalText
|
|
Flags
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
{
|
|
if (!Condition) {
|
|
|
|
ACPIPrint( (
|
|
ACPI_PRINT_CRITICAL,
|
|
"ACPIAssert: \n"
|
|
" ErrorCode = %08lx Flags = %08lx\n"
|
|
" ReplacmentText = %s\n"
|
|
" SupplmentalText = %s\n",
|
|
ErrorCode, Flags,
|
|
ReplacementText,
|
|
SupplementalText
|
|
) );
|
|
ASSERT(Condition);
|
|
|
|
}
|
|
return;
|
|
}
|
|
|
|
PNSOBJ
|
|
OSConvertDeviceHandleToPNSOBJ(
|
|
PVOID DeviceHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function converts a DeviceHandle (which will always be a
|
|
DeviceObject on NT) to a PNSObj handle.
|
|
|
|
Arguments:
|
|
|
|
DeviceHandle -- A DeviceObject whose PNSOBJ we want to determine.
|
|
|
|
Return Value:
|
|
|
|
The PNSOBJ for the given DeviceHandle or NULL if the conversion
|
|
could not be done.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_OBJECT deviceObject;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
deviceObject = (PDEVICE_OBJECT) DeviceHandle;
|
|
ASSERT( deviceObject != NULL );
|
|
if (deviceObject == NULL) {
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
deviceExtension = ACPIInternalGetDeviceExtension(deviceObject);
|
|
ASSERT( deviceExtension != NULL );
|
|
if (deviceExtension == NULL) {
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
return deviceExtension->AcpiObject;
|
|
}
|
|
|
|
NTSTATUS
|
|
NotifyHalWithMachineStates(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine marshals the information about C states and
|
|
S states that the HAL needs and then passes it down.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN overrideMpSleep = FALSE;
|
|
CHAR picMethod[] = "\\_PIC";
|
|
NTSTATUS status;
|
|
OBJDATA data;
|
|
PHAL_SLEEP_VAL sleepVals = NULL;
|
|
PNSOBJ pnsobj = NULL;
|
|
PUCHAR SleepState[] = { "\\_S1", "\\_S2", "\\_S3",
|
|
"\\_S4", "\\_S5" };
|
|
SYSTEM_POWER_STATE systemState;
|
|
UCHAR processor = 0;
|
|
UCHAR state;
|
|
ULONG flags = 0;
|
|
ULONG pNum = 0;
|
|
|
|
PSYSTEM_POWER_STATE_DISABLE_REASON pReasonOverride,pReasonBios,pReasonMP;
|
|
SYSTEM_POWER_LOGGING_ENTRY PowerLoggingEntry;
|
|
NTSTATUS LogStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Notify the HAL with the location of the PBLKs
|
|
//
|
|
while(ProcessorList[pNum] && pNum < ACPI_SUPPORTED_PROCESSORS) {
|
|
|
|
//
|
|
// find the number of processors
|
|
//
|
|
pNum++;
|
|
|
|
}
|
|
|
|
ACPIPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
"NotifyHalWithMachineStates: Number of processors is %d\n",
|
|
pNum
|
|
) );
|
|
|
|
sleepVals = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof(HAL_SLEEP_VAL) * 5,
|
|
ACPI_MISC_POOLTAG
|
|
);
|
|
|
|
if (!sleepVals) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
pReasonMP = NULL;
|
|
|
|
pReasonOverride = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
sizeof(SYSTEM_POWER_STATE_DISABLE_REASON),
|
|
ACPI_MISC_POOLTAG
|
|
);
|
|
|
|
if (!pReasonOverride) {
|
|
ExFreePool(sleepVals);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
pReasonBios = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
sizeof(SYSTEM_POWER_STATE_DISABLE_REASON),
|
|
ACPI_MISC_POOLTAG
|
|
);
|
|
if (!pReasonBios) {
|
|
ExFreePool(pReasonOverride);
|
|
ExFreePool(sleepVals);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(pReasonOverride,sizeof(SYSTEM_POWER_STATE_DISABLE_REASON));
|
|
RtlZeroMemory(pReasonBios,sizeof(SYSTEM_POWER_STATE_DISABLE_REASON));
|
|
pReasonOverride->PowerReasonCode = SPSD_REASON_NONE;
|
|
pReasonBios->PowerReasonCode = SPSD_REASON_NONE;
|
|
|
|
//
|
|
// If there are more than 1 processors (ie: this is an MP machine)
|
|
// and the OverrideMpSleep attribute is set, then we should remember that
|
|
// so that we disallow all S-States other than S0, S4, and S5
|
|
//
|
|
if (AcpiOverrideAttributes & ACPI_OVERRIDE_MP_SLEEP) {
|
|
|
|
overrideMpSleep = TRUE;
|
|
|
|
pReasonMP = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
sizeof(SYSTEM_POWER_STATE_DISABLE_REASON),
|
|
ACPI_MISC_POOLTAG
|
|
);
|
|
if (!pReasonMP) {
|
|
ExFreePool(pReasonBios);
|
|
ExFreePool(pReasonOverride);
|
|
ExFreePool(sleepVals);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(pReasonMP,sizeof(SYSTEM_POWER_STATE_DISABLE_REASON));
|
|
pReasonMP->PowerReasonCode = SPSD_REASON_NONE;
|
|
|
|
}
|
|
|
|
//
|
|
// Remember that the only s-states that we support are S0, S4, and S5,
|
|
// by default
|
|
//
|
|
AcpiSupportedSystemStates =
|
|
(1 << PowerSystemWorking) +
|
|
(1 << PowerSystemHibernate) +
|
|
(1 << PowerSystemShutdown);
|
|
|
|
//
|
|
// Get the values that the HAL needs for sleeping the machine
|
|
// for each sleep state that this machine supports.
|
|
//
|
|
for (systemState = PowerSystemSleeping1, state = 0;
|
|
state < 5;
|
|
systemState++, state++) {
|
|
|
|
if ( ( (systemState == PowerSystemSleeping1) &&
|
|
(AcpiOverrideAttributes & ACPI_OVERRIDE_DISABLE_S1) ) ||
|
|
( (systemState == PowerSystemSleeping2) &&
|
|
(AcpiOverrideAttributes & ACPI_OVERRIDE_DISABLE_S2) ) ||
|
|
( (systemState == PowerSystemSleeping3) &&
|
|
(AcpiOverrideAttributes & ACPI_OVERRIDE_DISABLE_S3) )) {
|
|
|
|
ACPIPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
"ACPI: SleepState %s disabled due to override\n",
|
|
SleepState[state]
|
|
) );
|
|
sleepVals[state].Supported = FALSE;
|
|
|
|
//
|
|
// the value of "state" variable is equivalent to the
|
|
// POWER_STATE_HANDLER_TYPE enum, which is what we want for the
|
|
// logging stuff, not systemState.
|
|
//
|
|
pReasonOverride->AffectedState[state] = TRUE;
|
|
pReasonOverride->PowerReasonCode = SPSD_REASON_BIOSINCOMPATIBLE;
|
|
continue;
|
|
|
|
}
|
|
|
|
status = AMLIGetNameSpaceObject(SleepState[state], NULL, &pnsobj, 0);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
ACPIPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
"ACPI: SleepState %s not supported\n",
|
|
SleepState[state]
|
|
) );
|
|
sleepVals[state].Supported = FALSE;
|
|
|
|
//
|
|
// the value of "state" variable is equivalent to the
|
|
// POWER_STATE_HANDLER_TYPE enum, which is what we want for the
|
|
// logging stuff, not systemState.
|
|
//
|
|
pReasonBios->AffectedState[state] = TRUE;
|
|
pReasonBios->PowerReasonCode = SPSD_REASON_NOBIOSSUPPORT;
|
|
|
|
continue;
|
|
|
|
}
|
|
if (overrideMpSleep && systemState < PowerSystemHibernate) {
|
|
|
|
ACPIPrint( (
|
|
ACPI_PRINT_WARNING,
|
|
"ACPI: SleepState %s not supported due to override\n",
|
|
SleepState[state]
|
|
) );
|
|
sleepVals[state].Supported = FALSE;
|
|
|
|
//
|
|
// the value of "state" variable is equivalent to the
|
|
// POWER_STATE_HANDLER_TYPE enum, which is what we want for the
|
|
// logging stuff, not systemState.
|
|
//
|
|
pReasonMP->AffectedState[state] = TRUE;
|
|
pReasonMP->PowerReasonCode = SPSD_REASON_MPOVERRIDE;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// Remember that we support this state
|
|
//
|
|
AcpiSupportedSystemStates |= (1 << systemState);
|
|
sleepVals[state].Supported = TRUE;
|
|
|
|
//
|
|
// Retrieve the value that will be written into the SLP_TYPa
|
|
// register.
|
|
//
|
|
AMLIEvalPackageElement (pnsobj, 0, &data);
|
|
sleepVals[state].Pm1aVal = (UCHAR)data.uipDataValue;
|
|
AMLIFreeDataBuffs(&data, 1);
|
|
|
|
//
|
|
// Retriece the value that will be written in to the SLp_TYPb
|
|
// register
|
|
//
|
|
AMLIEvalPackageElement (pnsobj, 1, &data);
|
|
sleepVals[state].Pm1bVal = (UCHAR)data.uipDataValue;
|
|
AMLIFreeDataBuffs(&data, 1);
|
|
|
|
}
|
|
|
|
//
|
|
// notify the power manager why we aren't supporting some
|
|
// power states
|
|
//
|
|
PowerLoggingEntry.LoggingType = LOGGING_TYPE_SPSD;
|
|
if (pReasonBios->PowerReasonCode != SPSD_REASON_NONE) {
|
|
PowerLoggingEntry.LoggingEntry = pReasonBios;
|
|
LogStatus = ZwPowerInformation(
|
|
SystemPowerLoggingEntry,
|
|
&PowerLoggingEntry,
|
|
sizeof(PowerLoggingEntry),
|
|
NULL,
|
|
0);
|
|
if (LogStatus != STATUS_SUCCESS) {
|
|
ExFreePool(pReasonBios);
|
|
}
|
|
} else {
|
|
ExFreePool(pReasonBios);
|
|
}
|
|
|
|
if (pReasonOverride->PowerReasonCode != SPSD_REASON_NONE) {
|
|
PowerLoggingEntry.LoggingEntry = pReasonOverride;
|
|
LogStatus = ZwPowerInformation(
|
|
SystemPowerLoggingEntry,
|
|
&PowerLoggingEntry,
|
|
sizeof(PowerLoggingEntry),
|
|
NULL,
|
|
0);
|
|
if (LogStatus != STATUS_SUCCESS) {
|
|
ExFreePool(pReasonOverride);
|
|
}
|
|
} else {
|
|
ExFreePool(pReasonOverride);
|
|
}
|
|
|
|
if (pReasonMP) {
|
|
if (pReasonMP->PowerReasonCode != SPSD_REASON_NONE) {
|
|
PowerLoggingEntry.LoggingEntry = pReasonMP;
|
|
LogStatus = ZwPowerInformation(
|
|
SystemPowerLoggingEntry,
|
|
&PowerLoggingEntry,
|
|
sizeof(PowerLoggingEntry),
|
|
NULL,
|
|
0);
|
|
if (LogStatus != STATUS_SUCCESS) {
|
|
ExFreePool(pReasonMP);
|
|
}
|
|
} else {
|
|
ExFreePool(pReasonMP);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Notify the HAL
|
|
//
|
|
HalAcpiMachineStateInit(NULL, sleepVals, &InterruptModel);
|
|
|
|
ExFreePool(sleepVals);
|
|
|
|
//
|
|
// Notify the namespace with the _PIC val.
|
|
//
|
|
if (InterruptModel > 0) {
|
|
|
|
status = AMLIGetNameSpaceObject(picMethod,NULL,&pnsobj,0);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// The OEM didn't supply a _PIC method. That's OK.
|
|
// We'll assume that IRQ will somehow work without it.
|
|
//
|
|
return status;
|
|
}
|
|
|
|
RtlZeroMemory(&data, sizeof(data));
|
|
data.dwDataType = OBJTYPE_INTDATA;
|
|
data.uipDataValue = InterruptModel;
|
|
|
|
status = AMLIEvalNameSpaceObject(pnsobj, NULL, 1, &data);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Failure to evaluate the _PIC method is not OK.
|
|
//
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_FAILED_PIC_METHOD,
|
|
InterruptModel,
|
|
status,
|
|
(ULONG_PTR) pnsobj
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|