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