|
|
/*++
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; }
|