/*++ Copyright (c) 1990 Microsoft Corporation Module Name: ntapi.c Abstract: NT api level routines for the po component reside in this file Author: Bryan Willman (bryanwi) 14-Nov-1996 Revision History: --*/ #include "pop.h" #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, NtSetThreadExecutionState) #pragma alloc_text(PAGE, NtRequestWakeupLatency) #pragma alloc_text(PAGE, NtInitiatePowerAction) #pragma alloc_text(PAGE, NtGetDevicePowerState) #pragma alloc_text(PAGE, NtCancelDeviceWakeupRequest) #pragma alloc_text(PAGE, NtIsSystemResumeAutomatic) #pragma alloc_text(PAGE, NtRequestDeviceWakeup) #pragma alloc_text(PAGELK, NtSetSystemPowerState) #endif extern POBJECT_TYPE IoFileObjectType; WORK_QUEUE_ITEM PopShutdownWorkItem; WORK_QUEUE_ITEM PopUnlockAfterSleepWorkItem; KEVENT PopUnlockComplete; extern ERESOURCE ExpTimeRefreshLock; NTSYSAPI NTSTATUS NTAPI NtSetThreadExecutionState( IN EXECUTION_STATE NewFlags, // ES_xxx flags OUT EXECUTION_STATE *PreviousFlags ) /*++ Routine Description: Implements Win32 API functionality. Tracks thread execution state attributes. Keeps global count of all such attributes set. Arguments: NewFlags - Attributes to set or pulse PreviousFlags - Threads 'set' attributes before applying NewFlags Return Value: Status --*/ { ULONG OldFlags; PKTHREAD Thread; KPROCESSOR_MODE PreviousMode; NTSTATUS Status; PAGED_CODE(); Thread = KeGetCurrentThread(); Status = STATUS_SUCCESS; // // Verify no reserved bits set // if (NewFlags & ~(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED | ES_CONTINUOUS)) { return STATUS_INVALID_PARAMETER; } try { // // Verify callers params // PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { ProbeForWriteUlong (PreviousFlags); } } except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); } // // Get current flags // OldFlags = Thread->PowerState | ES_CONTINUOUS; if (NT_SUCCESS(Status)) { PopAcquirePolicyLock (); // // If the continous bit is set, modify current thread flags // if (NewFlags & ES_CONTINUOUS) { Thread->PowerState = (UCHAR) NewFlags; PopApplyAttributeState (NewFlags, OldFlags); } else { PopApplyAttributeState (NewFlags, 0); } // // Release the lock here, but don't steal the poor caller's thread to // do the work. Otherwise we can get in weird message loop deadlocks as // this thread is waiting for the USER32 thread, which is broadcasting a // system message to this thread's window. // PopReleasePolicyLock (FALSE); PopCheckForWork(TRUE); // // Return the results // try { *PreviousFlags = OldFlags; } except(EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); } } return Status; } NTSYSAPI NTSTATUS NTAPI NtRequestWakeupLatency( IN LATENCY_TIME latency // LT_xxx flags ) /*++ Routine Description: Tracks process wakeup latecy attribute. Keeps global count of all such attribute settings. Arguments: latency - Current latency setting for process Return Value: Status --*/ { PEPROCESS Process; ULONG OldFlags, NewFlags; PAGED_CODE(); // // Verify latency is known // switch (latency) { case LT_DONT_CARE: NewFlags = ES_CONTINUOUS; break; case LT_LOWEST_LATENCY: NewFlags = ES_CONTINUOUS | POP_LOW_LATENCY; break; default: return STATUS_INVALID_PARAMETER; } Process = PsGetCurrentProcess(); PopAcquirePolicyLock (); // // Get changes // OldFlags = Process->Pcb.PowerState | ES_CONTINUOUS; // // Udpate latency flag in process field // Process->Pcb.PowerState = (UCHAR) NewFlags; // // Handle flags // PopApplyAttributeState (NewFlags, OldFlags); // // Done // PopReleasePolicyLock (TRUE); return STATUS_SUCCESS; } NTSYSAPI NTSTATUS NTAPI NtInitiatePowerAction( IN POWER_ACTION SystemAction, IN SYSTEM_POWER_STATE LightestSystemState, IN ULONG Flags, // POWER_ACTION_xxx flags IN BOOLEAN Asynchronous ) /*++ Routine Description: Implements functionality for Win32 APIs to initiate a power action. Causes s/w initiated trigger of requested action. Arguments: SystemAction - The action to initiate LightestSystemState - If a sleep action, the minimum state which must be entered Flags - Attributes of action Asynchronous - Function should initiate action and return, or should wait for the action to complete before returning Return Value: Status --*/ { KPROCESSOR_MODE PreviousMode; POWER_ACTION_POLICY Policy; POP_ACTION_TRIGGER Trigger; PPOP_TRIGGER_WAIT Wait; NTSTATUS Status; PAGED_CODE(); // // Verify callers access // PreviousMode = KeGetPreviousMode(); if (!SeSinglePrivilegeCheck( SeShutdownPrivilege, PreviousMode )) { return STATUS_PRIVILEGE_NOT_HELD; } if (SystemAction == PowerActionWarmEject) { if (PreviousMode != KernelMode) { return STATUS_INVALID_PARAMETER_1; } } if (Flags & POWER_ACTION_LIGHTEST_FIRST) { return STATUS_INVALID_PARAMETER_3; } // // Build a policy & trigger to cause the action // RtlZeroMemory (&Policy, sizeof(Policy)); RtlZeroMemory (&Trigger, sizeof(Trigger)); Policy.Action = SystemAction; Policy.Flags = Flags; Trigger.Type = PolicyInitiatePowerActionAPI; Trigger.Flags = PO_TRG_SET; Status = STATUS_SUCCESS; // // If this is a synchronous power action request attach trigger // wait structure to action // Wait = NULL; if (!Asynchronous) { Wait = ExAllocatePoolWithTag ( NonPagedPool, sizeof (POP_TRIGGER_WAIT), POP_PACW_TAG ); if (!Wait) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory (Wait, sizeof(POP_TRIGGER_WAIT)); Wait->Status = STATUS_SUCCESS; Wait->Trigger = &Trigger; KeInitializeEvent (&Wait->Event, NotificationEvent, FALSE); Trigger.Flags |= PO_TRG_SYNC; Trigger.Wait = Wait; } // // Acquire lock and fire it // PopAcquirePolicyLock (); try { PopSetPowerAction( &Trigger, 0, &Policy, LightestSystemState, SubstituteLightestOverallDownwardBounded ); } except (PopExceptionFilter(GetExceptionInformation(), TRUE)) { Status = GetExceptionCode(); } PopReleasePolicyLock (TRUE); // // If queued, wait for it to complete // if (Wait) { if (Wait->Link.Flink) { ASSERT(NT_SUCCESS(Status)); Status = KeWaitForSingleObject (&Wait->Event, Suspended, KernelMode, TRUE, NULL); if (NT_SUCCESS(Status)) { Status = Wait->Status; } // // Remove wait block from the queue // PopAcquirePolicyLock (); RemoveEntryList (&Wait->Link); PopReleasePolicyLock (FALSE); } else { // // The wait block was not queued, it must have either failed or succeeded // immediately. // Status = Wait->Status; } ExFreePool (Wait); } return Status; } NTSYSAPI NTSTATUS NTAPI NtSetSystemPowerState ( IN POWER_ACTION SystemAction, IN SYSTEM_POWER_STATE LightestSystemState, IN ULONG Flags // POWER_ACTION_xxx flags ) /*++ Routine Description: N.B. This function is only called by Winlogon. Winlogon calls this function in response to the policy manager calling PopStateCallout once user mode operations have completed. Arguments: SystemAction - The current system action being processed. LightestSystemState - The min system state for the action. Flags - The attribute flags for the action. Return Value: Status --*/ { KPROCESSOR_MODE PreviousMode; NTSTATUS Status, Status2; POWER_ACTION_POLICY Action; BOOLEAN QueryDevices; BOOLEAN TimerRefreshLockOwned; BOOLEAN BootStatusUpdated; BOOLEAN VolumesFlushed; BOOLEAN PolicyLockOwned; BOOLEAN OptionsExhausted; PVOID WakeTimerObject; PVOID S4DozeObject; HANDLE S4DozeTimer; OBJECT_ATTRIBUTES ObjectAttributes; TIMER_BASIC_INFORMATION TimerInformation; POP_ACTION_TRIGGER Trigger; SYSTEM_POWER_STATE DeepestSystemState; ULONGLONG WakeTime; ULONGLONG SleepTime; TIME_FIELDS WakeTimeFields; LARGE_INTEGER DueTime; POP_SUBSTITUTION_POLICY SubstitutionPolicy; NT_PRODUCT_TYPE NtProductType; PIO_ERROR_LOG_PACKET ErrLog; BOOLEAN WroteErrLog=FALSE; // // Verify callers access // PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { if (!SeSinglePrivilegeCheck( SeShutdownPrivilege, PreviousMode )) { return STATUS_PRIVILEGE_NOT_HELD; } // // Turn into kernel mode operation // return ZwSetSystemPowerState (SystemAction, LightestSystemState, Flags); } // // disable registry's lazzy flusher // CmSetLazyFlushState(FALSE); // // Setup // Status = STATUS_SUCCESS; TimerRefreshLockOwned = FALSE; BootStatusUpdated = FALSE; VolumesFlushed = FALSE; S4DozeObject = NULL; WakeTimerObject = NULL; WakeTime = 0; RtlZeroMemory (&Action, sizeof(Action)); Action.Action = SystemAction; Action.Flags = Flags; RtlZeroMemory (&Trigger, sizeof(Trigger)); Trigger.Type = PolicySetPowerStateAPI; Trigger.Flags = PO_TRG_SET; // // Lock any code dealing with shutdown or sleep // // PopUnlockComplete event is used to make sure that any previous unlock // has completed before we try and lock everything again. // ASSERT(ExPageLockHandle); KeWaitForSingleObject(&PopUnlockComplete, WrExecutive, KernelMode, FALSE, NULL); MmLockPagableSectionByHandle(ExPageLockHandle); ExNotifyCallback (ExCbPowerState, (PVOID) PO_CB_SYSTEM_STATE_LOCK, (PVOID) 0); ExSwapinWorkerThreads(FALSE); // // Acquire policy manager lock // PopAcquirePolicyLock (); PolicyLockOwned = TRUE; // // If we're not in the callout state, don't re-enter. // The caller (paction.c) will handle the collision. // if (PopAction.State != PO_ACT_IDLE && PopAction.State != PO_ACT_CALLOUT) { PoPrint (PO_PACT, ("NtSetSystemPowerState: already committed\n")); PopReleasePolicyLock (FALSE); MmUnlockPagableImageSection (ExPageLockHandle); ExSwapinWorkerThreads(TRUE); KeSetEvent(&PopUnlockComplete, 0, FALSE); // // try to catch weird case where we exit this routine with the // time refresh lock held. // ASSERT(!ExIsResourceAcquiredExclusive(&ExpTimeRefreshLock)); return STATUS_ALREADY_COMMITTED; } if (PopAction.State == PO_ACT_IDLE) { // // If there is no other request, we want to clean up PopAction before we start, // PopSetPowerAction() will not do this after we set State=PO_ACT_SET_SYSTEM_STATE. // PopResetActionDefaults(); } // // Update to action state to setting the system state // PopAction.State = PO_ACT_SET_SYSTEM_STATE; // // Set status to cancelled to start off as if this is a new request // Status = STATUS_CANCELLED; try { // // Verify params and promote the current action. // PopSetPowerAction( &Trigger, 0, &Action, LightestSystemState, SubstituteLightestOverallDownwardBounded ); } except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); ASSERT (!NT_SUCCESS(Status)); } // // Lagecy hal support. If the original action was PowerDown // change the action to be power down (as presumbly even if // there's no handler HalReturnToFirmware will know what to do) // if (SystemAction == PowerActionShutdownOff) { PopAction.Action = PowerActionShutdownOff; } // // Allocate the DevState here. From this point out we must be careful // that we never release the policy lock with State == PO_ACT_SET_SYSTEM_STATE // and PopAction.DevState not valid. Otherwise there is a race condition // with PopRestartSetSystemState. // PopAllocateDevState(); if (PopAction.DevState == NULL) { PopAction.State = PO_ACT_IDLE; PopReleasePolicyLock(FALSE); MmUnlockPagableImageSection( ExPageLockHandle ); ExSwapinWorkerThreads(TRUE); KeSetEvent(&PopUnlockComplete, 0, FALSE); // // try to catch weird case where we exit this routine with the // time refresh lock held. // ASSERT(!ExIsResourceAcquiredExclusive(&ExpTimeRefreshLock)); return STATUS_INSUFFICIENT_RESOURCES; } // // At this point in the cycle, its not possible to abort the operation // so this is a good time to ensure that the CPU is back running as close // to 100% as we can make it. // PopSetPerfFlag( PSTATE_DISABLE_THROTTLE_NTAPI, FALSE ); PopUpdateAllThrottles(); // // While there's some action pending handle it. // // N.B. We will never get here if no sleep states are supported, as // NtInitiatePowerAction will fail (PopVerifyPowerActionPolicy will return // Disabled == TRUE). Therefore we won't accidentally querying for S0. Note // that all the policy limitations were also verified at some point too. // for (; ;) { // // N.B. The system must be in the working state to be here // if (!PolicyLockOwned) { PopAcquirePolicyLock (); PolicyLockOwned = TRUE; } // // If there's nothing to do, stop // if (PopAction.Action == PowerActionNone) { break; } // // Hibernate actions are converted to sleep actions before here. // ASSERT (PopAction.Action != PowerActionHibernate); // // We're handling it - clear update flags // PopAction.Updates &= ~(PO_PM_USER | PO_PM_REISSUE | PO_PM_SETSTATE); // // If the last operation was cancelled, update state for the // new operation // if (Status == STATUS_CANCELLED) { // // If Re-issue is set we may need to abort back to PopSetPowerAction // to let apps know of the promotion // if (PopAction.Updates & PO_PM_REISSUE) { // // Only abort if apps notificiation is allowed // if (!(PopAction.Flags & (POWER_ACTION_CRITICAL)) && (PopAction.Flags & (POWER_ACTION_QUERY_ALLOWED | POWER_ACTION_UI_ALLOWED)) ) { // abort with STATUS_CANCELLED to PopSetPowerAction PopGetPolicyWorker (PO_WORKER_ACTION_NORMAL); break; } } // // Get limits and start (over) with the first sleep state to try. // PopActionRetrieveInitialState( &PopAction.LightestState, &DeepestSystemState, &PopAction.SystemState, &QueryDevices ); ASSERT (PopAction.SystemState != PowerActionNone); if ((PopAction.Action == PowerActionShutdown) || (PopAction.Action == PowerActionShutdownReset) || (PopAction.Action == PowerActionShutdownOff)) { // // This is a shutdown. // PopAction.Shutdown = TRUE; } Status = STATUS_SUCCESS; } // // Quick debug check. Our first sleep state must always be valid, ie // validation doesn't change it. // #if DBG if (QueryDevices && (PopAction.SystemState < PowerSystemShutdown)) { SYSTEM_POWER_STATE TempSystemState; TempSystemState = PopAction.SystemState; PopVerifySystemPowerState(&TempSystemState, SubstituteLightestOverallDownwardBounded); if ((TempSystemState != PopAction.SystemState) || (TempSystemState == PowerSystemWorking)) { PopInternalError (POP_INFO); } } #endif // // If not success, abort SetSystemPowerState operation // if (!NT_SUCCESS(Status)) { break; } // // Only need the lock while updating PopAction.Action + Updates, and // can not hold the lock while sending irps to device drivers // PopReleasePolicyLock(FALSE); PolicyLockOwned = FALSE; // // Fish PopSimulate out of the registry so that it can // modify some of our sleep/hiber behavior. // PopInitializePowerPolicySimulate(); // // Dump any previous device state error // PopReportDevState (FALSE); // // What would be our next state to try? // PopAction.NextSystemState = PopAction.SystemState; if (PopAction.Flags & POWER_ACTION_LIGHTEST_FIRST) { // // We started light, now we deepen our sleep state. // SubstitutionPolicy = SubstituteDeepenSleep; } else { // // We started deep, now we're lightening up. // SubstitutionPolicy = SubstituteLightenSleep; } PopAdvanceSystemPowerState(&PopAction.NextSystemState, SubstitutionPolicy, PopAction.LightestState, DeepestSystemState); // // If allowed, query devices // PopAction.IrpMinor = IRP_MN_QUERY_POWER; if (QueryDevices) { // // Issue query to devices // Status = PopSetDevicesSystemState (FALSE); // // If the last operation was a failure, but wasn't a total abort // continue with next best state // if (!NT_SUCCESS(Status) && Status != STATUS_CANCELLED) { // // Try next sleep state // PopAction.SystemState = PopAction.NextSystemState; // // If we're already exhausted all possible states, check // if we need to continue regardless of the device failures. // if (PopAction.SystemState == PowerSystemWorking) { if (PopAction.Flags & POWER_ACTION_CRITICAL) { // // It's critical. Stop querying and since the devices // aren't particularly happy with any of the possible // states, might as well use the max state // ASSERT( PopAction.Action != PowerActionWarmEject ); ASSERT( !(PopAction.Flags & POWER_ACTION_LIGHTEST_FIRST) ); QueryDevices = FALSE; PopAction.SystemState = DeepestSystemState; PopAction.Flags &= ~POWER_ACTION_LIGHTEST_FIRST; } else { // // The query failure is final. Don't retry // break; } } // // Try new settings // Status = STATUS_SUCCESS; continue; } } // // If some error, start over // if (!NT_SUCCESS(Status)) { continue; } // // Flush out any D irps on the queue. There shouldn't be any, but by // setting LastCall == TRUE this also resets PopCallSystemState so that // any D irps which occur as a side-effect of flushing the volumes get // processed correctly. // PopSystemIrpDispatchWorker(TRUE); // // If this is a server and we are going into hibernation, write an entry // into the eventlog. This allows for easy tracking of system downtime // by searching the eventlog for hibernate/resume events. // if (RtlGetNtProductType(&NtProductType) && (NtProductType != NtProductWinNt) && (PopAction.SystemState == PowerSystemHibernate)) { ErrLog = IoAllocateGenericErrorLogEntry(sizeof(IO_ERROR_LOG_PACKET)); if (ErrLog) { // // Fill it in and write it out // ErrLog->FinalStatus = STATUS_HIBERNATED; ErrLog->ErrorCode = STATUS_HIBERNATED; IoWriteErrorLogEntry(ErrLog); WroteErrLog = TRUE; } } // // Get hibernation context // Status = PopAllocateHiberContext (); if (!NT_SUCCESS(Status) || (PopAction.Updates & (PO_PM_REISSUE | PO_PM_SETSTATE))) { continue; } // // If boot status hasn't already been updated then do so now. // if(!BootStatusUpdated) { if(PopAction.Shutdown) { NTSTATUS bsdStatus; HANDLE bsdHandle; bsdStatus = RtlLockBootStatusData(&bsdHandle); if(NT_SUCCESS(bsdStatus)) { BOOLEAN t = TRUE; RtlGetSetBootStatusData(bsdHandle, FALSE, RtlBsdItemBootShutdown, &t, sizeof(t), NULL); RtlUnlockBootStatusData(bsdHandle); } } BootStatusUpdated = TRUE; } // // If not already flushed, flush the volumes // if (!VolumesFlushed) { VolumesFlushed = TRUE; PopFlushVolumes (); } // // Enter the SystemState // PopAction.IrpMinor = IRP_MN_SET_POWER; if (PopAction.Shutdown) { // // Force reacquisition of the dev list. We will be telling Pnp // to unload all possible devices, and therefore Pnp needs us to // release the Pnp Engine Lock. // IoFreePoDeviceNotifyList(&PopAction.DevState->Order); PopAction.DevState->GetNewDeviceList = TRUE; // // We shut down via a system worker thread so that the // current active process will exit cleanly. // if (PsGetCurrentProcess() != PsInitialSystemProcess) { ExInitializeWorkItem(&PopShutdownWorkItem, &PopGracefulShutdown, NULL); ExQueueWorkItem(&PopShutdownWorkItem, PO_SHUTDOWN_QUEUE); // Clean up in prep for wait... ASSERT(!PolicyLockOwned); // // If we acquired the timer refresh lock (can happen if we promoted to shutdown) // then we need to release it so that suspend actually suspends. // if (TimerRefreshLockOwned) { ExReleaseTimeRefreshLock(); } // And sleep until we're terminated. // Note that we do NOT clean up the dev state -- it's now // owned by the shutdown worker thread. // Note that we also do not unlock the pagable image // section referred to by ExPageLockHandle -- this keeps // all of our shutdown code in memory. KeSuspendThread(KeGetCurrentThread()); return STATUS_SYSTEM_SHUTDOWN; } else { PopGracefulShutdown (NULL); } } // // Get the timer refresh lock to hold off automated time of day // adjustments. On wake the time will be explicitly reset from Cmos // if (!TimerRefreshLockOwned) { TimerRefreshLockOwned = TRUE; ExAcquireTimeRefreshLock(TRUE); } // This is where PopAllocateHiberContext used to be before bug #212420 // // If there's a Doze to S4 timeout set, and this wasn't an S4 action // and the system can support and S4 state, set a timer for the doze time // // N.B. this must be set before the paging devices are turned off // if (S4DozeObject) { S4DozeObject = NULL; NtClose (S4DozeTimer); } if (PopPolicy->DozeS4Timeout && !S4DozeObject && PopAction.SystemState != PowerSystemHibernate && SystemAction != PowerActionHibernate && PopCapabilities.SystemS4 && PopCapabilities.SystemS5 && PopCapabilities.HiberFilePresent) { // // Create a timer to wake the machine up when we need to hibernate // InitializeObjectAttributes (&ObjectAttributes, NULL, 0, NULL, NULL); Status2 = NtCreateTimer ( &S4DozeTimer, TIMER_ALL_ACCESS, &ObjectAttributes, NotificationTimer ); if (NT_SUCCESS(Status2)) { // // Get the timer object for this timer // Status2 = ObReferenceObjectByHandle ( S4DozeTimer, TIMER_ALL_ACCESS, NULL, KernelMode, &S4DozeObject, NULL ); ASSERT(NT_SUCCESS(Status2)); ObDereferenceObject(S4DozeObject); } } // // Inform drivers of the system sleeping state // Status = PopSetDevicesSystemState (FALSE); if (!NT_SUCCESS(Status)) { continue; } // // Drivers have been informed, this operation is now committed, // get the next wakeup time // if (!(PopAction.Flags & POWER_ACTION_DISABLE_WAKES)) { // // Set S4Doze wakeup timer // if (S4DozeObject) { DueTime.QuadPart = -(LONGLONG) (US2SEC*US2TIME) * PopPolicy->DozeS4Timeout; NtSetTimer(S4DozeTimer, &DueTime, NULL, NULL, TRUE, 0, NULL); } ExGetNextWakeTime(&WakeTime, &WakeTimeFields, &WakeTimerObject); } // // Only enable RTC wake if the system is going to an S-state that // supports the RTC wake. // if (PopCapabilities.RtcWake != PowerSystemUnspecified && PopCapabilities.RtcWake >= PopAction.SystemState && WakeTime) { #if DBG ULONGLONG InterruptTime; InterruptTime = KeQueryInterruptTime(); PoPrint (PO_PACT, ("Wake alarm set%s: %d:%02d:%02d %d (%d seconds from now)\n", WakeTimerObject == S4DozeObject ? " for s4doze" : "", WakeTimeFields.Hour, WakeTimeFields.Minute, WakeTimeFields.Second, WakeTimeFields.Year, (WakeTime - InterruptTime) / (US2TIME * US2SEC) )); #endif HalSetWakeEnable(TRUE); HalSetWakeAlarm(WakeTime, &WakeTimeFields); } else { HalSetWakeEnable(TRUE); HalSetWakeAlarm( 0, NULL ); } // // Capture the last sleep time. // SleepTime = KeQueryInterruptTime(); // // Implement system handler for sleep operation // Status = PopSleepSystem (PopAction.SystemState, PopAction.HiberContext); // // A sleep or shutdown operation attempt was performed, clean up // break; } // // If the system slept successfully, update the system time to // match the CMOS clock. // if (NT_SUCCESS(Status)) { PopAction.SleepTime = SleepTime; ASSERT(TimerRefreshLockOwned); ExUpdateSystemTimeFromCmos (TRUE, 1); PERFINFO_HIBER_START_LOGGING(); } // // If DevState was allocated, notify drivers the system is awake // if (PopAction.DevState) { // // Log any failures // PopReportDevState (TRUE); // // Notify drivers that the system is now running // PopSetDevicesSystemState (TRUE); } // // Free the device notify list. This must be done before acquiring // the policy lock, otherwise we can deadlock with the PNP device // tree lock. // ASSERT(PopAction.DevState != NULL); IoFreePoDeviceNotifyList(&PopAction.DevState->Order); // // Get the policy lock for the rest of the cleanup // if (!PolicyLockOwned) { PopAcquirePolicyLock (); PolicyLockOwned = TRUE; } // // Cleanup DevState // PopCleanupDevState (); if (NT_SUCCESS(Status)) { // // Now that the time has been fixed, record the last state // the system has awoken from and the current time // PopAction.LastWakeState = PopAction.SystemState; PopAction.WakeTime = KeQueryInterruptTime(); // // If full wake hasn't been signalled, then set then start a // really agressive idle detection // if (!AnyBitsSet (PopFullWake, PO_FULL_WAKE_STATUS | PO_FULL_WAKE_PENDING)) { // // If there was an S4Doze timer set, check to see if it's // expired and update the idle detection to enter S4 // if (S4DozeObject) { NtQueryTimer (S4DozeTimer, TimerBasicInformation, &TimerInformation, sizeof (TimerInformation), NULL); if (TimerInformation.TimerState) { // // Update the idle detection action to be hibernate // PoPrint (PO_PACT, ("Wake with S4 timer expired\n")); // // If the s4timer was the alarm time, and we're awake // in under the idle reenter time, just drop right into // S4 without any idle detection. (we check the current // time in case in case the alarm time expired but for // some reason the system did not wake at that time) // if ((WakeTimerObject == S4DozeObject) && (PopAction.WakeTime - WakeTime < SYS_IDLE_REENTER_TIMEOUT * US2TIME * US2SEC)) { PopAction.Action = PowerActionSleep; PopAction.LightestState = PowerSystemHibernate; PopAction.Updates |= PO_PM_REISSUE; } } } // // Set the system idle detection code to re-enter this state // real agressively (assuming a full wake doesn't happen) // PopInitSIdle (); } } // // Free anything that's left of the hiber context // PopFreeHiberContext (TRUE); // // Clear out PopAction unless we have promoted directly to hibernate // if ((PopAction.Updates & PO_PM_REISSUE) == 0) { PopResetActionDefaults(); } // // We are no longer active // We don't check for work here as this may be "the thread" from winlogon. // So we explicitly queue pending policy work off to a worker thread below // after setting the win32k wake notifications. // PopAction.State = PO_ACT_CALLOUT; PopReleasePolicyLock (FALSE); // // If there's been some sort of error, make sure gdi is enabled // if (!NT_SUCCESS(Status)) { PopDisplayRequired (0); } // // If some win32k wake event is pending, tell win32k // if (PopFullWake & PO_FULL_WAKE_PENDING) { PopSetNotificationWork (PO_NOTIFY_FULL_WAKE); } else if (PopFullWake & PO_GDI_ON_PENDING) { PopSetNotificationWork (PO_NOTIFY_DISPLAY_REQUIRED); } // // If the timer refresh lock was acquired, release it // if (TimerRefreshLockOwned) { ExReleaseTimeRefreshLock(); } else { // // try to catch weird case where we exit this routine with the // time refresh lock held. // ASSERT(!ExIsResourceAcquiredExclusive(&ExpTimeRefreshLock)); } // // Unlock pageable code. The unlock is queued off to a delayed worker queue // since it is likely to block on pagable code, registry, etc. The PopUnlockComplete // event is used to prevent the unlock from racing with a subsequent lock. // ExQueueWorkItem(&PopUnlockAfterSleepWorkItem, DelayedWorkQueue); // // If a timer for s4 dozing was allocated, close it // if (S4DozeObject) { NtClose (S4DozeTimer); } // // If we wrote an errlog message indicating that we were hibernating, write a corresponding // one to indicate we have woken. // if (WroteErrLog) { ErrLog = IoAllocateGenericErrorLogEntry(sizeof(IO_ERROR_LOG_PACKET)); if (ErrLog) { // // Fill it in and write it out // ErrLog->FinalStatus = STATUS_RESUME_HIBERNATION; ErrLog->ErrorCode = STATUS_RESUME_HIBERNATION; IoWriteErrorLogEntry(ErrLog); } } // // Finally, we can revert the throttle back to a normal value // PopSetPerfFlag( PSTATE_DISABLE_THROTTLE_NTAPI, TRUE ); PopUpdateAllThrottles(); // // Done - kick off the policy worker thread to process any outstanding work in // a worker thread. // PopCheckForWork(TRUE); // // enable registry's lazzy flusher // CmSetLazyFlushState(TRUE); // // try to catch weird case where we exit this routine with the // time refresh lock held. // ASSERT(!ExIsResourceAcquiredExclusive(&ExpTimeRefreshLock)); return Status; } NTSYSAPI NTSTATUS NTAPI NtRequestDeviceWakeup( IN HANDLE Device ) /*++ Routine Description: This routine requests a WAIT_WAKE Irp on the specified handle. If the handle is to a device object, the WAIT_WAKE irp is sent to the top of that device's stack. If a WAIT_WAKE is already outstanding on the device, this routine increments the WAIT_WAKE reference count and return success. Arguments: Device - Supplies the device which should wake the system Return Value: NTSTATUS --*/ { PFILE_OBJECT fileObject; PDEVICE_OBJECT deviceObject; PDEVICE_OBJECT targetDevice; NTSTATUS status; PDEVICE_OBJECT_POWER_EXTENSION dope; // // Reference the file object in order to get to the device object // in question. // status = ObReferenceObjectByHandle(Device, 0L, IoFileObjectType, KeGetPreviousMode(), (PVOID *)&fileObject, NULL); if (!NT_SUCCESS(status)) { return(status); } // // Get the address of the target device object. // if (!(fileObject->Flags & FO_DIRECT_DEVICE_OPEN)) { deviceObject = IoGetAttachedDeviceReference( IoGetRelatedDeviceObject( fileObject )); } else { deviceObject = IoGetAttachedDeviceReference( fileObject->DeviceObject ); } // // Now that we have the device object, we are done with the file object // ObDereferenceObject(fileObject); ObDereferenceObject(deviceObject); return (STATUS_NOT_IMPLEMENTED); } NTSYSAPI NTSTATUS NTAPI NtCancelDeviceWakeupRequest( IN HANDLE Device ) /*++ Routine Description: This routine cancels a WAIT_WAKE irp sent to a device previously with NtRequestDeviceWakeup. The WAIT_WAKE reference count on the device is decremented. If this count goes to zero, the WAIT_WAKE irp is cancelled. Arguments: Device - Supplies the device which should wake the system Return Value: NTSTATUS --*/ { return(STATUS_NOT_IMPLEMENTED); } NTSYSAPI BOOLEAN NTAPI NtIsSystemResumeAutomatic( VOID ) /*++ Routine Description: Returns whether or not the most recent wake was automatic or due to a user action. Arguments: None Return Value: TRUE - The system was awakened due to a timer or device wake FALSE - The system was awakened due to a user action --*/ { if (AnyBitsSet(PopFullWake, PO_FULL_WAKE_STATUS | PO_FULL_WAKE_PENDING)) { return(FALSE); } else { return(TRUE); } } NTSYSAPI NTSTATUS NTAPI NtGetDevicePowerState( IN HANDLE Device, OUT DEVICE_POWER_STATE *State ) /*++ Routine Description: Queries the current power state of a device. Arguments: Device - Supplies the handle to a device. State - Returns the current power state of the device. Return Value: NTSTATUS --*/ { PFILE_OBJECT fileObject; PDEVICE_OBJECT deviceObject; NTSTATUS status; PDEVOBJ_EXTENSION doe; KPROCESSOR_MODE PreviousMode; DEVICE_POWER_STATE dev_state; PAGED_CODE(); // // Verify caller's parameter // PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { try { ProbeForWriteUlong((PULONG)State); } except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); return(status); } } // // Reference the file object in order to get to the device object // in question. // status = ObReferenceObjectByHandle(Device, 0L, IoFileObjectType, KeGetPreviousMode(), (PVOID *)&fileObject, NULL); if (!NT_SUCCESS(status)) { return(status); } // // Get the address of the target device object. // status = IoGetRelatedTargetDevice(fileObject, &deviceObject); // // Now that we have the device object, we are done with the file object // ObDereferenceObject(fileObject); if (!NT_SUCCESS(status)) { return(status); } doe = deviceObject->DeviceObjectExtension; dev_state = PopLockGetDoDevicePowerState(doe); try { *State = dev_state; } except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); } ObDereferenceObject(deviceObject); return (status); }