739 lines
15 KiB
C
739 lines
15 KiB
C
/*++
|
||
|
||
Copyright (c) 1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
pmsleep.c
|
||
|
||
Abstract:
|
||
|
||
This file provides the code that changes the system from
|
||
the ACPI S0 (running) state to any one of the sleep states.
|
||
|
||
Author:
|
||
|
||
Jake Oshins (jakeo) Feb. 11, 1997
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
#include "halp.h"
|
||
#include "acpitabl.h"
|
||
#include "xxacpi.h"
|
||
#include "kddll.h"
|
||
#include "ixsleep.h"
|
||
|
||
//
|
||
// Internal functions
|
||
//
|
||
|
||
NTSTATUS
|
||
HalpAcpiSleep(
|
||
IN PVOID Context,
|
||
IN LONG NumberProcessors,
|
||
IN volatile PLONG Number
|
||
);
|
||
|
||
VOID
|
||
HalpSetClockBeforeSleep(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
HalpSetClockAfterSleep(
|
||
VOID
|
||
);
|
||
|
||
BOOLEAN
|
||
HalpWakeupTimeElapsed(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
HalpFreeTiledCR3 (
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
HalpReenableAcpi(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
HalpPiix4Detect(
|
||
BOOLEAN DuringBoot
|
||
);
|
||
|
||
typedef struct _ERESOURCE {
|
||
LIST_ENTRY SystemResourcesList;
|
||
PVOID OwnerTable;
|
||
SHORT ActiveCount;
|
||
USHORT Flag;
|
||
PKSEMAPHORE SharedWaiters;
|
||
PKEVENT ExclusiveWaiters;
|
||
LIST_ENTRY OwnerThreads[2];
|
||
ULONG ContentionCount;
|
||
USHORT NumberOfSharedWaiters;
|
||
USHORT NumberOfExclusiveWaiters;
|
||
union {
|
||
PVOID Address;
|
||
ULONG CreatorBackTraceIndex;
|
||
};
|
||
|
||
KSPIN_LOCK SpinLock;
|
||
} ERESOURCE, *PERESOURCE;
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGELK, HalpAcpiPreSleep)
|
||
#pragma alloc_text(PAGELK, HalpAcpiPostSleep)
|
||
#pragma alloc_text(PAGELK, HalpWakeupTimeElapsed)
|
||
#pragma alloc_text(PAGELK, HalpReenableAcpi)
|
||
#pragma alloc_text(PAGELK, HaliSetWakeEnable)
|
||
#pragma alloc_text(PAGELK, HaliSetWakeAlarm)
|
||
#pragma alloc_text(PAGELK, HalpMapNvsArea)
|
||
#pragma alloc_text(PAGELK, HalpFreeNvsBuffers)
|
||
#endif
|
||
|
||
HAL_WAKEUP_STATE HalpWakeupState;
|
||
|
||
#if DBG
|
||
BOOLEAN HalpFailSleep = FALSE;
|
||
#endif
|
||
|
||
#define PM1_TMR_EN 0x0001
|
||
#define PM1_RTC_EN 0x0400
|
||
|
||
//
|
||
// For re-enabling the debugger's com port.
|
||
//
|
||
extern PUCHAR KdComPortInUse;
|
||
|
||
extern PACPI_BIOS_MULTI_NODE HalpAcpiMultiNode;
|
||
extern PUCHAR HalpAcpiNvsData;
|
||
extern PVOID *HalpNvsVirtualAddress;
|
||
|
||
|
||
BOOLEAN
|
||
HalpAcpiPreSleep(
|
||
SLEEP_STATE_CONTEXT Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
none
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
{
|
||
USHORT pmTimer;
|
||
PUSHORT pm1a;
|
||
PUSHORT pm1b;
|
||
PUSHORT pm1astatus;
|
||
PUSHORT pm1bstatus;
|
||
BOOLEAN wakeupElapsed;
|
||
|
||
pm1astatus = (PUSHORT) HalpFixedAcpiDescTable.pm1a_evt_blk_io_port;
|
||
pm1bstatus = (PUSHORT) HalpFixedAcpiDescTable.pm1b_evt_blk_io_port;
|
||
pm1a = (PUSHORT)(HalpFixedAcpiDescTable.pm1a_evt_blk_io_port +
|
||
(HalpFixedAcpiDescTable.pm1_evt_len / 2));
|
||
|
||
pm1b = (PUSHORT)(HalpFixedAcpiDescTable.pm1b_evt_blk_io_port +
|
||
(HalpFixedAcpiDescTable.pm1_evt_len / 2));
|
||
|
||
HalpSleepContext.AsULONG = Context.AsULONG;
|
||
|
||
#if DBG
|
||
if (HalpFailSleep) {
|
||
return FALSE;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// If we should have woken up already, don't sleep.
|
||
//
|
||
|
||
wakeupElapsed = HalpWakeupTimeElapsed();
|
||
|
||
//
|
||
// If an RTC alarm is set, then enable it and disable
|
||
// periodic interrupts (for profiling.)
|
||
//
|
||
if (!wakeupElapsed) {
|
||
HalpSetClockBeforeSleep();
|
||
}
|
||
|
||
//
|
||
// Save the (A)PIC for any sleep state, as we need to play
|
||
// with it on the way back up again.
|
||
//
|
||
HalpSaveInterruptControllerState();
|
||
if (Context.bits.Flags & SLEEP_STATE_SAVE_MOTHERBOARD) {
|
||
|
||
HalpSaveDmaControllerState();
|
||
HalpSaveTimerState();
|
||
|
||
}
|
||
|
||
//
|
||
// We need to make sure that the PM Timer is disabled from this
|
||
// point onward. We also need to make that the RTC Enable is only
|
||
// enabled if the RTC should wake up the computer
|
||
//
|
||
pmTimer = READ_PORT_USHORT(pm1a);
|
||
if (HalpFixedAcpiDescTable.pm1b_evt_blk_io_port) {
|
||
|
||
pmTimer |= READ_PORT_USHORT(pm1b);
|
||
|
||
}
|
||
|
||
//
|
||
// Clear the timer enable bit.
|
||
//
|
||
pmTimer &= ~PM1_TMR_EN;
|
||
|
||
//
|
||
// Check to see if we the machine supports RTC Wake in Fixed Feature
|
||
// space. Some machines implement RTC support via control methods
|
||
//
|
||
if ( !(HalpFixedAcpiDescTable.flags & RTC_WAKE_GENERIC) ) {
|
||
|
||
//
|
||
// Check to see if we need to disable/enable the RTC alarm
|
||
//
|
||
if (!HalpWakeupState.RtcWakeupEnable) {
|
||
|
||
pmTimer &= ~PM1_RTC_EN;
|
||
|
||
} else {
|
||
|
||
pmTimer |= PM1_RTC_EN;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Write it back into the hardware.
|
||
//
|
||
WRITE_PORT_USHORT(pm1a, pmTimer);
|
||
if (HalpFixedAcpiDescTable.pm1b_evt_blk_io_port) {
|
||
|
||
WRITE_PORT_USHORT(pm1b, pmTimer);
|
||
|
||
}
|
||
|
||
//
|
||
// At this point, we should be running with interrupts disabled and
|
||
// the TMR_EN bit cleared. This is a good place to clear the PM1 Status
|
||
// Register
|
||
//
|
||
pmTimer = READ_PORT_USHORT( pm1astatus );
|
||
if (HalpFixedAcpiDescTable.pm1b_evt_blk_io_port) {
|
||
|
||
pmTimer |= READ_PORT_USHORT( pm1bstatus );
|
||
|
||
}
|
||
WRITE_PORT_USHORT( pm1astatus, pmTimer );
|
||
if (HalpFixedAcpiDescTable.pm1b_evt_blk_io_port) {
|
||
|
||
WRITE_PORT_USHORT( pm1bstatus, pmTimer );
|
||
|
||
}
|
||
|
||
//
|
||
// Check to see if we need to disable all wakeup events.
|
||
//
|
||
|
||
if (!HalpWakeupState.GeneralWakeupEnable) {
|
||
|
||
AcpiEnableDisableGPEvents(FALSE);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Only call this before going to sleep --- waking up should
|
||
// reset the GPEs to the 'proper' value
|
||
//
|
||
AcpiGpeEnableWakeEvents();
|
||
|
||
}
|
||
|
||
HalpPreserveNvsArea();
|
||
|
||
if (wakeupElapsed) {
|
||
return FALSE;
|
||
} else {
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
BOOLEAN
|
||
HalpAcpiPostSleep(
|
||
ULONG Context
|
||
)
|
||
{
|
||
USHORT pmTimer;
|
||
PUSHORT pm1a;
|
||
PUSHORT pm1b;
|
||
BOOLEAN ProfileInterruptEnabled;
|
||
|
||
#ifdef PICACPI
|
||
extern ULONG HalpProfilingStopped;
|
||
|
||
ProfileInterruptEnabled = (HalpProfilingStopped == 0);
|
||
#else
|
||
extern ULONG HalpProfileRunning;
|
||
|
||
ProfileInterruptEnabled = (HalpProfileRunning == 1);
|
||
#endif
|
||
|
||
pm1a = (PUSHORT)(HalpFixedAcpiDescTable.pm1a_evt_blk_io_port +
|
||
(HalpFixedAcpiDescTable.pm1_evt_len / 2));
|
||
|
||
pm1b = (PUSHORT)(HalpFixedAcpiDescTable.pm1b_evt_blk_io_port +
|
||
(HalpFixedAcpiDescTable.pm1_evt_len / 2));
|
||
|
||
|
||
//
|
||
// Read the currently set PM1 Enable bits
|
||
//
|
||
pmTimer = READ_PORT_USHORT(pm1a);
|
||
if (HalpFixedAcpiDescTable.pm1b_evt_blk_io_port) {
|
||
|
||
pmTimer |= READ_PORT_USHORT(pm1b);
|
||
|
||
}
|
||
|
||
//
|
||
// Set the timer enable bit. Clear the RTC enable bit
|
||
//
|
||
pmTimer |= PM1_TMR_EN;
|
||
pmTimer &= ~PM1_RTC_EN;
|
||
|
||
//
|
||
// Write back the new PM1 Enable bits
|
||
//
|
||
WRITE_PORT_USHORT(pm1a, pmTimer);
|
||
if (HalpFixedAcpiDescTable.pm1b_evt_blk_io_port) {
|
||
|
||
WRITE_PORT_USHORT(pm1b, pmTimer);
|
||
|
||
}
|
||
|
||
//
|
||
// Unset the RTC alarm and re-enable periodic interrupts.
|
||
//
|
||
HalpSetClockAfterSleep();
|
||
|
||
HalpWakeupState.RtcWakeupEnable = FALSE;
|
||
|
||
*((PULONG)HalpWakeVector) = 0;
|
||
|
||
HalpSetInterruptControllerWakeupState(Context);
|
||
|
||
if (HalpSleepContext.bits.Flags & SLEEP_STATE_SAVE_MOTHERBOARD) {
|
||
|
||
//
|
||
// If Kd was in use, then invalidate it. It will re-sync itself.
|
||
//
|
||
if (KdComPortInUse) {
|
||
KdRestore(TRUE);
|
||
}
|
||
|
||
HalpRestoreDmaControllerState();
|
||
|
||
HalpRestoreTimerState();
|
||
|
||
}
|
||
|
||
HalpPiix4Detect(FALSE);
|
||
|
||
//
|
||
// Enable all GPEs, not just the wake ones
|
||
//
|
||
|
||
AcpiEnableDisableGPEvents(TRUE);
|
||
|
||
HalpRestoreNvsArea();
|
||
|
||
HalpResetSBF();
|
||
|
||
//
|
||
// If we were profiling before, fire up the profile interrupt
|
||
//
|
||
if (ProfileInterruptEnabled) {
|
||
HalStartProfileInterrupt(0);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
HalpWakeupTimeElapsed(
|
||
VOID
|
||
)
|
||
{
|
||
LARGE_INTEGER wakeupTime, currentTime;
|
||
TIME_FIELDS currentTimeFields;
|
||
|
||
//
|
||
// Check to see if a wakeup timer has already expired.
|
||
//
|
||
if (HalpWakeupState.RtcWakeupEnable) {
|
||
|
||
HalQueryRealTimeClock(¤tTimeFields);
|
||
|
||
RtlTimeFieldsToTime(¤tTimeFields,
|
||
¤tTime);
|
||
|
||
RtlTimeFieldsToTime(&HalpWakeupState.RtcWakeupTime,
|
||
&wakeupTime);
|
||
|
||
if ((ULONGLONG)wakeupTime.QuadPart <
|
||
(ULONGLONG)currentTime.QuadPart) {
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
NTSTATUS
|
||
HaliSetWakeAlarm (
|
||
IN ULONGLONG WakeSystemTime,
|
||
IN PTIME_FIELDS WakeTimeFields OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets the real-time clock's alarm to go
|
||
off at a specified time in the future and programs
|
||
the ACPI chipset so that this wakes the computer.
|
||
|
||
Arguments:
|
||
|
||
WakeSystemTime - amount of time that passes before we wake
|
||
WakeTimeFields - time to wake broken down into TIME_FIELDS
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
{
|
||
if (WakeSystemTime == 0) {
|
||
|
||
HalpWakeupState.RtcWakeupEnable = FALSE;
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
ASSERT( WakeTimeFields );
|
||
HalpWakeupState.RtcWakeupEnable = TRUE;
|
||
HalpWakeupState.RtcWakeupTime = *WakeTimeFields;
|
||
return HalpSetWakeAlarm(WakeSystemTime,
|
||
WakeTimeFields);
|
||
}
|
||
|
||
VOID
|
||
HaliSetWakeEnable(
|
||
IN BOOLEAN Enable
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to set the policy for waking up.
|
||
As we go to sleep, the global HalpWakeupState will be
|
||
read and the hardware set accordingly.
|
||
|
||
Arguments:
|
||
|
||
Enable - true or false
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// Always clear the RTC wake --- we expect that someone will
|
||
// set the alarm after they call this function
|
||
//
|
||
HalpWakeupState.RtcWakeupEnable = FALSE;
|
||
|
||
//
|
||
// Toggle the generate wake up bit
|
||
//
|
||
HalpWakeupState.GeneralWakeupEnable = Enable;
|
||
}
|
||
|
||
VOID
|
||
HalpReenableAcpi(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This calls into the ACPI driver to switch back into ACPI mode,
|
||
presumably after S4 and sets the ACPI registers that the HAL
|
||
controls.
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
// TEMPTEMP?
|
||
HalpInitializeClock();
|
||
|
||
AcpiInitEnableAcpi(TRUE);
|
||
AcpiEnableDisableGPEvents(TRUE);
|
||
}
|
||
|
||
VOID
|
||
HalpMapNvsArea(
|
||
VOID
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
ULONG i, bufferSize, bufferOffset, nodeCount;
|
||
|
||
PAGED_CODE();
|
||
|
||
status = HalpAcpiFindRsdt(&HalpAcpiMultiNode);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return;
|
||
}
|
||
|
||
if (HalpAcpiMultiNode->Count == 0) {
|
||
|
||
//
|
||
// There's no work to do here.
|
||
//
|
||
|
||
goto HalpMapNvsError;
|
||
}
|
||
|
||
//
|
||
// Find total size of the buffer we need.
|
||
//
|
||
|
||
bufferSize = 0;
|
||
nodeCount = 0;
|
||
|
||
for (i = 0; i < HalpAcpiMultiNode->Count; i++) {
|
||
|
||
if (HalpAcpiMultiNode->E820Entry[i].Type == AcpiAddressRangeNVS) {
|
||
|
||
ASSERT(HalpAcpiMultiNode->E820Entry[i].Length.HighPart == 0);
|
||
|
||
bufferSize += HalpAcpiMultiNode->E820Entry[i].Length.LowPart;
|
||
nodeCount++;
|
||
}
|
||
}
|
||
|
||
if (bufferSize == 0) {
|
||
|
||
//
|
||
// There's no work to do here.
|
||
//
|
||
|
||
goto HalpMapNvsError;
|
||
}
|
||
|
||
#if DBG
|
||
if (bufferSize > (20 * PAGE_SIZE)) {
|
||
DbgPrint("HALACPI: The BIOS wants the OS to preserve %x bytes\n", bufferSize);
|
||
}
|
||
#endif
|
||
|
||
HalpAcpiNvsData = ExAllocatePoolWithTag(NonPagedPool,
|
||
bufferSize,
|
||
'AlaH');
|
||
|
||
if (!HalpAcpiNvsData) {
|
||
|
||
DbgPrint("HALACPI: The BIOS's non-volatile data will not be preserved\n");
|
||
goto HalpMapNvsError;
|
||
}
|
||
|
||
HalpNvsVirtualAddress = ExAllocatePoolWithTag(NonPagedPool,
|
||
(nodeCount + 1) * sizeof(PVOID),
|
||
'AlaH');
|
||
|
||
if (!HalpNvsVirtualAddress) {
|
||
goto HalpMapNvsError;
|
||
}
|
||
|
||
|
||
//
|
||
// Make a mapping for each run.
|
||
//
|
||
|
||
bufferOffset = 0;
|
||
nodeCount = 0;
|
||
|
||
for (i = 0; i < HalpAcpiMultiNode->Count; i++) {
|
||
|
||
if (HalpAcpiMultiNode->E820Entry[i].Type == AcpiAddressRangeNVS) {
|
||
|
||
HalpNvsVirtualAddress[nodeCount] =
|
||
MmMapIoSpace(HalpAcpiMultiNode->E820Entry[i].Base,
|
||
HalpAcpiMultiNode->E820Entry[i].Length.LowPart,
|
||
TRUE);
|
||
|
||
ASSERT(HalpNvsVirtualAddress[nodeCount]);
|
||
|
||
nodeCount++;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Mark the end.
|
||
//
|
||
|
||
HalpNvsVirtualAddress[nodeCount] = NULL;
|
||
|
||
return;
|
||
|
||
HalpMapNvsError:
|
||
|
||
if (HalpAcpiMultiNode) ExFreePool(HalpAcpiMultiNode);
|
||
if (HalpNvsVirtualAddress) ExFreePool(HalpNvsVirtualAddress);
|
||
if (HalpAcpiNvsData) ExFreePool(HalpAcpiNvsData);
|
||
|
||
HalpAcpiMultiNode = NULL;
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
HalpPreserveNvsArea(
|
||
VOID
|
||
)
|
||
{
|
||
ULONG i, dataOffset = 0, nodeCount = 0;
|
||
|
||
if (!HalpAcpiMultiNode) {
|
||
|
||
//
|
||
// Either there was nothing to save or there
|
||
// was a fatal error.
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
for (i = 0; i < HalpAcpiMultiNode->Count; i++) {
|
||
|
||
if (HalpAcpiMultiNode->E820Entry[i].Type == AcpiAddressRangeNVS) {
|
||
|
||
//
|
||
// Copy from BIOS memory to temporary buffer.
|
||
//
|
||
|
||
RtlCopyMemory(HalpAcpiNvsData + dataOffset,
|
||
HalpNvsVirtualAddress[nodeCount],
|
||
HalpAcpiMultiNode->E820Entry[i].Length.LowPart);
|
||
|
||
nodeCount++;
|
||
dataOffset += HalpAcpiMultiNode->E820Entry[i].Length.LowPart;
|
||
}
|
||
}
|
||
}
|
||
|
||
VOID
|
||
HalpRestoreNvsArea(
|
||
VOID
|
||
)
|
||
{
|
||
ULONG i, dataOffset = 0, nodeCount = 0;
|
||
|
||
if (!HalpAcpiMultiNode) {
|
||
|
||
//
|
||
// Either there was nothing to save or there
|
||
// was a fatal error.
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
for (i = 0; i < HalpAcpiMultiNode->Count; i++) {
|
||
|
||
if (HalpAcpiMultiNode->E820Entry[i].Type == AcpiAddressRangeNVS) {
|
||
|
||
//
|
||
// Copy from temporary buffer to BIOS area.
|
||
//
|
||
|
||
RtlCopyMemory(HalpNvsVirtualAddress[nodeCount],
|
||
HalpAcpiNvsData + dataOffset,
|
||
HalpAcpiMultiNode->E820Entry[i].Length.LowPart);
|
||
|
||
nodeCount++;
|
||
dataOffset += HalpAcpiMultiNode->E820Entry[i].Length.LowPart;
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
VOID
|
||
HalpFreeNvsBuffers(
|
||
VOID
|
||
)
|
||
{
|
||
ULONG i, nodeCount = 0;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (!HalpAcpiMultiNode) {
|
||
|
||
//
|
||
// Either there was nothing to save or there
|
||
// was a fatal error.
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
for (i = 0; i < HalpAcpiMultiNode->Count; i++) {
|
||
|
||
if (HalpAcpiMultiNode->E820Entry[i].Type == AcpiAddressRangeNVS) {
|
||
|
||
//
|
||
// Give back all the PTEs that we took earlier
|
||
//
|
||
|
||
MmUnmapIoSpace(HalpNvsVirtualAddress[nodeCount],
|
||
HalpAcpiMultiNode->E820Entry[i].Length.LowPart);
|
||
|
||
nodeCount++;
|
||
}
|
||
}
|
||
|
||
ASSERT(HalpAcpiMultiNode);
|
||
ASSERT(HalpNvsVirtualAddress);
|
||
ASSERT(HalpAcpiNvsData);
|
||
|
||
ExFreePool(HalpAcpiMultiNode);
|
||
ExFreePool(HalpNvsVirtualAddress);
|
||
ExFreePool(HalpAcpiNvsData);
|
||
|
||
HalpAcpiMultiNode = NULL;
|
||
HalpNvsVirtualAddress = NULL;
|
||
HalpAcpiNvsData = NULL;
|
||
}
|