774 lines
18 KiB
C
774 lines
18 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:
|
|||
|
|
|||
|
Todd Kjos (HP) (v-tkjos) 1-Jun-1998: Initial port to IA64
|
|||
|
|
|||
|
--*/
|
|||
|
#include "halp.h"
|
|||
|
#include "acpitabl.h"
|
|||
|
#include "xxacpi.h"
|
|||
|
#include "ixsleep.h"
|
|||
|
#include "kddll.h"
|
|||
|
|
|||
|
//
|
|||
|
// Internal functions
|
|||
|
//
|
|||
|
|
|||
|
VOID
|
|||
|
HalpLockedIncrementUlong(
|
|||
|
PULONG SyncVariable
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
HalpReboot (
|
|||
|
VOID
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
HaliAcpiFakeSleep(
|
|||
|
IN PVOID Context,
|
|||
|
IN PENTER_STATE_SYSTEM_HANDLER SystemHandler OPTIONAL,
|
|||
|
IN PVOID SystemContext,
|
|||
|
IN LONG NumberProcessors,
|
|||
|
IN volatile PLONG Number
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
HaliAcpiSleep(
|
|||
|
IN PVOID Context,
|
|||
|
IN PENTER_STATE_SYSTEM_HANDLER SystemHandler OPTIONAL,
|
|||
|
IN PVOID SystemContext,
|
|||
|
IN LONG NumberProcessors,
|
|||
|
IN volatile PLONG Number
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
HalpSetClockBeforeSleep(
|
|||
|
VOID
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
HalpSetClockAfterSleep(
|
|||
|
VOID
|
|||
|
);
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
HalpWakeupTimeElapsed(
|
|||
|
VOID
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
HalpReenableAcpi(
|
|||
|
VOID
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
HalpSetInterruptControllerWakeupState(
|
|||
|
ULONG Context
|
|||
|
);
|
|||
|
|
|||
|
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, HaliAcpiSleep)
|
|||
|
#pragma alloc_text(PAGELK, HaliAcpiFakeSleep)
|
|||
|
#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)
|
|||
|
#endif
|
|||
|
|
|||
|
HAL_WAKEUP_STATE HalpWakeupState;
|
|||
|
ULONG Barrier;
|
|||
|
volatile ULONG HalpSleepSync;
|
|||
|
PKPROCESSOR_STATE HalpHiberProcState;
|
|||
|
|
|||
|
#if DBG
|
|||
|
BOOLEAN HalpFailSleep = FALSE;
|
|||
|
#endif
|
|||
|
|
|||
|
#define PM1_TMR_EN 0x0001
|
|||
|
#define PM1_RTC_EN 0x0400
|
|||
|
#define WAK_STS 0x8000
|
|||
|
|
|||
|
#define HAL_PRIMARY_PROCESSOR 0
|
|||
|
|
|||
|
//
|
|||
|
// For re-enabling the debugger's com port.
|
|||
|
//
|
|||
|
extern PUCHAR KdComPortInUse;
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
HalpAcpiFlushCache(
|
|||
|
)
|
|||
|
{
|
|||
|
HalSweepDcache();
|
|||
|
HalSweepIcache();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
HalpSaveProcessorStateAndWait(
|
|||
|
IN PKPROCESSOR_STATE ProcessorState,
|
|||
|
IN volatile PULONG Count
|
|||
|
)
|
|||
|
/*++
|
|||
|
Rountine description:
|
|||
|
|
|||
|
This function saves the volatile, non-volatile and special register
|
|||
|
state of the current processor.
|
|||
|
|
|||
|
N.B. floating point state is NOT captured.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ProcessorState - Address of processor state record to fill in.
|
|||
|
|
|||
|
pBarrier - Address of a value to use as a lock.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None. This function does not return.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
#if 0
|
|||
|
//
|
|||
|
// Fill in ProcessorState
|
|||
|
//
|
|||
|
|
|||
|
KeSaveStateForHibernate(ProcessorState);
|
|||
|
|
|||
|
//
|
|||
|
// Save return address, not caller's return address.
|
|||
|
//
|
|||
|
|
|||
|
ProcessorState->ContextFrame.StIIP = HalpGetReturnAddress();
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Flush the cache, as the processor may be about to power off.
|
|||
|
//
|
|||
|
//
|
|||
|
HalpAcpiFlushCache();
|
|||
|
|
|||
|
//
|
|||
|
// Singal that this processor has saved its state.
|
|||
|
//
|
|||
|
|
|||
|
HalpLockedIncrementUlong(Count);
|
|||
|
|
|||
|
//
|
|||
|
// Wait for the hibernation file to be written.
|
|||
|
// Processor 0 will zero Barrier when it is
|
|||
|
// finished.
|
|||
|
//
|
|||
|
// N.B. We can't return from this function
|
|||
|
// before the hibernation file is finished
|
|||
|
// because we would be tearing down the very same
|
|||
|
// stack that we will be jumping onto when the
|
|||
|
// processor resumes. But after the hibernation
|
|||
|
// file is written, it doesn't matter, because
|
|||
|
// the stack will be restored from disk.
|
|||
|
//
|
|||
|
|
|||
|
while (*Count != 0);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
HalpAcpiPreSleep(
|
|||
|
SLEEP_STATE_CONTEXT Context
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
none
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
status
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
USHORT pmTimer;
|
|||
|
GEN_ADDR pm1a;
|
|||
|
GEN_ADDR pm1b;
|
|||
|
|
|||
|
pm1a = HalpFixedAcpiDescTable.x_pm1a_evt_blk;
|
|||
|
pm1a.Address.QuadPart += (HalpFixedAcpiDescTable.x_pm1a_evt_blk.BitWidth / 2 / 8); // 2 because we want to cut it in half, 8 because we want to convert bits to bytes
|
|||
|
pm1a.BitWidth = HalpFixedAcpiDescTable.x_pm1a_evt_blk.BitWidth / 2;
|
|||
|
|
|||
|
pm1b = HalpFixedAcpiDescTable.x_pm1b_evt_blk;
|
|||
|
pm1b.Address.QuadPart += (HalpFixedAcpiDescTable.x_pm1b_evt_blk.BitWidth / 2 / 8);
|
|||
|
pm1b.BitWidth = HalpFixedAcpiDescTable.x_pm1b_evt_blk.BitWidth / 2;
|
|||
|
|
|||
|
HalpSleepContext.AsULONG = Context.AsULONG;
|
|||
|
|
|||
|
#if DBG
|
|||
|
if (HalpFailSleep) {
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// If we should have woken up already, don't sleep.
|
|||
|
//
|
|||
|
if (HalpWakeupTimeElapsed()) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If an RTC alarm is set, then enable it and disable
|
|||
|
// periodic interrupts (for profiling.)
|
|||
|
//
|
|||
|
HalpSetClockBeforeSleep();
|
|||
|
|
|||
|
//
|
|||
|
// 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();
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
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 shold wake up the compiler
|
|||
|
//
|
|||
|
|
|||
|
pmTimer = (USHORT)HalpReadGenAddr(&pm1a);
|
|||
|
|
|||
|
if (HalpFixedAcpiDescTable.x_pm1b_evt_blk.Address.QuadPart) {
|
|||
|
|
|||
|
pmTimer |= (USHORT)HalpReadGenAddr(&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 f 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.
|
|||
|
//
|
|||
|
|
|||
|
HalpWriteGenAddr(&pm1a, pmTimer);
|
|||
|
|
|||
|
if (HalpFixedAcpiDescTable.x_pm1b_evt_blk.Address.QuadPart) {
|
|||
|
|
|||
|
HalpWriteGenAddr(&pm1b, pmTimer);
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
HalpAcpiPostSleep(
|
|||
|
ULONG Context
|
|||
|
)
|
|||
|
{
|
|||
|
USHORT pmTimer;
|
|||
|
GEN_ADDR pm1a;
|
|||
|
GEN_ADDR pm1b;
|
|||
|
|
|||
|
pm1a = HalpFixedAcpiDescTable.x_pm1a_evt_blk;
|
|||
|
pm1a.Address.QuadPart += (HalpFixedAcpiDescTable.pm1_evt_len / 2);
|
|||
|
|
|||
|
pm1b = HalpFixedAcpiDescTable.x_pm1b_evt_blk;
|
|||
|
pm1b.Address.QuadPart += (HalpFixedAcpiDescTable.pm1_evt_len / 2);
|
|||
|
|
|||
|
//
|
|||
|
// Read te currently set PM1 Enable bits.
|
|||
|
//
|
|||
|
|
|||
|
pmTimer = (USHORT)HalpReadGenAddr(&pm1a);
|
|||
|
|
|||
|
if (HalpFixedAcpiDescTable.x_pm1b_evt_blk.Address.QuadPart) {
|
|||
|
|
|||
|
pmTimer |= (USHORT)HalpReadGenAddr(&pm1b);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the timer enable bit. Clear the RTC enable bit.
|
|||
|
//
|
|||
|
|
|||
|
pmTimer &= ~PM1_RTC_EN;
|
|||
|
|
|||
|
//
|
|||
|
// Write it back the new PM1 Enable bits
|
|||
|
//
|
|||
|
|
|||
|
HalpWriteGenAddr(&pm1a, pmTimer);
|
|||
|
|
|||
|
if (HalpFixedAcpiDescTable.x_pm1b_evt_blk.Address.QuadPart) {
|
|||
|
|
|||
|
HalpWriteGenAddr(&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();
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Enable all GPEs, not just the wake ones
|
|||
|
//
|
|||
|
|
|||
|
AcpiEnableDisableGPEvents(TRUE);
|
|||
|
|
|||
|
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 (wakeupTime.QuadPart < 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:
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
if (Enable) {
|
|||
|
HalpWakeupState.GeneralWakeupEnable = TRUE;
|
|||
|
} else {
|
|||
|
HalpWakeupState.GeneralWakeupEnable = FALSE;
|
|||
|
HalpWakeupState.RtcWakeupEnable = FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
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);
|
|||
|
}
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is a stub to allow us to perform device powerdown
|
|||
|
testing on IA64 machines before they actually support
|
|||
|
real sleep states.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
<standard sleep handler args>
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_NOT_SUPPORTED
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
HaliAcpiFakeSleep(
|
|||
|
IN PVOID Context,
|
|||
|
IN PENTER_STATE_SYSTEM_HANDLER SystemHandler OPTIONAL,
|
|||
|
IN PVOID SystemContext,
|
|||
|
IN LONG NumberProcessors,
|
|||
|
IN volatile PLONG Number
|
|||
|
)
|
|||
|
{
|
|||
|
return STATUS_NOT_SUPPORTED;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
HaliAcpiSleep(
|
|||
|
IN PVOID Context,
|
|||
|
IN PENTER_STATE_SYSTEM_HANDLER SystemHandler OPTIONAL,
|
|||
|
IN PVOID SystemContext,
|
|||
|
IN LONG NumberProcessors,
|
|||
|
IN volatile PLONG Number
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
At some point in time this function will be called to put PCs into a sleep
|
|||
|
state. It saves motherboard state and then bails out. For now this function
|
|||
|
is only called to implement S5 on Itanium.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status = STATUS_SUCCESS;
|
|||
|
KIRQL OldIrql;
|
|||
|
SLEEP_STATE_CONTEXT SleepContext;
|
|||
|
USHORT SlpTypA, SlpTypB, Pm1Control;
|
|||
|
PKPROCESSOR_STATE CurrentProcessorState;
|
|||
|
GEN_ADDR Pm1bEvt;
|
|||
|
PKPRCB Prcb;
|
|||
|
|
|||
|
//
|
|||
|
// initial setup.
|
|||
|
//
|
|||
|
HalpDisableInterrupts();
|
|||
|
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
|
|||
|
SleepContext.AsULONG = (ULONG) (((ULONGLONG) Context) & 0xffffffff);
|
|||
|
|
|||
|
SlpTypA = (USHORT)HalpReadGenAddr(&HalpFixedAcpiDescTable.x_pm1a_ctrl_blk);
|
|||
|
if (HalpFixedAcpiDescTable.x_pm1b_ctrl_blk.Address.QuadPart) {
|
|||
|
SlpTypB = (USHORT)HalpReadGenAddr(&HalpFixedAcpiDescTable.x_pm1b_ctrl_blk);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If it is not processor 0, then goto wait loop.
|
|||
|
//
|
|||
|
Prcb = PCR->Prcb;
|
|||
|
if (Prcb->Number != 0) {
|
|||
|
//
|
|||
|
// Get processor number, get size of proc state and generate an index
|
|||
|
// into HalpHiberProcState.
|
|||
|
//
|
|||
|
|
|||
|
CurrentProcessorState = HalpHiberProcState + Prcb->Number;
|
|||
|
HalpSaveProcessorStateAndWait(CurrentProcessorState,
|
|||
|
(PULONG) &HalpSleepSync);
|
|||
|
|
|||
|
//
|
|||
|
// Wait for next phase
|
|||
|
//
|
|||
|
|
|||
|
while (HalpSleepSync != 0); // wait for barrier to move
|
|||
|
|
|||
|
} else { // processor 0
|
|||
|
Barrier = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the other processors have saved their
|
|||
|
// state and begun to spin.
|
|||
|
//
|
|||
|
|
|||
|
HalpLockedIncrementUlong((PULONG) &HalpSleepSync);
|
|||
|
while (NumberProcessors != (LONG) HalpSleepSync);
|
|||
|
|
|||
|
//
|
|||
|
// Take care of chores (RTC, interrupt controller, etc.)
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// The hal has all of it's state saved into ram and is ready
|
|||
|
// for the power down. If there's a system state handler give
|
|||
|
// it a shot
|
|||
|
//
|
|||
|
|
|||
|
if (SystemHandler) {
|
|||
|
Status = (*SystemHandler)(SystemContext);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
HalpReenableAcpi();
|
|||
|
|
|||
|
//
|
|||
|
// Restore the SLP_TYP registers. (So that embedded controllers
|
|||
|
// and BIOSes can be sure that we think the machine is awake.)
|
|||
|
//
|
|||
|
HalpWriteGenAddr (&HalpFixedAcpiDescTable.x_pm1a_ctrl_blk, SlpTypA);
|
|||
|
if (HalpFixedAcpiDescTable.x_pm1b_ctrl_blk.Address.QuadPart) {
|
|||
|
HalpWriteGenAddr(&HalpFixedAcpiDescTable.x_pm1b_ctrl_blk, SlpTypB);
|
|||
|
}
|
|||
|
|
|||
|
HalpAcpiPostSleep(SleepContext.AsULONG);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
if (HalpAcpiPreSleep(SleepContext)) {
|
|||
|
|
|||
|
//
|
|||
|
// If we will not be losing processor state, go to sleep.
|
|||
|
//
|
|||
|
|
|||
|
if ((SleepContext.bits.Flags & SLEEP_STATE_FIRMWARE_RESTART) == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// Reset WAK_STS
|
|||
|
//
|
|||
|
|
|||
|
HalpWriteGenAddr(&HalpFixedAcpiDescTable.x_pm1a_evt_blk, (USHORT) WAK_STS);
|
|||
|
if (HalpFixedAcpiDescTable.x_pm1b_evt_blk.Address.QuadPart) {
|
|||
|
HalpWriteGenAddr(&HalpFixedAcpiDescTable.x_pm1b_evt_blk, (USHORT) WAK_STS);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Flush the caches if necessary
|
|||
|
//
|
|||
|
|
|||
|
if (SleepContext.bits.Flags & SLEEP_STATE_FLUSH_CACHE) {
|
|||
|
HalpAcpiFlushCache();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Issue SLP commands to PM1a_CNT and PM1b_CNT
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// nibble 0 is 1a sleep type, put it in position and enable sleep.
|
|||
|
// preserve some bits in Pm1aCnt.
|
|||
|
//
|
|||
|
Pm1Control = (USHORT)HalpReadGenAddr(&HalpFixedAcpiDescTable.x_pm1a_ctrl_blk);
|
|||
|
Pm1Control = (USHORT) ((Pm1Control & CTL_PRESERVE) |
|
|||
|
(SleepContext.bits.Pm1aVal << SLP_TYP_SHIFT) | SLP_EN);
|
|||
|
HalpWriteGenAddr (&HalpFixedAcpiDescTable.x_pm1a_ctrl_blk, Pm1Control);
|
|||
|
|
|||
|
//
|
|||
|
// nibble 1 is 1b sleep type, put it in position and enable sleep
|
|||
|
// preserve some bits in Pm1bCnt.
|
|||
|
//
|
|||
|
if (HalpFixedAcpiDescTable.x_pm1b_ctrl_blk.Address.QuadPart) {
|
|||
|
Pm1Control = (USHORT)HalpReadGenAddr(&HalpFixedAcpiDescTable.x_pm1b_ctrl_blk);
|
|||
|
Pm1Control = (USHORT) ((Pm1Control & CTL_PRESERVE) |
|
|||
|
(SleepContext.bits.Pm1bVal << SLP_TYP_SHIFT) | SLP_EN);
|
|||
|
HalpWriteGenAddr(&HalpFixedAcpiDescTable.x_pm1b_ctrl_blk, Pm1Control);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Wait for sleep to be over
|
|||
|
//
|
|||
|
|
|||
|
if (HalpFixedAcpiDescTable.x_pm1b_evt_blk.Address.QuadPart) {
|
|||
|
Pm1bEvt = HalpFixedAcpiDescTable.x_pm1b_evt_blk;
|
|||
|
} else {
|
|||
|
Pm1bEvt = HalpFixedAcpiDescTable.x_pm1a_evt_blk;
|
|||
|
}
|
|||
|
|
|||
|
while ( ((HalpReadGenAddr(&HalpFixedAcpiDescTable.x_pm1a_evt_blk) & WAK_STS) == 0) &&
|
|||
|
((HalpReadGenAddr(&Pm1bEvt) & WAK_STS) == 0) );
|
|||
|
|
|||
|
} else {
|
|||
|
CurrentProcessorState = HalpHiberProcState + Prcb->Number;
|
|||
|
// HalpSetupStateForResume(CurrentProcessorState);
|
|||
|
}
|
|||
|
|
|||
|
} // HalpAcpiPreSleep() == 0
|
|||
|
} // SystemHandler == 0
|
|||
|
|
|||
|
//
|
|||
|
// Notify other processor of completion
|
|||
|
//
|
|||
|
|
|||
|
HalpSleepSync = 0;
|
|||
|
|
|||
|
} // processor 0
|
|||
|
|
|||
|
//
|
|||
|
// Restore each processor's APIC state.
|
|||
|
//
|
|||
|
// HalpPostSleepMP<NumberProc, Barrier>;
|
|||
|
|
|||
|
//
|
|||
|
// Restore caller's IRQL.
|
|||
|
//
|
|||
|
KeLowerIrql(OldIrql);
|
|||
|
|
|||
|
//
|
|||
|
// Exit.
|
|||
|
//
|
|||
|
// HalpSleepSync = 0;
|
|||
|
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|