[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
This commit is contained in:
Sir Richard 2010-03-08 20:47:10 +00:00
parent c14fc3dc56
commit 8a4845b409
9 changed files with 1102 additions and 156 deletions

View file

@ -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 */

View file

@ -743,14 +743,14 @@ IoInitShutdownNotification(
VOID
NTAPI
IoShutdownRegisteredDevices(
VOID
IoShutdownSystem(
IN ULONG Phase
);
VOID
NTAPI
IoShutdownRegisteredFileSystems(
VOID
IopShutdownBaseFileSystems(
IN PLIST_ENTRY ListHead
);
//

View file

@ -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;

View file

@ -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 */

View file

@ -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,

View file

@ -438,8 +438,10 @@
<file>obwait.c</file>
</directory>
<directory name="po">
<file>events.c</file>
<file>power.c</file>
<file>events.c</file>
<file>poshtdwn.c</file>
<file>povolume.c</file>
</directory>
<directory name="ps">
<if property="ARCH" value="i386">

View file

@ -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 <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* 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 **********************************************************/

View file

@ -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 <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* 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 **********************************************************/

View file

@ -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;
}