605 lines
18 KiB
C
605 lines
18 KiB
C
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
poinit.c
|
|
|
|
Abstract:
|
|
|
|
Initialize power management component
|
|
|
|
Author:
|
|
|
|
Ken Reneris (kenr) 19-July-1994
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "pop.h"
|
|
|
|
VOID
|
|
PopRegisterForDeviceNotification (
|
|
IN LPGUID Guid,
|
|
IN POP_POLICY_DEVICE_TYPE DeviceType
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT, PoInitSystem)
|
|
#pragma alloc_text(INIT, PopRegisterForDeviceNotification)
|
|
#pragma alloc_text(INIT, PoInitDriverServices)
|
|
#pragma alloc_text(PAGE, PoInitHiberServices)
|
|
#pragma alloc_text(PAGE, PopDefaultPolicy)
|
|
#pragma alloc_text(PAGE, PopDefaultProcessorPolicy)
|
|
#endif
|
|
|
|
BOOLEAN
|
|
PoInitSystem(
|
|
IN ULONG Phase
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the Power Manager.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
The function value is a BOOLEAN indicating whether or not the Power Manager
|
|
was successfully initialized.
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE handle;
|
|
ULONG Length, i;
|
|
UNICODE_STRING UnicodeString;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PADMINISTRATOR_POWER_POLICY AdminPolicy;
|
|
PPOP_HEURISTICS HeuristicData;
|
|
struct {
|
|
KEY_VALUE_PARTIAL_INFORMATION Inf;
|
|
union {
|
|
POP_HEURISTICS Heuristics;
|
|
ADMINISTRATOR_POWER_POLICY AdminPolicy;
|
|
} Data;
|
|
} PartialInformation;
|
|
|
|
if (Phase == 0) {
|
|
//
|
|
// irp serialization, notify network, etc.
|
|
//
|
|
KeInitializeSpinLock(&PopIrpSerialLock);
|
|
KeInitializeSpinLock(&PopThermalLock);
|
|
InitializeListHead(&PopIrpSerialList);
|
|
InitializeListHead(&PopRequestedIrps);
|
|
ExInitializeResourceLite(&PopNotifyLock);
|
|
PopInvalidNotifyBlockCount = 0;
|
|
PopIrpSerialListLength = 0;
|
|
PopInrushPending = FALSE;
|
|
PopInrushIrpPointer = NULL;
|
|
PopInrushIrpReferenceCount = 0;
|
|
|
|
KeInitializeSpinLock(&PopWorkerLock);
|
|
PopCallSystemState = 0;
|
|
|
|
ExInitializeWorkItem(&PopUnlockAfterSleepWorkItem,PopUnlockAfterSleepWorker,NULL);
|
|
KeInitializeEvent(&PopUnlockComplete, SynchronizationEvent, TRUE);
|
|
|
|
//
|
|
// logging
|
|
//
|
|
InitializeListHead(&PowerStateDisableReasonListHead);
|
|
|
|
//
|
|
// poshtdwn.c
|
|
//
|
|
PopInitShutdownList();
|
|
|
|
//
|
|
// idle.c
|
|
//
|
|
KeInitializeSpinLock(&PopDopeGlobalLock);
|
|
InitializeListHead(&PopIdleDetectList);
|
|
|
|
//
|
|
// sidle.c
|
|
//
|
|
|
|
KeInitializeTimer(&PoSystemIdleTimer);
|
|
KeQueryPerformanceCounter(&PopPerfCounterFrequency);
|
|
|
|
//
|
|
// policy workers
|
|
//
|
|
|
|
KeInitializeSpinLock (&PopWorkerSpinLock);
|
|
InitializeListHead (&PopPolicyIrpQueue);
|
|
ExInitializeWorkItem (&PopPolicyWorker, PopPolicyWorkerThread, UIntToPtr(PO_WORKER_STATUS));
|
|
PopWorkerStatus = 0xffffffff;
|
|
|
|
//
|
|
// Policy manager
|
|
//
|
|
|
|
ExInitializeResourceLite (&PopPolicyLock);
|
|
KeInitializeGuardedMutex (&PopVolumeLock);
|
|
InitializeListHead (&PopVolumeDevices);
|
|
InitializeListHead (&PopSwitches);
|
|
InitializeListHead (&PopThermal);
|
|
InitializeListHead (&PopActionWaiters);
|
|
ExInitializeNPagedLookasideList(
|
|
&PopIdleHandlerLookAsideList,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
(sizeof(POP_IDLE_HANDLER) * MAX_IDLE_HANDLERS),
|
|
POP_IDLE_TAG,
|
|
(sizeof(POP_IDLE_HANDLER) * MAX_IDLE_HANDLERS * 3)
|
|
);
|
|
PopAction.Action = PowerActionNone;
|
|
|
|
PopDefaultPolicy (&PopAcPolicy);
|
|
PopDefaultPolicy (&PopDcPolicy);
|
|
PopPolicy = &PopAcPolicy;
|
|
|
|
PopDefaultProcessorPolicy( &PopAcProcessorPolicy );
|
|
PopDefaultProcessorPolicy( &PopDcProcessorPolicy );
|
|
PopProcessorPolicy = &PopAcProcessorPolicy;
|
|
|
|
PopAdminPolicy.MinSleep = PowerSystemSleeping1;
|
|
PopAdminPolicy.MaxSleep = PowerSystemHibernate;
|
|
PopAdminPolicy.MinVideoTimeout = 0;
|
|
PopAdminPolicy.MaxVideoTimeout = (ULONG) -1;
|
|
PopAdminPolicy.MinSpindownTimeout = 0;
|
|
PopAdminPolicy.MaxSpindownTimeout = (ULONG) -1;
|
|
|
|
PopFullWake = PO_FULL_WAKE_STATUS | PO_GDI_STATUS;
|
|
PopCoolingMode = PO_TZ_ACTIVE;
|
|
|
|
//
|
|
// Initialize composite battery status
|
|
//
|
|
|
|
KeInitializeEvent(&PopCB.Event, NotificationEvent, FALSE);
|
|
for (i=0; i < PO_NUM_POWER_LEVELS; i++) {
|
|
PopCB.Trigger[i].Type = PolicyDeviceBattery;
|
|
}
|
|
|
|
//
|
|
// Note the code overloads some POP flags into an ES flags
|
|
// Verify there's no overlap
|
|
//
|
|
|
|
ASSERT (!( (ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED | ES_USER_PRESENT) &
|
|
(POP_LOW_LATENCY | POP_DISK_SPINDOWN)
|
|
) );
|
|
|
|
|
|
//
|
|
// Set the default shutdown handler just in case there's hal out there
|
|
// that never registers a shutdown handler of his own. This will avoid
|
|
// the possible scenario where someone asks the machine to shutdown and
|
|
// it fails to call a shutdown handler (there isn't one), so it simply
|
|
// reboots instead.
|
|
//
|
|
PopPowerStateHandlers[PowerStateShutdownOff].Type = PowerStateShutdownOff;
|
|
PopPowerStateHandlers[PowerStateShutdownOff].RtcWake = FALSE;
|
|
PopPowerStateHandlers[PowerStateShutdownOff].Handler = PopShutdownHandler;
|
|
|
|
|
|
}
|
|
|
|
if (Phase == 1) {
|
|
|
|
//
|
|
// Reload PopSimulate to pick up any overrides
|
|
//
|
|
PopInitializePowerPolicySimulate();
|
|
|
|
//
|
|
// For testing, if simulate flag is set turn on
|
|
//
|
|
|
|
if (PopSimulate & POP_SIM_CAPABILITIES) {
|
|
PopCapabilities.SystemBatteriesPresent = TRUE;
|
|
PopCapabilities.BatteryScale[0].Granularity = 100;
|
|
PopCapabilities.BatteryScale[0].Capacity = 400;
|
|
PopCapabilities.BatteryScale[1].Granularity = 10;
|
|
PopCapabilities.BatteryScale[1].Capacity = 0xFFFF;
|
|
PopCapabilities.RtcWake = PowerSystemSleeping3;
|
|
PopCapabilities.DefaultLowLatencyWake = PowerSystemSleeping1;
|
|
}
|
|
|
|
//
|
|
// For testing, if super simulate flag set turn all the capabilities
|
|
// we can on
|
|
//
|
|
|
|
if (PopSimulate & POP_SIM_ALL_CAPABILITIES) {
|
|
PopCapabilities.PowerButtonPresent = TRUE;
|
|
PopCapabilities.SleepButtonPresent = TRUE;
|
|
PopCapabilities.LidPresent = TRUE;
|
|
PopCapabilities.SystemS1 = TRUE;
|
|
PopCapabilities.SystemS2 = TRUE;
|
|
PopCapabilities.SystemS3 = TRUE;
|
|
PopCapabilities.SystemS4 = TRUE;
|
|
PopAttributes[POP_DISK_SPINDOWN_ATTRIBUTE].Count += 1;
|
|
}
|
|
|
|
//
|
|
// Load current status and policie information
|
|
//
|
|
|
|
PopAcquirePolicyLock ();
|
|
|
|
Status = PopOpenPowerKey (&handle);
|
|
if (NT_SUCCESS(Status)) {
|
|
//
|
|
// Read heuristics structure
|
|
//
|
|
|
|
RtlInitUnicodeString (&UnicodeString, PopHeuristicsRegName);
|
|
Status = ZwQueryValueKey (
|
|
handle,
|
|
&UnicodeString,
|
|
KeyValuePartialInformation,
|
|
&PartialInformation,
|
|
sizeof (PartialInformation),
|
|
&Length
|
|
);
|
|
|
|
Length -= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
|
|
HeuristicData = (PPOP_HEURISTICS) PartialInformation.Inf.Data;
|
|
|
|
if (NT_SUCCESS(Status) &&
|
|
Length == sizeof(PopHeuristics)) {
|
|
|
|
//
|
|
// If we see a version 2 heuristics field, it probably has a
|
|
// bogus IoTransferWeight. So restart the sampling by setting
|
|
// the number of samples to zero and update the version to the
|
|
// current one. This little hack was put into place approx
|
|
// build 1920 and can be probably be removed sometime after
|
|
// shipping NT5 beta3
|
|
//
|
|
|
|
if (HeuristicData->Version <= POP_HEURISTICS_VERSION_CLEAR_TRANSFER) {
|
|
HeuristicData->Version = POP_HEURISTICS_VERSION;
|
|
HeuristicData->IoTransferSamples = 0;
|
|
}
|
|
if (HeuristicData->Version == POP_HEURISTICS_VERSION) {
|
|
//
|
|
// Restore values
|
|
//
|
|
|
|
RtlCopyMemory (&PopHeuristics, HeuristicData, sizeof(*HeuristicData));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Verify sane values
|
|
//
|
|
|
|
PopHeuristics.Version = POP_HEURISTICS_VERSION;
|
|
if (!PopHeuristics.IoTransferWeight) {
|
|
PopHeuristics.IoTransferWeight = 999999;
|
|
PopHeuristics.IoTransferSamples = 0;
|
|
PopHeuristics.IoTransferTotal = 0;
|
|
}
|
|
|
|
//
|
|
// Read administrator policy.
|
|
//
|
|
|
|
RtlInitUnicodeString (&UnicodeString, PopAdminRegName);
|
|
Status = ZwQueryValueKey (
|
|
handle,
|
|
&UnicodeString,
|
|
KeyValuePartialInformation,
|
|
&PartialInformation,
|
|
sizeof (PartialInformation),
|
|
&Length
|
|
);
|
|
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Length -= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
|
|
AdminPolicy = (PADMINISTRATOR_POWER_POLICY) PartialInformation.Inf.Data;
|
|
try {
|
|
Status = PopApplyAdminPolicy (FALSE, AdminPolicy, Length);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
ASSERT (GetExceptionCode());
|
|
}
|
|
} else if(Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
|
|
// It's okay if it isn't there. The key is optional.
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
NtClose (handle);
|
|
}
|
|
|
|
//
|
|
// Read and apply the current policies
|
|
//
|
|
Status = PopResetCurrentPolicies ();
|
|
PopReleasePolicyLock (FALSE);
|
|
|
|
//
|
|
// Turn on idle detection
|
|
//
|
|
PopIdleScanTime.HighPart = 0;
|
|
PopIdleScanTime.LowPart = 10*1000*1000 * PO_IDLE_SCAN_INTERVAL;
|
|
|
|
KeInitializeTimer(&PopIdleScanTimer);
|
|
KeSetTimerEx(
|
|
&PopIdleScanTimer,
|
|
PopIdleScanTime,
|
|
PO_IDLE_SCAN_INTERVAL*1000, // call wants milliseconds
|
|
&PopIdleScanDpc
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Success
|
|
//
|
|
|
|
return (BOOLEAN)NT_SUCCESS(Status);
|
|
}
|
|
|
|
VOID
|
|
PopDefaultPolicy (
|
|
IN OUT PSYSTEM_POWER_POLICY Policy
|
|
)
|
|
{
|
|
ULONG i;
|
|
|
|
|
|
RtlZeroMemory (Policy, sizeof(SYSTEM_POWER_POLICY));
|
|
Policy->Revision = 1;
|
|
Policy->PowerButton.Action = PowerActionShutdownOff;
|
|
Policy->SleepButton.Action = PowerActionSleep;
|
|
Policy->LidClose.Action = PowerActionNone;
|
|
Policy->LidOpenWake = PowerSystemWorking;
|
|
Policy->MinSleep = PowerSystemSleeping1;
|
|
Policy->MaxSleep = PowerSystemSleeping3;
|
|
Policy->ReducedLatencySleep = PowerSystemSleeping1;
|
|
Policy->WinLogonFlags = 0;
|
|
Policy->FanThrottleTolerance = PO_NO_FAN_THROTTLE;
|
|
Policy->ForcedThrottle = PO_NO_FORCED_THROTTLE;
|
|
Policy->OverThrottled.Action = PowerActionNone;
|
|
Policy->BroadcastCapacityResolution = 25;
|
|
for (i=0; i < NUM_DISCHARGE_POLICIES; i++) {
|
|
Policy->DischargePolicy[i].MinSystemState = PowerSystemSleeping1;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
PopDefaultProcessorPolicy(
|
|
IN OUT PPROCESSOR_POWER_POLICY Policy
|
|
)
|
|
{
|
|
int i;
|
|
|
|
RtlZeroMemory(Policy, sizeof(PROCESSOR_POWER_POLICY));
|
|
Policy->Revision = 1;
|
|
Policy->PolicyCount = 3;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
//
|
|
// Initialize the entries to some common values
|
|
//
|
|
Policy->Policy[i].TimeCheck = PopIdleTimeCheck;
|
|
Policy->Policy[i].PromoteLimit = PopIdleDefaultPromoteTime;
|
|
Policy->Policy[i].DemoteLimit = PopIdleDefaultDemoteTime;
|
|
Policy->Policy[i].PromotePercent = (UCHAR) PopIdleDefaultPromotePercent;
|
|
Policy->Policy[i].DemotePercent = (UCHAR) PopIdleDefaultDemotePercent;
|
|
Policy->Policy[i].AllowDemotion = 1;
|
|
Policy->Policy[i].AllowPromotion = 1;
|
|
|
|
//
|
|
// Special cases
|
|
//
|
|
if (i == 0) {
|
|
|
|
Policy->Policy[i].PromoteLimit = PopIdleDefaultPromoteFromC1Time;
|
|
Policy->Policy[i].PromotePercent = (UCHAR) PopIdleDefaultPromoteFromC1Percent;
|
|
Policy->Policy[i].TimeCheck = PopIdle0TimeCheck;
|
|
|
|
//
|
|
// Do Something special if we are a multiprocessor machine..
|
|
//
|
|
if (KeNumberProcessors > 1) {
|
|
|
|
Policy->Policy[i].DemotePercent = (UCHAR) PopIdleTo0Percent;
|
|
|
|
} else {
|
|
|
|
Policy->Policy[i].DemotePercent = 0;
|
|
Policy->Policy[i].AllowDemotion = 0;
|
|
|
|
}
|
|
|
|
} else if (i == 1) {
|
|
|
|
Policy->Policy[i].DemoteLimit = PopIdleDefaultDemoteToC1Time;
|
|
Policy->Policy[i].DemotePercent = (UCHAR) PopIdleDefaultDemoteToC1Percent;
|
|
|
|
} else if (i == 2) {
|
|
|
|
Policy->Policy[i].AllowPromotion = 0;
|
|
Policy->Policy[i].PromoteLimit = (ULONG) -1;
|
|
Policy->Policy[i].PromotePercent = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
PoInitDriverServices (
|
|
IN ULONG Phase
|
|
)
|
|
{
|
|
ULONG TickRate;
|
|
LARGE_INTEGER PerfRate;
|
|
|
|
if (Phase == 0) {
|
|
TickRate = KeQueryTimeIncrement();
|
|
KeQueryPerformanceCounter (&PerfRate);
|
|
|
|
//
|
|
// Connect to any policy devices which arrive
|
|
//
|
|
|
|
PopRegisterForDeviceNotification (
|
|
(LPGUID) &GUID_CLASS_INPUT,
|
|
PolicyDeviceSystemButton
|
|
);
|
|
|
|
PopRegisterForDeviceNotification (
|
|
(LPGUID) &GUID_DEVICE_THERMAL_ZONE,
|
|
PolicyDeviceThermalZone
|
|
);
|
|
|
|
PopRegisterForDeviceNotification (
|
|
(LPGUID) &GUID_DEVICE_SYS_BUTTON,
|
|
PolicyDeviceSystemButton
|
|
);
|
|
|
|
PopRegisterForDeviceNotification (
|
|
(LPGUID) &GUID_DEVICE_BATTERY,
|
|
PolicyDeviceBattery
|
|
);
|
|
|
|
|
|
//
|
|
// Initialize global idle values
|
|
//
|
|
PopIdle0PromoteTicks = PopIdleFrom0Delay * US2TIME / TickRate + 1;
|
|
PopIdle0PromoteLimit = (PopIdleFrom0Delay * US2TIME / TickRate) * 100 /
|
|
PopIdleFrom0IdlePercent;
|
|
|
|
//
|
|
// Initialize global perf values
|
|
//
|
|
PopPerfTimeTicks = PopPerfTimeDelta * US2TIME / TickRate + 1;
|
|
PopPerfCriticalTimeTicks = PopPerfCriticalTimeDelta * US2TIME / TickRate + 1;
|
|
|
|
//
|
|
// Initialize DPC for idle device timer
|
|
//
|
|
KeInitializeDpc(&PopIdleScanDpc, PopScanIdleList, NULL);
|
|
return ;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
PopRegisterForDeviceNotification (
|
|
IN LPGUID Guid,
|
|
IN POP_POLICY_DEVICE_TYPE DeviceType
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PVOID junk;
|
|
|
|
Status = IoRegisterPlugPlayNotification (
|
|
EventCategoryDeviceInterfaceChange,
|
|
0,
|
|
Guid,
|
|
IoPnpDriverObject,
|
|
PopNotifyPolicyDevice,
|
|
(PVOID) (ULONG_PTR) DeviceType,
|
|
&junk
|
|
);
|
|
|
|
ASSERT (NT_SUCCESS(Status));
|
|
}
|
|
|
|
VOID
|
|
PoInitHiberServices (
|
|
IN BOOLEAN Setup
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reserves the hiberfile if the function has been enabled.
|
|
It is called after autocheck (chkdsk) has run and the paging files
|
|
have been opened. (as performing IO to the hiberfil before this
|
|
time will mark the volume as dirty causing chkdsk to be required)
|
|
|
|
N.B. Caller's pervious mode must be kernel mode
|
|
|
|
Arguments:
|
|
|
|
SetupBoot - if TRUE this is text mode setup boot
|
|
if FALSE this is normal system boot
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
SYSTEM_POWER_CAPABILITIES PowerCapabilities;
|
|
|
|
UNREFERENCED_PARAMETER (Setup);
|
|
|
|
//
|
|
// If a hiber file was reserved before then try to reserve one this
|
|
// time too.
|
|
//
|
|
Status = ZwPowerInformation(SystemPowerCapabilities,
|
|
NULL,
|
|
0,
|
|
&PowerCapabilities,
|
|
sizeof(SYSTEM_POWER_CAPABILITIES));
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
if (PopHeuristics.HiberFileEnabled) {
|
|
PopAcquirePolicyLock();
|
|
PopEnableHiberFile(TRUE);
|
|
|
|
//
|
|
// If the system does not support S4 anymore (because someone enabled PAE
|
|
// or installed a legacy driver) then delete the hiberfile now. Note we have
|
|
// to enable it before disabling it or the file doesn't get deleted.
|
|
//
|
|
// Also force HiberFileEnabled back to TRUE in PopHeuristics. This is so we
|
|
// will try and reenable hibernation on the next boot. So if someone boots to
|
|
// safe mode, hibernation will still be enabled after they reboot.
|
|
//
|
|
if (!PowerCapabilities.SystemS4) {
|
|
PopEnableHiberFile(FALSE);
|
|
PopHeuristics.HiberFileEnabled = TRUE;
|
|
PopHeuristics.Dirty = TRUE;
|
|
PopSaveHeuristics();
|
|
}
|
|
PopReleasePolicyLock(TRUE);
|
|
}
|
|
|
|
|
|
//
|
|
// Base drivers are loaded, start dispatching policy irps
|
|
//
|
|
|
|
PopDispatchPolicyIrps = TRUE;
|
|
PopGetPolicyWorker (PO_WORKER_MAIN);
|
|
PopCheckForWork (TRUE);
|
|
}
|