/*++ Copyright (c) 1990 Microsoft Corporation Module Name: pinfo.c Abstract: This module implements the generic power policy information interfaces Author: Ken Reneris (kenr) 17-Jan-1997 Revision History: --*/ #include "pop.h" // // Internal prototypes // VOID PopVerifyPowerPolicy ( IN BOOLEAN Ac, IN PSYSTEM_POWER_POLICY InputPolicy, OUT PSYSTEM_POWER_POLICY PowerPolicy ); VOID PopVerifyProcessorPowerPolicy ( IN BOOLEAN Ac, IN PPROCESSOR_POWER_POLICY InputPolicy, OUT PPROCESSOR_POWER_POLICY PowerPolicy ); VOID PopVerifyThrottle ( IN PUCHAR Throttle, IN UCHAR Min ); VOID PopApplyPolicy ( IN BOOLEAN UpdateRegistry, IN BOOLEAN AcPolicy, IN PSYSTEM_POWER_POLICY NewPolicy, IN ULONG PolicyLength ); VOID PopApplyProcessorPolicy ( IN BOOLEAN UpdateRegistry, IN BOOLEAN AcPolicy, IN PPROCESSOR_POWER_POLICY NewPolicy, IN ULONG PolicyLength ); VOID PopFilterCapabilities( IN PSYSTEM_POWER_CAPABILITIES SourceCapabilities, OUT PSYSTEM_POWER_CAPABILITIES FilteredCapabilities ); BOOLEAN PopUserIsAdmin( VOID ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, NtPowerInformation) #pragma alloc_text(PAGE, PopApplyAdminPolicy) #pragma alloc_text(PAGE, PopApplyPolicy) #pragma alloc_text(PAGE, PopVerifyPowerPolicy) #pragma alloc_text(PAGE, PopVerifyPowerActionPolicy) #pragma alloc_text(PAGE, PopVerifySystemPowerState) #pragma alloc_text(PAGE, PopAdvanceSystemPowerState) #pragma alloc_text(PAGE, PopResetCurrentPolicies) #pragma alloc_text(PAGE, PopNotifyPolicyDevice) #pragma alloc_text(PAGE, PopConnectToPolicyDevice) #pragma alloc_text(PAGE, PopFilterCapabilities) #pragma alloc_text(PAGE, PopUserIsAdmin) #endif NTSTATUS NtPowerInformation ( IN POWER_INFORMATION_LEVEL InformationLevel, IN PVOID InputBuffer OPTIONAL, IN ULONG InputBufferLength, OUT PVOID OutputBuffer OPTIONAL, IN ULONG OutputBufferLength ) /*++ Routine Description: This function optional sets, and gets current power policy information based on the InformationLevel. Arguments: InputBuffer - Input to set InformationLevel information. InputBufferLength - Size, in bytes, of InputBuffer OutputBuffer - Buffer to return InformationLevel information. OutputBufferLength - Size, in bytes, of OutputBuffer Return Value: Status --*/ { NTSTATUS Status; PVOID ReturnBuffer; ULONG ReturnBufferLength; KPROCESSOR_MODE PreviousMode; PPOWER_STATE_HANDLER PowerHandler; PBOOLEAN CapFlag; PVOID CapBuffer; BOOLEAN Enable; ULONG HandlerType; SYSTEM_POWER_STATE RtcWake; BOOLEAN CheckForWork = InputBuffer ? TRUE : FALSE; union { PROCESSOR_POWER_POLICY ProcessorPolicy; SYSTEM_POWER_POLICY PowerPolicy; SYSTEM_BATTERY_STATE PowerState; SYSTEM_POWER_INFORMATION SysInfo; PROCESSOR_POWER_INFORMATION ProcInfo[MAXIMUM_PROCESSORS]; SYSTEM_POWER_CAPABILITIES PowerCapabilities; EXECUTION_STATE SystemExecutionState; } Buf; PAGED_CODE(); Status = STATUS_SUCCESS; ReturnBuffer = NULL; ReturnBufferLength = 0; CapBuffer = NULL; PreviousMode = KeGetPreviousMode(); PopAcquirePolicyLock (); try { // // If caller is user mode make some verifications // if (PreviousMode != KernelMode) { // // Is caller attempting to set anything? // if (InputBuffer) { if (InformationLevel != VerifySystemPolicyAc && InformationLevel != VerifySystemPolicyDc && InformationLevel != VerifyProcessorPowerPolicyAc && InformationLevel != VerifyProcessorPowerPolicyDc) { // // Make access check // if (InformationLevel == SystemReserveHiberFile) { // // Only allow callers that have create pagefile privilege // to enable/disable the hibernate file // if (!SeSinglePrivilegeCheck(SeCreatePagefilePrivilege,PreviousMode)) { ExRaiseStatus (STATUS_PRIVILEGE_NOT_HELD); } } else { if (!SeSinglePrivilegeCheck( SeShutdownPrivilege, PreviousMode )) { ExRaiseStatus (STATUS_PRIVILEGE_NOT_HELD); } } } // // Verify input addresses // ProbeForRead ( InputBuffer, InputBufferLength, InputBufferLength >= sizeof (ULONG) ? sizeof(ULONG) : sizeof(UCHAR) ); } // // Verify output addresses // if (OutputBuffer) { ProbeForWrite (OutputBuffer, OutputBufferLength, sizeof(ULONG)); } } // // Handle request // switch (InformationLevel) { case SystemPowerPolicyAc: // // We can be asked to set the AC policy through this mechanism // if (InputBuffer) { PopApplyPolicy ( TRUE, TRUE, (PSYSTEM_POWER_POLICY) InputBuffer, InputBufferLength ); } // // Return current AC policy // ReturnBuffer = &PopAcPolicy; ReturnBufferLength = sizeof(PopAcPolicy); break; case SystemPowerPolicyDc: // // We can be asked to set the DC policy through this mechanism // if (InputBuffer) { PopApplyPolicy ( TRUE, FALSE, (PSYSTEM_POWER_POLICY) InputBuffer, InputBufferLength ); } // // Return current DC policy // ReturnBuffer = &PopDcPolicy; ReturnBufferLength = sizeof(PopDcPolicy); break; case ProcessorPowerPolicyAc: // // We can be asked to set the AC processor policy // through this mechanism // if (InputBuffer) { PopApplyProcessorPolicy( TRUE, TRUE, (PPROCESSOR_POWER_POLICY) InputBuffer, InputBufferLength ); } // // Return current AC processor policy // ReturnBuffer = &PopAcProcessorPolicy; ReturnBufferLength = sizeof(PopAcProcessorPolicy); break; case ProcessorPowerPolicyDc: // // We can be asked to set the DC processor policy // through this mechanism // if (InputBuffer) { PopApplyProcessorPolicy( TRUE, FALSE, (PPROCESSOR_POWER_POLICY) InputBuffer, InputBufferLength ); } // // Return current DC processor policy // ReturnBuffer = &PopDcProcessorPolicy; ReturnBufferLength = sizeof(PopDcProcessorPolicy); break; case AdministratorPowerPolicy: if (InputBuffer) { if (PopUserIsAdmin()) { PopApplyAdminPolicy( TRUE, (PADMINISTRATOR_POWER_POLICY) InputBuffer, InputBufferLength ); PopResetCurrentPolicies (); } else { Status = STATUS_ACCESS_DENIED; } } // // Return administrator policy // ReturnBuffer = &PopAdminPolicy; ReturnBufferLength = sizeof(PopAdminPolicy); break; case VerifySystemPolicyAc: if (InputBuffer && OutputBuffer) { if (InputBufferLength >= sizeof (SYSTEM_POWER_POLICY)) { PopVerifyPowerPolicy (TRUE, InputBuffer, &Buf.PowerPolicy); ReturnBuffer = &Buf.PowerPolicy; ReturnBufferLength = sizeof(Buf.PowerPolicy); } else { Status = STATUS_BUFFER_TOO_SMALL; } } else { Status = STATUS_INVALID_PARAMETER; } break; case VerifySystemPolicyDc: if (InputBuffer && OutputBuffer) { if (InputBufferLength >= sizeof (SYSTEM_POWER_POLICY)) { PopVerifyPowerPolicy (FALSE, InputBuffer, &Buf.PowerPolicy); ReturnBuffer = &Buf.PowerPolicy; ReturnBufferLength = sizeof(Buf.PowerPolicy); } else { Status = STATUS_BUFFER_TOO_SMALL; } } else { Status = STATUS_INVALID_PARAMETER; } break; case VerifyProcessorPowerPolicyAc: if (InputBuffer && OutputBuffer) { if (InputBufferLength >= sizeof (PROCESSOR_POWER_POLICY)) { PopVerifyProcessorPowerPolicy( TRUE, InputBuffer, &Buf.ProcessorPolicy ); ReturnBuffer = &Buf.ProcessorPolicy; ReturnBufferLength = sizeof(Buf.ProcessorPolicy); } else { Status = STATUS_BUFFER_TOO_SMALL; } } else { Status = STATUS_INVALID_PARAMETER; } break; case VerifyProcessorPowerPolicyDc: if (InputBuffer && OutputBuffer) { if (InputBufferLength >= sizeof (PROCESSOR_POWER_POLICY)) { PopVerifyProcessorPowerPolicy( FALSE, InputBuffer, &Buf.ProcessorPolicy ); ReturnBuffer = &Buf.ProcessorPolicy; ReturnBufferLength = sizeof(Buf.ProcessorPolicy); } else { Status = STATUS_BUFFER_TOO_SMALL; } } else { Status = STATUS_INVALID_PARAMETER; } break; case SystemPowerPolicyCurrent: if (InputBuffer) { ExRaiseStatus (STATUS_INVALID_PARAMETER); } // // Return current policy // ReturnBuffer = PopPolicy; ReturnBufferLength = sizeof(PopAcPolicy); break; case ProcessorPowerPolicyCurrent: if (InputBuffer) { ExRaiseStatus (STATUS_INVALID_PARAMETER); } // // Return current policy // ReturnBuffer = PopProcessorPolicy; ReturnBufferLength = sizeof(PopAcProcessorPolicy); break; case SystemPowerCapabilities: // if we allow the simulation of capabilities (for testing), then // let it through if (InputBuffer) { if ((PopSimulate & POP_SIM_CAPABILITIES) && InputBufferLength == sizeof(PopCapabilities)) { memcpy (&PopCapabilities, InputBuffer, InputBufferLength); PopResetCurrentPolicies (); PopSetNotificationWork (PO_NOTIFY_CAPABILITIES); } else { ExRaiseStatus (STATUS_INVALID_PARAMETER); } } PopCapabilities.FullWake = (PopFullWake & PO_FULL_WAKE_STATUS) ? TRUE : FALSE; PopCapabilities.DiskSpinDown = PopAttributes[POP_DISK_SPINDOWN_ATTRIBUTE].Count ? TRUE : FALSE; PopFilterCapabilities(&PopCapabilities, &Buf.PowerCapabilities); ReturnBuffer = &Buf.PowerCapabilities; ReturnBufferLength = sizeof(PopCapabilities); break; case SystemBatteryState: PopCurrentPowerState (&Buf.PowerState); ReturnBuffer = &Buf.PowerState; ReturnBufferLength = sizeof(Buf.PowerState); break; case SystemPowerStateHandler: // // Caller must be kernel mode with the proper parameters // if (PreviousMode != KernelMode || OutputBuffer || !InputBuffer) { ExRaiseStatus (STATUS_INVALID_PARAMETER); } // // Handler can only be registered once // PowerHandler = (PPOWER_STATE_HANDLER) InputBuffer; HandlerType = PowerHandler->Type; if (HandlerType >= PowerStateMaximum) { ExRaiseStatus (STATUS_INVALID_PARAMETER); } // // He can only be registered once UNLESS it's the // PowerStateShutdownOff handler. That's because // we've set a default shutdown handler and would // sure welcome someone else (e.g. hal) to come along // and overwrite our default. // if( (PopPowerStateHandlers[HandlerType].Handler) ) { // // There's already a handler here. The only way // we're going to let this request through is if // they're setting the PowerStateShutdownOff // handler *AND* the current handler is pointing // to PopShutdownHandler(). // if( !((HandlerType == PowerStateShutdownOff) && (PopPowerStateHandlers[HandlerType].Handler == PopShutdownHandler)) ) { ExRaiseStatus (STATUS_INVALID_PARAMETER); } } // // Set new handler // PopPowerStateHandlers[HandlerType] = *PowerHandler; PopPowerStateHandlers[HandlerType].Spare[0] = 0; PopPowerStateHandlers[HandlerType].Spare[1] = 0; PopPowerStateHandlers[HandlerType].Spare[2] = 0; CapFlag = NULL; RtcWake = PowerSystemUnspecified; switch (HandlerType) { case PowerStateSleeping1: if (!(PopSimulate & POP_IGNORE_S1)) { CapFlag = &PopCapabilities.SystemS1; } RtcWake = PowerSystemSleeping1; break; case PowerStateSleeping2: if (!(PopSimulate & POP_IGNORE_S2)) { CapFlag = &PopCapabilities.SystemS2; } RtcWake = PowerSystemSleeping2; break; case PowerStateSleeping3: if (!(PopSimulate & POP_IGNORE_S3)) { CapFlag = &PopCapabilities.SystemS3; } RtcWake = PowerSystemSleeping3; break; case PowerStateSleeping4: if (!(PopSimulate & POP_IGNORE_S4)) { CapFlag = &PopCapabilities.SystemS4; } RtcWake = PowerSystemHibernate; break; case PowerStateShutdownOff: CapFlag = &PopCapabilities.SystemS5; break; default: break; } if (!PopPowerStateHandlers[HandlerType].RtcWake) { RtcWake = PowerSystemUnspecified; } if (RtcWake > PopCapabilities.RtcWake) { PopCapabilities.RtcWake = RtcWake; } if (CapFlag) { PopSetCapability (CapFlag); } break; case SystemPowerStateNotifyHandler: // // Caller must be kernel mode with the proper parameters // if (PreviousMode != KernelMode || OutputBuffer) { ExRaiseStatus (STATUS_INVALID_PARAMETER); } // // Notify handler can only be registered once. // if (PopPowerStateNotifyHandler.Handler && ((PPOWER_STATE_NOTIFY_HANDLER)InputBuffer)->Handler) { ExRaiseStatus (STATUS_INVALID_PARAMETER); } // // Set new handler // ASSERT(InputBufferLength == sizeof(POWER_STATE_NOTIFY_HANDLER)); RtlCopyMemory(&PopPowerStateNotifyHandler, InputBuffer, sizeof(POWER_STATE_NOTIFY_HANDLER)); break; case ProcessorStateHandler: case ProcessorStateHandler2: // // Caller must be kernel mode with the proper parameters // if (PreviousMode != KernelMode || OutputBuffer || !InputBuffer) { ExRaiseStatus (STATUS_INVALID_PARAMETER); } // // Install handlers // try { if (InformationLevel == ProcessorStateHandler2) { PopInitProcessorStateHandlers2 ((PPROCESSOR_STATE_HANDLER2) InputBuffer); } else { PopInitProcessorStateHandlers ((PPROCESSOR_STATE_HANDLER) InputBuffer); } } except (PopExceptionFilter(GetExceptionInformation(), FALSE)) { } // // Reset policies as capabilities may have changed // PopResetCurrentPolicies (); break; case SystemReserveHiberFile: // // If previous mode isn't kernel mode, change it // if (InputBufferLength != sizeof(BOOLEAN)) { ExRaiseStatus (STATUS_INVALID_PARAMETER); } Enable = *((PBOOLEAN) InputBuffer); if (PreviousMode != KernelMode) { PopReleasePolicyLock (FALSE); Status = ZwPowerInformation(SystemReserveHiberFile, &Enable, sizeof (Enable), NULL, 0); PopAcquirePolicyLock (); break; } try { Status = PopEnableHiberFile (Enable); } except (PopExceptionFilter(GetExceptionInformation(), FALSE)) { } break; case SystemPowerInformation: Buf.SysInfo.MaxIdlenessAllowed = PopSIdle.Sensitivity; Buf.SysInfo.Idleness = PopSIdle.Idleness; Buf.SysInfo.TimeRemaining = (PopSIdle.Timeout - PopSIdle.Time) * SYS_IDLE_WORKER; Buf.SysInfo.CoolingMode = (UCHAR) PopCoolingMode; ReturnBuffer = &Buf.SysInfo; ReturnBufferLength = sizeof(Buf.SysInfo); break; case ProcessorInformation: { ULONG length; length = sizeof(PROCESSOR_POWER_INFORMATION) * MAXIMUM_PROCESSORS; if (length > OutputBufferLength) { length = OutputBufferLength; } PopProcessorInformation ( Buf.ProcInfo, length, &ReturnBufferLength ); ReturnBuffer = &Buf.ProcInfo; break; } case LastWakeTime: // // This is output only // if (InputBuffer) { ExRaiseStatus(STATUS_INVALID_PARAMETER); } ReturnBuffer = &PopAction.WakeTime; ReturnBufferLength = sizeof(PopAction.WakeTime); break; case LastSleepTime: // // This is output only // // We don't check for work here as USER calls into here while in a power callout // and if we check for work at that point, promotion to hibernate doesn't work right. // CheckForWork = FALSE; if (InputBuffer) { ExRaiseStatus(STATUS_INVALID_PARAMETER); } ReturnBuffer = &PopAction.SleepTime; ReturnBufferLength = sizeof(PopAction.SleepTime); break; case SystemExecutionState: // // This is output only // if (InputBuffer) { ExRaiseStatus(STATUS_INVALID_PARAMETER); } Buf.SystemExecutionState = 0; ReturnBuffer = &Buf.SystemExecutionState; ReturnBufferLength = sizeof(Buf.SystemExecutionState); if (PopAttributes[POP_SYSTEM_ATTRIBUTE].Count) { Buf.SystemExecutionState |= ES_SYSTEM_REQUIRED; } if (PopAttributes[POP_DISPLAY_ATTRIBUTE].Count) { Buf.SystemExecutionState |= ES_DISPLAY_REQUIRED; } if (PopAttributes[POP_USER_ATTRIBUTE].Count) { Buf.SystemExecutionState |= ES_USER_PRESENT; } break; default: ExRaiseStatus (STATUS_INVALID_PARAMETER); } // // If there's a return buffer, return it // if (NT_SUCCESS(Status) && OutputBuffer && ReturnBuffer) { if (OutputBufferLength < ReturnBufferLength) { Status = STATUS_BUFFER_TOO_SMALL; } else { memcpy (OutputBuffer, ReturnBuffer, ReturnBufferLength); } } } except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); } // // Done. Release resources // if (CapBuffer) { ExFreePool(CapBuffer); } PopReleasePolicyLock (CheckForWork); return Status; } VOID PopApplyAdminPolicy ( IN BOOLEAN UpdateRegistry, IN PADMINISTRATOR_POWER_POLICY NewPolicy, IN ULONG PolicyLength ) /*++ Routine Description: N.B. PopPolicyLock must be held. Arguments: UpdateRegistry - TRUE if the policy being applied should be set in the register as the current policy NewPolicy - The policy to apply Return Value: None --*/ { ADMINISTRATOR_POWER_POLICY Policy; UNICODE_STRING UnicodeString; HANDLE handle; NTSTATUS Status; if (PolicyLength < sizeof (ADMINISTRATOR_POWER_POLICY)) { ExRaiseStatus (STATUS_BUFFER_TOO_SMALL); } if (PolicyLength > sizeof (ADMINISTRATOR_POWER_POLICY)) { ExRaiseStatus (STATUS_BUFFER_OVERFLOW); } memcpy (&Policy, NewPolicy, sizeof(Policy)); // // Verify values fall within proper range // if (Policy.MinSleep < PowerSystemSleeping1 || Policy.MinSleep > PowerSystemHibernate || Policy.MinSleep < PowerSystemSleeping1 || Policy.MaxSleep > PowerSystemHibernate || Policy.MinSleep > Policy.MaxSleep || Policy.MinVideoTimeout > Policy.MaxVideoTimeout || Policy.MinSpindownTimeout > Policy.MaxSpindownTimeout) { ExRaiseStatus (STATUS_INVALID_PARAMETER); } // // If the policy hasn't changed, return // if (!memcmp (&Policy, &PopAdminPolicy, sizeof(Policy))) { return ; } // // Change it // memcpy (&PopAdminPolicy, &Policy, sizeof(Policy)); // // Update registry copy of policy // if (UpdateRegistry) { Status = PopOpenPowerKey (&handle); if (NT_SUCCESS(Status)) { RtlInitUnicodeString (&UnicodeString, PopAdminRegName); ZwSetValueKey ( handle, &UnicodeString, 0L, REG_BINARY, &Policy, sizeof(ADMINISTRATOR_POWER_POLICY) ); ZwClose (handle); } } } VOID PopApplyPolicy ( IN BOOLEAN UpdateRegistry, IN BOOLEAN AcPolicy, IN PSYSTEM_POWER_POLICY NewPolicy, IN ULONG PolicyLength ) /*++ Routine Description: Update Dest policy to be Source policy. N.B. PopPolicyLock must be held. Arguments: UpdateRegistry - TRUE if the policy being applied should be set in the register as the current policy AcPolicy - TRUE if the new policy is for the systems AC policy, FALSE for the DC policy NewPolicy - The policy to apply Return Value: None --*/ { ULONG i; BOOLEAN DischargeChanged; SYSTEM_POWER_POLICY OrigPolicy, Policy; PSYSTEM_POWER_POLICY SystemPolicy; PSYSTEM_POWER_LEVEL DPolicy, SPolicy; UNICODE_STRING UnicodeString; HANDLE handle; NTSTATUS Status; const WCHAR* RegName; // // Setup for system policy change // if (AcPolicy) { RegName = PopAcRegName; SystemPolicy = &PopAcPolicy; } else { RegName = PopDcRegName; SystemPolicy = &PopDcPolicy; } // // Convert policy to current system capabilities // if (PolicyLength < sizeof (SYSTEM_POWER_POLICY)) { ExRaiseStatus (STATUS_BUFFER_TOO_SMALL); } if (PolicyLength > sizeof (SYSTEM_POWER_POLICY)) { ExRaiseStatus (STATUS_BUFFER_OVERFLOW); } memcpy (&OrigPolicy, NewPolicy, sizeof (SYSTEM_POWER_POLICY)); PopVerifyPowerPolicy (AcPolicy, &OrigPolicy, &Policy); // // If the policy hasn't changed, return // if (!memcmp (&Policy, SystemPolicy, sizeof(SYSTEM_POWER_POLICY))) { return ; } // // Check if any discharge setting has changed // DischargeChanged = FALSE; DPolicy = SystemPolicy->DischargePolicy; SPolicy = Policy.DischargePolicy; for (i=0; i < PO_NUM_POWER_LEVELS; i++) { if (SPolicy[i].Enable != DPolicy[i].Enable) { DischargeChanged = TRUE; break; } if (SPolicy[i].Enable && memcmp (&SPolicy[i], &DPolicy[i], sizeof (SYSTEM_POWER_LEVEL))) { DischargeChanged = TRUE; break; } } // // Change it // memcpy (SystemPolicy, &Policy, sizeof(SYSTEM_POWER_POLICY)); // // If this is the active policy, changes need to take effect now // if (SystemPolicy == PopPolicy) { // // Changing the active policy // PopSetNotificationWork (PO_NOTIFY_POLICY | PO_NOTIFY_POLICY_CALLBACK); // // If any discharge policy has changed, reset the composite // battery triggers // if (DischargeChanged) { PopResetCBTriggers (PO_TRG_SET | PO_TRG_SYSTEM | PO_TRG_USER); } // // Recompute thermal throttle and cooling mode // PopApplyThermalThrottle (); // // Recompute system idle values // PopInitSIdle (); } // // Update registry copy of policy // if (UpdateRegistry) { Status = PopOpenPowerKey (&handle); if (NT_SUCCESS(Status)) { RtlInitUnicodeString (&UnicodeString, RegName); ZwSetValueKey ( handle, &UnicodeString, 0L, REG_BINARY, &OrigPolicy, sizeof (SYSTEM_POWER_POLICY) ); ZwClose (handle); } } } VOID PopApplyProcessorPolicy ( IN BOOLEAN UpdateRegistry, IN BOOLEAN AcPolicy, IN PPROCESSOR_POWER_POLICY NewPolicy, IN ULONG PolicyLength ) /*++ Routine Description: Update Dest policy to be Source policy. N.B. PopPolicyLock must be held. Arguments: UpdateRegistry - TRUE if the policy being applied should be set in the register as the current policy AcPolicy - TRUE if the new policy is for the systems AC policy, FALSE for the DC policy NewPolicy - The policy to apply Return Value: None --*/ { PROCESSOR_POWER_POLICY OrigPolicy; PROCESSOR_POWER_POLICY Policy; PPROCESSOR_POWER_POLICY SystemPolicy; UNICODE_STRING UnicodeString; HANDLE handle; NTSTATUS Status; const WCHAR* RegName; // // Setup for system policy change // if (AcPolicy) { RegName = PopAcProcessorRegName; SystemPolicy = &PopAcProcessorPolicy; } else { RegName = PopDcProcessorRegName; SystemPolicy = &PopDcProcessorPolicy; } // // Convert policy to current system capabilities // if (PolicyLength < sizeof (PROCESSOR_POWER_POLICY)) { ExRaiseStatus (STATUS_BUFFER_TOO_SMALL); } if (PolicyLength > sizeof (PROCESSOR_POWER_POLICY)) { ExRaiseStatus (STATUS_BUFFER_OVERFLOW); } memcpy (&OrigPolicy, NewPolicy, sizeof (PROCESSOR_POWER_POLICY)); PopVerifyProcessorPowerPolicy (AcPolicy, &OrigPolicy, &Policy); // // If the policy hasn't changed, return // if (!memcmp (&Policy, SystemPolicy, sizeof(PROCESSOR_POWER_POLICY))) { return ; } // // Change it // memcpy (SystemPolicy, &Policy, sizeof(PROCESSOR_POWER_POLICY)); // // If this is the active policy, changes need to take effect now // if (SystemPolicy == PopProcessorPolicy) { // // Changing the active policy // PopSetNotificationWork( PO_NOTIFY_PROCESSOR_POLICY | PO_NOTIFY_PROCESSOR_POLICY_CALLBACK ); // // Recompute current throttle policy.... // PopUpdateAllThrottles(); PopIdleUpdateIdleHandlers(); } // // Update registry copy of policy // if (UpdateRegistry) { Status = PopOpenPowerKey (&handle); if (NT_SUCCESS(Status)) { RtlInitUnicodeString (&UnicodeString, RegName); ZwSetValueKey ( handle, &UnicodeString, 0L, REG_BINARY, &OrigPolicy, sizeof (PROCESSOR_POWER_POLICY) ); ZwClose (handle); } } } VOID PopVerifyPowerPolicy ( IN BOOLEAN Ac, IN PSYSTEM_POWER_POLICY InputPolicy, OUT PSYSTEM_POWER_POLICY PowerPolicy ) /*++ Routine Description: This function copies the InputPolicy to the output PowerPolicy and adjusts it to represent system capabilities and other requirements. If the input policy has some setting which can not be adjusted, an error status is raised. N.B. PopPolicyLock must be held. Arguments: Ac - Policy is to be adjusted as an AC or DC policy InputPolicy - The source policy to adjust PowerPolicy - The returned policy which can be used as is Return Value: None --*/ { ULONG i; PSYSTEM_POWER_LEVEL DPolicy; PAGED_CODE(); // // Setup initial output structure // memcpy (PowerPolicy, InputPolicy, sizeof (SYSTEM_POWER_POLICY)); // // Only revision 1 currently supported // if (PowerPolicy->Revision != 1) { ExRaiseStatus (STATUS_INVALID_PARAMETER); } // // some win9x upgrades or very old NT builds might have maxsleep set to S4. Fix that here. // if (PowerPolicy->MaxSleep > PowerSystemSleeping3) { PowerPolicy->MaxSleep = PowerSystemSleeping3; } // // Limit settings to administrator policy // if (PowerPolicy->MinSleep < PopAdminPolicy.MinSleep) { PowerPolicy->MinSleep = PopAdminPolicy.MinSleep; } if (PowerPolicy->MaxSleep > PopAdminPolicy.MaxSleep) { PowerPolicy->MaxSleep = PopAdminPolicy.MaxSleep; } if (PowerPolicy->VideoTimeout < PopAdminPolicy.MinVideoTimeout) { PowerPolicy->VideoTimeout = PopAdminPolicy.MinVideoTimeout; } if (PowerPolicy->VideoTimeout > PopAdminPolicy.MaxVideoTimeout) { PowerPolicy->VideoTimeout = PopAdminPolicy.MaxVideoTimeout; } if (PowerPolicy->SpindownTimeout < PopAdminPolicy.MinSpindownTimeout) { PowerPolicy->SpindownTimeout = PopAdminPolicy.MinSpindownTimeout; } if ((ULONG) PowerPolicy->SpindownTimeout > (ULONG) PopAdminPolicy.MaxSpindownTimeout) { PowerPolicy->SpindownTimeout = PopAdminPolicy.MaxSpindownTimeout; } // // Verify all the power action policies, and adjust all system // states to match what is supported by this platform // PopVerifyPowerActionPolicy(&PowerPolicy->PowerButton); PopVerifyPowerActionPolicy(&PowerPolicy->SleepButton); PopVerifyPowerActionPolicy(&PowerPolicy->LidClose); PopVerifyPowerActionPolicy(&PowerPolicy->Idle); PopVerifySystemPowerState( &PowerPolicy->LidOpenWake, SubstituteLightestOverallDownwardBounded ); PopVerifySystemPowerState( &PowerPolicy->MinSleep, SubstituteLightestOverallDownwardBounded ); PopVerifySystemPowerState( &PowerPolicy->MaxSleep, SubstituteLightestOverallDownwardBounded ); PopVerifySystemPowerState( &PowerPolicy->ReducedLatencySleep, SubstituteLightestOverallDownwardBounded ); for (i = 0; i < PO_NUM_POWER_LEVELS; i++) { DPolicy = &PowerPolicy->DischargePolicy[i]; if (DPolicy->Enable) { PopVerifyPowerActionPolicy ( &PowerPolicy->DischargePolicy[i].PowerPolicy ); PopVerifySystemPowerState( &PowerPolicy->DischargePolicy[i].MinSystemState, SubstituteLightestOverallDownwardBounded ); // // If the action is standby, make sure the min state is S3 or lighter // if ((PowerPolicy->DischargePolicy[i].PowerPolicy.Action == PowerActionSleep) && (PowerPolicy->DischargePolicy[i].MinSystemState > PowerSystemSleeping3)) { PowerPolicy->DischargePolicy[i].MinSystemState = PowerSystemSleeping3; PopVerifySystemPowerState( &PowerPolicy->DischargePolicy[i].MinSystemState, SubstituteLightestOverallDownwardBounded ); } if (DPolicy->BatteryLevel > 100) { DPolicy->BatteryLevel = 100; } } } PopVerifyPowerActionPolicy(&PowerPolicy->OverThrottled); // // Adjust other values based on capabilities // if (!PopCapabilities.ProcessorThrottle) { PowerPolicy->OptimizeForPower = FALSE; PowerPolicy->FanThrottleTolerance = PO_NO_FAN_THROTTLE; PowerPolicy->ForcedThrottle = PO_NO_FORCED_THROTTLE; } if (!PopCapabilities.ThermalControl) { PowerPolicy->FanThrottleTolerance = PO_NO_FAN_THROTTLE; } // // Sanity // if (!PowerPolicy->BroadcastCapacityResolution) { PowerPolicy->BroadcastCapacityResolution = 100; } // // If the system supports only S4 (legacy) there is no point in // idly hibernating the system as we can't turn it off anyway. // if ((PowerPolicy->Idle.Action == PowerActionHibernate) && (!PopCapabilities.SystemS5)) { PowerPolicy->Idle.Action = PowerActionNone; } if (PowerPolicy->Idle.Action == PowerActionNone) { PowerPolicy->IdleTimeout = 0; } if (PowerPolicy->IdleTimeout && PowerPolicy->IdleTimeout < PO_MIN_IDLE_TIMEOUT) { PowerPolicy->IdleTimeout = PO_MIN_IDLE_TIMEOUT; } if (PowerPolicy->IdleSensitivity > 100 - PO_MIN_IDLE_SENSITIVITY) { PowerPolicy->IdleSensitivity = 100 - PO_MIN_IDLE_SENSITIVITY; } if ((PowerPolicy->IdleTimeout > 0) && (PowerPolicy->IdleSensitivity == 0)) { // // This is basically saying "timeout when the system has been idle // for X minutes, but never declare the system idle" This makes no // sense, so we will set the idle sensitivity to the minimum. // PowerPolicy->IdleSensitivity = 100 - PO_MIN_IDLE_SENSITIVITY; } if (PowerPolicy->MaxSleep < PowerPolicy->MinSleep) { PowerPolicy->MaxSleep = PowerPolicy->MinSleep; } if (PowerPolicy->ReducedLatencySleep > PowerPolicy->MinSleep) { PowerPolicy->ReducedLatencySleep = PowerPolicy->MinSleep; } // // Ignore whatever the user said what the minimum throttle and force the // system to pick whatever the hardware supports as the min throttle // PowerPolicy->MinThrottle = 0; // // Verify all the throttle percentages (in tenths of a percentage points, // so 1000 decimal = 100.0% and 777 = 77.7%) in terms of both their maximum // and minimum values. Note: Percent is based on POP_PERF_SCALE, which is // currently 128. So, actually, 1280 decimal = 100.0%. // PopVerifyThrottle(&PowerPolicy->FanThrottleTolerance, PO_MAX_FAN_THROTTLE); PopVerifyThrottle(&PowerPolicy->MinThrottle, PO_MIN_MIN_THROTTLE); PopVerifyThrottle(&PowerPolicy->ForcedThrottle, PowerPolicy->MinThrottle); if (PowerPolicy->FanThrottleTolerance != PO_NO_FAN_THROTTLE || PowerPolicy->ForcedThrottle != PO_NO_FORCED_THROTTLE) { PowerPolicy->OptimizeForPower = TRUE; } } VOID PopVerifyProcessorPowerPolicy ( IN BOOLEAN Ac, IN PPROCESSOR_POWER_POLICY InputPolicy, OUT PPROCESSOR_POWER_POLICY PowerPolicy ) /*++ Routine Description: This function copies the InputPolicy to the output PowerPolicy and adjusts it to represent processor capabilities and other requirements. If the input policy has some setting which can not be adjusted, an error status is raised. N.B. PopPolicyLock must be held. Arguments: Ac - Policy is to be adjusted as an AC or DC policy InputPolicy - The source policy to adjust PowerPolicy - The returned policy which can be used as is Return Value: None --*/ { PPROCESSOR_POWER_POLICY_INFO pPolicy; ULONG i; PAGED_CODE(); // // Setup initial output structure // memcpy (PowerPolicy, InputPolicy, sizeof(PROCESSOR_POWER_POLICY)); // // Only revision 1 currently supported // if (PowerPolicy->Revision != 1) { ExRaiseStatus (STATUS_INVALID_PARAMETER); } // // Sanity check each level of the policy // for (i = 0; i < 3; i++) { pPolicy = &(PowerPolicy->Policy[i]); // // We don't allow demotion to Idle0 unless the machine is MP // if (i == 0 && KeNumberProcessors == 1) { pPolicy->DemotePercent = 0; pPolicy->AllowDemotion = 0; } // // Don't allow promotions past the last state // if (i == 2) { pPolicy->PromotePercent = 0; pPolicy->PromoteLimit = 0; pPolicy->AllowPromotion = 0; } // // Time check must be smaller than Demote Limit (if there is one) // if (pPolicy->TimeCheck < pPolicy->DemoteLimit) { pPolicy->TimeCheck = pPolicy->DemoteLimit; } if (pPolicy->DemotePercent == 0 && pPolicy->AllowPromotion && pPolicy->TimeCheck < pPolicy->PromoteLimit) { pPolicy->TimeCheck = pPolicy->PromoteLimit; } } if (PowerPolicy->DynamicThrottle >= PO_THROTTLE_MAXIMUM) { if (Ac) { PowerPolicy->DynamicThrottle = PO_THROTTLE_NONE; } else { PowerPolicy->DynamicThrottle = PO_THROTTLE_CONSTANT; } } } VOID PopVerifyThrottle ( IN PUCHAR Throttle, IN UCHAR Min ) { ULONG i; UCHAR t; t = *Throttle; // // Make sure max is POP_PERF_SCALE% // if (t > POP_PERF_SCALE) { t = POP_PERF_SCALE; } // // Make sure it's not below the specificied min. // if (t < Min) { t = Min; } // // Round the throttle up to the first supported value // Note that we don't need to check against ProcessorMinThrottle // or any other value since PopRoundThrottle() will do that for us. // PopRoundThrottle(t, NULL, Throttle, NULL, NULL); } BOOLEAN PopVerifyPowerActionPolicy ( IN PPOWER_ACTION_POLICY Action ) /*++ Routine Description: This function checks & edits the input Action to represent system capabilities and other requirements. N.B. PopPolicyLock must be held. Arguments: Action - Power action policy to check / verify Return Value: Boolean to indicate action was demoted to a disabled state --*/ { POWER_ACTION LastAction; BOOLEAN Disabled; BOOLEAN HiberSupport; ULONG SleepCount; NTSTATUS Status; PNP_VETO_TYPE VetoType; SYSTEM_POWER_CAPABILITIES PowerCapabilities; PAGED_CODE(); Disabled = FALSE; // // Verify reserved flag bits are clear // if (Action->Flags & !( POWER_ACTION_QUERY_ALLOWED | POWER_ACTION_UI_ALLOWED | POWER_ACTION_OVERRIDE_APPS | POWER_ACTION_LOCK_CONSOLE | POWER_ACTION_DISABLE_WAKES | POWER_ACTION_CRITICAL)) { // // N.B. - Later POWER_ACTION_LIGHTEST_FIRST? // // reserved bit set in action flags ExRaiseStatus (STATUS_INVALID_PARAMETER); } // // If the action is critical, then do not notify any applications // if (Action->Flags & POWER_ACTION_CRITICAL) { Action->Flags &= ~(POWER_ACTION_QUERY_ALLOWED | POWER_ACTION_UI_ALLOWED); Action->Flags |= POWER_ACTION_OVERRIDE_APPS; } // // If any legacy drivers are installed, then no sleeping states // are allowed at all. // if ((Action->Action == PowerActionSleep) || (Action->Action == PowerActionHibernate)) { Status = IoGetLegacyVetoList(NULL, &VetoType); if (NT_SUCCESS(Status) && (VetoType != PNP_VetoTypeUnknown)) { Action->Action = PowerActionNone; return(TRUE); } } // // Some components may disable some capabilities. So filter them here. // PopFilterCapabilities(&PopCapabilities, &PowerCapabilities); // // Count the supported sleeping states // SleepCount = 0; HiberSupport = FALSE; if (PowerCapabilities.SystemS1) { SleepCount += 1; } if (PowerCapabilities.SystemS2) { SleepCount += 1; } if (PowerCapabilities.SystemS3) { SleepCount += 1; } if (PowerCapabilities.SystemS4 && PowerCapabilities.HiberFilePresent) { HiberSupport = TRUE; } // // Verify the requested action is supported. // do { LastAction = Action->Action; switch (Action->Action) { case PowerActionNone: // can do nothing, not a problem break; case PowerActionReserved: // used to be doze action. does not exist anymore make it sleep Action->Action = PowerActionSleep; break; case PowerActionSleep: // // if no sleeping states supported, adjust action to be none // if (SleepCount < 1) { Disabled = TRUE; Action->Action = PowerActionNone; } break; case PowerActionHibernate: // // if no hibernate support, try sleep // if (!HiberSupport) { Action->Action = PowerActionSleep; } break; case PowerActionShutdown: case PowerActionShutdownReset: // all systems support shutdown & shutdown reset break; case PowerActionShutdownOff: // If action shutdown is not available, use Shutdown if (!PowerCapabilities.SystemS5) { Action->Action = PowerActionShutdown; } break; case PowerActionWarmEject: // // This is a system action associated with an individual device. // break; default: // unknown power action setting ExRaiseStatus (STATUS_INVALID_PARAMETER); } } while (LastAction != Action->Action); return Disabled; } VOID PopAdvanceSystemPowerState ( IN OUT PSYSTEM_POWER_STATE PowerState, IN POP_SUBSTITUTION_POLICY SubstitutionPolicy, IN SYSTEM_POWER_STATE LightestSystemState, IN SYSTEM_POWER_STATE DeepestSystemState ) /*++ Routine Description: This function uses the substitution policy to advance the sleep state (lighten or deepen) as appropriate. N.B. PopPolicyLock must be held. Arguments: PowerState - System power state to advance. SubstitutionPolicy - see definitions in pop.h. Return Value: None --*/ { SYSTEM_POWER_STATE State; BOOLEAN HibernateAllowed; PAGED_CODE(); // // Verify value is valid // State = *PowerState; if (State < PowerSystemSleeping1) { ExRaiseStatus (STATUS_INVALID_PARAMETER_1); } if (State >= PowerSystemShutdown) { // // There is nowhere else to go for these states. // *PowerState = PowerSystemWorking; return; } switch(SubstitutionPolicy) { case SubstituteLightestOverallDownwardBounded: *PowerState = (State - 1); PopVerifySystemPowerState(PowerState, SubstitutionPolicy); // // There are three cases to consider: // 1. We received in S1, which was previously validated. We try S0 // and it is automatically accepted. There are no other options // as we started in the lightest overall (S1). Thus we are // finished. // 2. We passed in Sx-1 for verification, but got back Sx. This // means we were already at the lightest state (Sx), and we've // exhausted the possibilities. Thus we are finished and so // we return PowerSystemWorking. // 3. We passed in Sx-1 and didn't get Sx. This means we've advanced // to another state, although it may be the last if Sx was S1, as // rule (1) is actually a special case of this rule. // if (*PowerState == State) { *PowerState = PowerSystemWorking; } break; case SubstituteLightenSleep: *PowerState = (State - 1); PopVerifySystemPowerState(PowerState, SubstitutionPolicy); break; case SubstituteDeepenSleep: // // Per above, Deepen goes straight into Hibernate. // if (State == PowerSystemHibernate) { *PowerState = PowerSystemWorking; break; } *PowerState = (State + 1); PopVerifySystemPowerState(PowerState, SubstitutionPolicy); break; default: ExRaiseStatus (STATUS_INVALID_PARAMETER_2); break; } if ((*PowerState != PowerSystemWorking) && ((*PowerState < LightestSystemState) || (*PowerState > DeepestSystemState))) { *PowerState = PowerSystemWorking; } } VOID PopVerifySystemPowerState ( IN OUT PSYSTEM_POWER_STATE PowerState, IN POP_SUBSTITUTION_POLICY SubstitutionPolicy ) /*++ Routine Description: This function checks & edits the input PowerState to represent system capabilities and other requirements. N.B. PopPolicyLock must be held. Arguments: PowerState - System power state to check / verify SubstitutionPolicy - See definitions in pop.h Return Value: None --*/ { SYSTEM_POWER_STATE State; BOOLEAN HibernateAllowed; PAGED_CODE(); // // Verify value is valid // State = *PowerState; if (State == PowerSystemUnspecified || State > PowerSystemShutdown) { ExRaiseStatus (STATUS_INVALID_PARAMETER_1); } // // PowerSystemShutdown is not allowed in any structures. It is generated // internally for the sole use of quering drivers before performing // a system shutdown // if (State == PowerSystemShutdown) { ExRaiseStatus (STATUS_INVALID_PARAMETER_1); } // // The working state is always supported // if (State == PowerSystemWorking) { return ; } // // Verify the power state is supported. If not, pick the next best state // HibernateAllowed = TRUE; switch(SubstitutionPolicy) { case SubstituteLightestOverallDownwardBounded: case SubstituteLightenSleep: // // In LightenSleep, we lighten the power state passed in until // we reach PowerStateWorking. Then we give up. // // In LightestOverall, instead of stopping, we turn around and // choose the lightest non-S0 sleep state overall, which may be // deeper than the one passed in. Note that we do *not* progress // into Hibernation though. // if (State == PowerSystemHibernate && (!PopCapabilities.SystemS4 || !PopCapabilities.HiberFilePresent)) { State = PowerSystemSleeping3; } if (State == PowerSystemSleeping3 && !PopCapabilities.SystemS3) { State = PowerSystemSleeping2; } if (State == PowerSystemSleeping2 && !PopCapabilities.SystemS2) { State = PowerSystemSleeping1; } if (State == PowerSystemSleeping1 && !PopCapabilities.SystemS1) { State = PowerSystemWorking; } if (State != PowerSystemWorking) { break; } if (SubstitutionPolicy != SubstituteLightestOverallDownwardBounded) { break; } // // Rounding down lead to PowerSystemWorking. Try to rounding up // towards deeper sleep states. Block the rounding at S3 however. // State = State + 1; HibernateAllowed = FALSE; // // Fall through... // case SubstituteDeepenSleep: if (State == PowerSystemSleeping1 && !PopCapabilities.SystemS1) { State = PowerSystemSleeping2; } if (State == PowerSystemSleeping2 && !PopCapabilities.SystemS2) { State = PowerSystemSleeping3; } if (State == PowerSystemSleeping3 && !PopCapabilities.SystemS3) { State = PowerSystemHibernate; } if (State == PowerSystemHibernate && (!HibernateAllowed || !PopCapabilities.SystemS4 || !PopCapabilities.HiberFilePresent)) { // nothing good supported, disable it State = PowerSystemWorking; } break; default: ExRaiseStatus (STATUS_INVALID_PARAMETER_2); break; } *PowerState = State; } VOID PopResetCurrentPolicies ( VOID ) /*++ Routine Description: Reads the current policies from the registry and applies them. N.B. PopPolicyLock must be held. Arguments: None Return Value: None --*/ { HANDLE handle; NTSTATUS Status; PSYSTEM_POWER_POLICY RegPolicy; UNICODE_STRING UnicodeString; ULONG Length; struct { KEY_VALUE_PARTIAL_INFORMATION Inf; union { SYSTEM_POWER_POLICY PowerPolicy; } Data; } PartialInformation; ASSERT_POLICY_LOCK_OWNED(); // // Initialize & open registry // RegPolicy = (PSYSTEM_POWER_POLICY) PartialInformation.Inf.Data; Status = PopOpenPowerKey (&handle); if (!NT_SUCCESS(Status)) { return ; } // // Read AC policy and apply it // RtlInitUnicodeString (&UnicodeString, PopAcRegName); Status = ZwQueryValueKey ( handle, &UnicodeString, KeyValuePartialInformation, &PartialInformation, sizeof (PartialInformation), &Length ); Length -= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data); if (!NT_SUCCESS(Status)) { PopDefaultPolicy (RegPolicy); Length = sizeof(SYSTEM_POWER_POLICY); } try { PopApplyPolicy (FALSE, TRUE, RegPolicy, Length); } except (EXCEPTION_EXECUTE_HANDLER) { ASSERT (GetExceptionCode()); } // // Read DC policy and apply it // RtlInitUnicodeString (&UnicodeString, PopDcRegName); Status = ZwQueryValueKey ( handle, &UnicodeString, KeyValuePartialInformation, &PartialInformation, sizeof (PartialInformation), &Length ); Length -= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data); if (!NT_SUCCESS(Status)) { PopDefaultPolicy (RegPolicy); Length = sizeof(SYSTEM_POWER_POLICY); } try { PopApplyPolicy (FALSE, FALSE, RegPolicy, Length); } except (EXCEPTION_EXECUTE_HANDLER) { ASSERT (GetExceptionCode()); } ZwClose (handle); } NTSTATUS PopNotifyPolicyDevice ( IN PVOID Notification, IN PVOID Context ) /*++ Routine Description: This function is the notinficant handle for when a new policy device appears. Arguments: Notification - PnP notification Context - Context registered on notification Return Value: None --*/ { PDEVICE_INTERFACE_CHANGE_NOTIFICATION Change; POP_POLICY_DEVICE_TYPE DeviceType; HANDLE DriverHandle; PDEVICE_OBJECT DeviceObject; OBJECT_ATTRIBUTES ObjA; IO_STATUS_BLOCK IOSB; PIRP Irp; PIO_STACK_LOCATION IrpSp; PAGED_CODE(); Change = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION) Notification; DeviceType = (POP_POLICY_DEVICE_TYPE) ((ULONG_PTR)Context); // // If it's not a device arrival, then we don't care // if (memcmp (&Change->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof (GUID))) { return STATUS_SUCCESS; } PopAcquirePolicyLock (); PopConnectToPolicyDevice (DeviceType, Change->SymbolicLinkName); PopReleasePolicyLock (TRUE); return STATUS_SUCCESS; } VOID PopConnectToPolicyDevice ( IN POP_POLICY_DEVICE_TYPE DeviceType, IN PUNICODE_STRING DriverName ) /*++ Routine Description: This function attempts to connect to the policy device specified. If the device is opened, the devices status IRP is allocated and sent to the device's IRP handler for initial dispatch. Arguments: DeviceType - Policy device type of device to connect DeviceName - Device name to attempt to open Return Value: If the device is connected, the *PresetFlag is set to TRUE and an initial IRP is queued for the IRP handler. --*/ { UNICODE_STRING UnicodeString; HANDLE DriverHandle; PDEVICE_OBJECT DeviceObject; PFILE_OBJECT FileObject; OBJECT_ATTRIBUTES ObjA; IO_STATUS_BLOCK IOSB; PIRP Irp; PIO_STACK_LOCATION IrpSp; PVOID Context; POP_IRP_HANDLER IrpHandler; PPOP_SWITCH_DEVICE SwitchDevice; PPOP_THERMAL_ZONE ThermalZone; NTSTATUS Status; PAGED_CODE(); ASSERT_POLICY_LOCK_OWNED(); Irp = NULL; // // If this is a new battery, then handle the composite battery device is // the device to open // if (DeviceType == PolicyDeviceBattery) { // // If the composite battery is already opened, kick the irp handler // if (PopCB.StatusIrp) { // Don't need to kick the IRP handler. When a new battery is added, // the battery tag for the composite battery will change, causing // the irp to complete. PoPrint(PO_WARN, ("PopConnectToPolicyDevice: Battery already connected - not done\n")); return ; } // // Try to open the composite battery now // RtlInitUnicodeString(&UnicodeString, PopCompositeBatteryName); DriverName = &UnicodeString; } // // Open the device // InitializeObjectAttributes( &ObjA, DriverName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0 ); Status = ZwOpenFile( &DriverHandle, SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, &ObjA, // Object &IOSB, // io status block FILE_SHARE_READ | FILE_SHARE_WRITE, // share access FILE_SYNCHRONOUS_IO_ALERT // open options ); if (!NT_SUCCESS(Status)) { PoPrint(PO_WARN, ("PopConnectToPolicyDevice: Device open failed\n")); goto Done; } // // Get a pointer to the device object // Status = ObReferenceObjectByHandle( DriverHandle, SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, // desired access NULL, KernelMode, &FileObject, NULL ); ASSERT (NT_SUCCESS(Status)); DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject); ObDereferenceObject(FileObject); ZwClose(DriverHandle); // // Get an IRP for the device // Irp = IoAllocateIrp ((CCHAR) (DeviceObject->StackSize + 1), FALSE); if (!Irp) { ObDereferenceObject(DeviceObject); goto Done; } IrpSp = IoGetNextIrpStackLocation(Irp); // // Setup based on device type // Context = NULL; IrpHandler = NULL; switch (DeviceType) { case PolicyDeviceSystemButton: SwitchDevice = ExAllocatePoolWithTag( NonPagedPool, sizeof (*SwitchDevice), POP_PSWT_TAG ); if (!SwitchDevice) { goto Done; } RtlZeroMemory (SwitchDevice, sizeof(*SwitchDevice)); SwitchDevice->IsInitializing = TRUE; SwitchDevice->Opened = TRUE; InsertTailList (&PopSwitches, &SwitchDevice->Link); IrpHandler = PopSystemButtonHandler; Context = SwitchDevice; break; case PolicyDeviceBattery: // // Loading up the composite battery - status irp is NULL. // PopSetCapability (&PopCapabilities.SystemBatteriesPresent); IrpHandler = PopCompositeBatteryDeviceHandler; PopCB.StatusIrp = Irp; break; case PolicyDeviceThermalZone: // // New thermal zone // ThermalZone = ExAllocatePoolWithTag ( NonPagedPool, sizeof (*ThermalZone), POP_THRM_TAG ); if (!ThermalZone) { goto Done; } // // Initialize thermal zone structure // RtlZeroMemory( ThermalZone, sizeof(POP_THERMAL_ZONE) ); KeInitializeTimer(&ThermalZone->PassiveTimer); KeInitializeDpc( &ThermalZone->PassiveDpc, PopThermalZoneDpc, ThermalZone ); ThermalZone->Mode = PO_TZ_INVALID_MODE; ThermalZone->ActivePoint = (UCHAR) -1; ThermalZone->PendingActivePoint = (UCHAR) -1; ThermalZone->Throttle = PO_TZ_NO_THROTTLE; ThermalZone->OverThrottled.Type = PolicyDeviceThermalZone; ThermalZone->OverThrottled.Flags = PO_TRG_SET; ThermalZone->Irp = Irp; // // Setup the capabilities of the thermal zones and get ready to // ask the thermal zone about itself... // PopSetCapability (&PopCapabilities.ThermalControl); Context = ThermalZone; IrpHandler = PopThermalDeviceHandler; // // Finally, add the thermal zone to the list of thermal zones // ExInterlockedInsertTailList( &PopThermal, &ThermalZone->Link, &PopThermalLock ); break; default: PopInternalError (POP_INFO); } // // Fill in values for IrpHandler dispatch // IrpSp->Parameters.Others.Argument1 = (PVOID) DeviceObject; IrpSp->Parameters.Others.Argument2 = (PVOID) Context; IrpSp->Parameters.Others.Argument3 = (PVOID) IrpHandler; IoSetNextIrpStackLocation (Irp); // // Fill in error to irp so irp handler will re-dispatch it // IrpSp = IoGetNextIrpStackLocation(Irp); Irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED; IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL; IrpSp->Parameters.DeviceIoControl.IoControlCode = 0; IrpSp->Parameters.DeviceIoControl.InputBufferLength = 0; IrpSp->Parameters.DeviceIoControl.OutputBufferLength = 0; // // Give irp to the completion handler which will dispatch it // PopCompletePolicyIrp (DeviceObject, Irp, Context); Irp = NULL; Done: if (Irp) { ObDereferenceObject( DeviceObject ); IoFreeIrp (Irp); } } POWER_ACTION PopMapInternalActionToIrpAction ( IN POWER_ACTION Action, IN SYSTEM_POWER_STATE SystemPowerState, IN BOOLEAN UnmapWarmEject ) /*++ Routine Description: This function maps an internal action and power state to the appropriate PowerAction a driver should see in it's S-IRP. Arguments: Action - The action we are using internally SystemPowerState - The system power state for that action UnmapWarmEject - If TRUE, PowerActionWarmEject is converted to PowerActionSleep or PowerActionHibernate as appropriate. Return Value: The appropriate PowerAction to place in the ShutdownType field of an S-IRP. --*/ { ASSERT( Action != PowerActionHibernate ); if (Action != PowerActionWarmEject) { // // We aren't doing a warm eject, so we simply return the original // power action unless it's the sleep is S4, in which case we switch // it to PowerActionHibernate. // return (SystemPowerState != PowerSystemHibernate) ? Action : PowerActionHibernate; } if (UnmapWarmEject) { // // This is a warm eject operation, but not neccessarily for this device. // return (SystemPowerState != PowerSystemHibernate) ? PowerActionSleep : PowerActionHibernate; } // // This is a warm eject operation, so we should only see a sleep state // (S1-S4). We do the check here because we could get a D0 request in // response to our S IRP, and stamp D-IRPs with the current power action. // ASSERT((SystemPowerState >= PowerSystemSleeping1) && (SystemPowerState <= PowerSystemHibernate)) ; return PowerActionWarmEject; } VOID PopFilterCapabilities( IN PSYSTEM_POWER_CAPABILITIES SourceCapabilities, OUT PSYSTEM_POWER_CAPABILITIES FilteredCapabilities ) /*++ Routine Description: This routine filters the actual reported capabilities of the system into the visible capabilities of the system. Some capabilities will be hidden based on the presence of legacy drivers. Arguments: SourceCapabilities - Supplies the original capabilities FilteredCapabilities - Returns the filtered capabilities. Return Value: None. --*/ { NTSTATUS Status; PNP_VETO_TYPE VetoType; PWSTR VetoList; PAGED_CODE(); RtlCopyMemory(FilteredCapabilities, SourceCapabilities, sizeof(SYSTEM_POWER_CAPABILITIES)); // // If any legacy drivers are installed, then no sleeping states // are allowed at all. // Status = IoGetLegacyVetoList(&VetoList, &VetoType); if (NT_SUCCESS(Status)) { if (VetoType != PNP_VetoTypeUnknown) { PoPrint(PO_WARN, ("PopFilterCapabilities: disabling sleep states due to legacy %s: %ws\n", (VetoType == PNP_VetoLegacyDriver) ? "driver" : "device", VetoList)); FilteredCapabilities->SystemS1 = FALSE; FilteredCapabilities->SystemS2 = FALSE; FilteredCapabilities->SystemS3 = FALSE; FilteredCapabilities->SystemS4 = FALSE; } if (VetoList != NULL) { ExFreePool(VetoList); } } // // If we previously tried and failed to hibernate, then we need to // disable any further attempts. // if( PopFailedHibernationAttempt ) { FilteredCapabilities->SystemS4 = FALSE; } // // If we are running in x86 PAE mode, then hibernation is disabled // #if defined(i386) if (SharedUserData->ProcessorFeatures[PF_PAE_ENABLED]) { FilteredCapabilities->SystemS4 = FALSE; } #endif } BOOLEAN PopUserIsAdmin( VOID ) /*++ Routine Description: Determines whether the current user is an administrator and therefore suitably privileged to change the administrative power policy. Arguments: None Return Value: TRUE - user is an administrator FALSE - user is not an administrator --*/ { SECURITY_SUBJECT_CONTEXT SubjectContext; PACCESS_TOKEN Token; BOOLEAN IsAdmin; PAGED_CODE(); SeCaptureSubjectContext(&SubjectContext); SeLockSubjectContext(&SubjectContext); Token = SeQuerySubjectContextToken(&SubjectContext); IsAdmin = SeTokenIsAdmin(Token); SeUnlockSubjectContext(&SubjectContext); SeReleaseSubjectContext(&SubjectContext); return(IsAdmin); }