reactos/ntoskrnl/po/power.c
Pierre Schweitzer c7ad200f8b
[NTOSKRNL] Reimplement the lazy writer in Cc and remove the "basic" one in Mm.
This removes the "modified page writer" thread in Mm that was regularly blindly
attempting to flush dirty pages to the disk.
Instead, this commit introduces a lazy writer that will monitor dirty pages count
and will flush them to disk when this count is above a threshold. The threshold is
computed on Cc init.
Compared to what was done previously, this lazy writer will only write down files
that are not marked as temporary.
The mechanisms involved in this lazy writer worker are well described in Windows
Internals 4th editions (constants are coming from it ;-)).
Also fixed a bad (and old!) bug in CcRosFlushDirtyPages() where target count could
be overflow and the function would spin forever while holding the VACBs lock. This is
mandatory as now lazy writer will call it with "random" values.
This also allows implementing CcWaitForCurrentLazyWriterActivity() :-).
Also renamed DirtyPageCount to its MS equivalent.

CORE-14235
2018-01-23 19:33:59 +01:00

1003 lines
28 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/po/power.c
* PURPOSE: Power Manager
* PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
* Hervé Poussineau (hpoussin@reactos.com)
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS *******************************************************************/
typedef struct _POWER_STATE_TRAVERSE_CONTEXT
{
SYSTEM_POWER_STATE SystemPowerState;
POWER_ACTION PowerAction;
PDEVICE_OBJECT PowerDevice;
} POWER_STATE_TRAVERSE_CONTEXT, *PPOWER_STATE_TRAVERSE_CONTEXT;
PDEVICE_NODE PopSystemPowerDeviceNode = NULL;
BOOLEAN PopAcpiPresent = FALSE;
POP_POWER_ACTION PopAction;
WORK_QUEUE_ITEM PopShutdownWorkItem;
/* PRIVATE FUNCTIONS *********************************************************/
static
NTSTATUS
NTAPI
PopRequestPowerIrpCompletion(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
{
PIO_STACK_LOCATION Stack;
PREQUEST_POWER_COMPLETE CompletionRoutine;
POWER_STATE PowerState;
Stack = IoGetCurrentIrpStackLocation(Irp);
CompletionRoutine = Context;
PowerState.DeviceState = (ULONG_PTR)Stack->Parameters.Others.Argument3;
CompletionRoutine(Stack->Parameters.Others.Argument1,
(UCHAR)(ULONG_PTR)Stack->Parameters.Others.Argument2,
PowerState,
Stack->Parameters.Others.Argument4,
&Irp->IoStatus);
IoSkipCurrentIrpStackLocation(Irp);
IoFreeIrp(Irp);
ObDereferenceObject(DeviceObject);
return STATUS_MORE_PROCESSING_REQUIRED;
}
VOID
NTAPI
PopCleanupPowerState(IN PPOWER_STATE PowerState)
{
//UNIMPLEMENTED;
}
NTSTATUS
PopSendQuerySystemPowerState(PDEVICE_OBJECT DeviceObject, SYSTEM_POWER_STATE SystemState, POWER_ACTION PowerAction)
{
KEVENT Event;
IO_STATUS_BLOCK IoStatusBlock;
PIO_STACK_LOCATION IrpSp;
PIRP Irp;
NTSTATUS Status;
KeInitializeEvent(&Event,
NotificationEvent,
FALSE);
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_POWER,
DeviceObject,
NULL,
0,
NULL,
&Event,
&IoStatusBlock);
if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
IrpSp = IoGetNextIrpStackLocation(Irp);
IrpSp->MinorFunction = IRP_MN_QUERY_POWER;
IrpSp->Parameters.Power.Type = SystemPowerState;
IrpSp->Parameters.Power.State.SystemState = SystemState;
IrpSp->Parameters.Power.ShutdownType = PowerAction;
Status = PoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
Status = IoStatusBlock.Status;
}
return Status;
}
NTSTATUS
PopSendSetSystemPowerState(PDEVICE_OBJECT DeviceObject, SYSTEM_POWER_STATE SystemState, POWER_ACTION PowerAction)
{
KEVENT Event;
IO_STATUS_BLOCK IoStatusBlock;
PIO_STACK_LOCATION IrpSp;
PIRP Irp;
NTSTATUS Status;
KeInitializeEvent(&Event,
NotificationEvent,
FALSE);
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_POWER,
DeviceObject,
NULL,
0,
NULL,
&Event,
&IoStatusBlock);
if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
IrpSp = IoGetNextIrpStackLocation(Irp);
IrpSp->MinorFunction = IRP_MN_SET_POWER;
IrpSp->Parameters.Power.Type = SystemPowerState;
IrpSp->Parameters.Power.State.SystemState = SystemState;
IrpSp->Parameters.Power.ShutdownType = PowerAction;
Status = PoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
Status = IoStatusBlock.Status;
}
return Status;
}
NTSTATUS
PopQuerySystemPowerStateTraverse(PDEVICE_NODE DeviceNode,
PVOID Context)
{
PPOWER_STATE_TRAVERSE_CONTEXT PowerStateContext = Context;
PDEVICE_OBJECT TopDeviceObject;
NTSTATUS Status;
DPRINT("PopQuerySystemPowerStateTraverse(%p, %p)\n", DeviceNode, Context);
if (DeviceNode == IopRootDeviceNode)
return STATUS_SUCCESS;
if (DeviceNode->Flags & DNF_LEGACY_DRIVER)
return STATUS_SUCCESS;
TopDeviceObject = IoGetAttachedDeviceReference(DeviceNode->PhysicalDeviceObject);
Status = PopSendQuerySystemPowerState(TopDeviceObject,
PowerStateContext->SystemPowerState,
PowerStateContext->PowerAction);
if (!NT_SUCCESS(Status))
{
DPRINT1("Device '%wZ' failed IRP_MN_QUERY_POWER\n", &DeviceNode->InstancePath);
}
ObDereferenceObject(TopDeviceObject);
#if 0
return Status;
#else
return STATUS_SUCCESS;
#endif
}
NTSTATUS
PopSetSystemPowerStateTraverse(PDEVICE_NODE DeviceNode,
PVOID Context)
{
PPOWER_STATE_TRAVERSE_CONTEXT PowerStateContext = Context;
PDEVICE_OBJECT TopDeviceObject;
NTSTATUS Status;
DPRINT("PopSetSystemPowerStateTraverse(%p, %p)\n", DeviceNode, Context);
if (DeviceNode == IopRootDeviceNode)
return STATUS_SUCCESS;
if (DeviceNode->PhysicalDeviceObject == PowerStateContext->PowerDevice)
return STATUS_SUCCESS;
if (DeviceNode->Flags & DNF_LEGACY_DRIVER)
return STATUS_SUCCESS;
TopDeviceObject = IoGetAttachedDeviceReference(DeviceNode->PhysicalDeviceObject);
if (TopDeviceObject == PowerStateContext->PowerDevice)
{
ObDereferenceObject(TopDeviceObject);
return STATUS_SUCCESS;
}
Status = PopSendSetSystemPowerState(TopDeviceObject,
PowerStateContext->SystemPowerState,
PowerStateContext->PowerAction);
if (!NT_SUCCESS(Status))
{
DPRINT1("Device '%wZ' failed IRP_MN_SET_POWER\n", &DeviceNode->InstancePath);
}
ObDereferenceObject(TopDeviceObject);
#if 0
return Status;
#else
return STATUS_SUCCESS;
#endif
}
NTSTATUS
NTAPI
PopSetSystemPowerState(SYSTEM_POWER_STATE PowerState, POWER_ACTION PowerAction)
{
PDEVICE_OBJECT DeviceObject;
PDEVICE_OBJECT Fdo;
NTSTATUS Status;
DEVICETREE_TRAVERSE_CONTEXT Context;
POWER_STATE_TRAVERSE_CONTEXT PowerContext;
Status = IopGetSystemPowerDeviceObject(&DeviceObject);
if (!NT_SUCCESS(Status))
{
DPRINT1("No system power driver available\n");
Fdo = NULL;
}
else
{
Fdo = IoGetAttachedDeviceReference(DeviceObject);
if (Fdo == DeviceObject)
{
DPRINT("An FDO was not attached\n");
return STATUS_UNSUCCESSFUL;
}
}
/* Set up context */
PowerContext.PowerAction = PowerAction;
PowerContext.SystemPowerState = PowerState;
PowerContext.PowerDevice = Fdo;
/* Query for system power change */
IopInitDeviceTreeTraverseContext(&Context,
IopRootDeviceNode,
PopQuerySystemPowerStateTraverse,
&PowerContext);
Status = IopTraverseDeviceTree(&Context);
if (!NT_SUCCESS(Status))
{
DPRINT1("Query system power state failed; changing state anyway\n");
}
/* Set system power change */
IopInitDeviceTreeTraverseContext(&Context,
IopRootDeviceNode,
PopSetSystemPowerStateTraverse,
&PowerContext);
IopTraverseDeviceTree(&Context);
if (!PopAcpiPresent) return STATUS_NOT_IMPLEMENTED;
if (Fdo != NULL)
{
if (PowerAction != PowerActionShutdownReset)
PopSendSetSystemPowerState(Fdo, PowerState, PowerAction);
ObDereferenceObject(Fdo);
}
return Status;
}
BOOLEAN
NTAPI
INIT_FUNCTION
PoInitSystem(IN ULONG BootPhase)
{
PVOID NotificationEntry;
PCHAR CommandLine;
BOOLEAN ForceAcpiDisable = FALSE;
/* Check if this is phase 1 init */
if (BootPhase == 1)
{
/* Register power button notification */
IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
(PVOID)&GUID_DEVICE_SYS_BUTTON,
IopRootDeviceNode->
PhysicalDeviceObject->DriverObject,
PopAddRemoveSysCapsCallback,
NULL,
&NotificationEntry);
/* Register lid notification */
IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
(PVOID)&GUID_DEVICE_LID,
IopRootDeviceNode->
PhysicalDeviceObject->DriverObject,
PopAddRemoveSysCapsCallback,
NULL,
&NotificationEntry);
return TRUE;
}
/* Get the Command Line */
CommandLine = KeLoaderBlock->LoadOptions;
/* Upcase it */
_strupr(CommandLine);
/* Check for ACPI disable */
if (strstr(CommandLine, "NOACPI")) ForceAcpiDisable = TRUE;
if (ForceAcpiDisable)
{
/* Set the ACPI State to False if it's been forced that way */
PopAcpiPresent = FALSE;
}
else
{
/* Otherwise check if the LoaderBlock has a ACPI Table */
PopAcpiPresent = KeLoaderBlock->Extension->AcpiTable != NULL ? TRUE : FALSE;
}
/* Initialize volume support */
InitializeListHead(&PopVolumeDevices);
KeInitializeGuardedMutex(&PopVolumeLock);
/* Initialize support for dope */
KeInitializeSpinLock(&PopDopeGlobalLock);
/* Initialize support for shutdown waits and work-items */
PopInitShutdownList();
return TRUE;
}
VOID
NTAPI
PopPerfIdle(PPROCESSOR_POWER_STATE PowerState)
{
DPRINT1("PerfIdle function: %p\n", PowerState);
}
VOID
NTAPI
PopPerfIdleDpc(IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2)
{
/* Call the Perf Idle function */
PopPerfIdle(&((PKPRCB)DeferredContext)->PowerState);
}
VOID
FASTCALL
PopIdle0(IN PPROCESSOR_POWER_STATE PowerState)
{
/* FIXME: Extremly naive implementation */
HalProcessorIdle();
}
VOID
NTAPI
INIT_FUNCTION
PoInitializePrcb(IN PKPRCB Prcb)
{
/* Initialize the Power State */
RtlZeroMemory(&Prcb->PowerState, sizeof(Prcb->PowerState));
Prcb->PowerState.Idle0KernelTimeLimit = 0xFFFFFFFF;
Prcb->PowerState.CurrentThrottle = 100;
Prcb->PowerState.CurrentThrottleIndex = 0;
Prcb->PowerState.IdleFunction = PopIdle0;
/* Initialize the Perf DPC and Timer */
KeInitializeDpc(&Prcb->PowerState.PerfDpc, PopPerfIdleDpc, Prcb);
KeSetTargetProcessorDpc(&Prcb->PowerState.PerfDpc, Prcb->Number);
KeInitializeTimerEx(&Prcb->PowerState.PerfTimer, SynchronizationTimer);
}
/* PUBLIC FUNCTIONS **********************************************************/
/*
* @unimplemented
*/
NTSTATUS
NTAPI
PoCancelDeviceNotify(IN PVOID NotifyBlock)
{
UNIMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
}
/*
* @unimplemented
*/
NTSTATUS
NTAPI
PoRegisterDeviceNotify(OUT PVOID Unknown0,
IN ULONG Unknown1,
IN ULONG Unknown2,
IN ULONG Unknown3,
IN PVOID Unknown4,
IN PVOID Unknown5)
{
UNIMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
}
/*
* @unimplemented
*/
VOID
NTAPI
PoShutdownBugCheck(IN BOOLEAN LogError,
IN ULONG BugCheckCode,
IN ULONG_PTR BugCheckParameter1,
IN ULONG_PTR BugCheckParameter2,
IN ULONG_PTR BugCheckParameter3,
IN ULONG_PTR BugCheckParameter4)
{
DPRINT1("PoShutdownBugCheck called\n");
/* FIXME: Log error if requested */
/* FIXME: Initiate a shutdown */
/* Bugcheck the system */
KeBugCheckEx(BugCheckCode,
BugCheckParameter1,
BugCheckParameter2,
BugCheckParameter3,
BugCheckParameter4);
}
/*
* @unimplemented
*/
VOID
NTAPI
PoSetHiberRange(IN PVOID HiberContext,
IN ULONG Flags,
IN OUT PVOID StartPage,
IN ULONG Length,
IN ULONG PageTag)
{
UNIMPLEMENTED;
return;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
PoCallDriver(IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp)
{
NTSTATUS Status;
/* Forward to Io -- FIXME! */
Status = IoCallDriver(DeviceObject, Irp);
/* Return status */
return Status;
}
/*
* @unimplemented
*/
PULONG
NTAPI
PoRegisterDeviceForIdleDetection(IN PDEVICE_OBJECT DeviceObject,
IN ULONG ConservationIdleTime,
IN ULONG PerformanceIdleTime,
IN DEVICE_POWER_STATE State)
{
UNIMPLEMENTED;
return NULL;
}
/*
* @unimplemented
*/
PVOID
NTAPI
PoRegisterSystemState(IN PVOID StateHandle,
IN EXECUTION_STATE Flags)
{
UNIMPLEMENTED;
return NULL;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
PoRequestPowerIrp(IN PDEVICE_OBJECT DeviceObject,
IN UCHAR MinorFunction,
IN POWER_STATE PowerState,
IN PREQUEST_POWER_COMPLETE CompletionFunction,
IN PVOID Context,
OUT PIRP *pIrp OPTIONAL)
{
PDEVICE_OBJECT TopDeviceObject;
PIO_STACK_LOCATION Stack;
PIRP Irp;
if (MinorFunction != IRP_MN_QUERY_POWER
&& MinorFunction != IRP_MN_SET_POWER
&& MinorFunction != IRP_MN_WAIT_WAKE)
return STATUS_INVALID_PARAMETER_2;
/* Always call the top of the device stack */
TopDeviceObject = IoGetAttachedDeviceReference(DeviceObject);
Irp = IoAllocateIrp(TopDeviceObject->StackSize + 2, FALSE);
if (!Irp)
{
ObDereferenceObject(TopDeviceObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
Irp->IoStatus.Information = 0;
IoSetNextIrpStackLocation(Irp);
Stack = IoGetNextIrpStackLocation(Irp);
Stack->Parameters.Others.Argument1 = DeviceObject;
Stack->Parameters.Others.Argument2 = (PVOID)(ULONG_PTR)MinorFunction;
Stack->Parameters.Others.Argument3 = (PVOID)(ULONG_PTR)PowerState.DeviceState;
Stack->Parameters.Others.Argument4 = Context;
Stack->DeviceObject = TopDeviceObject;
IoSetNextIrpStackLocation(Irp);
Stack = IoGetNextIrpStackLocation(Irp);
Stack->MajorFunction = IRP_MJ_POWER;
Stack->MinorFunction = MinorFunction;
if (MinorFunction == IRP_MN_WAIT_WAKE)
{
Stack->Parameters.WaitWake.PowerState = PowerState.SystemState;
}
else
{
Stack->Parameters.Power.Type = DevicePowerState;
Stack->Parameters.Power.State = PowerState;
}
if (pIrp != NULL)
*pIrp = Irp;
IoSetCompletionRoutine(Irp, PopRequestPowerIrpCompletion, CompletionFunction, TRUE, TRUE, TRUE);
PoCallDriver(TopDeviceObject, Irp);
/* Always return STATUS_PENDING. The completion routine
* will call CompletionFunction and complete the Irp.
*/
return STATUS_PENDING;
}
/*
* @unimplemented
*/
POWER_STATE
NTAPI
PoSetPowerState(IN PDEVICE_OBJECT DeviceObject,
IN POWER_STATE_TYPE Type,
IN POWER_STATE State)
{
POWER_STATE ps;
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
ps.SystemState = PowerSystemWorking; // Fully on
ps.DeviceState = PowerDeviceD0; // Fully on
return ps;
}
/*
* @unimplemented
*/
VOID
NTAPI
PoSetSystemState(IN EXECUTION_STATE Flags)
{
UNIMPLEMENTED;
}
/*
* @unimplemented
*/
VOID
NTAPI
PoStartNextPowerIrp(IN PIRP Irp)
{
UNIMPLEMENTED;
}
/*
* @unimplemented
*/
VOID
NTAPI
PoUnregisterSystemState(IN PVOID StateHandle)
{
UNIMPLEMENTED;
}
/*
* @unimplemented
*/
NTSTATUS
NTAPI
NtInitiatePowerAction(IN POWER_ACTION SystemAction,
IN SYSTEM_POWER_STATE MinSystemState,
IN ULONG Flags,
IN BOOLEAN Asynchronous)
{
UNIMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
}
/*
* @unimplemented
*/
NTSTATUS
NTAPI
NtPowerInformation(IN POWER_INFORMATION_LEVEL PowerInformationLevel,
IN PVOID InputBuffer OPTIONAL,
IN ULONG InputBufferLength,
OUT PVOID OutputBuffer OPTIONAL,
IN ULONG OutputBufferLength)
{
NTSTATUS Status;
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
PAGED_CODE();
DPRINT("NtPowerInformation(PowerInformationLevel 0x%x, InputBuffer 0x%p, "
"InputBufferLength 0x%x, OutputBuffer 0x%p, OutputBufferLength 0x%x)\n",
PowerInformationLevel,
InputBuffer, InputBufferLength,
OutputBuffer, OutputBufferLength);
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
ProbeForRead(InputBuffer, InputBufferLength, 1);
ProbeForWrite(OutputBuffer, OutputBufferLength, sizeof(ULONG));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
switch (PowerInformationLevel)
{
case SystemBatteryState:
{
PSYSTEM_BATTERY_STATE BatteryState = (PSYSTEM_BATTERY_STATE)OutputBuffer;
if (InputBuffer != NULL)
return STATUS_INVALID_PARAMETER;
if (OutputBufferLength < sizeof(SYSTEM_BATTERY_STATE))
return STATUS_BUFFER_TOO_SMALL;
_SEH2_TRY
{
/* Just zero the struct (and thus set BatteryState->BatteryPresent = FALSE) */
RtlZeroMemory(BatteryState, sizeof(SYSTEM_BATTERY_STATE));
BatteryState->EstimatedTime = MAXULONG;
Status = STATUS_SUCCESS;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
case SystemPowerCapabilities:
{
PSYSTEM_POWER_CAPABILITIES PowerCapabilities = (PSYSTEM_POWER_CAPABILITIES)OutputBuffer;
if (InputBuffer != NULL)
return STATUS_INVALID_PARAMETER;
if (OutputBufferLength < sizeof(SYSTEM_POWER_CAPABILITIES))
return STATUS_BUFFER_TOO_SMALL;
_SEH2_TRY
{
/* Just zero the struct (and thus set PowerCapabilities->SystemBatteriesPresent = FALSE) */
RtlZeroMemory(PowerCapabilities, sizeof(SYSTEM_POWER_CAPABILITIES));
//PowerCapabilities->SystemBatteriesPresent = 0;
Status = STATUS_SUCCESS;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
case ProcessorInformation:
{
PPROCESSOR_POWER_INFORMATION PowerInformation = (PPROCESSOR_POWER_INFORMATION)OutputBuffer;
if (InputBuffer != NULL)
return STATUS_INVALID_PARAMETER;
if (OutputBufferLength < sizeof(PROCESSOR_POWER_INFORMATION))
return STATUS_BUFFER_TOO_SMALL;
_SEH2_TRY
{
PowerInformation->Number = 0;
PowerInformation->MaxMhz = 1000;
PowerInformation->CurrentMhz = 1000;
PowerInformation->MhzLimit = 1000;
PowerInformation->MaxIdleState = 0;
PowerInformation->CurrentIdleState = 0;
Status = STATUS_SUCCESS;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
default:
Status = STATUS_NOT_IMPLEMENTED;
DPRINT1("PowerInformationLevel 0x%x is UNIMPLEMENTED! Have a nice day.\n",
PowerInformationLevel);
break;
}
return Status;
}
NTSTATUS
NTAPI
NtGetDevicePowerState(IN HANDLE Device,
IN PDEVICE_POWER_STATE PowerState)
{
UNIMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
}
BOOLEAN
NTAPI
NtIsSystemResumeAutomatic(VOID)
{
UNIMPLEMENTED;
return FALSE;
}
NTSTATUS
NTAPI
NtRequestWakeupLatency(IN LATENCY_TIME Latency)
{
UNIMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
NTAPI
NtSetThreadExecutionState(IN EXECUTION_STATE esFlags,
OUT EXECUTION_STATE *PreviousFlags)
{
PKTHREAD Thread = KeGetCurrentThread();
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
EXECUTION_STATE PreviousState;
PAGED_CODE();
/* Validate flags */
if (esFlags & ~(ES_CONTINUOUS | ES_USER_PRESENT))
{
/* Fail the request */
return STATUS_INVALID_PARAMETER;
}
/* Check for user parameters */
if (PreviousMode != KernelMode)
{
/* Protect the probes */
_SEH2_TRY
{
/* Check if the pointer is valid */
ProbeForWriteUlong(PreviousFlags);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* It isn't -- fail */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
/* Save the previous state, always masking in the continous flag */
PreviousState = Thread->PowerState | ES_CONTINUOUS;
/* Check if we need to update the power state */
if (esFlags & ES_CONTINUOUS) Thread->PowerState = (UCHAR)esFlags;
/* Protect the write back to user mode */
_SEH2_TRY
{
/* Return the previous flags */
*PreviousFlags = PreviousState;
}
_SEH2_EXCEPT(ExSystemExceptionFilter())
{
/* Something's wrong, fail */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
/* All is good */
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
NtSetSystemPowerState(IN POWER_ACTION SystemAction,
IN SYSTEM_POWER_STATE MinSystemState,
IN ULONG Flags)
{
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
POP_POWER_ACTION Action = {0};
NTSTATUS Status;
ULONG Dummy;
/* Check for invalid parameter combinations */
if ((MinSystemState >= PowerSystemMaximum) ||
(MinSystemState <= PowerSystemUnspecified) ||
(SystemAction > PowerActionWarmEject) ||
(SystemAction < PowerActionReserved) ||
(Flags & ~(POWER_ACTION_QUERY_ALLOWED |
POWER_ACTION_UI_ALLOWED |
POWER_ACTION_OVERRIDE_APPS |
POWER_ACTION_LIGHTEST_FIRST |
POWER_ACTION_LOCK_CONSOLE |
POWER_ACTION_DISABLE_WAKES |
POWER_ACTION_CRITICAL)))
{
DPRINT1("NtSetSystemPowerState: Bad parameters!\n");
DPRINT1(" SystemAction: 0x%x\n", SystemAction);
DPRINT1(" MinSystemState: 0x%x\n", MinSystemState);
DPRINT1(" Flags: 0x%x\n", Flags);
return STATUS_INVALID_PARAMETER;
}
/* Check for user caller */
if (PreviousMode != KernelMode)
{
/* Check for shutdown permission */
if (!SeSinglePrivilegeCheck(SeShutdownPrivilege, PreviousMode))
{
/* Not granted */
DPRINT1("ERROR: Privilege not held for shutdown\n");
return STATUS_PRIVILEGE_NOT_HELD;
}
/* Do it as a kernel-mode caller for consistency with system state */
return ZwSetSystemPowerState(SystemAction, MinSystemState, Flags);
}
/* Read policy settings (partial shutdown vs. full shutdown) */
if (SystemAction == PowerActionShutdown) PopReadShutdownPolicy();
/* Disable lazy flushing of registry */
DPRINT("Stopping lazy flush\n");
CmSetLazyFlushState(FALSE);
/* Setup the power action */
Action.Action = SystemAction;
Action.Flags = Flags;
/* Notify callbacks */
DPRINT("Notifying callbacks\n");
ExNotifyCallback(PowerStateCallback, (PVOID)3, NULL);
/* Swap in any worker thread stacks */
DPRINT("Swapping worker threads\n");
ExSwapinWorkerThreads(FALSE);
/* Make our action global */
PopAction = Action;
/* Start power loop */
Status = STATUS_CANCELLED;
while (TRUE)
{
/* Break out if there's nothing to do */
if (Action.Action == PowerActionNone) break;
/* Check for first-pass or restart */
if (Status == STATUS_CANCELLED)
{
/* Check for shutdown action */
if ((PopAction.Action == PowerActionShutdown) ||
(PopAction.Action == PowerActionShutdownReset) ||
(PopAction.Action == PowerActionShutdownOff))
{
/* Set the action */
PopAction.Shutdown = TRUE;
}
/* Now we are good to go */
Status = STATUS_SUCCESS;
}
/* Check if we're still in an invalid status */
if (!NT_SUCCESS(Status)) break;
#ifndef NEWCC
/* Flush dirty cache pages */
/* XXX: Is that still mandatory? As now we'll wait on lazy writer to complete? */
CcRosFlushDirtyPages(-1, &Dummy, FALSE, FALSE); //HACK: We really should wait here!
#else
Dummy = 0;
#endif
/* Flush all volumes and the registry */
DPRINT("Flushing volumes, cache flushed %lu pages\n", Dummy);
PopFlushVolumes(PopAction.Shutdown);
/* Set IRP for drivers */
PopAction.IrpMinor = IRP_MN_SET_POWER;
if (PopAction.Shutdown)
{
DPRINT("Queueing shutdown thread\n");
/* Check if we are running in the system context */
if (PsGetCurrentProcess() != PsInitialSystemProcess)
{
/* We're not, so use a worker thread for shutdown */
ExInitializeWorkItem(&PopShutdownWorkItem,
&PopGracefulShutdown,
NULL);
ExQueueWorkItem(&PopShutdownWorkItem, CriticalWorkQueue);
/* Spend us -- when we wake up, the system is good to go down */
KeSuspendThread(KeGetCurrentThread());
Status = STATUS_SYSTEM_SHUTDOWN;
goto Exit;
}
else
{
/* Do the shutdown inline */
PopGracefulShutdown(NULL);
}
}
/* You should not have made it this far */
// ASSERTMSG("System is still up and running?!", FALSE);
DPRINT1("System is still up and running, you may not have chosen a yet supported power option: %u\n", PopAction.Action);
break;
}
Exit:
/* We're done, return */
return Status;
}