|
|
/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
sys.c
Abstract:
This module interfaces to the system power state handler functions
Author:
Ken Reneris (kenr) 17-Jan-1997
Revision History:
--*/
#include "pop.h"
#include <inbv.h>
#include <stdio.h>
#if defined(i386)
VOID KeRestoreProcessorSpecificFeatures( VOID );
VOID KePrepareToLoseProcessorSpecificState( VOID );
__inline LONGLONG POP_GET_TICK_COUNT( VOID ) { _asm _emit 0x0f _asm _emit 0x31 } #endif
//
// Internal shared context structure used to coordinate
// invoking a power state handler
//
typedef struct { PPOWER_STATE_HANDLER Handler; PENTER_STATE_SYSTEM_HANDLER SystemHandler; PVOID SystemContext; PPOP_HIBER_CONTEXT HiberContext; PPOWER_STATE_NOTIFY_HANDLER NotifyHandler; POWER_STATE_HANDLER_TYPE NotifyState; BOOLEAN NotifyType; ULONG NumberProcessors; volatile ULONG TargetCount; volatile ULONG State; LONG HandlerBarrier; } POP_SYS_CONTEXT, *PPOP_SYS_CONTEXT;
typedef struct { ULONG LastState; BOOLEAN InterruptEnable; KIRQL Irql; BOOLEAN FloatSaved; KFLOATING_SAVE FloatSave; NTSTATUS Status; } POP_LOCAL_CONTEXT, *PPOP_LOCAL_CONTEXT;
#define POP_SH_UNINITIALIZED 0
#define POP_SH_COLLECTING_PROCESSORS 1
#define POP_SH_SAVE_CONTEXT 2
#define POP_SH_GET_STACKS 3
#define POP_SH_DISABLE_INTERRUPTS 4
#define POP_SH_INVOKE_HANDLER 5
#define POP_SH_INVOKE_NOTIFY_HANDLER 6
#define POP_SH_RESTORE_INTERRUPTS 7
#define POP_SH_RESTORE_CONTEXT 8
#define POP_SH_COMPLETE 9
extern ULONG MmAvailablePages; BOOLEAN PopFailedHibernationAttempt = FALSE; // we tried to hibernate and failed.
WCHAR PopHibernationErrorSubtstitionString[128];
//
// Internal prototypes
//
NTSTATUS PopInvokeSystemStateHandler ( IN POWER_STATE_HANDLER_TYPE Type, IN PVOID Memory );
VOID PopIssueNextState ( IN PPOP_SYS_CONTEXT Context, IN PPOP_LOCAL_CONTEXT LocalContext, IN ULONG NextState );
VOID PopHandleNextState ( IN PPOP_SYS_CONTEXT Context, IN PPOP_LOCAL_CONTEXT LocalContext );
VOID PopInvokeStateHandlerTargetProcessor ( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 );
NTSTATUS PopShutdownHandler ( IN PVOID Context, IN PENTER_STATE_SYSTEM_HANDLER SystemHandler OPTIONAL, IN PVOID SystemContext, IN LONG NumberProcessors, IN volatile PLONG Number );
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGELK, PopShutdownSystem)
#pragma alloc_text(PAGELK, PopSleepSystem)
#pragma alloc_text(PAGELK, PopInvokeSystemStateHandler)
#pragma alloc_text(PAGELK, PopInvokeStateHandlerTargetProcessor)
#pragma alloc_text(PAGELK, PopIssueNextState)
#pragma alloc_text(PAGELK, PopHandleNextState)
#pragma alloc_text(PAGELK, PopShutdownHandler)
#endif
VOID PopShutdownSystem ( IN POWER_ACTION SystemAction ) /*++
Routine Description:
Routine to implement a Shutdown style power actions
Arguments:
SystemAction - Action to implement (must be a valid shutdown type)
Return Value:
Status
--*/ {
//
// Tell the debugger we are shutting down
//
KD_SYMBOLS_INFO SymbolInfo = {0}; SymbolInfo.BaseOfDll = (PVOID)KD_REBOOT; DebugService2(NULL, &SymbolInfo, BREAKPOINT_UNLOAD_SYMBOLS);
//
// Perform the final shutdown operation
//
switch (SystemAction) { case PowerActionShutdownReset:
//
// Reset the system
//
PopInvokeSystemStateHandler (PowerStateShutdownReset, NULL);
//
// Didn't do it, go for legacy function
//
HalReturnToFirmware (HalRebootRoutine); break;
case PowerActionShutdownOff: case PowerActionShutdown:
//
// Power down the system
//
PopInvokeSystemStateHandler (PowerStateShutdownOff, NULL);
//
// Didn't do it, go for legacy function
//
HalReturnToFirmware (HalPowerDownRoutine);
//
// Due to simulations we can try to power down on systems
// which don't support it
//
PoPrint (PO_ERROR, ("PopShutdownSystem: HalPowerDownRoutine returned\n")); HalReturnToFirmware (HalRebootRoutine); break;
default: //
// Got some unexpected input...
//
HalReturnToFirmware (HalRebootRoutine); }
KeBugCheckEx (INTERNAL_POWER_ERROR, 5, 0, 0, 0); }
#if _MSC_FULL_VER >= 13008827
#pragma warning(push)
#pragma warning(disable:4715) // Not all control paths return (due to infinite loop at end)
#endif
NTSTATUS PopShutdownHandler ( IN PVOID Context, IN PENTER_STATE_SYSTEM_HANDLER SystemHandler OPTIONAL, IN PVOID SystemContext, IN LONG NumberProcessors, IN volatile PLONG Number ) { PKPRCB Prcb;
KeDisableInterrupts(); Prcb = KeGetCurrentPrcb();
//
// On processor 0 put up the shutdown screen
//
if (Prcb->Number == 0) {
if (InbvIsBootDriverInstalled()) {
PUCHAR Bitmap1, Bitmap2;
if (!InbvCheckDisplayOwnership()) { InbvAcquireDisplayOwnership(); }
InbvResetDisplay(); InbvSolidColorFill(0,0,639,479,0); InbvEnableDisplayString(TRUE); // enable display string
InbvSetScrollRegion(0,0,639,475); // set to use entire screen
Bitmap1 = InbvGetResourceAddress(3); Bitmap2 = InbvGetResourceAddress(5);
if (Bitmap1 && Bitmap2) { InbvBitBlt(Bitmap1, 215, 282); InbvBitBlt(Bitmap2, 217, 111); }
} else {
ULONG i;
//
// Skip to middle of the display
//
for (i=0; i<25; i++) { InbvDisplayString ("\n"); }
InbvDisplayString (" "); // 23 spaces
InbvDisplayString ("The system may be powered off now.\n"); } }
//
// Halt
//
for (; ;) { HalHaltSystem (); }
return STATUS_SUCCESS; }
#if _MSC_FULL_VER >= 13008827
#pragma warning(pop)
#endif
NTSTATUS PopSleepSystem ( IN SYSTEM_POWER_STATE SystemState, IN PVOID Memory ) /*++
Routine Description:
Routine to implement a Sleep style system power actions.
N.B. All devices must already be in a compatible sleeping state.
Arguments:
SystemState - System state to implement (must be a valid sleep type)
Return Value:
Status
--*/ { POWER_STATE_HANDLER_TYPE Type; NTSTATUS Status = STATUS_SUCCESS;
switch (SystemState) { case PowerSystemSleeping1: Type = PowerStateSleeping1; break; case PowerSystemSleeping2: Type = PowerStateSleeping2; break; case PowerSystemSleeping3: Type = PowerStateSleeping3; break; case PowerSystemHibernate: Type = PowerStateSleeping4; break;
default: Type = PowerStateMaximum; PopInternalError (POP_SYS); }
Status = PopInvokeSystemStateHandler (Type, Memory);
if( !NT_SUCCESS(Status) && (SystemState == PowerSystemHibernate) ) {
//
// Tell someone.
// We'll send in a friendly error code instead of the
// cryptic one we got back from PopInvokeSystemStateHandler()
//
IoRaiseInformationalHardError( Status, NULL, NULL ); // Remember that we failed so we don't try again.
PopFailedHibernationAttempt = TRUE; }
return Status; }
NTSTATUS PopInvokeSystemStateHandler ( IN POWER_STATE_HANDLER_TYPE Type, IN PPOP_HIBER_CONTEXT HiberContext ) /*++
Routine Description:
Invokes a power state handler on every processor concurrently.
Arguments:
Type - Index to the handle to invoke
Return Value:
Status
--*/ { KDPC Dpc; KIRQL OldIrql; KAFFINITY Targets; ULONG Processor; ULONG TargetCount; POP_SYS_CONTEXT Context; POP_LOCAL_CONTEXT LocalContext; POWER_STATE_HANDLER ShutdownHandler; KAFFINITY ActiveProcessors; ULONG result;
//
// No spinlocks can be held when this call is made
//
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
//
// Get system state handler
//
RtlZeroMemory (&Context, sizeof(Context)); RtlZeroMemory (&ShutdownHandler, sizeof(ShutdownHandler)); Context.Handler = &ShutdownHandler;
Context.NotifyHandler = &PopPowerStateNotifyHandler; Context.NotifyState = Type; if (Type != PowerStateMaximum) { Context.Handler = &PopPowerStateHandlers[Type]; if (!Context.Handler->Handler) { return STATUS_DEVICE_DOES_NOT_EXIST; } }
Context.NumberProcessors = (ULONG) KeNumberProcessors; Context.HandlerBarrier = KeNumberProcessors; Context.State = POP_SH_COLLECTING_PROCESSORS; Context.HiberContext = HiberContext; if (HiberContext) { Context.SystemContext = HiberContext; Context.SystemHandler = PopSaveHiberContext; }
RtlZeroMemory (&LocalContext, sizeof(LocalContext));
//
// Before we freeze the machine, attempt to collect up as much memory
// as we can from MM to avoid saving it into the hibernation file.
//
if (HiberContext && HiberContext->ReserveFreeMemory) { for (; ;) {
if (MmAvailablePages < POP_FREE_THRESHOLD) { break; }
//
// Collect the pages
//
result = PopGatherMemoryForHibernate (HiberContext, POP_FREE_ALLOCATE_SIZE, &HiberContext->Spares, FALSE);
if (!result) { break; } } }
//
// Switch to boot processor and raise to DISPATCH_LEVEL level to
// avoid getting any DPCs
//
KeSetSystemAffinityThread (1); KeRaiseIrql (DISPATCH_LEVEL, &OldIrql); KeInitializeDpc (&Dpc, PopInvokeStateHandlerTargetProcessor, &Context); KeSetImportanceDpc (&Dpc, HighImportance);
//
// Collect and halt the other processors
//
Targets = KeActiveProcessors & (~1);
while (Targets) { KeFindFirstSetLeftAffinity(Targets, &Processor); ClearMember (Processor, Targets);
//
// Prepare to wait
//
TargetCount = Context.TargetCount;
//
// Issue DPC to target processor
//
KeSetTargetProcessorDpc (&Dpc, (CCHAR) Processor); KeInsertQueueDpc (&Dpc, NULL, NULL);
//
// Wait for DPC to be processed
//
while (TargetCount == Context.TargetCount) ; }
//
// All processors halted and spinning at dispatch level
//
PopIssueNextState (&Context, &LocalContext, POP_SH_SAVE_CONTEXT);
#if defined(i386)
//
// Fast system call must be disabled until the context required
// to support it is restored.
//
KePrepareToLoseProcessorSpecificState();
#endif
//
// Enter system state
//
if (HiberContext) {
//
// Get each processors stacks in the memory map for
// special handling during hibernate
//
PopIssueNextState (&Context, &LocalContext, POP_SH_GET_STACKS);
//
// Build the rest of the map, and structures needed
// to write the file
//
LocalContext.Status = PopBuildMemoryImageHeader (HiberContext, Type);
//
// Disable interrupts on all processors
//
PopIssueNextState (&Context, &LocalContext, POP_SH_DISABLE_INTERRUPTS);
//
// With interrupts disabled on all the processors, the debugger
// really can't work the way its supposed to. It can't IPI the
// other processors to get them to stop. So we temporarily
// change the kernel's notion of active processors, making it
// think that the this processor is the only one it has to worry
// about.
//
ActiveProcessors = KeActiveProcessors; KeActiveProcessors = 1;
if (NT_SUCCESS(LocalContext.Status)) {
//
// Notify the Notify Handler of pending sleep
//
Context.NotifyType = TRUE; // notify before
PopIssueNextState (&Context, &LocalContext, POP_SH_INVOKE_NOTIFY_HANDLER);
//
// Invoke the Power State handler
//
PopIssueNextState (&Context, &LocalContext, POP_SH_INVOKE_HANDLER);
//
// If the sleep was successful, clear the fully awake flag
//
if (NT_SUCCESS(LocalContext.Status)) { InterlockedExchange(&PopFullWake, PO_GDI_ON_PENDING); PoPowerSequence = PoPowerSequence + 1; PopSIdle.Time = 1; }
//
// Notify the Notify Handler of resume
//
Context.NotifyType = FALSE; // notify after
PopIssueNextState (&Context, &LocalContext, POP_SH_INVOKE_NOTIFY_HANDLER);
}
//
// Hiber is over, call while the machine still stopped to allow
// memory verification, etc..
//
PopHiberComplete (LocalContext.Status, HiberContext);
//
// If there's a request for a reset here, do it
//
if (HiberContext->Reset) { Context.Handler = &PopPowerStateHandlers[PowerStateShutdownReset]; Context.HiberContext = NULL; if (Context.Handler->Handler) { PopIssueNextState (&Context, &LocalContext, POP_SH_INVOKE_HANDLER); } HalReturnToFirmware (HalRebootRoutine); }
//
// If we are past this point, then we are guaranteed to have awakened
// from Hibernation (or something went severely wrong).
//
// Our Hibercontext is no longer needed (in fact, we will free it soon).
//
// Thus we set its status value to indicate that this is a wake up.
// This status value is used later on when the context is being freed
// in order to clear things no longer needed after the system awakens properly.
//
PERFINFO_HIBER_REINIT_TRACE();
HiberContext->Status = STATUS_WAKE_SYSTEM;
//
// Now restore the kernel's previous notion of active processors
// before we enable interrupts on the others.
//
KeActiveProcessors = ActiveProcessors;
//
// Restore interrupts on all processors
//
PopIssueNextState (&Context, &LocalContext, POP_SH_RESTORE_INTERRUPTS);
//
// We are returning from hibernate, and we need to tell the system
// that win32k now owns the display again.
//
InbvSetDisplayOwnership(FALSE);
} else {
//
// Notify the Notify Handler of pending sleep
//
Context.NotifyType = TRUE; // notify before
PopIssueNextState (&Context, &LocalContext, POP_SH_INVOKE_NOTIFY_HANDLER);
//
// Invoke the sleep handle
//
if (PERFINFO_IS_GROUP_ON(PERF_POWER)) { PERFINFO_PO_PRESLEEP LogEntry; #if defined(i386)
if (PopAction.SystemState == PowerSystemSleeping3) { LogEntry.PerformanceFrequency.QuadPart = (ULONGLONG)KeGetCurrentPrcb()->MHz * 1000000; LogEntry.PerformanceCounter.QuadPart = 0; } else { LogEntry.PerformanceCounter = KeQueryPerformanceCounter(&LogEntry.PerformanceFrequency); } #else
LogEntry.PerformanceCounter = KeQueryPerformanceCounter(&LogEntry.PerformanceFrequency); #endif
PerfInfoLogBytes(PERFINFO_LOG_TYPE_PO_PRESLEEP, &LogEntry, sizeof(LogEntry)); } PopIssueNextState (&Context, &LocalContext, POP_SH_INVOKE_HANDLER); if (PERFINFO_IS_GROUP_ON(PERF_POWER)) { PERFINFO_PO_POSTSLEEP LogEntry; #if defined(i386)
if (PopAction.SystemState == PowerSystemSleeping3) { LogEntry.PerformanceCounter.QuadPart = POP_GET_TICK_COUNT(); } else { LogEntry.PerformanceCounter = KeQueryPerformanceCounter(NULL); } #else
LogEntry.PerformanceCounter = KeQueryPerformanceCounter(NULL); #endif
PerfInfoLogBytes(PERFINFO_LOG_TYPE_PO_POSTSLEEP, &LogEntry, sizeof(LogEntry)); }
//
// If the sleep was successful, clear the fully awake flag
//
if (NT_SUCCESS(LocalContext.Status)) {
//
// If somebody has set display required, then turn the
// display back on. Otherwise leave it off until there
// is some user activity signalled.
//
if (PopAttributes[POP_DISPLAY_ATTRIBUTE].Count > 0) { InterlockedExchange(&PopFullWake, PO_GDI_ON_PENDING); } else { InterlockedExchange(&PopFullWake, 0); } PoPowerSequence = PoPowerSequence + 1; PopSIdle.Time = 1; }
//
// Notify the Notify Handler of resume
//
Context.NotifyType = FALSE; // notify after
PopIssueNextState (&Context, &LocalContext, POP_SH_INVOKE_NOTIFY_HANDLER); }
//
// Restore other saved state on each processor
//
PopIssueNextState (&Context, &LocalContext, POP_SH_RESTORE_CONTEXT);
#if defined(i386)
//
// On x86, reload any processor specific data structures (MSRs).
//
if (NT_SUCCESS(LocalContext.Status)) { KeRestoreProcessorSpecificFeatures(); } #endif
//
// Let the other processor return
//
PopIssueNextState (&Context, &LocalContext, POP_SH_COMPLETE);
//
// Now that all processors have returned,
// put all the available memory back into the system. We don't do
// this earlier because on systems with large amounts of memory it
// can take significant time to free it all and this triggers the
// DPC timeouts.
//
if (HiberContext) { PopFreeHiberContext (FALSE); }
//
// If success, return status_success and count the number of
// times the sleep state has worked
//
if (NT_SUCCESS(LocalContext.Status)) { LocalContext.Status = STATUS_SUCCESS; if (Context.Handler->Spare[0] != 0xff) { Context.Handler->Spare[0] += 1; } }
//
// Done
//
KeLowerIrql (OldIrql); return LocalContext.Status; }
VOID PopIssueNextState ( IN PPOP_SYS_CONTEXT Context, IN PPOP_LOCAL_CONTEXT LocalContext, IN ULONG NextState ) /*++
Routine Description:
Called by the invoking processor to instruct all processors to the next state in the sequence needed to invoke/enter the target power handler.
Arguments:
Context - Shared context structure used to communicate the state transitions
NextState - New target state to enter
Return Value:
None
--*/ { ULONG LastState;
//
// Reset count for this operation
//
InterlockedExchange ((PVOID) &Context->TargetCount, 0);
//
// Issue new state
//
InterlockedExchange ((PVOID) &Context->State, NextState);
//
// Handle it ourselves
//
LocalContext->LastState = POP_SH_UNINITIALIZED; PopHandleNextState (Context, LocalContext);
//
// Wait for all processor to complete
//
while (Context->TargetCount != Context->NumberProcessors) { KeYieldProcessor (); } }
VOID PopHandleNextState ( IN PPOP_SYS_CONTEXT Context, IN PPOP_LOCAL_CONTEXT LocalContext ) /*++
Routine Description:
Wait for next state notification, and then handle it
Arguments:
Context - Shared context structure used to communicate the state transitions
LocalContext- Context local to this processor
Return Value:
None
--*/ { NTSTATUS Status; PPROCESSOR_POWER_STATE PState; PKPRCB Prcb;
Prcb = KeGetCurrentPrcb(); PState = &Prcb->PowerState;
//
// Wait for new state
//
while (Context->State == LocalContext->LastState) { KeYieldProcessor (); }
//
// Pickup new state and handle it
//
LocalContext->LastState = Context->State; switch (LocalContext->LastState) { case POP_SH_SAVE_CONTEXT: Status = KeSaveFloatingPointState(&LocalContext->FloatSave); LocalContext->FloatSaved = NT_SUCCESS(Status); break;
case POP_SH_GET_STACKS: PopCloneStack (Context->HiberContext); break;
case POP_SH_DISABLE_INTERRUPTS: LocalContext->Irql = KeGetCurrentIrql(); LocalContext->InterruptEnable = KeDisableInterrupts(); break;
case POP_SH_INVOKE_HANDLER: Status = Context->Handler->Handler ( Context->Handler->Context, Context->SystemHandler, Context->SystemContext, Context->NumberProcessors, &Context->HandlerBarrier );
LocalContext->Status = Status; break;
case POP_SH_INVOKE_NOTIFY_HANDLER:
if (Context->NotifyHandler->Handler) { Status = Context->NotifyHandler->Handler( Context->NotifyState, Context->NotifyHandler->Context, Context->NotifyType );
} break;
case POP_SH_RESTORE_INTERRUPTS: KeEnableInterrupts(LocalContext->InterruptEnable); KeLowerIrql(LocalContext->Irql); break;
case POP_SH_RESTORE_CONTEXT: if (LocalContext->FloatSaved) { KeRestoreFloatingPointState(&LocalContext->FloatSave); }
if (PState->Flags & PSTATE_SUPPORTS_THROTTLE) { PState->PerfSetThrottle(PState->CurrentThrottle); } break; }
//
// Signal that we are in the new state
//
InterlockedIncrement ((PULONG) &Context->TargetCount); }
VOID PopInvokeStateHandlerTargetProcessor ( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++
Routine Description:
Called by target processors when invoking a power state handler. Target processors wait for invoking processor to coordinate the states required to enter the particular power state handler.
Arguments:
Dpc - Not used
DeferredContext - Shared context structure
Return Value:
None
--*/ { POP_LOCAL_CONTEXT LocalContext; PPOP_SYS_CONTEXT Context; KIRQL OldIrql;
Context = (PPOP_SYS_CONTEXT) DeferredContext; RtlZeroMemory (&LocalContext, sizeof(LocalContext)); LocalContext.LastState = POP_SH_UNINITIALIZED;
//
// Handle new states
//
do {
PopHandleNextState (Context, &LocalContext);
} while (LocalContext.LastState != POP_SH_COMPLETE); }
|