/*++ Copyright (c) 1990 Microsoft Corporation Module Name: paction.c Abstract: This module implements power action handling for triggered power actions Author: Ken Reneris (kenr) 17-Jan-1997 Revision History: --*/ #include "pop.h" // // Interal prototypes // VOID PopPromoteActionFlag ( OUT PUCHAR Updates, IN ULONG UpdateFlag, IN ULONG Flags, IN BOOLEAN Set, IN ULONG FlagBit ); NTSTATUS PopIssueActionRequest ( IN BOOLEAN Promote, IN POWER_ACTION Action, IN SYSTEM_POWER_STATE PowerState, IN ULONG Flags ); VOID PopCompleteAction ( PPOP_ACTION_TRIGGER Trigger, NTSTATUS Status ); NTSTATUS PopDispatchStateCallout( IN PKWIN32_POWERSTATE_PARAMETERS Parms, IN PULONG SessionId OPTIONAL ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, PoShutdownBugCheck) #pragma alloc_text(PAGE, PopPromoteActionFlag) #pragma alloc_text(PAGE, PopSetPowerAction) #pragma alloc_text(PAGE, PopCompareActions) #pragma alloc_text(PAGE, PopPolicyWorkerAction) #pragma alloc_text(PAGE, PopIssueActionRequest) #pragma alloc_text(PAGE, PopCriticalShutdown) #pragma alloc_text(PAGE, PopCompleteAction) #pragma alloc_text(PAGE, PopPolicyWorkerActionPromote) #pragma alloc_text(PAGE, PopDispatchStateCallout) #endif VOID PoShutdownBugCheck ( IN BOOLEAN AllowCrashDump, IN ULONG BugCheckCode, IN ULONG_PTR BugCheckParameter1, IN ULONG_PTR BugCheckParameter2, IN ULONG_PTR BugCheckParameter3, IN ULONG_PTR BugCheckParameter4 ) /*++ Routine Description: This function is used to issue a controlled shutdown & then a bug check. This type of bugcheck can only be issued on a working system. Arguments: AllowCrashDump - If FALSE crashdump will be disabled BugCode - The bug code for the shutdown Return Value: Does not return --*/ { POP_SHUTDOWN_BUG_CHECK BugCode; // // If crash dumps aren't allowed for this bugcheck, then go clear // the current crash dump state // if (!AllowCrashDump) { IoConfigureCrashDump (CrashDumpDisable); } // // Indicate the bugcheck to issue once the system has been shutdown // BugCode.Code = BugCheckCode; BugCode.Parameter1 = BugCheckParameter1; BugCode.Parameter2 = BugCheckParameter2; BugCode.Parameter3 = BugCheckParameter3; BugCode.Parameter4 = BugCheckParameter4; PopAction.ShutdownBugCode = &BugCode; // // Initiate a critical shutdown event // ZwInitiatePowerAction ( PowerActionShutdown, PowerSystemSleeping3, POWER_ACTION_OVERRIDE_APPS | POWER_ACTION_DISABLE_WAKES | POWER_ACTION_CRITICAL, FALSE ); // // Should not return, but just in case... // KeBugCheckEx ( BugCheckCode, BugCheckParameter1, BugCheckParameter2, BugCheckParameter3, BugCheckParameter4 ); } VOID PopCriticalShutdown ( POP_POLICY_DEVICE_TYPE Type ) /*++ Routine Description: Issue a critical system shutdown. No application notification (presumably they've ignored the issue til now), flush OS state and shut off. N.B. PopPolicyLock must be held. Arguments: Type - Root cause of critical shutdown Return Value: None. --*/ { POP_ACTION_TRIGGER Trigger; POWER_ACTION_POLICY Action; ASSERT_POLICY_LOCK_OWNED(); PoPrint (PO_ERROR, ("PopCriticalShutdown: type %x\n", Type)); // // Go directly to setting the power state // RtlZeroMemory (&Action, sizeof(Action)); Action.Action = PowerActionShutdownOff; Action.Flags = POWER_ACTION_OVERRIDE_APPS | POWER_ACTION_DISABLE_WAKES | POWER_ACTION_CRITICAL; RtlZeroMemory (&Trigger, sizeof(Trigger)); Trigger.Type = Type; Trigger.Flags = PO_TRG_SET; try { // // The substitution policy and LightestState do not matter here as // the action restricts this to a shutdown. // PopSetPowerAction( &Trigger, 0, &Action, PowerSystemHibernate, SubstituteLightestOverallDownwardBounded ); } except (EXCEPTION_EXECUTE_HANDLER) { ASSERT (!GetExceptionCode()); } } VOID PopSetPowerAction( IN PPOP_ACTION_TRIGGER Trigger, IN ULONG UserNotify, IN PPOWER_ACTION_POLICY ActionPolicy, IN SYSTEM_POWER_STATE LightestState, IN POP_SUBSTITUTION_POLICY SubstitutionPolicy ) /*++ Routine Description: This function is called to "fire" an ActionPolicy. If there is already an action being taken this action is merged in, else a new power action is initiated. N.B. PopPolicyLock must be held. Arguments: Trigger - Action trigger structure (for ActionPolicy). UserNotify - Additional USER notifications to fire if trigger occurs ActionPolicy - This action policy which has fired. LightestState - For sleep type actions, the minimum sleeping state which must be entered for this operation. (Inferred to be PowerSystemHibernate for PowerActionHibernate and PowerActionWarmEject) SubstitutionPolicy - Specifies how LightestState should be treated if it is not supported. Return Value: None. --*/ { UCHAR Updates; ULONG i, Flags; BOOLEAN Pending; BOOLEAN Disabled; POWER_ACTION Action; ASSERT_POLICY_LOCK_OWNED(); if (PERFINFO_IS_GROUP_ON(PERF_POWER)) { PERFINFO_SET_POWER_ACTION LogEntry; LogEntry.PowerAction = (ULONG) ActionPolicy->Action; LogEntry.LightestState = (ULONG) LightestState; LogEntry.Trigger = Trigger; PerfInfoLogBytes(PERFINFO_LOG_TYPE_SET_POWER_ACTION, &LogEntry, sizeof(LogEntry)); } // // If the trigger isn't set, then we're done // if (!(Trigger->Flags & PO_TRG_SET)) { PopCompleteAction (Trigger, STATUS_SUCCESS); return ; } PoPrint (PO_PACT, ("PopSetPowerAction: %s, Flags %x, Min=%s\n", PopPowerActionString(ActionPolicy->Action), ActionPolicy->Flags, PopSystemStateString(LightestState) )); // // Round request to system capabilities // PopVerifySystemPowerState(&LightestState, SubstitutionPolicy); Disabled = PopVerifyPowerActionPolicy (ActionPolicy); if (Disabled) { PopCompleteAction (Trigger, STATUS_NOT_SUPPORTED); return ; } // // If system action not already triggered, do so now // Pending = FALSE; if (!(Trigger->Flags & PO_TRG_SYSTEM)) { Trigger->Flags |= PO_TRG_SYSTEM; Action = ActionPolicy->Action; Flags = ActionPolicy->Flags; // // If state is idle, then clear residue values // if (PopAction.State == PO_ACT_IDLE) { PopResetActionDefaults(); } // // If the action is for something other then none, then check it against the // current action // if (Action != PowerActionNone) { Updates = 0; // // Hibernate actions are treated like sleep actions with a min state // of hibernate. Warm ejects are like sleeps that start light and // deepen only as neccessary, but we do not map them to the same // action because we don't want to be constrained by the users // power policy. // if (Action == PowerActionWarmEject) { ASSERT (LightestState <= PowerSystemHibernate); Flags |= POWER_ACTION_LIGHTEST_FIRST; } if (Action == PowerActionHibernate) { ASSERT (LightestState <= PowerSystemHibernate); LightestState = PowerSystemHibernate; } // // Is this action is as good as the current action? // if ( PopCompareActions(Action, PopAction.Action) >= 0) { // // allow the absence of query_allowed, ui_allowed. // PopPromoteActionFlag (&Updates, PO_PM_USER, Flags, FALSE, POWER_ACTION_QUERY_ALLOWED); PopPromoteActionFlag (&Updates, PO_PM_USER, Flags, FALSE, POWER_ACTION_UI_ALLOWED); // // Always favor the deepest sleep first, and restart if we // switch. // PopPromoteActionFlag (&Updates, PO_PM_SETSTATE, Flags, FALSE, POWER_ACTION_LIGHTEST_FIRST); // // If this is a sleep action, then make sure Lightest is at least whatever // the current policy is set for // if (Action == PowerActionSleep && LightestState < PopPolicy->MinSleep) { LightestState = PopPolicy->MinSleep; } // // If LightestState is more restrictive (deeper) than the one // specified by the current action, promote it. // if (LightestState > PopAction.LightestState) { PopAction.LightestState = LightestState; Updates |= PO_PM_SETSTATE; } } // // Promote the critical & override_apps flags // PopPromoteActionFlag (&Updates, PO_PM_USER, Flags, TRUE, POWER_ACTION_OVERRIDE_APPS); PopPromoteActionFlag (&Updates, PO_PM_USER | PO_PM_SETSTATE, Flags, TRUE, POWER_ACTION_CRITICAL); // // Promote disable_wake flag. No updates are needed for this - it will be // picked up in NtSetSystemPowerState regardless of the params passed from // user mode // PopPromoteActionFlag (&Updates, 0, Flags, TRUE, POWER_ACTION_DISABLE_WAKES); // // If the new action is more agressive then the old action, promote it // if ( PopCompareActions(Action, PopAction.Action) > 0) { // // If we are promoting, the old action certainly cannot be a // shutdown, as that is the deepest action. // ASSERT(PopCompareActions(PopAction.Action, PowerActionShutdownOff) < 0); // // If we are promoting into a deeper *action*, and the new // action is hibernate or shutdown, then we want to reissue. // // ADRIAO N.B. 08/02/1999 - // We might want to reissue for hibernate only if the new // state brings in POWER_ACTION_CRITICAL to the mix. This is // because there are two scenario's for hibernate, one where // the user selects standby then hibernate in quick succession // (consider a lid switch set to hiber, and the user closes the // lid after selecting standby), or this might be hibernate due // to low battery (ie, we're deepening standby). Believe it or // not, the user can disable the "critical flag" in the critical // power-down menu. // if (PopCompareActions(Action, PowerActionHibernate) >= 0) { Updates |= PO_PM_REISSUE; } Updates |= PO_PM_USER | PO_PM_SETSTATE; PopAction.Action = Action; } if (Action == PowerActionHibernate) { Action = PowerActionSleep; } // // PopAction.Action may be explicitely set to PowerActionHibernate // by NtSetSystemPowerState during a wake. // if (PopAction.Action == PowerActionHibernate) { PopAction.Action = PowerActionSleep; } // // If the current action was updated, then get a worker // if (Updates) { Pending = TRUE; if (PopAction.State == PO_ACT_IDLE || PopAction.State == PO_ACT_NEW_REQUEST) { // // New request // PopAction.State = PO_ACT_NEW_REQUEST; PopAction.Status = STATUS_SUCCESS; PopGetPolicyWorker (PO_WORKER_ACTION_NORMAL); } else { // // Something outstanding. Promote it. // PopAction.Updates |= Updates; PopGetPolicyWorker (PO_WORKER_ACTION_PROMOTE); } } } } // // If user events haven't been handled, do it now // if (!(Trigger->Flags & PO_TRG_USER)) { Trigger->Flags |= PO_TRG_USER; // // If there's an eventcode for the action, dispatch it // if (ActionPolicy->EventCode) { // if event code already queued, drop it for (i=0; i < POP_MAX_EVENT_CODES; i++) { if (PopEventCode[i] == ActionPolicy->EventCode) { break; } } if (i >= POP_MAX_EVENT_CODES) { // not queued, add it for (i=0; i < POP_MAX_EVENT_CODES; i++) { if (!PopEventCode[i]) { PopEventCode[i] = ActionPolicy->EventCode; UserNotify |= PO_NOTIFY_EVENT_CODES; break; } } if (i >= POP_MAX_EVENT_CODES) { PoPrint (PO_WARN, ("PopAction: dropped user event %x\n", ActionPolicy->EventCode)); } } } PopSetNotificationWork (UserNotify); } // // If sync request, queue it or complete it // if (Trigger->Flags & PO_TRG_SYNC) { if (Pending) { InsertTailList (&PopActionWaiters, &Trigger->Wait->Link); } else { PopCompleteAction (Trigger, STATUS_SUCCESS); } } } LONG PopCompareActions( IN POWER_ACTION FutureAction, IN POWER_ACTION CurrentAction ) /*++ Routine Description: Used to determine whether the current action should be promoted to the future action or not. N.B. PopPolicyLock must be held. Arguments: FutureAction - Action which we are now being asked to do. CurrentAction - Action which we are currently doing. Return Value: Zero if the current and future actions are identical. Positive if the future action should be used. Negative if the current action is already more important than the future request. --*/ { // // We could just return (FutureAction - CurrentAction) if it weren't for // PowerActionWarmEject, which is less important than sleeping (because // sleeping may be induced by critically low power). So we "insert" // PowerActionWarmEject right before PowerActionSleep. // if (FutureAction == PowerActionWarmEject) { FutureAction = PowerActionSleep; } else if (FutureAction >= PowerActionSleep) { FutureAction++; } if (CurrentAction == PowerActionWarmEject) { CurrentAction = PowerActionSleep; } else if (CurrentAction >= PowerActionSleep) { CurrentAction++; } return (FutureAction - CurrentAction); } VOID PopPromoteActionFlag ( OUT PUCHAR Updates, IN ULONG UpdateFlag, IN ULONG Flags, IN BOOLEAN Set, IN ULONG FlagBit ) /*++ Routine Description: Used to merge existing action flags with new action flags. The FlagBit bit in PopAction.Flags is promoted to set/clear according UpdateFlag. If a change occured Updates is updated. N.B. PopPolicyLock must be held. Arguments: Updates - Current outstanding updates to the power action which is in progress UpdateFlag - Bit(s) to set into Updates if a change is made Flags - Flags to test for FlagBit Set - To test either set or clear FlagBit - The bit to check in Flags Return Value: None. --*/ { ULONG New, Current; ULONG Mask; Mask = Set ? 0 : FlagBit; New = (Flags & FlagBit) ^ Mask; Current = (PopAction.Flags & FlagBit) ^ Mask; // // If the bit is not set accordingly in Flags but is set accordingly in // PoAction.Flags then update it // if (New & ~Current) { PopAction.Flags = (PopAction.Flags | New) & ~Mask; *Updates |= (UCHAR) UpdateFlag; } } ULONG PopPolicyWorkerAction ( VOID ) /*++ Routine Description: Dispatch function for: worker_action_normal. This worker thread checks for an initial pending action and synchronously issued it to USER. The thread is returned after the USER has completed the action. (e.g., apps have been notified if allowed, etc..) Arguments: None. Return Value: None. --*/ { POWER_ACTION Action; SYSTEM_POWER_STATE LightestState; ULONG Flags; NTSTATUS Status; PLIST_ENTRY Link; PPOP_TRIGGER_WAIT SyncRequest; PopAcquirePolicyLock (); if (PopAction.State == PO_ACT_NEW_REQUEST) { // // We'll handle this update // Action = PopAction.Action; LightestState = PopAction.LightestState; Flags = PopAction.Flags; PopAction.State = PO_ACT_CALLOUT; // // Perform callout // Status = PopIssueActionRequest (FALSE, Action, LightestState, Flags); // // Clear switch triggers // PopResetSwitchTriggers (); // // If the system was sleeping // if (!NT_SUCCESS(Status)) { PoPrint (PO_WARN | PO_PACT, ("PopPolicyWorkerAction: action request %d failed %08lx\n", Action, Status)); } if (PopAction.Updates & PO_PM_REISSUE) { // // There's a new outstanding request. Claim it. // PopAction.Updates &= ~PO_PM_REISSUE; PopAction.State = PO_ACT_NEW_REQUEST; PopGetPolicyWorker (PO_WORKER_ACTION_NORMAL); } else { // // All power actions are complete. // if (PERFINFO_IS_GROUP_ON(PERF_POWER)) { PERFINFO_SET_POWER_ACTION_RET LogEntry; LogEntry.Trigger = (PVOID)Action; LogEntry.Status = Status; PerfInfoLogBytes(PERFINFO_LOG_TYPE_SET_POWER_ACTION_RET, &LogEntry, sizeof(LogEntry)); } PopAction.Status = Status; PopAction.State = PO_ACT_IDLE; if (IsListEmpty(&PopActionWaiters)) { // // If there was an error and no one is waiting for it, issue a notify // if (!NT_SUCCESS(Status)) { PopSetNotificationWork (PO_NOTIFY_STATE_FAILURE); } } else { // // Free any synchronous waiters // for (Link = PopActionWaiters.Flink; Link != &PopActionWaiters; Link = Link->Flink) { SyncRequest = CONTAINING_RECORD (Link, POP_TRIGGER_WAIT, Link); PopCompleteAction (SyncRequest->Trigger, Status); } } // // Let promotion worker check for anything else // PopGetPolicyWorker (PO_WORKER_ACTION_PROMOTE); } } PopReleasePolicyLock (FALSE); return 0; } VOID PopCompleteAction ( PPOP_ACTION_TRIGGER Trigger, NTSTATUS Status ) { PPOP_TRIGGER_WAIT SyncRequest; if (Trigger->Flags & PO_TRG_SYNC) { Trigger->Flags &= ~PO_TRG_SYNC; SyncRequest = Trigger->Wait; SyncRequest->Status = Status; KeSetEvent (&SyncRequest->Event, 0, FALSE); } } ULONG PopPolicyWorkerActionPromote ( VOID ) /*++ Routine Description: Dispatch function for: worker_action_promote. This worker thread checks for a pending promotion needed for a power action request in USER and calls USER with the promotion. This function, PopPolicyWorkerAction, and NtSetSystemPowerState corridinate to handle ordering issues of when each function is called. N.B. Part of the cleanup from PopPolicyWorkerAction is to invoke this function. So this worker function may have 2 threads at any one time. (But in this case, the normal action worker thread would only find a promotion turning into to a new request and then exit back to a normal action worker) Arguments: None. Return Value: None. --*/ { ULONG Updates; NTSTATUS Status; PopAcquirePolicyLock (); if (PopAction.Updates) { // // Get update info // Updates = PopAction.Updates; // // Handle based on state of original request worker // switch (PopAction.State) { case PO_ACT_IDLE: // // Normal worker is no longer in progress, this is no // longer a promotion. If the updates have PO_PM_REISSUE // then turn this into a new request, else the promotion can // be skipped as the original operation completion // was good enough // if (Updates & PO_PM_REISSUE) { PopAction.State = PO_ACT_NEW_REQUEST; PopGetPolicyWorker (PO_WORKER_ACTION_NORMAL); } else { Updates = 0; } break; case PO_ACT_SET_SYSTEM_STATE: // // If reissue or setstate is set, abort the current operation. // if (Updates & (PO_PM_REISSUE | PO_PM_SETSTATE)) { PopRestartSetSystemState (); } break; case PO_ACT_CALLOUT: // // Worker is in the callout. Call again to issue the promotion // Status = PopIssueActionRequest ( TRUE, PopAction.Action, PopAction.LightestState, PopAction.Flags ); if (NT_SUCCESS(Status)) { // // Promotion worked, clear the updates we performed // PopAction.Updates &= ~Updates; } else { // // If the state has changed, test again else do nothing // (the original worker thread will recheck on exit) // if (PopAction.State != PO_ACT_CALLOUT) { PopGetPolicyWorker (PO_WORKER_ACTION_PROMOTE); } } break; default: PoPrint (PO_ERROR, ("PopAction: invalid state %d\n", PopAction.State)); } } PopReleasePolicyLock (FALSE); return 0; } NTSTATUS PopIssueActionRequest ( IN BOOLEAN Promote, IN POWER_ACTION Action, IN SYSTEM_POWER_STATE LightestState, IN ULONG Flags ) /*++ Routine Description: This function is used by the normal action worker or the promtion worker when its time to call USER or NtSetSystemPowerState with a new request. Arguments: Promote - Indicates flag for USER call Action - The action to take LightestState - The minimum power state to enter Flags - Flags for the action to take. E.g., how it should be processed Return Value: Status as returned from USER or NtSetSystemPowerState --*/ { BOOLEAN DirectCall; NTSTATUS Status; ULONG Console; // // If there's no vector to call, then its a direct call // DirectCall = PopStateCallout ? FALSE : TRUE; // // If the critical flag is set and it's a ShutdownReset or ShutdownOff, // then it's done via a direct call. // if ((Flags & POWER_ACTION_CRITICAL) && (Action == PowerActionShutdownReset || Action == PowerActionShutdown || Action == PowerActionShutdownOff)) { DirectCall = TRUE; } // // If this is a direct call, then drop any reissue flag // if (DirectCall) { PopAction.Updates &= ~PO_PM_REISSUE; } // // If the policy has lock console set, make sure it's set in // the flags as well // if (PopPolicy->WinLogonFlags & WINLOGON_LOCK_ON_SLEEP) { Flags |= POWER_ACTION_LOCK_CONSOLE; } // // Debug // PoPrint (PO_PACT, ("PowerAction: %s%s, Min=%s, Flags %x\n", Promote ? "Promote, " : "", PopPowerActionString(Action), PopSystemStateString(LightestState), Flags )); if (DirectCall) { PoPrint (PO_PACT, ("PowerAction: Setting with direct call\n")); } // // Drop lock while performing callout to dispatch request // PopReleasePolicyLock (FALSE); if (DirectCall) { Status = ZwSetSystemPowerState (Action, LightestState, Flags); } else { WIN32_POWERSTATE_PARAMETERS Parms; Parms.Promotion = Promote; Parms.SystemAction = Action; Parms.MinSystemState = LightestState; Parms.Flags = Flags; Parms.fQueryDenied = FALSE; if (!Promote) { // // we want to deliver some messages to only the console session. // lets find out active console session here, and ask that active console win2k // to block the console switch while we are in power switch // LARGE_INTEGER ShortSleep; ShortSleep.QuadPart = -10 * 1000 * 10; // 10 milliseconds Console = -1; while (Console == -1) { Console = SharedUserData->ActiveConsoleId; if (Console != -1) { // // lets ask this console session, not to switch console, // untill we are done with power callouts. // Parms.PowerStateTask = PowerState_BlockSessionSwitch; Status = PopDispatchStateCallout(&Parms, &Console); if (Status == STATUS_CTX_NOT_CONSOLE) { // // we failed to block status switch // loop again Console = -1; } } if (Console == -1) { // // we are in session switch, wait till we get a valid active console session // KeDelayExecutionThread(KernelMode, FALSE, &ShortSleep); } } } ASSERT(NT_SUCCESS(Status)); Parms.PowerStateTask = PowerState_Init; Status = PopDispatchStateCallout(&Parms, NULL); if (!Promote && NT_SUCCESS(Status)) { Parms.PowerStateTask = PowerState_QueryApps; Status = PopDispatchStateCallout(&Parms, NULL); if (!NT_SUCCESS(Status) || Parms.fQueryDenied) { // // ISSUE-2000/11/28-jamesca: // // Win32k depends on PowerState_QueryFailed to unset the // fInProgress bit, set during PowerState_Init. Ideally, some // other operation should be used to do that, without having to // issue a PowerState_QueryFailed cancel message to sessions // (and apps) that never received PowerState_QueryApps query. // Parms.PowerStateTask = PowerState_QueryFailed; PopDispatchStateCallout(&Parms, NULL); } else { Parms.PowerStateTask = PowerState_SuspendApps; PopDispatchStateCallout(&Parms, NULL); Parms.PowerStateTask = PowerState_ShowUI; PopDispatchStateCallout(&Parms, NULL); Parms.PowerStateTask = PowerState_NotifyWL; Status = PopDispatchStateCallout(&Parms, &Console); Parms.PowerStateTask = PowerState_ResumeApps; PopDispatchStateCallout(&Parms, NULL); } } if (!Promote) { // // we are done with power callouts, now its ok if active console session switches // Parms.PowerStateTask = PowerState_UnBlockSessionSwitch; PopDispatchStateCallout(&Parms, &Console); } } PopAcquirePolicyLock (); return Status; } VOID PopResetActionDefaults( VOID ) /*++ Routine Description: This function is used to initialize the current PopAction to reflect the idle state. Arguments: None. Return Value: None. --*/ { PopAction.Updates = 0; PopAction.Shutdown = FALSE; PopAction.Action = PowerActionNone; PopAction.LightestState = PowerSystemUnspecified; PopAction.Status = STATUS_SUCCESS; PopAction.IrpMinor = 0; PopAction.SystemState = PowerSystemUnspecified; // // When we promote a power action (say from idle), various flags must be // agreed upon by both actions to stay around. We must set those flags // here otherwise they can never be set after promoting (idle). // PopAction.Flags = ( POWER_ACTION_QUERY_ALLOWED | POWER_ACTION_UI_ALLOWED | POWER_ACTION_LIGHTEST_FIRST ); } VOID PopActionRetrieveInitialState( IN OUT PSYSTEM_POWER_STATE LightestSystemState, OUT PSYSTEM_POWER_STATE DeepestSystemState, OUT PSYSTEM_POWER_STATE InitialSystemState, OUT PBOOLEAN QueryDevices ) /*++ Routine Description: This function is used to determine the lightest, deepest, and initial Sx states prior to putting the system to sleep, or turning it off. Power policies for sleep are also applied if the action is a sleep. Arguments: LightestSystemState - Lightest sleep state. May be adjusted if the action in progress is a shutdown. DeepestSystemState - Deepest sleep state possible. InitialSystemState - State to start with. QueryDevices - TRUE if devices should be queries, FALSE if devices shouldn't be queried. Return Value: None. --*/ { // // Check if the action is a shutdown. If so, map it to the appropiate // system shutdown state // if ((PopAction.Action == PowerActionShutdown) || (PopAction.Action == PowerActionShutdownReset) || (PopAction.Action == PowerActionShutdownOff)) { // // This is a shutdown. The lightest we can do is S5. // *LightestSystemState = PowerSystemShutdown; *DeepestSystemState = PowerSystemShutdown; } else if (PopAction.Action == PowerActionWarmEject) { // // Warm Ejects have an implicit policy of either S1-S4 or S4-S4. // The caller passes in LightestSystemState to choose the lightest, // and the deepest is always a hibernate. // *DeepestSystemState = PowerSystemHibernate; PopVerifySystemPowerState (DeepestSystemState, SubstituteLightenSleep); } else { // // This a sleep request. Min is current set to the best the hardware // can do relative to our caller. We apply the minimum from the current // policy here. We also choose the maximum from either the policy or // the default for current latency setting. Note that all of these // values in PopPolicy have been verified at some point. // // Note that PopSetPowerAction fixes up PowerActionHibernate long before // we get here. // if (PopAttributes[POP_LOW_LATENCY_ATTRIBUTE].Count && (PopPolicy->MaxSleep >= PopPolicy->ReducedLatencySleep)) { *DeepestSystemState = PopPolicy->ReducedLatencySleep; } else { *DeepestSystemState = PopPolicy->MaxSleep; } if (PopPolicy->MinSleep > *LightestSystemState) { *LightestSystemState = PopPolicy->MinSleep; } } // // If there's an explicit min state which is deeper than the // max state, then raise the max to allow it // if (*LightestSystemState > *DeepestSystemState) { *DeepestSystemState = *LightestSystemState; } // // We query devices unless this is a critical operation with no range. // *QueryDevices = TRUE; if ((PopAction.Flags & POWER_ACTION_CRITICAL) && *LightestSystemState == *DeepestSystemState) { *QueryDevices = FALSE; } // // Pick the appropriate initial state. // if (PopAction.Flags & POWER_ACTION_LIGHTEST_FIRST) { *InitialSystemState = *LightestSystemState; } else { *InitialSystemState = *DeepestSystemState; } } NTSTATUS PopDispatchStateCallout( IN PKWIN32_POWERSTATE_PARAMETERS Parms, IN PULONG SessionId OPTIONAL ) /*++ Routine Description: Dispatches a session state callout to PopStateCallout Arguments: Parms - Supplies the parameters SessionId - Optionally, supplies the specific session the callout should be dispatched to. If not present, the callout will be dispatched to all sessions. Return Value: NTSTATUS code. Note: For compatibility reasons, the previous behavior of MmDispatchSessionCallout only returning the status of the callout to session 0 has been maintained. --*/ { NTSTATUS Status = STATUS_SUCCESS, CallStatus = STATUS_NOT_FOUND; PVOID OpaqueSession; KAPC_STATE ApcState; if (PERFINFO_IS_GROUP_ON(PERF_POWER)) { PERFINFO_PO_SESSION_CALLOUT LogEntry; LogEntry.SystemAction = Parms->SystemAction; LogEntry.MinSystemState = Parms->MinSystemState; LogEntry.Flags = Parms->Flags; LogEntry.PowerStateTask = Parms->PowerStateTask; PerfInfoLogBytes(PERFINFO_LOG_TYPE_PO_SESSION_CALLOUT, &LogEntry, sizeof(LogEntry)); } if (ARGUMENT_PRESENT(SessionId)) { // // Dispatch only to the specified session. // ASSERT(*SessionId != (ULONG)-1); if ((PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_IN_SESSION) && (*SessionId == PsGetCurrentProcessSessionId())) { // // If the call is from a user mode process, and we are asked to call // the current session, call directly. // CallStatus = PopStateCallout((PVOID)Parms); } else { // // Attach to the specified session. // OpaqueSession = MmGetSessionById(*SessionId); if (OpaqueSession) { Status = MmAttachSession(OpaqueSession, &ApcState); ASSERT(NT_SUCCESS(Status)); if (NT_SUCCESS(Status)) { CallStatus = PopStateCallout((PVOID)Parms); Status = MmDetachSession(OpaqueSession, &ApcState); ASSERT(NT_SUCCESS(Status)); } Status = MmQuitNextSession(OpaqueSession); ASSERT(NT_SUCCESS(Status)); } } } else { // // Should be dispatched to all sessions. // for (OpaqueSession = MmGetNextSession(NULL); OpaqueSession != NULL; OpaqueSession = MmGetNextSession(OpaqueSession)) { if ((PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_IN_SESSION) && (MmGetSessionId(OpaqueSession) == PsGetCurrentProcessSessionId())) { // // If the call is from a user mode process, and we are asked to // call the current session, call directly. // if (MmGetSessionId(OpaqueSession) == 0) { CallStatus = PopStateCallout((PVOID)Parms); } else { PopStateCallout((PVOID)Parms); } } else { // // Attach to the specified session. // Status = MmAttachSession(OpaqueSession, &ApcState); ASSERT(NT_SUCCESS(Status)); if (NT_SUCCESS(Status)) { if (MmGetSessionId(OpaqueSession) == 0) { CallStatus = PopStateCallout((PVOID)Parms); } else { PopStateCallout((PVOID)Parms); } Status = MmDetachSession(OpaqueSession, &ApcState); ASSERT(NT_SUCCESS(Status)); } } } } if (PERFINFO_IS_GROUP_ON(PERF_POWER)) { PERFINFO_PO_SESSION_CALLOUT_RET LogEntry; LogEntry.Status = CallStatus; PerfInfoLogBytes(PERFINFO_LOG_TYPE_PO_SESSION_CALLOUT_RET, &LogEntry, sizeof(LogEntry)); } return(CallStatus); }