From 8a4845b4092b1a865dc0e678ab4545216a70e111 Mon Sep 17 00:00:00 2001 From: Sir Richard Date: Mon, 8 Mar 2010 20:47:10 +0000 Subject: [PATCH] [NTOS]: Have I/O Manager Volume Device Objects register with the Power Manager so that they can receive dope. [NTOS]: Reimplement NtShutdownSystem. [NTOS]: Implement NtSetSystemPowerState for the shutdown/reboot cases. [NTOS]: Use the dope from the volume device objects to flush all writeable (non-floppy) devices. Pending hard-disk changes are now flushed to disks before shutdown. [NTOS]: Flush \\REGISTRY during shutdown. This flushes all pending changes. [NTOS]: Call into Cc to flush lazy writer during shutdown. [NTOS]: Stop killing processes on shutdown. The kernel should not be doing this. [NTOS]: Don't only shutdown disk file systems, but also cdrom and tape. [NTOS]: Don't only notify drivers of first-chance shutdown -- also parse the last-change shutdown list. [NTOS]: Reference drivers registering for shutdown notifications so that they remain loaded for them to get the notification at shutdown. [NTOS]: Notify drivers that have registered/opened the Power State callback. [NTOS]: A lot of the Po* power state code is highly simplified, but provides a good roadmap to anyone interested in this functionality long-term. svn path=/trunk/; revision=46004 --- reactos/ntoskrnl/ex/shutdown.c | 141 ++-------- reactos/ntoskrnl/include/internal/io.h | 8 +- reactos/ntoskrnl/include/internal/po.h | 263 ++++++++++++++++++ reactos/ntoskrnl/io/iomgr/device.c | 162 ++++++++--- reactos/ntoskrnl/io/iomgr/volume.c | 6 +- reactos/ntoskrnl/ntoskrnl-generic.rbuild | 4 +- reactos/ntoskrnl/po/poshtdwn.c | 196 +++++++++++++ reactos/ntoskrnl/po/povolume.c | 334 +++++++++++++++++++++++ reactos/ntoskrnl/po/power.c | 144 ++++++++++ 9 files changed, 1102 insertions(+), 156 deletions(-) create mode 100644 reactos/ntoskrnl/po/poshtdwn.c create mode 100644 reactos/ntoskrnl/po/povolume.c diff --git a/reactos/ntoskrnl/ex/shutdown.c b/reactos/ntoskrnl/ex/shutdown.c index 9a84bd45c19..30f8606724e 100644 --- a/reactos/ntoskrnl/ex/shutdown.c +++ b/reactos/ntoskrnl/ex/shutdown.c @@ -14,123 +14,40 @@ /* FUNCTIONS *****************************************************************/ -VOID NTAPI -ShutdownThreadMain(PVOID Context) -{ - SHUTDOWN_ACTION Action = (SHUTDOWN_ACTION)Context; - PUCHAR Logo1, Logo2; - ULONG i; - - /* Run the thread on the boot processor */ - KeSetSystemAffinityThread(1); - - PspShutdownProcessManager(); - - CmShutdownSystem(); - IoShutdownRegisteredFileSystems(); - IoShutdownRegisteredDevices(); - - if (Action == ShutdownNoReboot) - { - /* Try the platform driver */ - PopSetSystemPowerState(PowerSystemShutdown); - - /* If that didn't work, try legacy switch off */ - //HalReturnToFirmware(HalPowerDownRoutine); - - /* If that still didn't work, stop all interrupts */ - KeRaiseIrqlToDpcLevel(); - _disable(); - - /* Do we have boot video */ - if (InbvIsBootDriverInstalled()) - { - /* Yes we do, cleanup for shutdown screen */ - if (!InbvCheckDisplayOwnership()) InbvAcquireDisplayOwnership(); - InbvResetDisplay(); - InbvSolidColorFill(0, 0, 639, 479, 0); - InbvEnableDisplayString(TRUE); - InbvSetScrollRegion(0, 0, 639, 479); - - /* Display shutdown logo and message */ - Logo1 = InbvGetResourceAddress(IDB_SHUTDOWN_LOGO); - Logo2 = InbvGetResourceAddress(IDB_LOGO); - if ((Logo1) && (Logo2)) - { - InbvBitBlt(Logo1, 215, 352); - InbvBitBlt(Logo2, 217, 111); - } - } - else - { - /* Do it in text-mode */ - for (i = 0; i < 25; i++) InbvDisplayString("\n"); - InbvDisplayString(" "); - InbvDisplayString("The system may be powered off now.\n"); - } - - /* Hang the system */ - for (;;) HalHaltSystem(); - } - else if (Action == ShutdownReboot) - { - HalReturnToFirmware (HalRebootRoutine); - } - else - { - HalReturnToFirmware (HalHaltRoutine); - } -} - - -NTSTATUS NTAPI -NtSetSystemPowerState(IN POWER_ACTION SystemAction, - IN SYSTEM_POWER_STATE MinSystemState, - IN ULONG Flags) -{ - /* Windows 2000 only */ - return(STATUS_NOT_IMPLEMENTED); -} - /* * @implemented */ -NTSTATUS NTAPI +NTSTATUS +NTAPI NtShutdownSystem(IN SHUTDOWN_ACTION Action) { - NTSTATUS Status; - HANDLE ThreadHandle; - PETHREAD ShutdownThread; - - if (Action > ShutdownPowerOff) - return STATUS_INVALID_PARAMETER; - Status = PsCreateSystemThread(&ThreadHandle, - THREAD_ALL_ACCESS, - NULL, - NULL, - NULL, - ShutdownThreadMain, - (PVOID)Action); - if (!NT_SUCCESS(Status)) - { - ASSERT(FALSE); - } - Status = ObReferenceObjectByHandle(ThreadHandle, - THREAD_ALL_ACCESS, - PsThreadType, - KernelMode, - (PVOID*)&ShutdownThread, - NULL); - NtClose(ThreadHandle); - if (!NT_SUCCESS(Status)) - { - ASSERT(FALSE); - } - - KeSetPriorityThread(&ShutdownThread->Tcb, LOW_REALTIME_PRIORITY + 1); - ObDereferenceObject(ShutdownThread); - - return STATUS_SUCCESS; + POWER_ACTION PowerAction; + + /* Convert to power action */ + if (Action == ShutdownNoReboot) + { + PowerAction = PowerActionShutdown; + } + else if (Action == ShutdownReboot) + { + PowerAction = PowerActionShutdownReset; + } + else if (Action == ShutdownPowerOff) + { + PowerAction = PowerActionShutdownOff; + } + else + { + return STATUS_INVALID_PARAMETER; + } + + /* Now call the power manager */ + DPRINT1("Setting state to: %lx\n", PowerAction); + return NtSetSystemPowerState(PowerAction, + PowerSystemSleeping3, + POWER_ACTION_OVERRIDE_APPS | + POWER_ACTION_DISABLE_WAKES | + POWER_ACTION_CRITICAL); } /* EOF */ diff --git a/reactos/ntoskrnl/include/internal/io.h b/reactos/ntoskrnl/include/internal/io.h index cf45a025943..c8b671693af 100644 --- a/reactos/ntoskrnl/include/internal/io.h +++ b/reactos/ntoskrnl/include/internal/io.h @@ -743,14 +743,14 @@ IoInitShutdownNotification( VOID NTAPI -IoShutdownRegisteredDevices( - VOID +IoShutdownSystem( + IN ULONG Phase ); VOID NTAPI -IoShutdownRegisteredFileSystems( - VOID +IopShutdownBaseFileSystems( + IN PLIST_ENTRY ListHead ); // diff --git a/reactos/ntoskrnl/include/internal/po.h b/reactos/ntoskrnl/include/internal/po.h index e45a0d95957..01ddf3b5c37 100644 --- a/reactos/ntoskrnl/include/internal/po.h +++ b/reactos/ntoskrnl/include/internal/po.h @@ -32,6 +32,228 @@ #define POTRACE(x, ...) DPRINT(__VA_ARGS__) #endif +typedef struct _PO_HIBER_PERF +{ + ULONGLONG IoTicks; + ULONGLONG InitTicks; + ULONGLONG CopyTicks; + ULONGLONG StartCount; + ULONG ElapsedTime; + ULONG IoTime; + ULONG CopyTime; + ULONG InitTime; + ULONG PagesWritten; + ULONG PagesProcessed; + ULONG BytesCopied; + ULONG DumpCount; + ULONG FileRuns; +} PO_HIBER_PERF, *PPO_HIBER_PERF; + +typedef struct _PO_MEMORY_IMAGE +{ + ULONG Signature; + ULONG Version; + ULONG CheckSum; + ULONG LengthSelf; + PFN_NUMBER PageSelf; + ULONG PageSize; + ULONG ImageType; + LARGE_INTEGER SystemTime; + ULONGLONG InterruptTime; + ULONG FeatureFlags; + UCHAR HiberFlags; + UCHAR spare[3]; + ULONG NoHiberPtes; + ULONG_PTR HiberVa; + PHYSICAL_ADDRESS HiberPte; + ULONG NoFreePages; + ULONG FreeMapCheck; + ULONG WakeCheck; + PFN_NUMBER TotalPages; + PFN_NUMBER FirstTablePage; + PFN_NUMBER LastFilePage; + PO_HIBER_PERF PerfInfo; +} PO_MEMORY_IMAGE, *PPO_MEMORY_IMAGE; + +typedef struct _PO_MEMORY_RANGE_ARRAY_RANGE +{ + PFN_NUMBER PageNo; + PFN_NUMBER StartPage; + PFN_NUMBER EndPage; + ULONG CheckSum; +} PO_MEMORY_RANGE_ARRAY_RANGE; + +typedef struct _PO_MEMORY_RANGE_ARRAY_LINK +{ + struct _PO_MEMORY_RANGE_ARRAY *Next; + PFN_NUMBER NextTable; + ULONG CheckSum; + ULONG EntryCount; +} PO_MEMORY_RANGE_ARRAY_LINK; + +typedef struct _PO_MEMORY_RANGE_ARRAY +{ + union + { + PO_MEMORY_RANGE_ARRAY_RANGE Range; + PO_MEMORY_RANGE_ARRAY_LINK Link; + }; +} PO_MEMORY_RANGE_ARRAY, *PPO_MEMORY_RANGE_ARRAY; + +typedef struct _POP_HIBER_CONTEXT +{ + BOOLEAN WriteToFile; + BOOLEAN ReserveLoaderMemory; + BOOLEAN ReserveFreeMemory; + BOOLEAN VerifyOnWake; + BOOLEAN Reset; + UCHAR HiberFlags; + BOOLEAN LinkFile; + HANDLE LinkFileHandle; + PKSPIN_LOCK Lock; + BOOLEAN MapFrozen; + RTL_BITMAP MemoryMap; + LIST_ENTRY ClonedRanges; + ULONG ClonedRangeCount; + PLIST_ENTRY NextCloneRange; + PFN_NUMBER NextPreserve; + PMDL LoaderMdl; + PMDL Clones; + PUCHAR NextClone; + ULONG NoClones; + PMDL Spares; + ULONGLONG PagesOut; + PVOID IoPage; + PVOID CurrentMcb; + PVOID DumpStack; + PKPROCESSOR_STATE WakeState; + ULONG NoRanges; + ULONG_PTR HiberVa; + PHYSICAL_ADDRESS HiberPte; + NTSTATUS Status; + PPO_MEMORY_IMAGE MemoryImage; + PPO_MEMORY_RANGE_ARRAY TableHead; + PVOID CompressionWorkspace; + PUCHAR CompressedWriteBuffer; + PULONG PerformanceStats; + PVOID CompressionBlock; + PVOID DmaIO; + PVOID TemporaryHeap; + PO_HIBER_PERF PerfInfo; +} POP_HIBER_CONTEXT, *PPOP_HIBER_CONTEXT; + +typedef struct _PO_NOTIFY_ORDER_LEVEL +{ + KEVENT LevelReady; + ULONG DeviceCount; + ULONG ActiveCount; + LIST_ENTRY WaitSleep; + LIST_ENTRY ReadySleep; + LIST_ENTRY Pending; + LIST_ENTRY Complete; + LIST_ENTRY ReadyS0; + LIST_ENTRY WaitS0; +} PO_NOTIFY_ORDER_LEVEL, *PPO_NOTIFY_ORDER_LEVEL; + +typedef struct _POP_SHUTDOWN_BUG_CHECK +{ + HANDLE ThreadHandle; + HANDLE ThreadId; + HANDLE ProcessId; + ULONG Code; + ULONG_PTR Parameter1; + ULONG_PTR Parameter2; + ULONG_PTR Parameter3; + ULONG_PTR Parameter4; +} POP_SHUTDOWN_BUG_CHECK, *PPOP_SHUTDOWN_BUG_CHECK; + +typedef struct _POP_DEVICE_POWER_IRP +{ + SINGLE_LIST_ENTRY Free; + PIRP Irp; + PPO_DEVICE_NOTIFY Notify; + LIST_ENTRY Pending; + LIST_ENTRY Complete; + LIST_ENTRY Abort; + LIST_ENTRY Failed; +} POP_DEVICE_POWER_IRP, *PPOP_DEVICE_POWER_IRP; + +typedef struct _PO_DEVICE_NOTIFY_ORDER +{ + ULONG DevNodeSequence; + PDEVICE_OBJECT *WarmEjectPdoPointer; + PO_NOTIFY_ORDER_LEVEL OrderLevel[8]; +} PO_DEVICE_NOTIFY_ORDER, *PPO_DEVICE_NOTIFY_ORDER; + +typedef struct _POP_DEVICE_SYS_STATE +{ + UCHAR IrpMinor; + SYSTEM_POWER_STATE SystemState; + PKEVENT Event; + KSPIN_LOCK SpinLock; + PKTHREAD Thread; + BOOLEAN GetNewDeviceList; + PO_DEVICE_NOTIFY_ORDER Order; + NTSTATUS Status; + PDEVICE_OBJECT FailedDevice; + BOOLEAN Waking; + BOOLEAN Cancelled; + BOOLEAN IgnoreErrors; + BOOLEAN IgnoreNotImplemented; + BOOLEAN _WaitAny; + BOOLEAN _WaitAll; + LIST_ENTRY PresentIrpQueue; + POP_DEVICE_POWER_IRP Head; + POP_DEVICE_POWER_IRP PowerIrpState[20]; +} POP_DEVICE_SYS_STATE, *PPOP_DEVICE_SYS_STATE; + +typedef struct _POP_POWER_ACTION +{ + UCHAR Updates; + UCHAR State; + BOOLEAN Shutdown; + POWER_ACTION Action; + SYSTEM_POWER_STATE LightestState; + ULONG Flags; + NTSTATUS Status; + UCHAR IrpMinor; + SYSTEM_POWER_STATE SystemState; + SYSTEM_POWER_STATE NextSystemState; + PPOP_SHUTDOWN_BUG_CHECK ShutdownBugCode; + PPOP_DEVICE_SYS_STATE DevState; + PPOP_HIBER_CONTEXT HiberContext; + ULONGLONG WakeTime; + ULONGLONG SleepTime; +} POP_POWER_ACTION, *PPOP_POWER_ACTION; + +typedef enum _POP_DEVICE_IDLE_TYPE +{ + DeviceIdleNormal, + DeviceIdleDisk, +} POP_DEVICE_IDLE_TYPE, *PPOP_DEVICE_IDLE_TYPE; + +typedef struct _POWER_CHANNEL_SUMMARY +{ + ULONG Signature; + ULONG TotalCount; + ULONG D0Count; + LIST_ENTRY NotifyList; +} POWER_CHANNEL_SUMMARY, *PPOWER_CHANNEL_SUMMARY; + +typedef struct _DEVICE_OBJECT_POWER_EXTENSION +{ + ULONG IdleCount; + ULONG ConservationIdleTime; + ULONG PerformanceIdleTime; + PDEVICE_OBJECT DeviceObject; + LIST_ENTRY IdleList; + DEVICE_POWER_STATE State; + LIST_ENTRY NotifySourceList; + LIST_ENTRY NotifyTargetList; + POWER_CHANNEL_SUMMARY PowerChannelSummary; + LIST_ENTRY Volume; +} DEVICE_OBJECT_POWER_EXTENSION, *PDEVICE_OBJECT_POWER_EXTENSION; + // // Initialization routines // @@ -47,6 +269,21 @@ PoInitializePrcb( IN PKPRCB Prcb ); +// +// I/O Routines +// +VOID +NTAPI +PoInitializeDeviceObject( + IN OUT PDEVOBJ_EXTENSION DeviceObjectExtension +); + +VOID +NTAPI +PoVolumeDevice( + IN PDEVICE_OBJECT DeviceObject +); + // // Power State routines // @@ -78,7 +315,33 @@ PoNotifySystemTimeSet( VOID ); +// +// Shutdown routines +// +VOID +NTAPI +PopReadShutdownPolicy( + VOID +); + +VOID +NTAPI +PopGracefulShutdown( + IN PVOID Context +); + +VOID +NTAPI +PopFlushVolumes( + IN BOOLEAN ShuttingDown +); + // // Global data inside the Power Manager // extern PDEVICE_NODE PopSystemPowerDeviceNode; +extern KGUARDED_MUTEX PopVolumeLock; +extern LIST_ENTRY PopVolumeDevices; +extern KSPIN_LOCK PopDopeGlobalLock; +extern POP_POWER_ACTION PopAction; + diff --git a/reactos/ntoskrnl/io/iomgr/device.c b/reactos/ntoskrnl/io/iomgr/device.c index 482f7300fc9..7434970734f 100644 --- a/reactos/ntoskrnl/io/iomgr/device.c +++ b/reactos/ntoskrnl/io/iomgr/device.c @@ -17,9 +17,11 @@ /* GLOBALS ********************************************************************/ ULONG IopDeviceObjectNumber = 0; - LIST_ENTRY ShutdownListHead, LastChanceShutdownListHead; KSPIN_LOCK ShutdownListLock; +extern LIST_ENTRY IopDiskFsListHead; +extern LIST_ENTRY IopCdRomFsListHead; +extern LIST_ENTRY IopTapeFsListHead; /* PRIVATE FUNCTIONS **********************************************************/ @@ -95,7 +97,15 @@ IopAttachDeviceToDeviceStackSafe(IN PDEVICE_OBJECT SourceDevice, VOID NTAPI -IoShutdownRegisteredDevices(VOID) +IoShutdownPnpDevices(VOID) +{ + /* This routine is only used by Driver Verifier to validate shutdown */ + return; +} + +VOID +NTAPI +IoShutdownSystem(IN ULONG Phase) { PLIST_ENTRY ListEntry; PDEVICE_OBJECT DeviceObject; @@ -104,46 +114,108 @@ IoShutdownRegisteredDevices(VOID) PIRP Irp; KEVENT Event; NTSTATUS Status; - + /* Initialize an event to wait on */ KeInitializeEvent(&Event, NotificationEvent, FALSE); - - /* Get the first entry and start looping */ - ListEntry = ExInterlockedRemoveHeadList(&ShutdownListHead, - &ShutdownListLock); - while (ListEntry) + + /* What phase? */ + if (Phase == 0) { - /* Get the shutdown entry */ - ShutdownEntry = CONTAINING_RECORD(ListEntry, - SHUTDOWN_ENTRY, - ShutdownList); + /* Shutdown PnP */ + IoShutdownPnpDevices(); - /* Get the attached device */ - DeviceObject = IoGetAttachedDevice(ShutdownEntry->DeviceObject); - - /* Build the shutdown IRP and call the driver */ - Irp = IoBuildSynchronousFsdRequest(IRP_MJ_SHUTDOWN, - DeviceObject, - NULL, - 0, - NULL, - &Event, - &StatusBlock); - Status = IoCallDriver(DeviceObject, Irp); - if (Status == STATUS_PENDING) - { - /* Wait on the driver */ - KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); - } - - /* Free the shutdown entry and reset the event */ - ExFreePoolWithTag(ShutdownEntry, TAG_SHUTDOWN_ENTRY); - KeClearEvent(&Event); - - /* Go to the next entry */ + /* Loop first-chance shutdown notifications */ ListEntry = ExInterlockedRemoveHeadList(&ShutdownListHead, &ShutdownListLock); - } + while (ListEntry) + { + /* Get the shutdown entry */ + ShutdownEntry = CONTAINING_RECORD(ListEntry, + SHUTDOWN_ENTRY, + ShutdownList); + + /* Get the attached device */ + DeviceObject = IoGetAttachedDevice(ShutdownEntry->DeviceObject); + + /* Build the shutdown IRP and call the driver */ + Irp = IoBuildSynchronousFsdRequest(IRP_MJ_SHUTDOWN, + DeviceObject, + NULL, + 0, + NULL, + &Event, + &StatusBlock); + Status = IoCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + /* Wait on the driver */ + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + } + + /* Get rid of our reference to it */ + ObDereferenceObject(DeviceObject); + + /* Free the shutdown entry and reset the event */ + ExFreePoolWithTag(ShutdownEntry, TAG_SHUTDOWN_ENTRY); + KeClearEvent(&Event); + + /* Go to the next entry */ + ListEntry = ExInterlockedRemoveHeadList(&ShutdownListHead, + &ShutdownListLock); + } + } + else if (Phase == 1) + { + /* Shutdown disk file systems */ + IopShutdownBaseFileSystems(&IopDiskFsListHead); + + /* Shutdown cdrom file systems */ + IopShutdownBaseFileSystems(&IopCdRomFsListHead); + + /* Shutdown tape filesystems */ + IopShutdownBaseFileSystems(&IopTapeFsListHead); + + /* Loop last-chance shutdown notifications */ + ListEntry = ExInterlockedRemoveHeadList(&LastChanceShutdownListHead, + &ShutdownListLock); + while (ListEntry) + { + /* Get the shutdown entry */ + ShutdownEntry = CONTAINING_RECORD(ListEntry, + SHUTDOWN_ENTRY, + ShutdownList); + + /* Get the attached device */ + DeviceObject = IoGetAttachedDevice(ShutdownEntry->DeviceObject); + + /* Build the shutdown IRP and call the driver */ + Irp = IoBuildSynchronousFsdRequest(IRP_MJ_SHUTDOWN, + DeviceObject, + NULL, + 0, + NULL, + &Event, + &StatusBlock); + Status = IoCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + /* Wait on the driver */ + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + } + + /* Get rid of our reference to it */ + ObDereferenceObject(DeviceObject); + + /* Free the shutdown entry and reset the event */ + ExFreePoolWithTag(ShutdownEntry, TAG_SHUTDOWN_ENTRY); + KeClearEvent(&Event); + + /* Go to the next entry */ + ListEntry = ExInterlockedRemoveHeadList(&LastChanceShutdownListHead, + &ShutdownListLock); + } + + } } NTSTATUS @@ -843,6 +915,9 @@ IoCreateDevice(IN PDRIVER_OBJECT DriverObject, /* Set the Type and Size. Question: why is Size 0 on Windows? */ DeviceObjectExtension->Type = IO_TYPE_DEVICE_OBJECT_EXTENSION; DeviceObjectExtension->Size = 0; + + /* Initialize with Power Manager */ + PoInitializeDeviceObject(DeviceObjectExtension); /* Link the Object and Extension */ DeviceObjectExtension->DeviceObject = CreatedDeviceObject; @@ -932,6 +1007,9 @@ IoCreateDevice(IN PDRIVER_OBJECT DriverObject, ASSERT((DriverObject->Flags & DRVO_UNLOAD_INVOKED) == 0); CreatedDeviceObject->DriverObject = DriverObject; IopEditDeviceList(DriverObject, CreatedDeviceObject, IopAdd); + + /* Link with the power manager */ + if (CreatedDeviceObject->Vpb) PoVolumeDevice(CreatedDeviceObject); /* Close the temporary handle and return to caller */ ObCloseHandle(TempHandle, KernelMode); @@ -1351,6 +1429,9 @@ IoRegisterLastChanceShutdownNotification(IN PDEVICE_OBJECT DeviceObject) /* Set the DO */ Entry->DeviceObject = DeviceObject; + + /* Reference it so it doesn't go away */ + ObReferenceObject(DeviceObject); /* Insert it into the list */ ExInterlockedInsertHeadList(&LastChanceShutdownListHead, @@ -1379,6 +1460,9 @@ IoRegisterShutdownNotification(PDEVICE_OBJECT DeviceObject) /* Set the DO */ Entry->DeviceObject = DeviceObject; + + /* Reference it so it doesn't go away */ + ObReferenceObject(DeviceObject); /* Insert it into the list */ ExInterlockedInsertHeadList(&ShutdownListHead, @@ -1420,6 +1504,9 @@ IoUnregisterShutdownNotification(PDEVICE_OBJECT DeviceObject) /* Free the entry */ ExFreePoolWithTag(ShutdownEntry, TAG_SHUTDOWN_ENTRY); + + /* Get rid of our reference to it */ + ObDereferenceObject(DeviceObject); } /* Go to the next entry */ @@ -1444,6 +1531,9 @@ IoUnregisterShutdownNotification(PDEVICE_OBJECT DeviceObject) /* Free the entry */ ExFreePoolWithTag(ShutdownEntry, TAG_SHUTDOWN_ENTRY); + + /* Get rid of our reference to it */ + ObDereferenceObject(DeviceObject); } /* Go to the next entry */ diff --git a/reactos/ntoskrnl/io/iomgr/volume.c b/reactos/ntoskrnl/io/iomgr/volume.c index 3f019534e4b..606f8fd3b35 100644 --- a/reactos/ntoskrnl/io/iomgr/volume.c +++ b/reactos/ntoskrnl/io/iomgr/volume.c @@ -245,7 +245,7 @@ IopNotifyFileSystemChange(IN PDEVICE_OBJECT DeviceObject, VOID NTAPI -IoShutdownRegisteredFileSystems(VOID) +IopShutdownBaseFileSystems(IN PLIST_ENTRY ListHead) { PLIST_ENTRY ListEntry; PDEVICE_OBJECT DeviceObject; @@ -260,8 +260,8 @@ IoShutdownRegisteredFileSystems(VOID) KeInitializeEvent(&Event, NotificationEvent, FALSE); /* Get the first entry and start looping */ - ListEntry = IopDiskFsListHead.Flink; - while (ListEntry != &IopDiskFsListHead) + ListEntry = ListHead->Flink; + while (ListEntry != ListHead) { /* Get the device object */ DeviceObject = CONTAINING_RECORD(ListEntry, diff --git a/reactos/ntoskrnl/ntoskrnl-generic.rbuild b/reactos/ntoskrnl/ntoskrnl-generic.rbuild index abf321949a9..c1211761c4f 100644 --- a/reactos/ntoskrnl/ntoskrnl-generic.rbuild +++ b/reactos/ntoskrnl/ntoskrnl-generic.rbuild @@ -438,8 +438,10 @@ obwait.c + events.c power.c - events.c + poshtdwn.c + povolume.c diff --git a/reactos/ntoskrnl/po/poshtdwn.c b/reactos/ntoskrnl/po/poshtdwn.c new file mode 100644 index 00000000000..25ee2e3ddf4 --- /dev/null +++ b/reactos/ntoskrnl/po/poshtdwn.c @@ -0,0 +1,196 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: BSD - See COPYING.ARM in the top level directory + * FILE: ntoskrnl/po/poshtdwn.c + * PURPOSE: Power Manager Shutdown Code + * PROGRAMMERS: ReactOS Portable Systems Group + */ + +/* INCLUDES ******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS *******************************************************************/ + +ULONG PopShutdownPowerOffPolicy; + +/* PRIVATE FUNCTIONS *********************************************************/ + +VOID +NTAPI +PopShutdownHandler(VOID) +{ + PUCHAR Logo1, Logo2; + ULONG i; + + /* Stop all interrupts */ + KeRaiseIrqlToDpcLevel(); + _disable(); + + /* Do we have boot video */ + if (InbvIsBootDriverInstalled()) + { + /* Yes we do, cleanup for shutdown screen */ + if (!InbvCheckDisplayOwnership()) InbvAcquireDisplayOwnership(); + InbvResetDisplay(); + InbvSolidColorFill(0, 0, 639, 479, 0); + InbvEnableDisplayString(TRUE); + InbvSetScrollRegion(0, 0, 639, 479); + + /* Display shutdown logo and message */ + Logo1 = InbvGetResourceAddress(IDB_SHUTDOWN_LOGO); + Logo2 = InbvGetResourceAddress(IDB_LOGO); + if ((Logo1) && (Logo2)) + { + InbvBitBlt(Logo1, 215, 352); + InbvBitBlt(Logo2, 217, 111); + } + } + else + { + /* Do it in text-mode */ + for (i = 0; i < 25; i++) InbvDisplayString("\n"); + InbvDisplayString(" "); + InbvDisplayString("The system may be powered off now.\n"); + } + + /* Hang the system */ + for (;;) HalHaltSystem(); +} + +VOID +NTAPI +PopShutdownSystem(IN POWER_ACTION SystemAction) +{ + /* Note should notify caller of NtPowerInformation(PowerShutdownNotification) */ + + /* Unload symbols */ + DPRINT1("It's the final countdown...%lx\n", SystemAction); + DbgUnLoadImageSymbols(NULL, (PVOID)-1, 0); + + /* Run the thread on the boot processor */ + KeSetSystemAffinityThread(1); + + /* Now check what the caller wants */ + switch (SystemAction) + { + /* Reset */ + case PowerActionShutdownReset: + + /* Try platform driver first, then legacy */ + //PopInvokeSystemStateHandler(PowerStateShutdownReset, NULL); + HalReturnToFirmware(HalRebootRoutine); + break; + + case PowerActionShutdown: + + /* Check for group policy that says to use "it is now safe" screen */ + if (PopShutdownPowerOffPolicy) + { + /* FIXFIX: Switch to legacy shutdown handler */ + //PopPowerStateHandlers[PowerStateShutdownOff].Handler = PopShutdownHandler; + } + + case PowerActionShutdownOff: + + /* Call shutdown handler */ + //PopInvokeSystemStateHandler(PowerStateShutdownOff, NULL); + + /* ReactOS Hack */ + PopSetSystemPowerState(PowerSystemShutdown); + PopShutdownHandler(); + + /* If that didn't work, call the HAL */ + HalReturnToFirmware(HalPowerDownRoutine); + break; + + default: + break; + } + + /* Anything else should not happen */ + KeBugCheckEx(INTERNAL_POWER_ERROR, 5, 0, 0, 0); +} + +VOID +NTAPI +PopGracefulShutdown(IN PVOID Context) +{ + /* First, the HAL handles any "end of boot" special functionality */ + DPRINT1("HAL shutting down\n"); + HalEndOfBoot(); + + /* In this step, the I/O manager does first-chance shutdown notification */ + DPRINT1("I/O manager shutting down in phase 0\n"); + IoShutdownSystem(0); + + /* In this step, all workers are killed and hives are flushed */ + DPRINT1("Configuration Manager shutting down\n"); + CmShutdownSystem(); + + /* Note that modified pages should be written here (MiShutdownSystem) */ + + /* In this step, the I/O manager does last-chance shutdown notification */ + DPRINT1("I/O manager shutting down in phase 1\n"); + IoShutdownSystem(1); + CcWaitForCurrentLazyWriterActivity(); + + /* Note that here, we should broadcast the power IRP to devices */ + + /* In this step, the HAL disables any wake timers */ + DPRINT1("Disabling wake timers\n"); + HalSetWakeEnable(FALSE); + + /* And finally the power request is sent */ + DPRINT1("Taking the system down\n"); + PopShutdownSystem(PopAction.Action); +} + +VOID +NTAPI +PopReadShutdownPolicy(VOID) +{ + UNICODE_STRING KeyString; + OBJECT_ATTRIBUTES ObjectAttributes; + NTSTATUS Status; + HANDLE KeyHandle; + ULONG Length; + UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)]; + PKEY_VALUE_PARTIAL_INFORMATION Info = (PVOID)Buffer; + + /* Setup object attributes */ + RtlInitUnicodeString(&KeyString, + L"\\Registry\\Machine\\Software\\Policies\\Microsoft\\Windows NT"); + InitializeObjectAttributes(&ObjectAttributes, + &KeyString, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + /* Open the key */ + Status = ZwOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes); + if (NT_SUCCESS(Status)) + { + /* Open the policy value and query it */ + RtlInitUnicodeString(&KeyString, L"DontPowerOffAfterShutdown"); + Status = ZwQueryValueKey(KeyHandle, + &KeyString, + KeyValuePartialInformation, + &Info, + sizeof(Info), + &Length); + if ((NT_SUCCESS(Status)) && (Info->Type == REG_DWORD)) + { + /* Read the policy */ + PopShutdownPowerOffPolicy = *Info->Data == 1; + } + + /* Close the key */ + ZwClose(KeyHandle); + } +} + +/* PUBLIC FUNCTIONS **********************************************************/ + diff --git a/reactos/ntoskrnl/po/povolume.c b/reactos/ntoskrnl/po/povolume.c new file mode 100644 index 00000000000..58cc3327deb --- /dev/null +++ b/reactos/ntoskrnl/po/povolume.c @@ -0,0 +1,334 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: BSD - See COPYING.ARM in the top level directory + * FILE: ntoskrnl/po/povolume.c + * PURPOSE: Power Manager DOPE and Volume Management + * PROGRAMMERS: ReactOS Portable Systems Group + */ + +/* INCLUDES ******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS *******************************************************************/ + +typedef struct _POP_FLUSH_VOLUME +{ + LIST_ENTRY List; + LONG Count; + KEVENT Wait; +} POP_FLUSH_VOLUME, *PPOP_FLUSH_VOLUME; + +ULONG PopFlushPolicy = 0; + +KGUARDED_MUTEX PopVolumeLock; +LIST_ENTRY PopVolumeDevices; +KSPIN_LOCK PopDopeGlobalLock; + +/* PRIVATE FUNCTIONS *********************************************************/ + +PDEVICE_OBJECT_POWER_EXTENSION +NTAPI +PopGetDope(IN PDEVICE_OBJECT DeviceObject) +{ + PEXTENDED_DEVOBJ_EXTENSION DeviceExtension; + PDEVICE_OBJECT_POWER_EXTENSION Dope; + KIRQL OldIrql; + PAGED_CODE(); + + /* If the device already has the dope, return it */ + DeviceExtension = IoGetDevObjExtension(DeviceObject); + if (DeviceExtension->Dope) goto Return; + + /* Allocate some dope for the device */ + Dope = ExAllocatePoolWithTag(NonPagedPool, + sizeof(DEVICE_OBJECT_POWER_EXTENSION), + 'Dope'); + if (!Dope) goto Return; + + /* Initialize the initial contents of the dope */ + RtlZeroMemory(Dope, sizeof(DEVICE_OBJECT_POWER_EXTENSION)); + Dope->DeviceObject = DeviceObject; + Dope->State = PowerDeviceUnspecified; + InitializeListHead(&Dope->IdleList); + + /* Make sure only one caller can assign dope to a device */ + KeAcquireSpinLock(&PopDopeGlobalLock, &OldIrql); + + /* Make sure the device still has no dope */ + if (!DeviceExtension->Dope) + { + /* Give the local dope to this device, and remember we won the race */ + DeviceExtension->Dope = (PVOID)Dope; + Dope = NULL; + } + + /* Allow other dope transactions now */ + KeReleaseSpinLock(&PopDopeGlobalLock, OldIrql); + + /* Check if someone other than us already assigned the dope, so free ours */ + if (Dope) ExFreePoolWithTag(Dope, 'Dope'); + + /* Return the dope to the caller */ +Return: + return (PDEVICE_OBJECT_POWER_EXTENSION)DeviceExtension->Dope; +} + +VOID +NTAPI +PoVolumeDevice(IN PDEVICE_OBJECT DeviceObject) +{ + PDEVICE_OBJECT_POWER_EXTENSION Dope; + PAGED_CODE(); + + /* Get dope from the device (if the device has no dope, it will receive some) */ + DPRINT1("New volume: %p\n", DeviceObject); + Dope = PopGetDope(DeviceObject); + if (Dope) + { + /* Make sure we can flush safely */ + DPRINT1("Acquiring volume lock\n"); + KeAcquireGuardedMutex(&PopVolumeLock); + + /* Add this volume into the list of power-manager volumes */ + DPRINT1("Got DOPE: %p\n", Dope); + if (!Dope->Volume.Flink) InsertTailList(&PopVolumeDevices, &Dope->Volume); + + /* Allow flushes to go through */ + KeReleaseGuardedMutex(&PopVolumeLock); + } +} + +VOID +NTAPI +PopFlushVolumeWorker(IN PVOID Context) +{ + PPOP_FLUSH_VOLUME FlushContext = (PPOP_FLUSH_VOLUME)Context; + PDEVICE_OBJECT_POWER_EXTENSION Dope; + PLIST_ENTRY NextEntry; + NTSTATUS Status; + UCHAR Buffer[sizeof(OBJECT_NAME_INFORMATION) + 512]; + POBJECT_NAME_INFORMATION NameInfo = (PVOID)Buffer; + ULONG Length; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE VolumeHandle; + IO_STATUS_BLOCK IoStatusBlock; + + /* Acquire the flush lock since we're messing with the list */ + KeAcquireGuardedMutex(&PopVolumeLock); + + /* Loop the flush list */ + while (!IsListEmpty(&FlushContext->List)) + { + /* Grab the next (ie: current) entry and remove it */ + NextEntry = FlushContext->List.Flink; + RemoveEntryList(NextEntry); + + /* Add it back on the volume list */ + InsertTailList(&PopVolumeDevices, NextEntry); + + /* Done touching the volume list */ + KeReleaseGuardedMutex(&PopVolumeLock); + + /* Get the dope from the volume link */ + Dope = CONTAINING_RECORD(NextEntry, DEVICE_OBJECT_POWER_EXTENSION, Volume); + + /* Get the name */ + Status = ObQueryNameString(Dope->DeviceObject, + NameInfo, + sizeof(Buffer), + &Length); + if ((NT_SUCCESS(Status)) && (NameInfo->Name.Buffer)) + { + /* Open the volume */ + DPRINT1("Opening: %wZ\n", &NameInfo->Name); + InitializeObjectAttributes(&ObjectAttributes, + &NameInfo->Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + 0, + 0); + Status = ZwCreateFile(&VolumeHandle, + SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, + &ObjectAttributes, + &IoStatusBlock, + NULL, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + 0, + NULL, + 0); + if (NT_SUCCESS(Status)) + { + /* Flush it and close it */ + DPRINT1("Sending flush to: %lx\n", VolumeHandle); + ZwFlushBuffersFile(VolumeHandle, &IoStatusBlock); + ZwClose(VolumeHandle); + } + } + + /* Acquire the flush lock again since we'll touch the list */ + KeAcquireGuardedMutex(&PopVolumeLock); + } + + /* One more flush completed... if it was the last, signal the caller */ + if (!--FlushContext->Count) KeSetEvent(&FlushContext->Wait, IO_NO_INCREMENT, FALSE); + + /* Serialize with flushers */ + KeReleaseGuardedMutex(&PopVolumeLock); +} + +VOID +NTAPI +PopFlushVolumes(IN BOOLEAN ShuttingDown) +{ + POP_FLUSH_VOLUME FlushContext = {{0}}; + ULONG FlushPolicy; + UNICODE_STRING RegistryName = RTL_CONSTANT_STRING(L"\\Registry"); + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE RegistryHandle; + PLIST_ENTRY NextEntry; + PDEVICE_OBJECT_POWER_EXTENSION Dope; + ULONG VolumeCount = 0; + NTSTATUS Status; + HANDLE ThreadHandle; + ULONG ThreadCount; + + /* Setup the flush context */ + InitializeListHead(&FlushContext.List); + KeInitializeEvent(&FlushContext.Wait, NotificationEvent, FALSE); + + /* What to flush */ + FlushPolicy = ShuttingDown ? 1 | 2 : PopFlushPolicy; + if ((FlushPolicy & 1)) + { + /* Registry flush requested, so open it */ + DPRINT1("Opening registry\n"); + InitializeObjectAttributes(&ObjectAttributes, + &RegistryName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + Status = ZwOpenKey(&RegistryHandle, KEY_READ, &ObjectAttributes); + if (NT_SUCCESS(Status)) + { + /* Flush the registry */ + DPRINT1("Flushing registry\n"); + ZwFlushKey(RegistryHandle); + ZwClose(RegistryHandle); + } + } + + /* Serialize with other flushes */ + KeAcquireGuardedMutex(&PopVolumeLock); + + /* Scan the volume list */ + NextEntry = PopVolumeDevices.Flink; + while (NextEntry != &PopVolumeDevices) + { + /* Get the dope from the link */ + Dope = CONTAINING_RECORD(NextEntry, DEVICE_OBJECT_POWER_EXTENSION, Volume); + + /* Grab the next entry now, since we'll be modifying the list */ + NextEntry = NextEntry->Flink; + + /* Make sure the object is mounted, writable, exists, and is not a floppy */ + if (!(Dope->DeviceObject->Vpb->Flags & VPB_MOUNTED) || + (Dope->DeviceObject->Characteristics & FILE_FLOPPY_DISKETTE) || + (Dope->DeviceObject->Characteristics & FILE_READ_ONLY_DEVICE) || + ((Dope->DeviceObject->Vpb->RealDevice) && + (Dope->DeviceObject->Vpb->RealDevice->Characteristics & FILE_FLOPPY_DISKETTE))) + { + /* Not flushable */ + continue; + } + + /* Remove it from the dope and add it to the flush context list */ + RemoveEntryList(&Dope->Volume); + InsertTailList(&FlushContext.List, &Dope->Volume); + + /* Next */ + VolumeCount++; + } + + /* Check if we should skip non-removable devices */ + if (!(FlushPolicy & 2)) + { + /* ReactOS only implements this routine for shutdown, which requires it */ + UNIMPLEMENTED; + while (TRUE); + } + + /* Check if there were no volumes at all */ + if (!VolumeCount) + { + /* Nothing to do */ + KeReleaseGuardedMutex(&PopVolumeLock); + return; + } + + /* Allocate up to 8 flusher threads */ + ThreadCount = min(VolumeCount, 8); + InitializeObjectAttributes(&ObjectAttributes, + NULL, + OBJ_KERNEL_HANDLE, + NULL, + NULL); + + /* We will ourselves become a flusher thread */ + FlushContext.Count = 1; + ThreadCount--; + + /* Look for any extra ones we might need */ + while (ThreadCount > 0) + { + /* Create a new one */ + ThreadCount--; + DPRINT1("Creating flush thread\n"); + Status = PsCreateSystemThread(&ThreadHandle, + THREAD_ALL_ACCESS, + &ObjectAttributes, + 0L, + NULL, + PopFlushVolumeWorker, + &FlushContext); + if (NT_SUCCESS(Status)) + { + /* One more created... */ + FlushContext.Count++; + ZwClose(ThreadHandle); + } + } + + /* Allow flushes to go through */ + KeReleaseGuardedMutex(&PopVolumeLock); + + /* Enter the flush work */ + DPRINT1("Local flush\n"); + PopFlushVolumeWorker(&FlushContext); + + /* Wait for all flushes to be over */ + DPRINT1("Waiting for flushes\n"); + KeWaitForSingleObject(&FlushContext.Wait, Executive, KernelMode, FALSE, NULL); + DPRINT1("Flushes have completed\n"); +} + +VOID +NTAPI +PoInitializeDeviceObject(IN OUT PDEVOBJ_EXTENSION DeviceObjectExtension) +{ + PEXTENDED_DEVOBJ_EXTENSION DeviceExtension = (PVOID)DeviceObjectExtension; + PAGED_CODE(); + + /* Initialize the power flags */ + DeviceExtension->PowerFlags = PowerSystemUnspecified & 0xF; + DeviceExtension->PowerFlags |= ((PowerDeviceUnspecified << 4) & 0xF0); + + /* The device object is not on drugs yet */ + DeviceExtension->Dope = NULL; +} + +/* PUBLIC FUNCTIONS **********************************************************/ + diff --git a/reactos/ntoskrnl/po/power.c b/reactos/ntoskrnl/po/power.c index f103898d2f5..96a211b5680 100644 --- a/reactos/ntoskrnl/po/power.c +++ b/reactos/ntoskrnl/po/power.c @@ -24,6 +24,8 @@ typedef struct _REQUEST_POWER_ITEM PDEVICE_NODE PopSystemPowerDeviceNode = NULL; BOOLEAN PopAcpiPresent = FALSE; +POP_POWER_ACTION PopAction; +WORK_QUEUE_ITEM PopShutdownWorkItem; /* PRIVATE FUNCTIONS *********************************************************/ @@ -165,6 +167,13 @@ PoInitSystem(IN ULONG BootPhase) PopAcpiPresent = KeLoaderBlock->Extension->AcpiTable != NULL ? TRUE : FALSE; } + + /* Initialize volume support */ + InitializeListHead(&PopVolumeDevices); + KeInitializeGuardedMutex(&PopVolumeLock); + + /* Initialize support for dope */ + KeInitializeSpinLock(&PopDopeGlobalLock); return TRUE; } @@ -636,3 +645,138 @@ NtSetThreadExecutionState(IN EXECUTION_STATE esFlags, /* 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; + + /* 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; HACK! + } + + /* 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 */ + DPRINT1("Stopping lazy flush\n"); + CmSetLazyFlushState(FALSE); + + /* Setup the power action */ + Action.Action = SystemAction; + Action.Flags = Flags; + + /* Notify callbacks */ + DPRINT1("Notifying callbacks\n"); + ExNotifyCallback(PowerStateCallback, (PVOID)3, NULL); + + /* Swap in any worker thread stacks */ + DPRINT1("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 */ + DPRINT1("Flushing volumes\n"); + PopFlushVolumes(PopAction.Shutdown); + + /* Set IRP for drivers */ + PopAction.IrpMinor = IRP_MN_SET_POWER; + if (PopAction.Shutdown) + { + DPRINT1("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 */ + ASSERT(FALSE && "System is still up and running?!"); + break; + } + +Exit: + /* We're done, return */ + return Status; +}