windows-nt/Source/XPSP1/NT/base/ntos/po/pinfo.c
2020-09-26 16:20:57 +08:00

2607 lines
68 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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