/* * 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 #define NDEBUG #include /* 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; SYSTEM_POWER_CAPABILITIES PopCapabilities; /* PRIVATE FUNCTIONS *********************************************************/ static WORKER_THREAD_ROUTINE PopPassivePowerCall; _Use_decl_annotations_ static VOID NTAPI PopPassivePowerCall( PVOID Parameter) { PIRP Irp = Parameter; PIO_STACK_LOCATION IoStack; ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); _Analysis_assume_(Irp != NULL); IoStack = IoGetNextIrpStackLocation(Irp); (VOID)IoCallDriver(IoStack->DeviceObject, Irp); } _IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_same_ static NTSTATUS PopPresentIrp( _In_ PIO_STACK_LOCATION NextStack, _In_ PIRP Irp) { NTSTATUS Status; BOOLEAN CallAtPassiveLevel; PDEVICE_OBJECT DeviceObject; PWORK_QUEUE_ITEM WorkQueueItem; ASSERT(NextStack->MajorFunction == IRP_MJ_POWER); DeviceObject = NextStack->DeviceObject; /* Determine whether the IRP must be handled at PASSIVE_LEVEL. * Only SET_POWER to working state can happen at raised IRQL. */ CallAtPassiveLevel = TRUE; if ((NextStack->MinorFunction == IRP_MN_SET_POWER) && !(DeviceObject->Flags & DO_POWER_PAGABLE)) { if (NextStack->Parameters.Power.Type == DevicePowerState && NextStack->Parameters.Power.State.DeviceState == PowerDeviceD0) { CallAtPassiveLevel = FALSE; } if (NextStack->Parameters.Power.Type == SystemPowerState && NextStack->Parameters.Power.State.SystemState == PowerSystemWorking) { CallAtPassiveLevel = FALSE; } } if (CallAtPassiveLevel) { /* We need to fit a work item into the DriverContext below */ C_ASSERT(sizeof(Irp->Tail.Overlay.DriverContext) >= sizeof(WORK_QUEUE_ITEM)); if (KeGetCurrentIrql() == PASSIVE_LEVEL) { /* Already at passive, call next driver directly */ return IoCallDriver(DeviceObject, Irp); } /* Need to schedule a work item and return pending */ NextStack->Control |= SL_PENDING_RETURNED; WorkQueueItem = (PWORK_QUEUE_ITEM)&Irp->Tail.Overlay.DriverContext; ExInitializeWorkItem(WorkQueueItem, PopPassivePowerCall, Irp); ExQueueWorkItem(WorkQueueItem, DelayedWorkQueue); return STATUS_PENDING; } /* Direct call. Raise IRQL in debug to catch invalid paged memory access. */ #if DBG { KIRQL OldIrql; KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); #endif Status = IoCallDriver(DeviceObject, Irp); #if DBG KeLowerIrql(OldIrql); } #endif return Status; } static IO_COMPLETION_ROUTINE PopRequestPowerIrpCompletion; static NTSTATUS NTAPI PopRequestPowerIrpCompletion( _In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_reads_opt_(_Inexpressible_("varies")) 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; if (CompletionRoutine) { 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; } CODE_SEG("INIT") BOOLEAN NTAPI 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; } /* Initialize the power capabilities */ RtlZeroMemory(&PopCapabilities, sizeof(SYSTEM_POWER_CAPABILITIES)); /* 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; } /* Enable shutdown by power button */ if (PopAcpiPresent) PopCapabilities.SystemS5 = TRUE; /* 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(); } CODE_SEG("INIT") VOID NTAPI 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 */ _IRQL_requires_max_(DISPATCH_LEVEL) NTSTATUS NTAPI PoCallDriver( _In_ PDEVICE_OBJECT DeviceObject, _Inout_ __drv_aliasesMem PIRP Irp) { PIO_STACK_LOCATION NextStack; ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); ASSERT(DeviceObject); ASSERT(Irp); NextStack = IoGetNextIrpStackLocation(Irp); ASSERT(NextStack->MajorFunction == IRP_MJ_POWER); /* Set DeviceObject for PopPresentIrp */ NextStack->DeviceObject = DeviceObject; /* Only QUERY_POWER and SET_POWER use special handling */ if (NextStack->MinorFunction != IRP_MN_SET_POWER && NextStack->MinorFunction != IRP_MN_QUERY_POWER) { return IoCallDriver(DeviceObject, Irp); } /* Call the next driver, either directly or at PASSIVE_LEVEL */ return PopPresentIrp(NextStack, Irp); } /* * @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_opt_ PREQUEST_POWER_COMPLETE CompletionFunction, _In_opt_ __drv_aliasesMem PVOID Context, _Outptr_opt_ PIRP *pIrp) { 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_ONCE; } /* * @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; // BatteryState->AcOnLine = TRUE; 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 { RtlCopyMemory(PowerCapabilities, &PopCapabilities, sizeof(SYSTEM_POWER_CAPABILITIES)); 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; /* FIXME: return structures for all processors */ _SEH2_TRY { /* FIXME: some values are hardcoded */ PowerInformation->Number = 0; PowerInformation->MaxMhz = 1000; PowerInformation->CurrentMhz = KeGetCurrentPrcb()->MHz; 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; /* Flush all volumes and the registry */ DPRINT("Flushing volumes\n"); PopFlushVolumes(PopAction.Shutdown); #ifndef NEWCC /* Flush dirty cache pages */ /* XXX: Is that still mandatory? As now we'll wait on lazy writer to complete? */ CcRosFlushDirtyPages(MAXULONG, &Dummy, TRUE, FALSE); DPRINT("Cache flushed %lu pages\n", Dummy); #else Dummy = 0; #endif /* 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?!\n", 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; }