From 84fa53c75a3861c8a9ad92f59820202ab10bd8ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20Bi=C8=99oc?= Date: Sun, 30 Jun 2024 15:17:41 +0200 Subject: [PATCH] [NTOS:PO] WIP --- boot/bootdata/livecd.ini | 2 +- boot/freeldr/freeldr/bootmgr.c | 3 + boot/freeldr/freeldr/include/ui/tui.h | 1 + boot/freeldr/freeldr/ui/tui.c | 20 + ntoskrnl/config/cmdata.c | 14 + ntoskrnl/include/internal/pep.h | 25 + ntoskrnl/include/internal/po.h | 1226 +++++++++++-- ntoskrnl/include/internal/po_x.h | 291 ++++ ntoskrnl/include/internal/pofx.h | 234 +++ ntoskrnl/include/internal/ppm.h | 202 +++ ntoskrnl/include/internal/ps.h | 12 +- ntoskrnl/include/internal/tag.h | 12 +- ntoskrnl/io/iomgr/device.c | 5 +- ntoskrnl/io/iomgr/iofunc.c | 22 - ntoskrnl/io/iomgr/irp.c | 13 +- ntoskrnl/ntos.cmake | 39 +- ntoskrnl/ntoskrnl.spec | 12 + ntoskrnl/ob/obinit.c | 7 + ntoskrnl/po/act.c | 20 + ntoskrnl/po/amd64/.keep | 0 ntoskrnl/po/aoac.c | 20 + ntoskrnl/po/arm/.keep | 0 ntoskrnl/po/batt.c | 808 +++++++++ ntoskrnl/po/bugchk.c | 33 + ntoskrnl/po/debug.c | 648 +++++++ ntoskrnl/po/drips.c | 16 + ntoskrnl/po/events.c | 304 ---- ntoskrnl/po/guid.c | 4 - ntoskrnl/po/hibersup.c | 32 + ntoskrnl/po/i386/.keep | 0 ntoskrnl/po/init.c | 578 +++++++ ntoskrnl/po/irp.c | 1517 +++++++++++++++++ ntoskrnl/po/misc.c | 246 +++ ntoskrnl/po/notif.c | 64 + ntoskrnl/po/ntapi.c | 891 ++++++++++ ntoskrnl/po/poapi.c | 612 +++++++ ntoskrnl/po/pocs.c | 481 ++++++ ntoskrnl/po/pofx/comp.c | 16 + ntoskrnl/po/pofx/device.c | 16 + ntoskrnl/po/pofx/fxapi.c | 16 + ntoskrnl/po/pofx/plugin.c | 16 + ntoskrnl/po/policy.c | 784 +++++++++ ntoskrnl/po/poreq.c | 16 + ntoskrnl/po/posett.c | 16 + ntoskrnl/po/poshtdwn.c | 403 ----- ntoskrnl/po/power.c | 1149 ------------- ntoskrnl/po/ppm/amd64/.keep | 0 ntoskrnl/po/ppm/arm/.keep | 0 ntoskrnl/po/ppm/cpustat.c | 16 + ntoskrnl/po/ppm/eng.c | 18 + ntoskrnl/po/ppm/i386/.keep | 0 ntoskrnl/po/ppm/idle.c | 27 + ntoskrnl/po/ppm/init.c | 29 + ntoskrnl/po/ppm/perf.c | 31 + ntoskrnl/po/ppm/policy.c | 16 + ntoskrnl/po/shtdwn.c | 422 +++++ ntoskrnl/po/state.c | 645 +++++++ ntoskrnl/po/thermreq.c | 18 + ntoskrnl/po/thermzn.c | 22 + ntoskrnl/po/thrtmgr.c | 16 + ntoskrnl/po/{povolume.c => voldope.c} | 436 ++--- ntoskrnl/po/wakesrc.c | 25 + ntoskrnl/ps/kill.c | 17 +- sdk/include/ddk/ntpoapi.h | 48 +- sdk/include/ndk/iotypes.h | 106 ++ sdk/include/ndk/pofuncs.h | 2 +- sdk/include/ndk/umtypes.h | 30 + sdk/include/reactos/mc/bugcodes.mc | 8 + sdk/include/xdk/iotypes.h | 5 +- sdk/include/xdk/ketypes.h | 9 + sdk/include/xdk/pofuncs.h | 40 +- sdk/include/xdk/potypes.h | 68 +- sdk/include/xdk/winnt_old.h | 5 +- sdk/lib/drivers/ntoskrnl_vista/CMakeLists.txt | 1 - sdk/lib/drivers/ntoskrnl_vista/po.c | 63 - 75 files changed, 10670 insertions(+), 2299 deletions(-) create mode 100644 ntoskrnl/include/internal/pep.h create mode 100644 ntoskrnl/include/internal/po_x.h create mode 100644 ntoskrnl/include/internal/pofx.h create mode 100644 ntoskrnl/include/internal/ppm.h create mode 100644 ntoskrnl/po/act.c create mode 100644 ntoskrnl/po/amd64/.keep create mode 100644 ntoskrnl/po/aoac.c create mode 100644 ntoskrnl/po/arm/.keep create mode 100644 ntoskrnl/po/batt.c create mode 100644 ntoskrnl/po/bugchk.c create mode 100644 ntoskrnl/po/debug.c create mode 100644 ntoskrnl/po/drips.c delete mode 100644 ntoskrnl/po/events.c delete mode 100644 ntoskrnl/po/guid.c create mode 100644 ntoskrnl/po/hibersup.c create mode 100644 ntoskrnl/po/i386/.keep create mode 100644 ntoskrnl/po/init.c create mode 100644 ntoskrnl/po/irp.c create mode 100644 ntoskrnl/po/misc.c create mode 100644 ntoskrnl/po/notif.c create mode 100644 ntoskrnl/po/ntapi.c create mode 100644 ntoskrnl/po/poapi.c create mode 100644 ntoskrnl/po/pocs.c create mode 100644 ntoskrnl/po/pofx/comp.c create mode 100644 ntoskrnl/po/pofx/device.c create mode 100644 ntoskrnl/po/pofx/fxapi.c create mode 100644 ntoskrnl/po/pofx/plugin.c create mode 100644 ntoskrnl/po/policy.c create mode 100644 ntoskrnl/po/poreq.c create mode 100644 ntoskrnl/po/posett.c delete mode 100644 ntoskrnl/po/poshtdwn.c delete mode 100644 ntoskrnl/po/power.c create mode 100644 ntoskrnl/po/ppm/amd64/.keep create mode 100644 ntoskrnl/po/ppm/arm/.keep create mode 100644 ntoskrnl/po/ppm/cpustat.c create mode 100644 ntoskrnl/po/ppm/eng.c create mode 100644 ntoskrnl/po/ppm/i386/.keep create mode 100644 ntoskrnl/po/ppm/idle.c create mode 100644 ntoskrnl/po/ppm/init.c create mode 100644 ntoskrnl/po/ppm/perf.c create mode 100644 ntoskrnl/po/ppm/policy.c create mode 100644 ntoskrnl/po/shtdwn.c create mode 100644 ntoskrnl/po/state.c create mode 100644 ntoskrnl/po/thermreq.c create mode 100644 ntoskrnl/po/thermzn.c create mode 100644 ntoskrnl/po/thrtmgr.c rename ntoskrnl/po/{povolume.c => voldope.c} (50%) create mode 100644 ntoskrnl/po/wakesrc.c delete mode 100644 sdk/lib/drivers/ntoskrnl_vista/po.c diff --git a/boot/bootdata/livecd.ini b/boot/bootdata/livecd.ini index f36197c2e1d5a..b1838aba8c347 100644 --- a/boot/bootdata/livecd.ini +++ b/boot/bootdata/livecd.ini @@ -23,7 +23,7 @@ Options=/FASTDETECT /MININT [LiveCD_Debug] BootType=Windows2003 SystemPath=\reactos -Options=/DEBUG /DEBUGPORT=COM1 /BAUDRATE=115200 /SOS /FASTDETECT /MININT +Options=/HAL=HALACPI.DLL /DEBUG /DEBUGPORT=COM1 /BAUDRATE=115200 /SOS /FASTDETECT /MININT [LiveCD_Macpi] BootType=Windows2003 diff --git a/boot/freeldr/freeldr/bootmgr.c b/boot/freeldr/freeldr/bootmgr.c index ce977170d5d71..649274655c802 100644 --- a/boot/freeldr/freeldr/bootmgr.c +++ b/boot/freeldr/freeldr/bootmgr.c @@ -452,6 +452,9 @@ VOID RunLoader(VOID) goto Reboot; } + /* Display the "Red Screen of Hell" if ROS previously shutdown due to thermal event */ + //TuiDisplayThermalScreen(); + /* Load the chosen operating system */ LoadOperatingSystem(&OperatingSystemList[SelectedOperatingSystem]); diff --git a/boot/freeldr/freeldr/include/ui/tui.h b/boot/freeldr/freeldr/include/ui/tui.h index 9017c0b4ef67c..f94d49fe76624 100644 --- a/boot/freeldr/freeldr/include/ui/tui.h +++ b/boot/freeldr/freeldr/include/ui/tui.h @@ -129,6 +129,7 @@ UCHAR TuiTextToFillStyle(PCSTR FillStyleText); // Converts the VOID TuiFadeInBackdrop(VOID); // Draws the backdrop and fades the screen in VOID TuiFadeOut(VOID); // Fades the screen out +VOID TuiDisplayThermalScreen(VOID); // Display the Red Screen of Hell informing the user of a thermal condition /* Menu Functions ************************************************************/ diff --git a/boot/freeldr/freeldr/ui/tui.c b/boot/freeldr/freeldr/ui/tui.c index 5960dcd5d4456..3cdb64b3b1a61 100644 --- a/boot/freeldr/freeldr/ui/tui.c +++ b/boot/freeldr/freeldr/ui/tui.c @@ -1035,6 +1035,26 @@ VOID TuiFadeOut(VOID) } +VOID TuiDisplayThermalScreen(VOID) +{ + MachVideoHideShowTextCursor(FALSE); + MachVideoClearScreen(ATTR(COLOR_WHITE, COLOR_RED)); + + TuiPrintf("\nOn previous session, ReactOS has shutdown due to a thermal condition\n" + "that would have otherwise caused fatal damage to the system.\n\n" + "It's extremely advised to check that your system has proper cooling\n" + "and that nothing blocks the airflow of your system (dust, debris, etc).\n\n" + "Replace the thermal paste if necessary. Avoid skin contact with the hot\n" + "system at all costs (if it's a laptop).\n\n" + "Press ENTER to continue."); + + /* Have this dummy loop to act like the system was waiting on something */ + for (;;) + { + + } +} + BOOLEAN TuiEditBox(PCSTR MessageText, PCHAR EditTextBuffer, ULONG Length) { CHAR key; diff --git a/ntoskrnl/config/cmdata.c b/ntoskrnl/config/cmdata.c index 693c9ac7b2667..0c601e4b0e3da 100644 --- a/ntoskrnl/config/cmdata.c +++ b/ntoskrnl/config/cmdata.c @@ -540,6 +540,13 @@ DATA_SEG("INITDATA") CM_SYSTEM_CONTROL_VECTOR CmControlVector[] = &ObpUnsecureGlobalNamesLength, NULL }, + { + L"Session Manager\\Kernel", + L"PoCleanShutdownFlags", + &PopShutdownCleanly, + NULL, + NULL + }, { L"Session Manager\\I/O System", L"CountOperations", @@ -603,6 +610,13 @@ DATA_SEG("INITDATA") CM_SYSTEM_CONTROL_VECTOR CmControlVector[] = NULL, NULL }, + { + L"Session Manager", + L"PowerPolicySimulate", + &PopSimulate, + NULL, + NULL + }, { L"ProductOptions", L"ProductType", diff --git a/ntoskrnl/include/internal/pep.h b/ntoskrnl/include/internal/pep.h new file mode 100644 index 0000000000000..3e2132ef8e555 --- /dev/null +++ b/ntoskrnl/include/internal/pep.h @@ -0,0 +1,25 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Internal header for the Power Manager Platform Extension Plug-ins (PEPs) + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +// +// Platform Extension Plugin (PEP) handle +// +typedef struct _PEPHANDLE__ +{ + LONG unused; +} PEPHANDLE__, *PPEPHANDLE__; + +// +// PEP crash dump information +// +typedef struct _PEP_CRASHDUMP_INFORMATION +{ + PPEPHANDLE__ DeviceHandle; + PVOID DeviceContext; +} PEP_CRASHDUMP_INFORMATION, *PPEP_CRASHDUMP_INFORMATION; + +/* EOF */ diff --git a/ntoskrnl/include/internal/po.h b/ntoskrnl/include/internal/po.h index 32fb6f441b852..471f3c14a5474 100644 --- a/ntoskrnl/include/internal/po.h +++ b/ntoskrnl/include/internal/po.h @@ -1,14 +1,21 @@ /* -* PROJECT: ReactOS Kernel -* LICENSE: GPL - See COPYING in the top level directory -* FILE: ntoskrnl/include/internal/po.h -* PURPOSE: Internal header for the Power Manager -* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) -*/ + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Internal header for the Power Manager + * COPYRIGHT: Copyright 2006 Alex Ionescu + * Copyright 2023 George BiÈ™oc + */ #include #include +// +// Power Manager Dependencies +// +#include "pep.h" +#include "pofx.h" +#include "ppm.h" + // // Define this if you want debugging support // @@ -18,6 +25,21 @@ // These define the Debug Masks Supported // #define PO_STATE_DEBUG 0x01 +#define PO_HIBER_DEBUG 0x02 +#define PO_THERMAL_DEBUG 0x04 +#define PO_THROTTLE_DEBUG 0x06 +#define PO_POWER_ACTION_DEBUG 0x08 +#define PO_VOLUME_DOPE_DEBUG 0x10 +#define PO_BATTERY_MGR_DEBUG 0x12 +#define PO_IRP_DEBUG 0x14 +#define PO_NOTIFY_DEBUG 0x16 +#define PO_SHUTDOWN_DEBUG 0x18 +#define PO_POLICY_DEBUG 0x20 +#define PO_IDLE_STATE_DEBUG 0x40 +#define PO_NT_SYSCALL_DEBUG 0x60 +#define PO_MISC_DEBUG 0x80 +#define PO_CONTROL_SWITCH_DEBUG 0x100 +#define PO_INIT_SUBSYSTEM_DEBUG 0x900 // // Debug/Tracing support @@ -33,19 +55,231 @@ #define POTRACE(x, fmt, ...) DPRINT(fmt, ##__VA_ARGS__) #endif -typedef enum _POP_POLICY_DEVICE_TYPE +// +// Internal bugcheck code reasons (for INTERNAL_POWER_ERROR) +// +#define POP_PO_INIT_FAILURE 1 +#define POP_IDLE_DETECT_UNKNOWN_DEVICE 2 +#define POP_DEVICE_POLICY_IRP_ALLOC_FAILED 3 +#define POP_INVALID_CONTROL_SWITCH_MODE 4 +#define POP_BATTERY_UNKNOWN_MODE_REQUEST 5 + +/****************************************************************************** + * irp.c * + ******************************************************************************/ + +// +// Device Power Failure Triage (0x9F) Signature +// +#define POP_9F_TRIAGE_SIGNATURE 0x8000 + +// +// Device Power Failure Triage (0x9F) Revision +// +#define POP_9F_TRIAGE_REVISION_V1 1 + +// +// IRP watchdog duetime in seconds (600 s = 10 min) +// +#define POP_IRP_WATCHDOG_DUETIME 60 * 10 + +// +// Maximum number of IRP dispatch worker threads the system can create +// +#define POP_MAX_IRP_WORKERS_COUNT 10 + +// +// Maximum number of IRPs that can be queued +// +#define POP_MAX_IRP_QUEUE_LIST 100 +#define POP_MAX_INRUSH_IRP_QUEUE_LIST 60 + +// +// IRP worker system thread priority +// +#define POP_WORKER_THREAD_PRIORITY 2 + +// +// PEXTENDED_DEVOBJ_EXTENSION power flags +// +#define POP_DOE_SYSTEM_IRP_ACTIVE 0x200 +#define POP_DOE_DEVICE_IRP_ACTIVE 0x400 +#define POP_DOE_PENDING_PROCESS 0x600 +#define POP_DOE_HAS_INRUSH_DEVICE 0x900 + +// +// Power system device context flag +// +#define POP_SYS_CONTEXT_SYSTEM_IRP 0xA +#define POP_SYS_CONTEXT_DEVICE_PWR_REQUEST 0xC +#define POP_SYS_CONTEXT_WAKE_REQUEST 0xD + +/****************************************************************************** + * thrmzn.c * + ******************************************************************************/ + +// +// Processor throttling constants +// +#define POP_CURRENT_THROTTLE_MAX 100 + +// +// Thermal zone flags +// +#define POP_THERMAL_ZONE_NONE 0x0 +#define POP_THERMAL_ZONE_IS_ACTIVE 0x2 + +/****************************************************************************** + * policy.c * + ******************************************************************************/ + +// +// Power Policy Revision +// +#define POP_SYSTEM_POWER_POLICY_REVISION_V1 1 + +// +// Policy Worker Function Signature +// +_Function_class_(POP_POLICY_WORKER_FUNC) +typedef VOID +(NTAPI *PPOP_POLICY_WORKER_FUNC) ( + VOID); + +/****************************************************************************** + * pocs.c * + ******************************************************************************/ + +// +// POP_CONTROL_SWITCH flags +// +#define POP_CS_INITIALIZING 0x00000001 +#define POP_CS_CLEANUP 0x00000100 + +// +// Power control switch modes +// +#define POP_CS_NO_MODE 0 +#define POP_CS_QUERY_CAPS_MODE 1 +#define POP_CS_QUERY_EVENT_MODE 2 + +/****************************************************************************** + * batt.c * + ******************************************************************************/ + +// +// POP_BATTERY flags +// +#define POP_CB_NO_BATTERY 0x00000001 +#define POP_CB_PENDING_NEW_BATTERY 0x00000002 +#define POP_CB_PROCESSING_MODE_REQUEST 0x00000004 +#define POP_CB_RETRY_IO_REQUEST 0x00000020 +#define POP_CB_REMOVE_BATTERY 0x00000100 + +// +// Power composite battery modes +// +#define POP_CB_NO_MODE 0 +#define POP_CB_READ_TAG_MODE 1 +#define POP_CB_QUERY_INFORMATION_MODE 2 +#define POP_CB_QUERY_STATUS_MODE 3 +#define POP_CB_QUERY_BATTERY_ESTIMATION_TIME_MODE 4 +#define POP_CB_QUERY_TEMPERATURE_MODE 5 + +/****************************************************************************** + * voldope.c * + ******************************************************************************/ + +// +// Volume DOE flags +// +#define POP_DOE_SYSTEM_POWER_FLAG_BIT 0xF +#define POP_DOE_DEVICE_POWER_FLAG_BIT 0xF0 + +// +// Volume flushing flags (PopFlushVolumes) +// +#define POP_FLUSH_REGISTRY 1 +#define POP_FLUSH_NON_REM_DEVICES 2 + +/****************************************************************************** + * poapi.c * + ******************************************************************************/ + +// +// BusyCount & BusyReference field offsets (PoSetDeviceBusyEx and PoStartDeviceBusy/PoEndDeviceBusy) +// +#define POP_BUSY_COUNT_OFFSET 1 +#define POP_BUSY_REFERENCE_OFFSET 2 + +/****************************************************************************** + * Data Structures & Enums * + ******************************************************************************/ + +// +// Power policy worker types +// +typedef enum _POP_POWER_POLICY_WORKER_TYPES +{ + PolicyWorkerNotification, + PolicyWorkerSystemIdle, + PolicyWorkerTimeChange, + PolicyWorkerMax +} POP_POWER_POLICY_WORKER_TYPES; + +// +// Power policy type enumeration +// +typedef enum _POP_POWER_POLICY_TYPE +{ + PolicyAc, + PolicyDc +} POP_POWER_POLICY_TYPE; + +// +// Device idle type enumeration +// +typedef enum _POP_DEVICE_IDLE_TYPE +{ + DeviceIdleNormal, + DeviceIdleDisk +} POP_DEVICE_IDLE_TYPE; + +// +// Power control switch type (lid, power button, etc.) +// +typedef enum _POP_SWITCH_TYPE +{ + SwitchNone, + SwitchLid, + SwitchButtonPower, + SwitchButtonSleep, +} POP_SWITCH_TYPE; + +// +// Power state handler comands enumeration +// +typedef enum _POP_POWER_HANDLER_COMMAND +{ + SaveFloatingPointContext = 1, + InvokeNotifyHandler, + InvokePowerStateHandler, + RestoreFloatingPointContext, + QuitDpc +} POP_POWER_HANDLER_COMMAND; + +// +// Search IRP data by mode enumeration +// +typedef enum _POP_SEARCH_BY { - PolicyDeviceSystemButton = 0, - PolicyDeviceThermalZone = 1, - PolicyDeviceBattery = 2, - PolicyDeviceMemory = 3, - PolicyInitiatePowerActionAPI = 4, - PolicySetPowerStateAPI = 5, - PolicyImmediateDozeS4 = 6, - PolicySystemIdle = 7, - PolicyDeviceMax = 8, -} POP_POLICY_DEVICE_TYPE; + SearchByIrp, + SearchByDevice +} POP_SEARCH_BY; +// +// Hibernation performance counters +// typedef struct _PO_HIBER_PERF { ULONGLONG IoTicks; @@ -61,17 +295,22 @@ typedef struct _PO_HIBER_PERF ULONG BytesCopied; ULONG DumpCount; ULONG FileRuns; + ULONGLONG ResumeAppStartTime; + ULONGLONG ResumeAppEndTime; + ULONGLONG HiberFileResumeTime; } PO_HIBER_PERF, *PPO_HIBER_PERF; +// +// Power hibernation metadata image file +// typedef struct _PO_MEMORY_IMAGE { ULONG Signature; - ULONG Version; + ULONG ImageType; ULONG CheckSum; ULONG LengthSelf; PFN_NUMBER PageSelf; ULONG PageSize; - ULONG ImageType; LARGE_INTEGER SystemTime; ULONGLONG InterruptTime; ULONG FeatureFlags; @@ -87,8 +326,14 @@ typedef struct _PO_MEMORY_IMAGE PFN_NUMBER FirstTablePage; PFN_NUMBER LastFilePage; PO_HIBER_PERF PerfInfo; + PFN_NUMBER NoBootLoaderLogPages; + PFN_NUMBER BootLoaderLogPages[8]; + ULONG TotalPhysicalMemoryCount; } PO_MEMORY_IMAGE, *PPO_MEMORY_IMAGE; +// +// Hibernation memory array range +// typedef struct _PO_MEMORY_RANGE_ARRAY_RANGE { PFN_NUMBER PageNo; @@ -97,6 +342,9 @@ typedef struct _PO_MEMORY_RANGE_ARRAY_RANGE ULONG CheckSum; } PO_MEMORY_RANGE_ARRAY_RANGE; +// +// Hibernation memory array linkage +// typedef struct _PO_MEMORY_RANGE_ARRAY_LINK { struct _PO_MEMORY_RANGE_ARRAY *Next; @@ -105,6 +353,9 @@ typedef struct _PO_MEMORY_RANGE_ARRAY_LINK ULONG EntryCount; } PO_MEMORY_RANGE_ARRAY_LINK; +// +// Hibernation memory array +// typedef struct _PO_MEMORY_RANGE_ARRAY { union @@ -114,6 +365,9 @@ typedef struct _PO_MEMORY_RANGE_ARRAY }; } PO_MEMORY_RANGE_ARRAY, *PPO_MEMORY_RANGE_ARRAY; +// +// Hibernation context data +// typedef struct _POP_HIBER_CONTEXT { BOOLEAN WriteToFile; @@ -122,53 +376,53 @@ typedef struct _POP_HIBER_CONTEXT BOOLEAN VerifyOnWake; BOOLEAN Reset; UCHAR HiberFlags; - BOOLEAN LinkFile; - HANDLE LinkFileHandle; - PKSPIN_LOCK Lock; + BOOLEAN WroteHiberFile; + KSPIN_LOCK Lock; BOOLEAN MapFrozen; RTL_BITMAP MemoryMap; + RTL_BITMAP DiscardedMemoryPages; LIST_ENTRY ClonedRanges; ULONG ClonedRangeCount; PLIST_ENTRY NextCloneRange; PFN_NUMBER NextPreserve; PMDL LoaderMdl; - PMDL Clones; - PUCHAR NextClone; - ULONG NoClones; - PMDL Spares; + PMDL AllocatedMdl; ULONGLONG PagesOut; - PVOID IoPage; + PVOID IoPages; PVOID CurrentMcb; - PVOID DumpStack; + PDUMP_STACK_CONTEXT DumpStack; PKPROCESSOR_STATE WakeState; - ULONG NoRanges; - ULONG_PTR HiberVa; + ULONG HiberVa; PHYSICAL_ADDRESS HiberPte; NTSTATUS Status; PPO_MEMORY_IMAGE MemoryImage; PPO_MEMORY_RANGE_ARRAY TableHead; - PVOID CompressionWorkspace; + PUCHAR CompressionWorkspace; PUCHAR CompressedWriteBuffer; PULONG PerformanceStats; PVOID CompressionBlock; PVOID DmaIO; PVOID TemporaryHeap; PO_HIBER_PERF PerfInfo; + PMDL BootLoaderLogMdl; } POP_HIBER_CONTEXT, *PPOP_HIBER_CONTEXT; +// +// Power notification order level +// 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; +// +// Power shutdown bugcheck reasoning +// typedef struct _POP_SHUTDOWN_BUG_CHECK { HANDLE ThreadHandle; @@ -181,46 +435,43 @@ typedef struct _POP_SHUTDOWN_BUG_CHECK 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; - +// +// Power device notification order +// typedef struct _PO_DEVICE_NOTIFY_ORDER { - ULONG DevNodeSequence; + BOOLEAN Locked; PDEVICE_OBJECT *WarmEjectPdoPointer; PO_NOTIFY_ORDER_LEVEL OrderLevel[8]; } PO_DEVICE_NOTIFY_ORDER, *PPO_DEVICE_NOTIFY_ORDER; +// +// Power device system state +// typedef struct _POP_DEVICE_SYS_STATE { UCHAR IrpMinor; SYSTEM_POWER_STATE SystemState; - PKEVENT Event; KSPIN_LOCK SpinLock; PKTHREAD Thread; + PKEVENT AbortEvent; + PKSEMAPHORE ReadySemaphore; + PKSEMAPHORE FinishedSemaphore; BOOLEAN GetNewDeviceList; PO_DEVICE_NOTIFY_ORDER Order; + LIST_ENTRY Pending; 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]; + BOOLEAN TimeRefreshLockAcquired; } POP_DEVICE_SYS_STATE, *PPOP_DEVICE_SYS_STATE; +// +// Power actions +// typedef struct _POP_POWER_ACTION { UCHAR Updates; @@ -230,50 +481,503 @@ typedef struct _POP_POWER_ACTION SYSTEM_POWER_STATE LightestState; ULONG Flags; NTSTATUS Status; + POWER_POLICY_DEVICE_TYPE DeviceType; + ULONG DeviceTypeFlags; UCHAR IrpMinor; + BOOLEAN Waking; SYSTEM_POWER_STATE SystemState; SYSTEM_POWER_STATE NextSystemState; + SYSTEM_POWER_STATE EffectiveSystemState; + SYSTEM_POWER_STATE CurrentSystemState; PPOP_SHUTDOWN_BUG_CHECK ShutdownBugCode; PPOP_DEVICE_SYS_STATE DevState; PPOP_HIBER_CONTEXT HiberContext; ULONGLONG WakeTime; ULONGLONG SleepTime; + SYSTEM_POWER_CONDITION WakeAlarmSignaled; + struct + { + ULONGLONG ProgrammedTime; + struct _DIAGNOSTIC_BUFFER* TimeInfo; + } WakeAlarm[3]; + SYSTEM_POWER_CAPABILITIES FilteredCapabilities; } POP_POWER_ACTION, *PPOP_POWER_ACTION; -typedef enum _POP_DEVICE_IDLE_TYPE +// +// Power waitable trigger +// +typedef struct _POP_TRIGGER_WAIT { - DeviceIdleNormal, - DeviceIdleDisk, -} POP_DEVICE_IDLE_TYPE, *PPOP_DEVICE_IDLE_TYPE; + KEVENT Event; + NTSTATUS Status; + LIST_ENTRY Link; + struct _POP_ACTION_TRIGGER* Trigger; +} POP_TRIGGER_WAIT, *PPOP_TRIGGER_WAIT; -typedef struct _POWER_CHANNEL_SUMMARY +// +// Power action trigger +// +typedef struct _POP_ACTION_TRIGGER { - ULONG Signature; - ULONG TotalCount; - ULONG D0Count; - LIST_ENTRY NotifyList; -} POWER_CHANNEL_SUMMARY, *PPOWER_CHANNEL_SUMMARY; + POWER_POLICY_DEVICE_TYPE Type; + ULONG Flags; + PPOP_TRIGGER_WAIT Wait; + union + { + struct + { + ULONG Level; + } Battery; + + struct + { + ULONG Type; + } Button; + } DUMMYUNIONNAME; +} POP_ACTION_TRIGGER, *PPOP_ACTION_TRIGGER; +// +// Device object power extensions (DOPE) +// typedef struct _DEVICE_OBJECT_POWER_EXTENSION { - ULONG IdleCount; + /* + * The device idle counter. This gets incremented every second until it hits the + * idle timers defined by ConservationIdleTime and PerformanceIdleTime. The idle + * counter could reset as a result of the device getting busy by an instance of + * PoSetDeviceBusy call. + */ + volatile ULONG IdleCount; + + /* + * The device busy counter and busy reference. The busy counter gets incremented + * by one each time the device explicitly reports as being busy for a short period + * of time by the following function - PoSetDeviceBusyEx. The busy reference is used + * to keep active references of PoStartDeviceBusy instance calls. + */ + volatile ULONG BusyCount; + volatile ULONG BusyReference; + + /* The total count of busy times the device has been busy, for debugging purposes */ + ULONG TotalBusyCount; + + /* + * Idle time values, defined by the device owner. ConservationIdleTime is for idle + * time when the system must conserve power after this time value was hit. + * PerformanceIdleTime is for idle time when the system must use all its power for + * performance reasons. + */ ULONG ConservationIdleTime; ULONG PerformanceIdleTime; + + /* The device object and link list of which it is linked with the global idle detect list */ PDEVICE_OBJECT DeviceObject; LIST_ENTRY IdleList; - DEVICE_POWER_STATE State; - LIST_ENTRY NotifySourceList; - LIST_ENTRY NotifyTargetList; - POWER_CHANNEL_SUMMARY PowerChannelSummary; + + /* The type of device that is being idle (normal device or disk/mass storage device) */ + POP_DEVICE_IDLE_TYPE IdleType; + + /* The requested device power state to be enforced when the device is idling */ + DEVICE_POWER_STATE IdleState; + + /* + * The current power state of which the device currently operates. Usually this is set + * to PowerDeviceD0 at the time the device has requested for idle detection. Afterwards + * the state is then modified to the state of which the caller requested after the device + * is fully idle. + */ + DEVICE_POWER_STATE CurrentState; + + /* The associated power volume with this device */ LIST_ENTRY Volume; + + /* The idle and non-idle time values of the disk/mass storage device (currently not used) */ + union + { + struct + { + ULONG IdleTime; + ULONG NonIdleTime; + } Disk; + } Specific; } DEVICE_OBJECT_POWER_EXTENSION, *PDEVICE_OBJECT_POWER_EXTENSION; +// +// Power shutdown wait entry list +// typedef struct _POP_SHUTDOWN_WAIT_ENTRY { struct _POP_SHUTDOWN_WAIT_ENTRY *NextEntry; PETHREAD Thread; } POP_SHUTDOWN_WAIT_ENTRY, *PPOP_SHUTDOWN_WAIT_ENTRY; +// +// Power flush volumes +// +typedef struct _POP_FLUSH_VOLUME +{ + LIST_ENTRY List; + LONG Count; + KEVENT Wait; +} POP_FLUSH_VOLUME, *PPOP_FLUSH_VOLUME; + +// +// Power system idle +// +typedef struct _POP_SYSTEM_IDLE +{ + LONG AverageIdleness; + LONG LowestIdleness; + ULONG Time; + ULONG Timeout; + ULONG LastUserInput; + POWER_ACTION_POLICY Action; + SYSTEM_POWER_STATE MinState; + ULONG SystemRequired; + UCHAR IdleWorker; + UCHAR Sampling; + ULONGLONG LastTick; + ULONG LastSystemRequiredTime; +} POP_SYSTEM_IDLE, *PPOP_SYSTEM_IDLE; + +// +// Power thermal zone +// +typedef struct _POP_THERMAL_ZONE +{ + LIST_ENTRY Link; + UCHAR State; + UCHAR Flags; + UCHAR Mode; + BOOLEAN PendingMode; + BOOLEAN ActivePoint; + BOOLEAN PendingActivePoint; + LONG Throttle; + ULONGLONG LastTime; + ULONG SampleRate; + ULONG LastTemp; + KTIMER PassiveTimer; + KDPC PassiveDpc; + POP_ACTION_TRIGGER OverThrottled; + PIRP Irp; + THERMAL_INFORMATION_EX Info; +} POP_THERMAL_ZONE, *PPOP_THERMAL_ZONE; + +// +// Power composite battery +// +typedef struct _POP_BATTERY +{ + ULONG Flags; + + UCHAR Mode; + UCHAR PreviousMode; + + UNICODE_STRING BatteryName; + PDEVICE_OBJECT DeviceObject; + PIRP Irp; + + POP_ACTION_TRIGGER Trigger; + + ULONG BatteryTag; + ULONG Temperature; + BATTERY_STATUS Status; + BATTERY_INFORMATION BattInfo; + + ULONG EstimatedBatteryTime; +} POP_BATTERY, *PPOP_BATTERY; + +// +// Power control switch +// +typedef struct _POP_CONTROL_SWITCH +{ + /* List entry that links with the global control switches list */ + LIST_ENTRY Link; + + /* + * Control switch flags that govern the existence of the switch, the following + * flag constructs are: + * + * POP_CS_INITIALIZING - The switch has been freshly created and awaits for querying + * its capabilities by the CS handler; + * + * POP_CS_CLEANUP - The switch is about to be delisted and freed. Mostly this happens + * when this switch was disabled by the device owner or we got an unexpected error + * during a I/O operation. + */ + ULONG Flags; + + /* + * Control switch operation modes, that govern what is the switch doing at the moment. + * The following values are: + * + * POP_CS_NO_MODE - No operation mode (set during control switch creation); + * + * POP_CS_QUERY_CAPS_MODE - Query power capabilities operation mode; + * + * POP_CS_QUERY_EVENT_MODE - Query power button event operation mode. + */ + UCHAR Mode; + + /* The associated device object of this switch and current I/O packet holding the request */ + PDEVICE_OBJECT DeviceObject; + PIRP Irp; + + /* The type of switch (power, sleep buttons or lid) */ + POP_SWITCH_TYPE SwitchType; + + /* Specific switch constructs */ + union + { + struct + { + BOOLEAN Opened; + } Lid; + + struct + { + BOOLEAN Triggered; + } Button; + } Switch; +} POP_CONTROL_SWITCH, *PPOP_CONTROL_SWITCH; + +// +// Fans +// +typedef struct _POP_FAN +{ + LIST_ENTRY Link; + PDEVICE_OBJECT DeviceObject; + PIRP Irp; + UCHAR ControlPercent; + ULONG TripPoint; + ULONG Speed; + ULONG NoiseLevel; + ULONG PowerConsumed; +} POP_FAN, *PPOP_FAN; + +typedef struct _POP_DEVICE_POLICY_WORKITEM_DATA +{ + /* Policy queue work item used to queue a device policy handler */ + WORK_QUEUE_ITEM WorkItem; + + /* + * A pointer to an arbitrary data that refers to a policy device to be dispatched + * to the respective policy device handler. Typically such a policy could be a + * control switch (button or lid), thermal zone, battery, fan, etc. + */ + PVOID PolicyData; + + /* The type of power policy device being dispatched */ + POWER_POLICY_DEVICE_TYPE PolicyType; +} POP_DEVICE_POLICY_WORKITEM_DATA, *PPOP_DEVICE_POLICY_WORKITEM_DATA; + +// +// Power policy worker +// +typedef struct _POP_POLICY_WORKER +{ + /* Set to TRUE if a worker of this type has been requested; FALSE otherwise */ + BOOLEAN Pending; + + /* Thread that requested this worker (for debugging purposes) */ + PKTHREAD Thread; + + /* Policy worker dispatch function */ + PPOP_POLICY_WORKER_FUNC WorkerFunction; +} POP_POLICY_WORKER, *PPOP_POLICY_WORKER; + +// +// Power IRP data +// +typedef struct _POP_IRP_DATA +{ + /* List entry that links with the centralized power IRP data list */ + LIST_ENTRY Link; + + /* The power I/O packet request used to transport power I/O data */ + PIRP Irp; + + /* The Policy Device Owner (PDO) that inquired the power I/O request */ + PDEVICE_OBJECT Pdo; + + /* + * The target device object. Typically this points to the top device object + * of the device stack depth of which the power request IRP must be dispatched. + */ + PDEVICE_OBJECT TargetDevice; + + /* + * The current device object that takes the power IRP request. This member gets + * updated each time the IRP gets walked down in the device stack as it is being + * dispatched with a call of IoCallDriver. This is for debugging purposes. + */ + PDEVICE_OBJECT CurrentDevice; + + /* + * The start of the IRP watchdog timer interval, in seconds. This gets decremented + * by one second by the watchdog timer DPC. + */ + ULONGLONG WatchdogStart; + + /* The IRP watchdog timer object */ + KTIMER WatchdogTimer; + + /* + * The IRP watchdog DPC object that executes the watchdog deferred routine. + * Such DPC fires up every second by the timer. + */ + KDPC WatchdogDpc; + + /* The minor power function (IRP_MN_SET_POWER/IRP_MN_QUERY_POWER/IRP_MN_WAIT_WAKE) */ + UCHAR MinorFunction; + + /* The type of the power state request instantiated (device or system) */ + POWER_STATE_TYPE PowerStateType; + + /* The power state requested by the caller of PoRequestPowerIrp */ + POWER_STATE PowerState; + + /* If set to TRUE, the IRP watchdog is enabled as a result of the IRP being dispatched, FALSE otherwise */ + BOOLEAN WatchdogEnabled; + + /* A pointer to a framework device (PoFx) containing specific framework data */ + PPOP_FX_DEVICE FxDevice; + + /* If set to TRUE, this IRP makes the system take a new power transition */ + BOOLEAN SystemTransition; + + /* If set to TRUE, this IRP causes a PEP to be notified upon IRP completion */ + BOOLEAN NotifyPEP; + + union + { + /* Device related power IRP data */ + struct + { + PREQUEST_POWER_COMPLETE CallerCompletion; + PVOID CallerContext; + PDEVICE_OBJECT CallerDevice; + BOOLEAN SystemWake; + } Device; + + /* System related power IRP data */ + struct + { + PPO_DEVICE_NOTIFY NotifyDevice; + BOOLEAN FxDeviceActivated; + } System; + } DUMMYUNIONNAME; +} POP_IRP_DATA, *PPOP_IRP_DATA; + +typedef struct _POP_IRP_QUEUE_ENTRY +{ + LIST_ENTRY Link; + PPOP_IRP_DATA IrpData; +} POP_IRP_QUEUE_ENTRY, *PPOP_IRP_QUEUE_ENTRY; + +typedef struct _POP_IRP_THREAD_ENTRY +{ + LIST_ENTRY Link; + PKTHREAD Thread; + PIRP Irp; +} POP_IRP_THREAD_ENTRY, *PPOP_IRP_THREAD_ENTRY; + +// +// Power state handler command context +// +typedef struct _POP_POWER_STATE_HANDLER_COMMAND_CONTEXT +{ + PPOWER_STATE_HANDLER StateHandler; + PPOWER_STATE_NOTIFY_HANDLER StateNotifyHandler; + + PPOP_HIBER_CONTEXT HiberContext; + + POP_POWER_HANDLER_COMMAND ExecutingCommand; + + BOOLEAN InitializingDpcs; + ULONG DpcReadyForProcess; + + ULONG ProcessorHandledCommand; + + union + { + struct + { + PVOID Context; + PENTER_STATE_SYSTEM_HANDLER SystemHandler; + PVOID SystemContext; + LONG NumberProcessors; + volatile LONG Number; + } StateHandlerData; + + struct + { + POWER_STATE_HANDLER_TYPE State; + PVOID Context; + BOOLEAN Entering; + } StateNotifyHandlerData; + } ContextData; +} POP_POWER_STATE_HANDLER_COMMAND_CONTEXT, *PPOP_POWER_STATE_HANDLER_COMMAND_CONTEXT; + +// +// Power state handler processor context +// +typedef struct _POP_POWER_STATE_HANDLER_PROCESSOR_CONTEXT +{ + KFLOATING_SAVE FpContext; + NTSTATUS FpStatus; + NTSTATUS Status; + POP_POWER_HANDLER_COMMAND CurrentCommand; +} POP_POWER_STATE_HANDLER_PROCESSOR_CONTEXT, *PPOP_POWER_STATE_HANDLER_PROCESSOR_CONTEXT; + +/****************************************************************************** + * Functions * + ******************************************************************************/ + +// +// Power Manager System Worker Thread routines +// +_Function_class_(KSTART_ROUTINE) +VOID +NTAPI +PopMasterDispatchIrp( + _In_ PVOID StartContext); + +// +// Power Manager Executive Worker Thread routines +// +_Use_decl_annotations_ +VOID +NTAPI +PopGracefulShutdown( + _In_ PVOID Parameter); + +_Use_decl_annotations_ +VOID +NTAPI +PopPolicyManagerWorker( + _In_ PVOID Parameter); + +_Use_decl_annotations_ +VOID +NTAPI +PopUnlockMemoryWorker( + _In_ PVOID Parameter); + +_Use_decl_annotations_ +VOID +NTAPI +PopControlSwitchHandler( + _In_ PVOID Parameter); + +_Use_decl_annotations_ +VOID +NTAPI +PopCompositeBatteryHandler( + _In_ PVOID Parameter); + // // Initialization routines // @@ -281,102 +985,408 @@ CODE_SEG("INIT") BOOLEAN NTAPI PoInitSystem( - IN ULONG BootPhase -); + _In_ ULONG BootPhase); CODE_SEG("INIT") VOID NTAPI PoInitializePrcb( - IN PKPRCB Prcb -); + _Inout_ PKPRCB Prcb); + +// +// Power Policy Manager routines +// +VOID +NTAPI +PopPowerPolicyNotification( + VOID); + +VOID +NTAPI +PopPowerPolicySystemIdle( + VOID); + +VOID +NTAPI +PopPowerPolicyTimeChange( + VOID); + +VOID +NTAPI +PopInitializePowerPolicy( + _Out_ PSYSTEM_POWER_POLICY PowerPolicy); + +VOID +NTAPI +PopDefaultPolicies( + VOID); + +VOID +NTAPI +PopRegisterPowerPolicyWorker( + _In_ POP_POWER_POLICY_WORKER_TYPES WorkerType, + _In_ PPOP_POLICY_WORKER_FUNC WorkerFunction); + +VOID +NTAPI +PopRequestPolicyWorker( + _In_ POP_POWER_POLICY_WORKER_TYPES WorkerType); VOID NTAPI -PopInitShutdownList( - VOID -); +PopCheckForPendingWorkers( + VOID); + +NTSTATUS +NTAPI +PopDevicePolicyCallback( + _In_ PVOID NotificationStructure, + _In_ PVOID Context); + +NTSTATUS +NTAPI +PopGetPolicyDeviceObject( + _In_ PUNICODE_STRING DeviceName, + _Out_ PDEVICE_OBJECT *DeviceObject); // -// I/O Routines +// Power Manager Composite Battery routines // +NTSTATUS +NTAPI +PopRemoveCompositeBattery( + VOID); + +NTSTATUS +NTAPI +PopAddCompositeBattery( + _In_ PDEVICE_OBJECT BatteryDevice, + _In_ PUNICODE_STRING BatteryName); + VOID NTAPI -PoInitializeDeviceObject( - IN OUT PDEVOBJ_EXTENSION DeviceObjectExtension -); +PopMarkNewBatteryPending( + VOID); VOID NTAPI -PoVolumeDevice( - IN PDEVICE_OBJECT DeviceObject -); +PopQueryBatteryState( + _Out_ PSYSTEM_BATTERY_STATE BatteryState); + +// +// Power Manager Switches Control routines +// +PPOP_CONTROL_SWITCH +NTAPI +PopGetControlSwitchByDevice( + _In_ PDEVICE_OBJECT DeviceObject); + +NTSTATUS +NTAPI +PopCreateControlSwitch( + _In_ PDEVICE_OBJECT DeviceObject, + _Out_ PPOP_CONTROL_SWITCH *ControlSwitch); VOID NTAPI -PoRemoveVolumeDevice( - IN PDEVICE_OBJECT DeviceObject); +PopSetButtonPowerAction( + _In_ PPOWER_ACTION_POLICY Button, + _In_ POWER_ACTION Action); // // Power State routines // NTSTATUS NTAPI -PopSetSystemPowerState( - SYSTEM_POWER_STATE PowerState, - POWER_ACTION PowerAction -); +PopInvokeSystemStateHandler( + _In_ POWER_STATE_HANDLER_TYPE HandlerType, + _In_opt_ PPOP_HIBER_CONTEXT HiberContext); + +ULONG +NTAPI +PopGetDoePowerState( + _In_ PEXTENDED_DEVOBJ_EXTENSION DevObjExts, + _In_ BOOLEAN GetSystem); + +VOID +NTAPI +PopSetDoePowerState( + _In_ PEXTENDED_DEVOBJ_EXTENSION DevObjExts, + _In_ POWER_STATE NewState, + _In_ BOOLEAN SetSystem); +_Function_class_(KDEFERRED_ROUTINE) VOID NTAPI -PopCleanupPowerState( - IN PPOWER_STATE PowerState -); +PopScanForIdleStateDevicesDpcRoutine( + _In_ PKDPC Dpc, + _In_ PVOID DeferredContext, + _In_ PVOID SystemArgument1, + _In_ PVOID SystemArgument2); NTSTATUS NTAPI -PopAddRemoveSysCapsCallback( - IN PVOID NotificationStructure, - IN PVOID Context -); +PopRegisterSystemStateHandler( + _In_ POWER_STATE_HANDLER_TYPE Type, + _In_ BOOLEAN RtcWake, + _In_ PENTER_STATE_HANDLER Handler, + _In_opt_ PVOID Context); + +VOID +NTAPI +PopIndicateSystemStateActivity( + _In_ EXECUTION_STATE StateActivity); // -// Notifications +// Notification routines // VOID NTAPI PoNotifySystemTimeSet( - VOID -); + VOID); // // Shutdown routines // +_Function_class_(ENTER_STATE_HANDLER) +NTSTATUS +NTAPI +PopShutdownHandler( + _In_opt_ PVOID Context, + _In_opt_ PENTER_STATE_SYSTEM_HANDLER SystemHandler, + _In_opt_ PVOID SystemContext, + _In_ LONG NumberProcessors, + _In_opt_ LONG volatile *Number); + +// +// IRP routines +// +PPOP_IRP_DATA +NTAPI +PopFindIrpData( + _In_opt_ PIRP Irp, + _In_opt_ PDEVICE_OBJECT DeviceObject, + _In_ POP_SEARCH_BY SearchBy); + +BOOLEAN +NTAPI +PopHasDoOutstandingIrp( + _In_ PDEVICE_OBJECT DeviceObject); + +NTSTATUS +NTAPI +PopRequestPowerIrp( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ UCHAR MinorFunction, + _In_ POWER_STATE PowerState, + _In_ BOOLEAN IsFxDevice, + _In_ BOOLEAN NotifyPEP, + _In_opt_ PREQUEST_POWER_COMPLETE CompletionFunction, + _In_opt_ __drv_aliasesMem PVOID Context, + _Outptr_opt_ PIRP *Irp); + +NTSTATUS +NTAPI +PopCreateIrpWorkerThread( + _In_ PKSTART_ROUTINE WorkerRoutine); + +NTSTATUS +FASTCALL +PoHandlePowerIrp( + _In_ PIRP Irp); + +// +// Volume and device object power extension (DOPE) routines +// +PDEVICE_OBJECT_POWER_EXTENSION +NTAPI +PopGetDope( + _In_ PDEVICE_OBJECT DeviceObject); + VOID NTAPI -PopReadShutdownPolicy( - VOID -); +PopFlushVolumes( + _In_ BOOLEAN ShuttingDown); VOID NTAPI -PopGracefulShutdown( - IN PVOID Context -); +PopRemoveVolumeDevice( + _In_ PDEVICE_OBJECT DeviceObject); VOID NTAPI -PopFlushVolumes( - IN BOOLEAN ShuttingDown -); +PoVolumeDevice( + _In_ PDEVICE_OBJECT DeviceObject); + +VOID +NTAPI +PoInitializeDeviceObject( + _Inout_ PDEVOBJ_EXTENSION DeviceObjectExtension); + +// +// Miscellaneous routines +// +VOID +NTAPI +PopCreatePowerPolicyDatabase( + VOID); + +NTSTATUS +NTAPI +PopReadPowerSettings( + _In_ PUNICODE_STRING PowerValue, + _In_ ULONG ValueType, + _Out_ PKEY_VALUE_PARTIAL_INFORMATION *ReturnedData); + +PVOID +NTAPI +PopAllocatePool( + _In_ SIZE_T PoolSize, + _In_ BOOLEAN Paged, + _In_ ULONG Tag); + +VOID +NTAPI +PopFreePool( + _In_ _Post_invalid_ PVOID PoolBuffer, + _In_ ULONG Tag); + +VOID +NTAPI +PoRundownDeviceObject( + _In_ PDEVICE_OBJECT DeviceObject); + +ULONG +NTAPI +PopQueryActiveProcessors( + VOID); + +// +// Debugging routines +// +VOID +NTAPI +PopReportWatchdogTime( + _In_ PPOP_IRP_DATA IrpData); + +PCSTR +NTAPI +PopTranslateSystemPowerStateToString( + _In_ SYSTEM_POWER_STATE SystemState); + +PCSTR +NTAPI +PopTranslateDevicePowerStateToString( + _In_ DEVICE_POWER_STATE DeviceState); + +PCWSTR +NTAPI +PopGetPowerInformationLevelName( + _In_ POWER_INFORMATION_LEVEL InformationLevel); // // Global data inside the Power Manager // -extern PDEVICE_NODE PopSystemPowerDeviceNode; + +/* Power Manager synchronization objects */ extern KGUARDED_MUTEX PopVolumeLock; -extern LIST_ENTRY PopVolumeDevices; extern KSPIN_LOCK PopDopeGlobalLock; +extern KGUARDED_MUTEX PopShutdownListMutex; +extern KSPIN_LOCK PopIrpLock; +extern KSEMAPHORE PopIrpDispatchMasterSemaphore; +extern ERESOURCE PopNotifyDeviceLock; +extern ERESOURCE PopPowerPolicyLock; +extern KSPIN_LOCK PopPowerPolicyWorkerLock; +extern FAST_MUTEX PopPowerSettingLock; +extern KSPIN_LOCK PopThermalZoneLock; + +/* Power Manager Policy constructs */ +extern POP_POLICY_WORKER PopPolicyWorker[]; +extern BOOLEAN PopPendingPolicyWorker; +extern ULONG PopShutdownPowerOffPolicy; +extern LIST_ENTRY PopPowerPolicyIrpQueueList; +extern WORK_QUEUE_ITEM PopPowerPolicyWorkItem; +extern PKTHREAD PopPowerPolicyOwnerLockThread; +extern SYSTEM_POWER_POLICY PopAcPowerPolicy; +extern SYSTEM_POWER_POLICY PopDcPowerPolicy; +extern PSYSTEM_POWER_POLICY PopDefaultPowerPolicy; +extern PKWIN32_POWEREVENT_CALLOUT PopEventCallout; + +/* Power Manager Shutdown constructs */ +extern BOOLEAN PopShutdownCleanly; +extern BOOLEAN PopShutdownListAvailable; +extern WORK_QUEUE_ITEM PopShutdownWorkItem; +extern KEVENT PopShutdownEvent; +extern PPOP_SHUTDOWN_WAIT_ENTRY PopShutdownThreadList; +extern LIST_ENTRY PopShutdownQueue; + +/* Power Manager Callbacks */ +extern PCALLBACK_OBJECT SetSystemTimeCallback; + +/* Power Manager Volumes & Device Nodes */ +extern PDEVICE_NODE PopSystemPowerDeviceNode; +extern LIST_ENTRY PopVolumeDevices; +extern ULONG PopVolumeFlushPolicy; + +/* Power Manager Centralized Actions & Capabilities */ extern POP_POWER_ACTION PopAction; extern SYSTEM_POWER_CAPABILITIES PopCapabilities; +extern ADMINISTRATOR_POWER_POLICY PopAdminPowerPolicy; + +/* Power Manager IRP constructs */ +extern LIST_ENTRY PopDispatchWorkerIrpList; +extern LIST_ENTRY PopQueuedIrpList; +extern LIST_ENTRY PopQueuedInrushIrpList; +extern LIST_ENTRY PopIrpThreadList; +extern LIST_ENTRY PopIrpDataList; +extern PIRP PopInrushIrp; +extern ULONG PopIrpWatchdogTickIntervalInSeconds; +extern ULONG PopPendingIrpDispatcWorkerCount; +extern ULONG PopIrpDispatchWorkerCount; +extern PKTHREAD PopIrpOwnerLockThread; +extern KEVENT PopIrpDispatchPendingEvent; +extern BOOLEAN PopIrpDispatchWorkerPending; + +/* Power Manager Wake Source constructs */ +extern LIST_ENTRY PopWakeSourceDevicesList; +extern ULONG PopSystemFullWake; + +/* Power Manager Thermal constructs */ +extern LIST_ENTRY PopThermalZones; +extern ULONG PopCoolingSystemMode; + +/* Power Manager States constructs */ +extern ULONG PopIdleScanIntervalInSeconds; +extern KDPC PopIdleScanDevicesDpc; +extern KTIMER PopIdleScanDevicesTimer; +extern LIST_ENTRY PopIdleDetectList; +extern POWER_STATE_HANDLER PopDefaultPowerStateHandlers[]; + +/* Power Manager Switch constructs */ +extern LIST_ENTRY PopControlSwitches; + +/* Power Manager Actions constructs */ +extern LIST_ENTRY PopActionWaiters; + +/* Power Manager Composite Battery constructs */ +extern PPOP_BATTERY PopBattery; +extern KEVENT PopFreshBatteryDataEvent; + +/* Power Manager miscellaneous constructs */ +extern BOOLEAN PopSimulate; +extern BOOLEAN PopAcpiPresent; +extern BOOLEAN PopAoAcPresent; +extern WORK_QUEUE_ITEM PopUnlockMemoryWorkItem; +extern KEVENT PopUnlockMemoryCompleteEvent; + +/* Power Manager registry constructs */ +extern UNICODE_STRING PopPowerRegPath; +extern UNICODE_STRING RegAcPolicy; +extern UNICODE_STRING RegDcPolicy; + +// +// Inlined functions +// +#include "po_x.h" +/* EOF */ diff --git a/ntoskrnl/include/internal/po_x.h b/ntoskrnl/include/internal/po_x.h new file mode 100644 index 0000000000000..4dafd7207d925 --- /dev/null +++ b/ntoskrnl/include/internal/po_x.h @@ -0,0 +1,291 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Inlined Functions for the Power Manager + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +// +// Ensures the passed IRP is a Power IRP (PoCallDriver & PoHandlePowerIrp) +// +#define POP_ASSERT_IRP_IS_POWER(IrpStack) \ + ASSERT(IrpStack->MajorFunction == IRP_MJ_POWER) + +// +// Ensures the current thread actually owns the IRP lock +// +#define POP_ASSERT_IRP_LOCK_THREAD_OWNERSHIP() \ + ASSERT(PopIrpOwnerLockThread == KeGetCurrentThread()) + +// +// Ensures the IRP is a wake Power Wake IRP +// +#define POP_ASSERT_IRP_IS_WAKE(IrpStack) \ + ASSERT(IrpStack->MinorFunction == IRP_MN_WAIT_WAKE) + +// +// Ensures the current thread owns the policy lock +// +#define POP_ASSERT_POWER_POLICY_LOCK_OWNERSHIP() \ + ASSERT(PopPowerPolicyOwnerLockThread == KeGetCurrentThread()) + +// +// Ensures the composite battery is not processing any mode requests +// +#define POP_ASSERT_NO_BATTERY_REQUEST_MODE() \ + ASSERT((PopBattery->Flags & POP_CB_PROCESSING_MODE_REQUEST) == 0) + +// +// DOPE locking mechanism +// +FORCEINLINE +VOID +PopAcquireDopeLock( + _Out_ PKIRQL Irql) +{ + KeAcquireSpinLock(&PopDopeGlobalLock, Irql); +} + +FORCEINLINE +VOID +PopReleaseDopeLock( + _In_ KIRQL OldIrql) +{ + KeReleaseSpinLock(&PopDopeGlobalLock, OldIrql); +} + +// +// Volume locking mechanism +// +FORCEINLINE +VOID +PopAcquireVolumeLock(VOID) +{ + KeAcquireGuardedMutex(&PopVolumeLock); +} + +FORCEINLINE +VOID +PopReleaseVolumeLock(VOID) +{ + KeReleaseGuardedMutex(&PopVolumeLock); +} + +// +// Power IRP dispatcher inliner +// +FORCEINLINE +NTSTATUS +PopDispatchPowerIrp( + _In_ PIRP Irp, + _In_ PDEVICE_OBJECT Device, + _In_ PIO_STACK_LOCATION IoStack) +{ + PDRIVER_OBJECT Driver = Device->DriverObject; + + return Driver->MajorFunction[IoStack->MajorFunction](Device, + Irp); +} + +// +// IRP locking mechanism +// +FORCEINLINE +VOID +PopAcquireIrpLock( + _Out_ PKLOCK_QUEUE_HANDLE LockHandle) +{ + KeAcquireInStackQueuedSpinLock(&PopIrpLock, LockHandle); + PopIrpOwnerLockThread = KeGetCurrentThread(); +} + +FORCEINLINE +VOID +PopReleaseIrpLock( + _In_ PKLOCK_QUEUE_HANDLE LockHandle) +{ + POP_ASSERT_IRP_LOCK_THREAD_OWNERSHIP(); + PopIrpOwnerLockThread = NULL; + KeReleaseInStackQueuedSpinLock(LockHandle); +} + +// +// Raising & Lowering IRQL during IRP dispatching +// +FORCEINLINE +VOID +PopRaiseIrqlToDpc( + _In_ PDEVICE_OBJECT Device, + _Out_ PKIRQL OldIrql) +{ + if (!(Device->Flags & DO_POWER_PAGABLE)) + { + KeRaiseIrql(DISPATCH_LEVEL, OldIrql); + } +} + +FORCEINLINE +VOID +PopLowerIrqlBack( + _In_ KIRQL Irql) +{ + if (KeGetCurrentIrql() == DISPATCH_LEVEL) + { + KeLowerIrql(Irql); + } +} + +// +// Mark current location of a power IRP as completed +// +FORCEINLINE +VOID +PopMarkIrpCurrentLocationComplete( + _Inout_ PIRP Irp) +{ + Irp->CurrentLocation = (Irp->StackCount + 2); +} + +// +// Selects & Releases an inrush IRP for the system to process +// +FORCEINLINE +VOID +PopSelectInrushIrp( + _In_ PIRP Irp) +{ + POP_ASSERT_IRP_LOCK_THREAD_OWNERSHIP(); + PopInrushIrp = Irp; +} + +FORCEINLINE +VOID +PopReleaseInrushIrp( + _In_ PIRP Irp) +{ + POP_ASSERT_IRP_LOCK_THREAD_OWNERSHIP(); + PopInrushIrp = NULL; +} + +// +// Applies a power action that is global for the whole system +// +FORCEINLINE +VOID +PopApplyPowerAction( + _In_ POWER_ACTION Action) +{ + ASSERT((Action >= PowerActionNone) && (Action <= PowerActionDisplayOff)); + PopAction.Action = Action; +} + +// +// Applies a thermal zone state for the whole system +// +FORCEINLINE +VOID +PopApplyThermalZoneState( + _In_ ULONG TzState) +{ + PopCoolingSystemMode |= TzState; +} + +// +// Sets the default global policy for the system +// +FORCEINLINE +VOID +PopSetDefaultPowerPolicy( + _In_ POP_POWER_POLICY_TYPE PolicyType) +{ + switch (PolicyType) + { + case PolicyAc: + { + PopDefaultPowerPolicy = &PopAcPowerPolicy; + break; + } + + case PolicyDc: + { + PopDefaultPowerPolicy = &PopDcPowerPolicy; + break; + } + + default: + { + ASSERT(FALSE); + } + } +} + +// +// Policy locking mechanism +// +FORCEINLINE +VOID +PopAcquirePowerPolicyLock(VOID) +{ + /* The current thread's code has to be paged */ + ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + + /* Acquire the policy lock */ + KeEnterCriticalRegion(); + ExAcquireResourceExclusiveLite(&PopPowerPolicyLock, TRUE); + PopPowerPolicyOwnerLockThread = KeGetCurrentThread(); +} + +FORCEINLINE +VOID +PopReleasePowerPolicyLock(VOID) +{ + /* The current thread's code has to be paged */ + ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + + /* Release the policy lock */ + POP_ASSERT_POWER_POLICY_LOCK_OWNERSHIP(); + PopPowerPolicyOwnerLockThread = NULL; + ExReleaseResourceLite(&PopPowerPolicyLock); + + /* And check for pending policy workers */ + PopCheckForPendingWorkers(); + KeLeaveCriticalRegion(); +} + +// +// Policy worker locking mechanism +// +FORCEINLINE +VOID +PopAcquirePowerPolicyWorkerLock( + _Out_ PKIRQL Irql) +{ + KeAcquireSpinLock(&PopPowerPolicyWorkerLock, Irql); +} + +FORCEINLINE +VOID +PopReleasePowerPolicyWorkerLock( + _In_ KIRQL OldIrql) +{ + KeReleaseSpinLock(&PopPowerPolicyWorkerLock, OldIrql); +} + +// +// Shutdown locking mechanism +// +FORCEINLINE +VOID +PopAcquireShutdownLock(VOID) +{ + KeAcquireGuardedMutex(&PopShutdownListMutex); +} + +FORCEINLINE +VOID +PopReleaseShutdownLock(VOID) +{ + KeReleaseGuardedMutex(&PopShutdownListMutex); +} + +/* EOF */ diff --git a/ntoskrnl/include/internal/pofx.h b/ntoskrnl/include/internal/pofx.h new file mode 100644 index 0000000000000..ff69e7ed33494 --- /dev/null +++ b/ntoskrnl/include/internal/pofx.h @@ -0,0 +1,234 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Internal header for the Power Manager Framework (PoFx) + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +// +// Define this if you want debugging support +// +#define _POFX_DEBUG_ 0x00 + +// +// These define the Debug Masks Supported +// +#define POFX_DEVICE_DEBUG 0x01 +#define POFX_COMPONENT_DEBUG 0x02 +#define POFX_PEP_DEBUG 0x04 +#define POFX_DEPENDENT_DEBUG 0x06 +#define POFX_CALLBACKS_DEBUG 0x08 +#define POFX_IDLE_STATE_DEBUG 0x10 + +// +// Debug/Tracing support +// +#if _POFX_DEBUG_ +#ifdef NEW_DEBUG_SYSTEM_IMPLEMENTED // enable when Debug Filters are implemented +#define POFXTRACE DbgPrintEx +#else +#define POFXTRACE(x, ...) \ + if (x & PopFxTraceLevel) DbgPrint(__VA_ARGS__) +#endif +#else +#define POFXTRACE(x, fmt, ...) DPRINT(fmt, ##__VA_ARGS__) +#endif + +// +// PoFx component flags +// +typedef union _POP_FX_COMPONENT_FLAGS +{ + struct + { + LONG Value; + LONG Value2; + }; + ULONG RefCount:30; + ULONG Idling:1; + struct + { + ULONG Active:1; + ULONG CriticalIdleOverride:1; + ULONG ResidentOverride:1; + ULONG CompleteIdleStatePending:1; + }; + ULONG Reserved:29; +} POP_FX_COMPONENT_FLAGS, *PPOP_FX_COMPONENT_FLAGS; + +// +// PoFx device status +// +typedef union _POP_FX_DEVICE_STATUS +{ + LONG Value; + ULONG SystemTransition:1; + ULONG PepD0Notify:1; + ULONG IdleTimerOn:1; + ULONG IgnoreIdleTimeout:1; + ULONG IrpInUse:1; + ULONG IrpPending:1; + ULONG DPNRDeviceNotified:1; + ULONG DPNRReceivedFromPep:1; + ULONG Reserved:24; +} POP_FX_DEVICE_STATUS, *PPOP_FX_DEVICE_STATUS; + +// +// PoFx device and component accounting +// +typedef struct _POP_FX_ACCOUNTING +{ + ULONG Lock; + BOOLEAN Active; + ULONG DripsRequiredState; + LONG Level; + LONGLONG ActiveStamp; + ULONGLONG CsActiveTime; + LONGLONG CriticalActiveTime; +} POP_FX_ACCOUNTING, *PPOP_FX_ACCOUNTING; + +// +// PoFx dependents +// +typedef struct _POP_FX_DEPENDENT +{ + ULONG Index; + ULONG ProviderIndex; +} POP_FX_DEPENDENT, *PPOP_FX_DEPENDENT; + +// +// PoFx providers +// +typedef struct _POP_FX_PROVIDER +{ + ULONG Index; + BOOLEAN Activating; +} POP_FX_PROVIDER, *PPOP_FX_PROVIDER; + +// +// PoFx power idle state +// +typedef struct _POP_FX_IDLE_STATE +{ + ULONGLONG TransitionLatency; + ULONGLONG ResidencyRequirement; + ULONG NominalPower; +} POP_FX_IDLE_STATE, *PPOP_FX_IDLE_STATE; + +// +// PoFx driver callbacks +// +typedef struct _POP_FX_DRIVER_CALLBACKS +{ + VOID (*ComponentActive)(PVOID arg1, ULONG arg2); + VOID (*ComponentIdle)(PVOID arg1, ULONG arg2); + VOID (*ComponentIdleState)(PVOID arg1, ULONG arg2, ULONG arg3); + VOID (*DevicePowerRequired)(PVOID arg1); + VOID (*DevicePowerNotRequired)(PVOID arg1); + LONG (*PowerControl)(PVOID arg1, PGUID arg2, PVOID arg3, ULONG arg4, PVOID arg5, ULONG arg6, PULONG arg7); + VOID (*ComponentCriticalTransition)(PVOID arg1, ULONG arg2, UCHAR arg3); +} POP_FX_DRIVER_CALLBACKS, *PPOP_FX_DRIVER_CALLBACKS; + +// +// PoFx worker watchdog info +// +typedef struct _POP_FX_WORK_ORDER_WATCHDOG_INFO +{ + KTIMER Timer; + KDPC Dpc; + struct _POP_FX_WORK_ORDER* WorkOrder; +} POP_FX_WORK_ORDER_WATCHDOG_INFO, *PPOP_FX_WORK_ORDER_WATCHDOG_INFO; + +// +// PoFx worker order +// +typedef struct _POP_FX_WORK_ORDER +{ + WORK_QUEUE_ITEM WorkItem; + LONG WorkCount; + PVOID Context; + PPOP_FX_WORK_ORDER_WATCHDOG_INFO WatchdogTimerInfo; +} POP_FX_WORK_ORDER, *PPOP_FX_WORK_ORDER; + +// +// PoFx power extension plug-in (PEP) +// +typedef struct _POP_FX_PLUGIN +{ + LIST_ENTRY Link; + ULONG Version; + ULONGLONG Flags; + KQUEUE WorkQueue; + UCHAR (*AcceptDeviceNotification)(ULONG arg1, PVOID arg2); + UCHAR (*AcceptProcessorNotification)(PPEPHANDLE__ arg1, ULONG arg2, PVOID arg3); + ULONG WorkOrderCount; + POP_FX_WORK_ORDER WorkOrders[1]; +} POP_FX_PLUGIN, *PPOP_FX_PLUGIN; + +// +// PoFx device +// +typedef struct _POP_FX_DEVICE +{ + LIST_ENTRY Link; + PIRP Irp; + struct _POP_IRP_DATA* IrpData; + volatile POP_FX_DEVICE_STATUS Status; + LONG PowerReqCall; + LONG PowerNotReqCall; + PPOP_FX_PLUGIN Plugin; + PPEPHANDLE__ PluginHandle; + PPOP_FX_PLUGIN MiniPlugin; + PPEPHANDLE__ MiniPluginHandle; + PDEVICE_NODE DevNode; + PDEVICE_OBJECT DeviceObject; + PDEVICE_OBJECT TargetDevice; + POP_FX_DRIVER_CALLBACKS Callbacks; + PVOID DriverContext; + IO_REMOVE_LOCK RemoveLock; + POP_FX_WORK_ORDER WorkOrder; + ULONG IdleLock; + KTIMER IdleTimer; + KDPC IdleDpc; + ULONGLONG IdleTimeout; + ULONGLONG IdleStamp; + PDEVICE_OBJECT NextIrpDeviceObject; + POWER_STATE NextIrpPowerState; + VOID (*NextIrpCallerCompletion)(PDEVICE_OBJECT arg1, UCHAR arg2, POWER_STATE arg3, PVOID arg4, PIO_STATUS_BLOCK arg5); + PVOID NextIrpCallerContext; + KEVENT IrpCompleteEvent; + UCHAR (*PowerOnDumpDeviceCallback)(PPEP_CRASHDUMP_INFORMATION arg1); + POP_FX_ACCOUNTING Accounting; + ULONG ComponentCount; + struct _POP_FX_COMPONENT* Components[1]; +} POP_FX_DEVICE, *PPOP_FX_DEVICE; + +// +// PoFx component +// +typedef struct _POP_FX_COMPONENT +{ + GUID Id; + ULONG Index; + POP_FX_WORK_ORDER WorkOrder; + PPOP_FX_DEVICE Device; + volatile POP_FX_COMPONENT_FLAGS Flags; + volatile LONG Resident; + KEVENT ActiveEvent; + ULONG IdleLock; + volatile LONG IdleConditionComplete; + volatile LONG IdleStateComplete; + ULONGLONG IdleStamp; + volatile ULONG CurrentIdleState; + ULONG IdleStateCount; + PPOP_FX_IDLE_STATE IdleStates; + ULONG DeepestWakeableIdleState; + ULONG ProviderCount; + PPOP_FX_PROVIDER Providers; + ULONG IdleProviderCount; + ULONG DependentCount; + PPOP_FX_DEPENDENT Dependents; + POP_FX_ACCOUNTING Accounting; +} POP_FX_COMPONENT, *PPOP_FX_COMPONENT; + +/* EOF */ diff --git a/ntoskrnl/include/internal/ppm.h b/ntoskrnl/include/internal/ppm.h new file mode 100644 index 0000000000000..d031c5793ca09 --- /dev/null +++ b/ntoskrnl/include/internal/ppm.h @@ -0,0 +1,202 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Internal header for the Processor Power Management (PPM) + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +// +// Define this if you want debugging support +// +#define _PPM_DEBUG_ 0x00 + +// +// These define the Debug Masks Supported +// +#define PPM_VETO_DEBUG 0x01 +#define PPM_HETERO_DEBUG 0x02 +#define PPM_CORE_PARK_DEBUG 0x04 +#define PPM_PERF_DEBUG 0x06 +#define PPM_INIT_SUBSYSTEM_DEBUG 0x08 + +// +// Debug/Tracing support +// +#if _PPM_DEBUG_ +#ifdef NEW_DEBUG_SYSTEM_IMPLEMENTED // enable when Debug Filters are implemented +#define PPMTRACE DbgPrintEx +#else +#define PPMTRACE(x, ...) \ + if (x & PpmTraceLevel) DbgPrint(__VA_ARGS__) +#endif +#else +#define PPMTRACE(x, fmt, ...) DPRINT(fmt, ##__VA_ARGS__) +#endif + +typedef enum _PPM_PROCESSOR_CSTATE_TYPES +{ + ProcessorC0State, + ProcessorC1State, + ProcessorC2State, + ProcessorC3State +} PPM_PROCESSOR_CSTATE_TYPES; + +#if 0 +// +// Idle synchronization state +// +typedef union _PPM_IDLE_SYNCHRONIZATION_STATE +{ + struct + { + LONG Value; + LONG Value2; + }; + ULONG RefCount:30; + ULONG Idling:1; + struct + { + ULONG Active:1; + ULONG CriticalIdleOverride:1; + ULONG ResidentOverride:1; + ULONG CompleteIdleStatePending:1; + }; + ULONG Reserved:29; +} PPM_IDLE_SYNCHRONIZATION_STATE, *PPPM_IDLE_SYNCHRONIZATION_STATE; + +// +// Idle bucket time type +// +typedef enum _PPM_IDLE_BUCKET_TIME_TYPE +{ + PpmIdleBucketTimeInQpc, + PpmIdleBucketTimeIn100ns, + PpmIdleBucketTimeMaximum +} PPM_IDLE_BUCKET_TIME_TYPE; + +// +// Performance state selection +// +typedef struct _PERFINFO_PPM_STATE_SELECTION +{ + ULONG SelectedState; + ULONG VetoedStates; + ULONG VetoReason[1]; +} PERFINFO_PPM_STATE_SELECTION, *PPERFINFO_PPM_STATE_SELECTION; + +// +// Concurrency accounting +// +typedef struct _PPM_CONCURRENCY_ACCOUNTING +{ + ULONG Lock; + ULONG Processors; + ULONG ActiveProcessors; + ULONGLONG LastUpdateTime; + ULONGLONG TotalTime; + ULONGLONG AccumulatedTime[1]; +} PPM_CONCURRENCY_ACCOUNTING, *PPPM_CONCURRENCY_ACCOUNTING; + +// +// Fixed Function Hardware (FFH) throttle state info +// +typedef struct _PPM_FFH_THROTTLE_STATE_INFO +{ + BOOLEAN EnableLogging; + ULONG MismatchCount; + UCHAR Initialized; + ULONGLONG LastValue; + LARGE_INTEGER LastLogTickCount; +} PPM_FFH_THROTTLE_STATE_INFO, *PPPM_FFH_THROTTLE_STATE_INFO; + +// +// Selection statistics +// +typedef struct _PPM_SELECTION_STATISTICS +{ + ULONGLONG PlatformOnlyCount; + ULONGLONG PreVetoCount; + ULONGLONG VetoCount; + ULONGLONG IdleDurationCount; + ULONGLONG LatencyCount; + ULONGLONG InterruptibleCount; + ULONGLONG DeviceDependencyCount; + ULONGLONG ProcessorDependencyCount; + ULONGLONG WrongProcessorCount; + ULONGLONG LegacyOverrideCount; + ULONGLONG CstateCheckCount; + ULONGLONG NoCStateCount; + ULONGLONG SelectedCount; +} PPM_SELECTION_STATISTICS, *PPPM_SELECTION_STATISTICS; + +// +// Veto accounting +// +typedef struct _PPM_VETO_ACCOUNTING +{ + volatile LONG VetoPresent; + LIST_ENTRY VetoListHead; +} PPM_VETO_ACCOUNTING, *PPPM_VETO_ACCOUNTING; + +// +// Power processor idle state +// +typedef struct _PPM_IDLE_STATE +{ + KAFFINITY_EX DomainMembers; + ULONG Latency; + ULONG BreakEvenDuration; + ULONG Power; + ULONG StateFlags; + PPM_VETO_ACCOUNTING VetoAccounting; + UCHAR StateType; + BOOLEAN InterruptsEnabled; + UCHAR Interruptible; + BOOLEAN ContextRetained; + BOOLEAN CacheCoherent; + BOOLEAN WakesSpuriously; + BOOLEAN PlatformOnly; + BOOLEAN NoCState; +} PPM_IDLE_STATE, *PPPM_IDLE_STATE; +#endif + +// +// Processor P/C states +// +typedef struct _PPM_PROCESSOR_STATES +{ + PPM_PROCESSOR_CSTATE_TYPES CurrentCState; + PPM_PROCESSOR_CSTATE_TYPES TargetCstate; + // PPM_PROCESSOR_PSTATE PState; +} PPM_PROCESSOR_STATES, *PPPM_PROCESSOR_STATES; + + +// +// Initialization routines +// +CODE_SEG("INIT") +NTSTATUS +NTAPI +PpmInitialize( + _In_ BOOLEAN EarlyPhase); + +// +// Processor idle functions +// +VOID +FASTCALL +PpmIdle( + _In_ PPROCESSOR_POWER_STATE PowerState); + +// +// Processor performance functions +// +VOID +NTAPI +PpmPerfIdleDpcRoutine( + _In_ PKDPC Dpc, + _In_ PVOID DeferredContext, + _In_ PVOID SystemArgument1, + _In_ PVOID SystemArgument2); + +/* EOF */ diff --git a/ntoskrnl/include/internal/ps.h b/ntoskrnl/include/internal/ps.h index 5be13cfc632d5..7d505648658ee 100644 --- a/ntoskrnl/include/internal/ps.h +++ b/ntoskrnl/include/internal/ps.h @@ -90,12 +90,6 @@ typedef struct _GET_SET_CTX_CONTEXT // // Initialization Functions // -VOID -NTAPI -PspShutdownProcessManager( - VOID -); - CODE_SEG("INIT") BOOLEAN NTAPI @@ -103,6 +97,12 @@ PsInitSystem( IN PLOADER_PARAMETER_BLOCK LoaderBlock ); +VOID +NTAPI +PsShutdownSystem( + VOID +); + // // Utility Routines // diff --git a/ntoskrnl/include/internal/tag.h b/ntoskrnl/include/internal/tag.h index 35cf14d6ae620..f832a140f9253 100644 --- a/ntoskrnl/include/internal/tag.h +++ b/ntoskrnl/include/internal/tag.h @@ -126,7 +126,17 @@ #define TAG_OB_HANDLE 'dHbO' /* Power Manager Tag */ -#define TAG_PO_DOPE 'EPOD' +#define TAG_PO ' oP' +#define TAG_PO_DOPE 'EPOD' +#define TAG_IRP_WATCHDOG 'aWrI' +#define TAG_PO_REGISTRY 'eRoP' +#define TAG_PO_CONTROL_SWITCH 'sCoP' +#define TAG_PO_POLICY_DEVICE_WORKITEM_DATA 'aDiW' +#define TAG_PO_SHUTDOWN_EVENT 'eSoP' +#define TAG_PO_INPUT_INFO_CLASS_BUFFER 'uBcI' +#define TAG_PO_IRP_DATA 'aDrI' +#define TAG_PO_IRP_QUEUE_ENTRY 'nEuQ' +#define TAG_PO_COMPOSITE_BATTERY 'aBoC' /* Process Manager Tags */ #define TAG_CIDOBJECT 'ODIC' diff --git a/ntoskrnl/io/iomgr/device.c b/ntoskrnl/io/iomgr/device.c index 096fa77286279..f41aa615689cb 100644 --- a/ntoskrnl/io/iomgr/device.c +++ b/ntoskrnl/io/iomgr/device.c @@ -1279,7 +1279,10 @@ IoDeleteDevice(IN PDEVICE_OBJECT DeviceObject) IoGetDevObjExtension(DeviceObject)->ExtensionFlags |= DOE_DELETE_PENDING; /* Unlink with the power manager */ - if (DeviceObject->Vpb) PoRemoveVolumeDevice(DeviceObject); + if (DeviceObject->Vpb) + { + PoRundownDeviceObject(DeviceObject); + } /* Check if the device object can be unloaded */ if (!DeviceObject->ReferenceCount) IopUnloadDevice(DeviceObject); diff --git a/ntoskrnl/io/iomgr/iofunc.c b/ntoskrnl/io/iomgr/iofunc.c index f672f4b5af69d..48f67d0c9411a 100644 --- a/ntoskrnl/io/iomgr/iofunc.c +++ b/ntoskrnl/io/iomgr/iofunc.c @@ -4622,25 +4622,3 @@ NtSetVolumeInformationFile(IN HANDLE FileHandle, /* Return status */ return Status; } - -/* - * @unimplemented - */ -NTSTATUS -NTAPI -NtCancelDeviceWakeupRequest(IN HANDLE DeviceHandle) -{ - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - -/* - * @unimplemented - */ -NTSTATUS -NTAPI -NtRequestDeviceWakeup(IN HANDLE DeviceHandle) -{ - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} diff --git a/ntoskrnl/io/iomgr/irp.c b/ntoskrnl/io/iomgr/irp.c index 2de843975e01a..bb4438a3276d8 100644 --- a/ntoskrnl/io/iomgr/irp.c +++ b/ntoskrnl/io/iomgr/irp.c @@ -1282,7 +1282,18 @@ IofCallDriver(IN PDEVICE_OBJECT DeviceObject, /* Get the Device Object */ StackPtr->DeviceObject = DeviceObject; - /* Call it */ + /* + * For Query/Set Power IRPs we must handle these on + * a special case, aka we must poke the Power Manager. + */ + if (StackPtr->MajorFunction == IRP_MJ_POWER && + (StackPtr->MinorFunction == IRP_MN_SET_POWER || + StackPtr->MinorFunction == IRP_MN_QUERY_POWER)) + { + return PoHandlePowerIrp(Irp); + } + + /* Otherwise call the function of the driver */ return DriverObject->MajorFunction[StackPtr->MajorFunction](DeviceObject, Irp); } diff --git a/ntoskrnl/ntos.cmake b/ntoskrnl/ntos.cmake index 082f61216c5a3..48ad617202d6d 100644 --- a/ntoskrnl/ntos.cmake +++ b/ntoskrnl/ntos.cmake @@ -251,11 +251,39 @@ list(APPEND SOURCE ${REACTOS_SOURCE_DIR}/ntoskrnl/ob/obsdcach.c ${REACTOS_SOURCE_DIR}/ntoskrnl/ob/obsecure.c ${REACTOS_SOURCE_DIR}/ntoskrnl/ob/obwait.c - ${REACTOS_SOURCE_DIR}/ntoskrnl/po/events.c - ${REACTOS_SOURCE_DIR}/ntoskrnl/po/guid.c - ${REACTOS_SOURCE_DIR}/ntoskrnl/po/poshtdwn.c - ${REACTOS_SOURCE_DIR}/ntoskrnl/po/povolume.c - ${REACTOS_SOURCE_DIR}/ntoskrnl/po/power.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/pofx/comp.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/pofx/device.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/pofx/fxapi.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/pofx/plugin.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/ppm/cpustat.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/ppm/eng.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/ppm/idle.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/ppm/init.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/ppm/perf.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/ppm/policy.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/act.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/aoac.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/batt.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/bugchk.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/drips.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/hibersup.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/init.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/irp.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/misc.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/notif.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/ntapi.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/poapi.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/pocs.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/policy.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/poreq.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/posett.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/shtdwn.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/state.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/thermreq.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/thermzn.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/thrtmgr.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/voldope.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/po/wakesrc.c ${REACTOS_SOURCE_DIR}/ntoskrnl/ps/apphelp.c ${REACTOS_SOURCE_DIR}/ntoskrnl/ps/debug.c ${REACTOS_SOURCE_DIR}/ntoskrnl/ps/job.c @@ -295,6 +323,7 @@ list(APPEND SOURCE ${REACTOS_SOURCE_DIR}/ntoskrnl/wmi/wmidrv.c) if(DBG) + list(APPEND SOURCE ${REACTOS_SOURCE_DIR}/ntoskrnl/po/debug.c) list(APPEND SOURCE ${REACTOS_SOURCE_DIR}/ntoskrnl/se/debug.c) endif() diff --git a/ntoskrnl/ntoskrnl.spec b/ntoskrnl/ntoskrnl.spec index 4d5bf883c601b..bfb2f28558ad4 100644 --- a/ntoskrnl/ntoskrnl.spec +++ b/ntoskrnl/ntoskrnl.spec @@ -930,18 +930,30 @@ @ stdcall PfxRemovePrefix(ptr ptr) @ stdcall PoCallDriver(ptr ptr) @ stdcall PoCancelDeviceNotify(ptr) +@ stdcall PoCreatePowerRequest(ptr ptr ptr) +@ stdcall PoClearPowerRequest(ptr long) +@ stdcall PoDeletePowerRequest(ptr) +@ stdcall PoEndDeviceBusy(ptr) +@ stdcall PoGetSystemWake(ptr) @ stdcall PoQueueShutdownWorkItem(ptr) +@ stdcall PoQueryWatchdogTime(ptr ptr) @ stdcall PoRegisterDeviceForIdleDetection(ptr long long long) @ stdcall PoRegisterDeviceNotify(ptr long long long ptr ptr) @ stdcall PoRegisterSystemState(ptr long) +@ stdcall PoRegisterPowerSettingCallback(ptr ptr ptr ptr ptr) @ stdcall PoRequestPowerIrp(ptr long long ptr ptr ptr) @ stdcall PoRequestShutdownEvent(ptr) +@ stdcall PoSetDeviceBusyEx(ptr) @ stdcall PoSetHiberRange(ptr long ptr long long) @ stdcall PoSetPowerState(ptr long long) +@ stdcall PoSetPowerRequest(ptr long) @ stdcall PoSetSystemState(long) +@ stdcall PoSetSystemWake(ptr) @ stdcall PoShutdownBugCheck(long long ptr ptr ptr ptr) @ stdcall PoStartNextPowerIrp(ptr) +@ stdcall PoStartDeviceBusy(ptr) @ stdcall PoUnregisterSystemState(ptr) +@ stdcall PoUnregisterPowerSettingCallback(ptr) @ stdcall ProbeForRead(ptr long long) @ stdcall ProbeForWrite(ptr long long) @ stdcall PsAssignImpersonationToken(ptr ptr) diff --git a/ntoskrnl/ob/obinit.c b/ntoskrnl/ob/obinit.c index 8c61db5475694..48a942ed6b045 100644 --- a/ntoskrnl/ob/obinit.c +++ b/ntoskrnl/ob/obinit.c @@ -434,4 +434,11 @@ ObInitSystem(VOID) return TRUE; } +VOID +NTAPI +ObShutdownSystem(VOID) +{ + UNIMPLEMENTED; +} + /* EOF */ diff --git a/ntoskrnl/po/act.c b/ntoskrnl/po/act.c new file mode 100644 index 0000000000000..73096bcaa9987 --- /dev/null +++ b/ntoskrnl/po/act.c @@ -0,0 +1,20 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power action operations routines + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +LIST_ENTRY PopActionWaiters; + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* EOF */ diff --git a/ntoskrnl/po/amd64/.keep b/ntoskrnl/po/amd64/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ntoskrnl/po/aoac.c b/ntoskrnl/po/aoac.c new file mode 100644 index 0000000000000..e99c0e3ccf097 --- /dev/null +++ b/ntoskrnl/po/aoac.c @@ -0,0 +1,20 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power Manager Modern Standby support (Always On & Always Connected) + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +BOOLEAN PopAoAcPresent = FALSE; + +/* PUBLIC FUNCTIONS ***********************************************************/ + +/* EOF */ diff --git a/ntoskrnl/po/arm/.keep b/ntoskrnl/po/arm/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ntoskrnl/po/batt.c b/ntoskrnl/po/batt.c new file mode 100644 index 0000000000000..88af0d1f51b28 --- /dev/null +++ b/ntoskrnl/po/batt.c @@ -0,0 +1,808 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Composite battery power management routines + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +PPOP_BATTERY PopBattery; +KEVENT PopFreshBatteryDataEvent; + +/* COMPLETION ROUTINE *********************************************************/ + +static IO_COMPLETION_ROUTINE PopBatteryIrpCompletion; + +/* PRIVATE FUNCTIONS **********************************************************/ + +static +VOID +PopIsSystemDrainingAcSource( + _In_ ULONG PowerState, + _Out_ PBOOLEAN Changed) +{ + PSYSTEM_POWER_POLICY SystemPowerPolicy; + + /* Assume the power policy has not changed */ + *Changed = FALSE; + SystemPowerPolicy = PopDefaultPowerPolicy; + + /* + * A fully charged up battery is indicated when the machine drains + * electrical power from the AC source and not from the battery itself. + */ + if (PowerState & BATTERY_POWER_ON_LINE) + { + /* The system is indeed consuming power from AC source, set the AC policy */ + PopSetDefaultPowerPolicy(PolicyAc); + } + else + { + /* The system is no longer accessing power from AC source, set the DC policy */ + PopSetDefaultPowerPolicy(PolicyDc); + } + + /* + * Now check if the power policy has changed at all. If it did, it indicates + * the event of the system draining AC power is occurring just now. + */ + if (SystemPowerPolicy != PopDefaultPowerPolicy) + { + *Changed = TRUE; + } +} + +static +NTSTATUS +NTAPI +PopBatteryIrpCompletion( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_ PVOID Context) +{ + NTSTATUS Status; + UCHAR Mode; + ULONG Tag; + ULONG EstTime; + BATTERY_STATUS BattStatus; + BATTERY_INFORMATION BattInfo; + BOOLEAN PolicyChanged; + PPOP_DEVICE_POLICY_WORKITEM_DATA WorkItemData; + + /* We do not care about these as we already have them in the composite battery */ + UNREFERENCED_PARAMETER(DeviceObject); + UNREFERENCED_PARAMETER(Irp); + + /* Check if our I/O request has succeeded */ + Status = PopBattery->Irp->IoStatus.Status; + if (!NT_SUCCESS(Status)) + { + /* + * It failed. However it could also be that the IRP has been cancelled + * by the Battery Class driver of which we must re-issue the I/O request + * from the beginning. + */ + if (Status == STATUS_CANCELLED || + PopBattery->Irp->Cancel) + { + DPRINT1("IRP 0x%p has been CANCELLED, must re-issue a new I/O battery request\n", PopBattery->Irp); + PopBattery->Flags |= POP_CB_RETRY_IO_REQUEST; + IoFreeIrp(PopBattery->Irp); + PopBattery->Irp = NULL; + goto InvokeHandler; + } + + /* + * We have failed the request by other means. There is nothing that we can do + * at this point. We must inquire the removal process of the battery and jump + * back to AC power policy and such. + */ + DPRINT1("The composite battery I/O request has failed (Status 0x%08lx)\n", Status); + PopBattery->Flags |= POP_CB_REMOVE_BATTERY; + goto InvokeHandler; + } + + /* Process this request depending on what we actually asked */ + Mode = PopBattery->Mode; + switch (Mode) + { + case POP_CB_READ_TAG_MODE: + { + /* + * We have gotten the battery tag of which we can use it to identify + * it and query critical battery data based on this tag. Take away + * the processing mode request. + */ + PopBattery->Flags &= ~POP_CB_PROCESSING_MODE_REQUEST; + + /* Copy the battery tag into the kernel structure */ + Tag = *(PULONG)Irp->AssociatedIrp.SystemBuffer; + DPRINT1("THE BATTERY TAG ISSSSS %lu\n", Tag); + PopBattery->BatteryTag = Tag; + + /* The system now has a composite battery, take away the "no battery" flag */ + PopBattery->Flags &= ~POP_CB_NO_BATTERY; + + /* FIXME: Once I implement battery trigger levels, the CB levels must be reset here */ + + /* + * If this was a new battery candidate or the I/O operation had to be + * retried because the composite battery driver cancelled our IRP, then + * take away these flags. + */ + if (PopBattery->Flags & POP_CB_PENDING_NEW_BATTERY) + { + PopBattery->Flags &= ~POP_CB_PENDING_NEW_BATTERY; + } + + if (PopBattery->Flags & POP_CB_RETRY_IO_REQUEST) + { + PopBattery->Flags &= ~POP_CB_RETRY_IO_REQUEST; + } + + break; + } + + case POP_CB_QUERY_INFORMATION_MODE: + { + /* + * We have the necessary battery information so that the battery + * status can be queried later. Take away the processing mode request + * and copy the information. + */ + PopBattery->Flags &= ~POP_CB_PROCESSING_MODE_REQUEST; + BattInfo = *(PBATTERY_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + RtlCopyMemory(&PopBattery->BattInfo, &BattInfo, sizeof(BattInfo)); + break; + } + + case POP_CB_QUERY_STATUS_MODE: + { + /* + * We have queried the current battery status. Take away the + * processing mode request. + */ + PopBattery->Flags &= ~POP_CB_PROCESSING_MODE_REQUEST; + + /* + * Figure out if the current global power policy must be changed or + * not, to DC or AC. We have to determine that by checking if the + * machine is draining electrical power from the AC source and not + * from the battery. + */ + BattStatus = *(PBATTERY_STATUS)Irp->AssociatedIrp.SystemBuffer; + PopIsSystemDrainingAcSource(BattStatus.PowerState, &PolicyChanged); + + /* + * The system power policy has changed. We need to recompute the thermal throttle + * gauges and the system idleness. + * + * FIXME: And of course code does not get written by itself.... a human can do that. + */ + if (PolicyChanged) + { + DPRINT1("System power policy has been changed, recomputing system idleness and throttle not IMPLEMENTED yet\n"); + } + + /* Copy the currently queried battery status */ + RtlCopyMemory(&PopBattery->Status, &BattStatus, sizeof(BattStatus)); + break; + } + + case POP_CB_QUERY_BATTERY_ESTIMATION_TIME_MODE: + { + /* + * We have the estimated time remaining of the battery life. + * Take away the processing mode request. + */ + PopBattery->Flags &= ~POP_CB_PROCESSING_MODE_REQUEST; + + /* Copy the battery estimation time */ + EstTime = *(PULONG)Irp->AssociatedIrp.SystemBuffer; + PopBattery->EstimatedBatteryTime = EstTime; + + /* + * As we got fresh battery data at this point of processing this + * request, alert whatever threads that waited on new battery data. + */ + KeSetEvent(&PopFreshBatteryDataEvent, IO_NO_INCREMENT, FALSE); + break; + } + + default: + { + /* + * We got an unknown battery request mode of which we have no idea + * what to do. This is a serious bug in our end so behead the system. + */ + KeBugCheckEx(INTERNAL_POWER_ERROR, + 0x300, + POP_BATTERY_UNKNOWN_MODE_REQUEST, + (ULONG_PTR)DeviceObject, + (ULONG_PTR)Irp); + } + } + + /* + * Cache the current executed operation mode so that the composite + * battery handler understands what has been processed. + */ + PopBattery->PreviousMode = Mode; + + /* Free the current IRP so that the handler can issue a new one */ + IoFreeIrp(PopBattery->Irp); + PopBattery->Irp = NULL; + +InvokeHandler: + /* Allocate a policy workitem data so that we can invoke the CB handler */ + WorkItemData = PopAllocatePool(sizeof(POP_DEVICE_POLICY_WORKITEM_DATA), + FALSE, + TAG_PO_POLICY_DEVICE_WORKITEM_DATA); + NT_ASSERT(WorkItemData != NULL); + + WorkItemData->PolicyData = NULL; + WorkItemData->PolicyType = PolicyDeviceBattery; + ExInitializeWorkItem(&WorkItemData->WorkItem, + PopCompositeBatteryHandler, + WorkItemData); + + /* Enqueue the control switch back to the handler */ + ExQueueWorkItem(&WorkItemData->WorkItem, DelayedWorkQueue); + return STATUS_MORE_PROCESSING_REQUIRED; +} + +static +ULONG +PopCalculateWaitStatusTimeout(VOID) +{ + PAGED_CODE(); + + /* + * If the event of new battery data is still in non-signaled state then + * it means the threads that seek battery state information (typically + * when you query such information with a call of NtPowerInformation) are + * still waiting and the Power Manager has not provided fresh battery data yet. + * In this scenario we want the Battery Class driver to return information as + * soon as possible. + */ + if (!KeReadStateEvent(&PopFreshBatteryDataEvent)) + { + return 0; + } + + return 0xFFFFFFFF; +} + +static +VOID +PopCalculateBatteryCapacityThresholds( + _Inout_ PBATTERY_WAIT_STATUS WaitStatus) +{ + ULONG EstimatedCapacity; + ULONG FullChargeCapacity; + + PAGED_CODE(); + + /* + * Calculate the estimated current charge capacity based on the amount of + * resolutions the system must be notified of battery capacity state change + * and the full charge capacity of this battery. + */ + FullChargeCapacity = PopBattery->BattInfo.FullChargedCapacity; + EstimatedCapacity = (FullChargeCapacity * PopDefaultPowerPolicy->BroadcastCapacityResolution) / 100; + + /* + * It could happen the full charge capacity is really low therefore we might + * get an estimated capacity of 0, in this case we have to take the maximum + * capacity (that is being 1). + */ + EstimatedCapacity = max(1, EstimatedCapacity); + + /* Set the default low and high capacities, we will calculate them later */ + WaitStatus->LowCapacity = 0; + WaitStatus->HighCapacity = 0xFFFFFFFF; + + /* + * Assign the low capacity if we are sure the reported current capacity + * from the battery status is above the estimated capacity. + */ + if (PopBattery->Status.Capacity > EstimatedCapacity) + { + WaitStatus->LowCapacity = PopBattery->Status.Capacity - EstimatedCapacity; + } + + /* Always set up the highest capacity, regardless if the current reported capacity is low */ + WaitStatus->HighCapacity = PopBattery->Status.Capacity + EstimatedCapacity; +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID +NTAPI +PopMarkNewBatteryPending(VOID) +{ + PopBattery->Flags |= POP_CB_PENDING_NEW_BATTERY; +} + +VOID +NTAPI +PopQueryBatteryState( + _Out_ PSYSTEM_BATTERY_STATE BatteryState) +{ + PAGED_CODE(); + + /* The policy lock must be owned as when quering battery state */ + POP_ASSERT_POWER_POLICY_LOCK_OWNERSHIP(); + + /* Fill the battery state field with zeroes */ + RtlZeroMemory(BatteryState, sizeof(*BatteryState)); + + /* + * No composite battery was ever connected with the Power Manager, + * so directly indicate that. + */ + if (PopBattery->Flags & POP_CB_NO_BATTERY) + { + BatteryState->AcOnLine = TRUE; + BatteryState->BatteryPresent = FALSE; + return; + } + + /* + * We must wait on the Power Manager to fetch for us the latest + * battery state updates. We have to release the policy lock + * while waiting so that the rest of the paging code can + * continue execution. + */ + PopReleasePowerPolicyLock(); + KeWaitForSingleObject(&PopFreshBatteryDataEvent, Executive, KernelMode, TRUE, NULL); + PopAcquirePowerPolicyLock(); + + /* + * We got signaled from the Power Manager it has done fetching battery data. + * However we must ensure the battery did not disappear while we were waiting. + */ + if (PopBattery->Flags & POP_CB_NO_BATTERY) + { + /* + * Something happened while we waited (an I/O error coming from the composite + * battery driver) or the user physically removed the battery. + */ + BatteryState->AcOnLine = TRUE; + BatteryState->BatteryPresent = FALSE; + KeClearEvent(&PopFreshBatteryDataEvent); + return; + } + + /* + * Set the event to non-signaled state to avoid further callers thinking + * the event was already signaled. + */ + KeClearEvent(&PopFreshBatteryDataEvent); + + /* + * If the system is powered by external AC source (power charging cord plugged in) + * with battery fully charged, acknowledge that. + */ + BatteryState->AcOnLine = FALSE; + if (PopBattery->Status.PowerState & BATTERY_POWER_ON_LINE) + { + BatteryState->AcOnLine = TRUE; + } + + /* At least one battery is present at this point */ + BatteryState->BatteryPresent = TRUE; + + /* If the battery is charging, acknowledge that */ + BatteryState->Charging = FALSE; + if (PopBattery->Status.PowerState & BATTERY_CHARGING) + { + BatteryState->Charging = TRUE; + } + + /* If the battery is discharging, acknowledge that */ + BatteryState->Discharging = FALSE; + if (PopBattery->Status.PowerState & BATTERY_DISCHARGING) + { + BatteryState->Discharging = TRUE; + } + + /* Return the rest of the battery state data to caller */ + BatteryState->MaxCapacity = PopBattery->BattInfo.FullChargedCapacity; + BatteryState->RemainingCapacity = PopBattery->Status.Capacity; + BatteryState->Rate = PopBattery->Status.Rate; + BatteryState->EstimatedTime = PopBattery->EstimatedBatteryTime; + BatteryState->DefaultAlert1 = PopBattery->BattInfo.DefaultAlert1; + BatteryState->DefaultAlert2 = PopBattery->BattInfo.DefaultAlert2; +} + +_Use_decl_annotations_ +VOID +NTAPI +PopCompositeBatteryHandler( + _In_ PVOID Parameter) +{ + PIRP Irp; + ULONG Tag; + PVOID IoBuffer; + BATTERY_QUERY_INFORMATION QueryInfo; + BATTERY_WAIT_STATUS WaitStatus; + ULONG IoControlCode; + PIO_STACK_LOCATION IrpStack; + PDEVICE_OBJECT DeviceObject; + ULONG InputBufferLength, OutputBufferLength; + PPOP_DEVICE_POLICY_WORKITEM_DATA WorkItemData = (PPOP_DEVICE_POLICY_WORKITEM_DATA)Parameter; + + PAGED_CODE(); + + /* We entered into a device policy handler, acquire the policy lock */ + PopAcquirePowerPolicyLock(); + + /* + * Ensure we got the right policy device as this handler only processes + * composite batteries and not anything else. We do not care about about + * the policy data as the kernel holds the composite battery data into + * a global variable, PopBattery. + */ + ASSERT(WorkItemData->PolicyType == PolicyDeviceBattery); + + /* + * A battery is about to be removed. This means we got an error of which no + * possible solution could be thought to handle it, or the battery just + * simply disappeared. + */ + if (PopBattery->Flags & POP_CB_REMOVE_BATTERY) + { + PopRemoveCompositeBattery(); + PopReleasePowerPolicyLock(); + PopFreePool(WorkItemData, TAG_PO_POLICY_DEVICE_WORKITEM_DATA); + return; + } + + /* + * Ensure the composite battery does not have an outstanding IRP that still needs + * to be completed. The CB IRP completion handler is responsible to free it once + * it has got data from the ACPI driver of which the IRP is no longer needed. + */ + ASSERT(PopBattery->Irp == NULL); + + /* Allocate a new fresh IRP to satisfy the required request */ + DeviceObject = PopBattery->DeviceObject; + Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); + if (!Irp) + { + /* + * Failing this request on our end is fatal. Most likely the I/O manager + * tried so hard to allocate an IRP for us it failed on doing so, due to + * a serious low memory condition. Not something that we can do to salvage + * the system so kill it. + */ + KeBugCheckEx(INTERNAL_POWER_ERROR, + 0, + POP_DEVICE_POLICY_IRP_ALLOC_FAILED, + PolicyDeviceBattery, + (ULONG_PTR)DeviceObject); + } + + /* + * We do not have any composite battery policy connected and we got a battery + * for the first time, or we got a new one, or the I/O request has been cancelled + * and we have to retry the operation back from the beginning. We must query the + * tag information in order to gather battery information and other related data. + */ + if ((PopBattery->Flags & POP_CB_NO_BATTERY) || + (PopBattery->Flags & POP_CB_PENDING_NEW_BATTERY) || + (PopBattery->Flags & POP_CB_RETRY_IO_REQUEST)) + { + DPRINT1("Connect battery in progress, requesting battery tag (next mode -- POP_CB_READ_TAG_MODE)\n"); + + /* Setup the necessary lengths for the battery information to query */ + InputBufferLength = sizeof(ULONG); + OutputBufferLength = sizeof(ULONG); + + /* Setup the battery tag ourselves so that the battery driver can fill it in */ + Tag = 0; + IoBuffer = &Tag; + + /* Setup the appropriate I/O control code and mode to query the tag */ + IoControlCode = IOCTL_BATTERY_QUERY_TAG; + PopBattery->Mode = POP_CB_READ_TAG_MODE; + + /* Acknowledge the Power Manager the policy battery is processing a mode request */ + PopBattery->Flags |= POP_CB_PROCESSING_MODE_REQUEST; + } + else + { + /* + * We already have a connected composite battery with a tag, we must read + * the battery information in order to proceed with battery status and such. + * For this we must check the prior mode request and act accordingly on that mode. + */ + if (PopBattery->PreviousMode == POP_CB_READ_TAG_MODE) + { + DPRINT1("Querying battery information in progress (next mode -- POP_CB_QUERY_INFORMATION_MODE)\n"); + + /* The battery should have already processed this request */ + POP_ASSERT_NO_BATTERY_REQUEST_MODE(); + + /* We have a tag, setup the appropriate lengths to query battery information */ + InputBufferLength = sizeof(BATTERY_QUERY_INFORMATION); + OutputBufferLength = sizeof(BATTERY_INFORMATION); + +#if 0 + /* + * By this point we should already have a battery tag that identifies this + * battery, otherwise we got bogus data from the Battery Class driver. + */ + ASSERT(PopBattery->BatteryTag != 0); +#endif + + /* Setup the right query battery information for this operation */ + RtlZeroMemory(&QueryInfo, sizeof(QueryInfo)); + QueryInfo.BatteryTag = PopBattery->BatteryTag; + QueryInfo.InformationLevel = BatteryInformation; + IoBuffer = &QueryInfo; + + /* Setup the appropriate I/O control code and mode to query the tag */ + IoControlCode = IOCTL_BATTERY_QUERY_INFORMATION; + PopBattery->Mode = POP_CB_QUERY_INFORMATION_MODE; + + /* Acknowledge the Power Manager the policy battery is processing a mode request */ + PopBattery->Flags |= POP_CB_PROCESSING_MODE_REQUEST; + } + else if (PopBattery->PreviousMode == POP_CB_QUERY_INFORMATION_MODE) + { + /* + * We have gotten the required battery information in order + * to read battery status. + */ + DPRINT1("Querying battery status in progress (next mode -- POP_CB_QUERY_STATUS_MODE)\n"); + + /* The battery should have already processed this request */ + POP_ASSERT_NO_BATTERY_REQUEST_MODE(); + + /* Setup the appropriate lengths to query battery status */ + InputBufferLength = sizeof(BATTERY_WAIT_STATUS); + OutputBufferLength = sizeof(BATTERY_STATUS); + + /* + * Fill in battery tag and the information required to read the + * status of the battery. + */ + RtlZeroMemory(&WaitStatus, sizeof(WaitStatus)); + WaitStatus.BatteryTag = PopBattery->BatteryTag; + WaitStatus.Timeout = PopCalculateWaitStatusTimeout(); + WaitStatus.PowerState = PopBattery->Status.PowerState; + + /* Calculate the low and high capacities of which we should be notified of new battery status */ + PopCalculateBatteryCapacityThresholds(&WaitStatus); + + /* Assign the I/O buffer to that of the battery wait status */ + IoBuffer = &WaitStatus; + + /* Setup the appropriate I/O control code and mode to query the tag */ + IoControlCode = IOCTL_BATTERY_QUERY_STATUS; + PopBattery->Mode = POP_CB_QUERY_INFORMATION_MODE; + + /* Acknowledge the Power Manager the policy battery is processing a mode request */ + PopBattery->Flags |= POP_CB_PROCESSING_MODE_REQUEST; + } + else if (PopBattery->PreviousMode == POP_CB_QUERY_STATUS_MODE) + { + /* + * We have successfully read the battery status, the last operation + * is to query the estimated time of the battery after a successful + * read of the battery status. + */ + DPRINT1("Querying battery estimation time in progress (next mode -- POP_CB_QUERY_BATTERY_ESTIMATION_TIME_MODE)\n"); + + /* The battery should have already processed this request */ + POP_ASSERT_NO_BATTERY_REQUEST_MODE(); + + /* Setup the appropriate lengths to query the battery estimation time */ + InputBufferLength = sizeof(BATTERY_QUERY_INFORMATION); + OutputBufferLength = sizeof(ULONG); + + /* Setup the right query battery information for this operation */ + RtlZeroMemory(&QueryInfo, sizeof(QueryInfo)); + QueryInfo.BatteryTag = PopBattery->BatteryTag; + QueryInfo.InformationLevel = BatteryEstimatedTime; + QueryInfo.AtRate = 0; + IoBuffer = &QueryInfo; + + /* Setup the appropriate I/O control code and mode to query the tag */ + IoControlCode = IOCTL_BATTERY_QUERY_INFORMATION; + PopBattery->Mode = POP_CB_QUERY_BATTERY_ESTIMATION_TIME_MODE; + + /* Acknowledge the Power Manager the policy battery is processing a mode request */ + PopBattery->Flags |= POP_CB_PROCESSING_MODE_REQUEST; + } + else if (PopBattery->PreviousMode == POP_CB_QUERY_BATTERY_ESTIMATION_TIME_MODE) + { + /* + * We have finally queried the battery estimation time, now we + * have to repeat the operation by reading the battery status again. + */ + DPRINT1("Battery estimation time queried, rolling back to read battery status (next mode -- POP_CB_QUERY_STATUS_MODE)\n"); + + /* The battery should have already processed this request */ + POP_ASSERT_NO_BATTERY_REQUEST_MODE(); + + /* Setup the appropriate lengths to query battery status */ + InputBufferLength = sizeof(BATTERY_WAIT_STATUS); + OutputBufferLength = sizeof(BATTERY_STATUS); + + /* + * Fill in battery tag and the information required to read the + * status of the battery. + */ + RtlZeroMemory(&WaitStatus, sizeof(WaitStatus)); + WaitStatus.BatteryTag = PopBattery->BatteryTag; + WaitStatus.Timeout = PopCalculateWaitStatusTimeout(); + WaitStatus.PowerState = PopBattery->Status.PowerState; + + /* Calculate the low and high capacities of which we should be notified of new battery status */ + PopCalculateBatteryCapacityThresholds(&WaitStatus); + + /* Assign the I/O buffer to that of the battery wait status */ + IoBuffer = &WaitStatus; + + /* Setup the appropriate I/O control code and mode to query the tag */ + IoControlCode = IOCTL_BATTERY_QUERY_STATUS; + PopBattery->Mode = POP_CB_QUERY_INFORMATION_MODE; + + /* Acknowledge the Power Manager the policy battery is processing a mode request */ + PopBattery->Flags |= POP_CB_PROCESSING_MODE_REQUEST; + } + else + { + /* + * We received an unknown operation mode request of which we have no + * idea on what to do. This is a bug in our end, so punt the system. + */ + KeBugCheckEx(INTERNAL_POWER_ERROR, + 0x301, + POP_BATTERY_UNKNOWN_MODE_REQUEST, + (ULONG_PTR)DeviceObject, + (ULONG_PTR)Irp); + } + } + + /* Setup the IRP stack parameters based on the requested operation */ + IrpStack = IoGetNextIrpStackLocation(Irp); + IrpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL; + IrpStack->Parameters.DeviceIoControl.IoControlCode = IoControlCode; + IrpStack->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength; + IrpStack->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength; + + /* Setup the system buffer based on the requested operation */ + Irp->AssociatedIrp.SystemBuffer = IoBuffer; + + /* + * Register the IRP completion CB handler and give the IRP to the composite battery, + * so that when the Battery Class driver returns back our IRP the completion handler + * will further process the CB operation event. + */ + PopBattery->Irp = Irp; + IoSetCompletionRoutine(Irp, + PopBatteryIrpCompletion, + NULL, + TRUE, + TRUE, + TRUE); + + /* + * We are going to leave the device policy handler soon, release the lock + * before dispatching the IRP to the Battery Class driver. + */ + PopReleasePowerPolicyLock(); + IoCallDriver(DeviceObject, Irp); + + /* Free the work item data as we no longer need it */ + PopFreePool(WorkItemData, TAG_PO_POLICY_DEVICE_WORKITEM_DATA); +} + +NTSTATUS +NTAPI +PopRemoveCompositeBattery(VOID) +{ + PIRP Irp; + PDEVICE_OBJECT DeviceObject; + + PAGED_CODE(); + + DPRINT1("Removing battery %wZ (Device 0x%p)\n", PopBattery->BatteryName, PopBattery->DeviceObject); + + /* The policy lock must be owned as we remove the composite battery */ + POP_ASSERT_POWER_POLICY_LOCK_OWNERSHIP(); + + /* Enforce the AC power policy as the battery disappeared */ + if (PopDefaultPowerPolicy != &PopAcPowerPolicy) + { + PopSetDefaultPowerPolicy(PolicyAc); + + /* FIXME: We must recalculate the thermal throttle gauges and system idleness here */ + } + + /* Discard the battery device driver name */ + RtlInitUnicodeString(&PopBattery->BatteryName, NULL); + + /* Report to the system that we do not have system batteries globally */ + PopCapabilities.SystemBatteriesPresent = FALSE; + + /* Clear any of the flags that have been set before and enforce the "no battery" flag */ + PopBattery->Flags &= ~(POP_CB_PENDING_NEW_BATTERY | POP_CB_PROCESSING_MODE_REQUEST | POP_CB_RETRY_IO_REQUEST | POP_CB_REMOVE_BATTERY); + PopBattery->Flags |= POP_CB_NO_BATTERY; + + /* + * Alert any threads that were waiting on for latest updated battery + * status, only to find out for them that the battery disappeared. + */ + KeSetEvent(&PopFreshBatteryDataEvent, IO_NO_INCREMENT, FALSE); + + /* Free the IRP and take the reference onto the battery device object we held away */ + Irp = PopBattery->Irp; + IoFreeIrp(Irp); + PopBattery->Irp = NULL; + + DeviceObject = PopBattery->DeviceObject; + ObDereferenceObject(DeviceObject); + PopBattery->DeviceObject = NULL; + + /* Reset the battery operation modes */ + PopBattery->Mode = POP_CB_NO_MODE; + PopBattery->PreviousMode = POP_CB_NO_MODE; + + /* And finally reset all the battery information and status data */ + PopBattery->BatteryTag = 0; + PopBattery->Temperature = 0; + RtlZeroMemory(&PopBattery->Status, sizeof(PopBattery->Status)); + RtlZeroMemory(&PopBattery->BattInfo, sizeof(PopBattery->BattInfo)); + PopBattery->EstimatedBatteryTime = 0; + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +PopAddCompositeBattery( + _In_ PDEVICE_OBJECT BatteryDevice, + _In_ PUNICODE_STRING BatteryName) +{ + PAGED_CODE(); + + DPRINT1("Adding a battery from %wZ (Device 0x%p)\n", BatteryName, BatteryDevice); + + /* + * Report that system batteries are present to the system globally + * if we have not done it before. + */ + PopCapabilities.SystemBatteriesPresent = TRUE; + + /* Copy the battery name into the kernel CB structure */ + RtlCopyUnicodeString(&PopBattery->BatteryName, BatteryName); + + /* + * The system must have been running on AC power at this point of + * receiving a system battery. Switch the power policy to that of + * DC policy. + */ + ASSERT(PopDefaultPowerPolicy == &PopAcPowerPolicy); + PopSetDefaultPowerPolicy(PolicyDc); + + /* + * Associate the battery device with the composite battery (CB) kernel + * structure. The device policy manager will kick the respective battery + * policy handler to read the battery tag for the first time. + */ + PopBattery->DeviceObject = BatteryDevice; + return STATUS_SUCCESS; +} + +/* EOF */ diff --git a/ntoskrnl/po/bugchk.c b/ntoskrnl/po/bugchk.c new file mode 100644 index 0000000000000..226ad874c3f8d --- /dev/null +++ b/ntoskrnl/po/bugchk.c @@ -0,0 +1,33 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power Manager graceful bug check support for PoShutdownBugCheck + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID +NTAPI +PoShutdownBugCheck( + _In_ BOOLEAN LogError, + _In_ ULONG BugCheckCode, + _In_ ULONG_PTR BugCheckParameter1, + _In_ ULONG_PTR BugCheckParameter2, + _In_ ULONG_PTR BugCheckParameter3, + _In_ ULONG_PTR BugCheckParameter4) +{ + /* FIXME */ + UNIMPLEMENTED; + NOTHING; +} + +/* EOF */ diff --git a/ntoskrnl/po/debug.c b/ntoskrnl/po/debug.c new file mode 100644 index 0000000000000..61d524ee9035f --- /dev/null +++ b/ntoskrnl/po/debug.c @@ -0,0 +1,648 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power Manager debuggging routines + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID +NTAPI +PopReportWatchdogTime( + _In_ PPOP_IRP_DATA IrpData) +{ + PIRP Irp = IrpData->Irp; + PDEVICE_OBJECT DeviceObject = IrpData->CurrentDevice; + ULONGLONG TimeRemaining = IrpData->WatchdogStart; + + if (TimeRemaining == 540) + { + DbgPrint("IRP watchdog has reached 1 minute until time-out (DO 0x%p, IRP 0x%p, IrpData 0x%p)\n", + DeviceObject, Irp, IrpData); + } + + if (TimeRemaining == 480) + { + DbgPrint("IRP watchdog has reached 2 minutes until time-out (DO 0x%p, IRP 0x%p, IrpData 0x%p)\n", + DeviceObject, Irp, IrpData); + } + + if (TimeRemaining == 360) + { + DbgPrint("IRP watchdog has reached 4 minutes until time-out (DO 0x%p, IRP 0x%p, IrpData 0x%p)\n", + DeviceObject, Irp, IrpData); + } + + if (TimeRemaining == 300) + { + DbgPrint("WARNING, SYSTEM IS DELAYED! IRP watchdog has reached 5 minutes until time-out (DO 0x%p, IRP 0x%p, IrpData 0x%p)\n", + DeviceObject, Irp, IrpData); + } + + if (TimeRemaining == 240) + { + DbgPrint("WARNING, SYSTEM IS DELAYED! IRP watchdog has reached 6 minutes until time-out (DO 0x%p, IRP 0x%p, IrpData 0x%p)\n", + DeviceObject, Irp, IrpData); + } + + if (TimeRemaining == 180) + { + DbgPrint("WARNING, SYSTEM IS DELAYED FOR TOO LONG, DEATH IS IMMINENT! IRP watchdog has reached 7 minutes until time-out (DO 0x%p, IRP 0x%p, IrpData 0x%p)\n", + DeviceObject, Irp, IrpData); + } + + if (TimeRemaining == 120) + { + DbgPrint("CRITICAL, THE SYSTEM IS APPROACHING DEATH! IRP watchdog has reached 8 minutes until time-out (DO 0x%p, IRP 0x%p, IrpData 0x%p)\n", + DeviceObject, Irp, IrpData); + } +} + +PCSTR +NTAPI +PopTranslateSystemPowerStateToString( + _In_ SYSTEM_POWER_STATE SystemState) +{ + switch (SystemState) + { + case PowerSystemWorking: + { + return "S0"; + } + + case PowerSystemSleeping1: + { + return "S1"; + } + + case PowerSystemSleeping2: + { + return "S2"; + } + + case PowerSystemSleeping3: + { + return "S3"; + } + + case PowerSystemHibernate: + { + return "S4"; + } + + case PowerSystemShutdown: + { + return "S5"; + } + + default: + { + return "Unknown"; + } + } + + return NULL; +} + +PCSTR +NTAPI +PopTranslateDevicePowerStateToString( + _In_ DEVICE_POWER_STATE DeviceState) +{ + switch (DeviceState) + { + case PowerDeviceD0: + { + return "D0"; + } + + case PowerDeviceD1: + { + return "D1"; + } + + case PowerDeviceD2: + { + return "D2"; + } + + case PowerDeviceD3: + { + return "D3"; + } + + default: + { + return "Unknown"; + } + } + + return NULL; +} + +PCWSTR +NTAPI +PopGetPowerInformationLevelName( + _In_ POWER_INFORMATION_LEVEL InformationLevel) +{ + switch (InformationLevel) + { + case SystemPowerPolicyAc: + { + return L"SystemPowerPolicyAc"; + } + + case SystemPowerPolicyDc: + { + return L"SystemPowerPolicyDc"; + } + + case VerifySystemPolicyAc: + { + return L"VerifySystemPolicyAc"; + } + + case VerifySystemPolicyDc: + { + return L"VerifySystemPolicyDc"; + } + + case SystemPowerCapabilities: + { + return L"SystemPowerCapabilities"; + } + + case SystemBatteryState: + { + return L"SystemBatteryState"; + } + + case SystemPowerStateHandler: + { + return L"SystemPowerStateHandler"; + } + + case ProcessorStateHandler: + { + return L"ProcessorStateHandler"; + } + + case SystemPowerPolicyCurrent: + { + return L"SystemPowerPolicyCurrent"; + } + + case AdministratorPowerPolicy: + { + return L"AdministratorPowerPolicy"; + } + + case SystemReserveHiberFile: + { + return L"SystemReserveHiberFile"; + } + + case ProcessorInformation: + { + return L"ProcessorInformation"; + } + + case SystemPowerInformation: + { + return L"SystemPowerInformation"; + } + + case ProcessorStateHandler2: + { + return L"ProcessorStateHandler2"; + } + + case LastWakeTime: + { + return L"LastWakeTime"; + } + + case LastSleepTime: + { + return L"LastSleepTime"; + } + + case SystemExecutionState: + { + return L"SystemExecutionState"; + } + + case SystemPowerStateNotifyHandler: + { + return L"SystemPowerStateNotifyHandler"; + } + + case ProcessorPowerPolicyAc: + { + return L"ProcessorPowerPolicyAc"; + } + + case ProcessorPowerPolicyDc: + { + return L"ProcessorPowerPolicyDc"; + } + + case VerifyProcessorPowerPolicyAc: + { + return L"VerifyProcessorPowerPolicyAc"; + } + + case VerifyProcessorPowerPolicyDc: + { + return L"VerifyProcessorPowerPolicyDc"; + } + + case ProcessorPowerPolicyCurrent: + { + return L"ProcessorPowerPolicyCurrent"; + } + + case SystemPowerStateLogging: + { + return L"SystemPowerStateLogging"; + } + + case SystemPowerLoggingEntry: + { + return L"SystemPowerLoggingEntry"; + } + + case SetPowerSettingValue: + { + return L"SetPowerSettingValue"; + } + + case NotifyUserPowerSetting: + { + return L"NotifyUserPowerSetting"; + } + + case PowerInformationLevelUnused0: + { + return L"PowerInformationLevelUnused0"; + } + + case SystemMonitorHiberBootPowerOff: + { + return L"SystemMonitorHiberBootPowerOff"; + } + + case SystemVideoState: + { + return L"SystemVideoState"; + } + + case TraceApplicationPowerMessage: + { + return L"TraceApplicationPowerMessage"; + } + + case TraceApplicationPowerMessageEnd: + { + return L"TraceApplicationPowerMessageEnd"; + } + + case ProcessorPerfStates: + { + return L"ProcessorPerfStates"; + } + + case ProcessorIdleStates: + { + return L"ProcessorIdleStates"; + } + + case ProcessorCap: + { + return L"ProcessorCap"; + } + + case SystemWakeSource: + { + return L"SystemWakeSource"; + } + + case SystemHiberFileInformation: + { + return L"SystemHiberFileInformation"; + } + + case TraceServicePowerMessage: + { + return L"TraceServicePowerMessage"; + } + + case ProcessorLoad: + { + return L"ProcessorLoad"; + } + + case PowerShutdownNotification: + { + return L"PowerShutdownNotification"; + } + + case MonitorCapabilities: + { + return L"MonitorCapabilities"; + } + + case SessionPowerInit: + { + return L"SessionPowerInit"; + } + + case SessionDisplayState: + { + return L"SessionDisplayState"; + } + + case PowerRequestCreate: + { + return L"PowerRequestCreate"; + } + + case PowerRequestAction: + { + return L"PowerRequestAction"; + } + + case GetPowerRequestList: + { + return L"GetPowerRequestList"; + } + + case ProcessorInformationEx: + { + return L"ProcessorInformationEx"; + } + + case NotifyUserModeLegacyPowerEvent: + { + return L"NotifyUserModeLegacyPowerEvent"; + } + + case GroupPark: + { + return L"GroupPark"; + } + + case ProcessorIdleDomains: + { + return L"ProcessorIdleDomains"; + } + + case WakeTimerList: + { + return L"WakeTimerList"; + } + + case SystemHiberFileSize: + { + return L"SystemHiberFileSize"; + } + + case ProcessorIdleStatesHv: + { + return L"ProcessorIdleStatesHv"; + } + + case ProcessorPerfStatesHv: + { + return L"ProcessorPerfStatesHv"; + } + + case ProcessorPerfCapHv: + { + return L"ProcessorPerfCapHv"; + } + + case ProcessorSetIdle: + { + return L"ProcessorSetIdle"; + } + + case LogicalProcessorIdling: + { + return L"LogicalProcessorIdling"; + } + + case UserPresence: + { + return L"UserPresence"; + } + + case PowerSettingNotificationName: + { + return L"PowerSettingNotificationName"; + } + + case GetPowerSettingValue: + { + return L"GetPowerSettingValue"; + } + + case IdleResiliency: + { + return L"IdleResiliency"; + } + + case SessionRITState: + { + return L"SessionRITState"; + } + + case SessionConnectNotification: + { + return L"SessionConnectNotification"; + } + + case SessionPowerCleanup: + { + return L"SessionPowerCleanup"; + } + + case SessionLockState: + { + return L"SessionLockState"; + } + + case SystemHiberbootState: + { + return L"SystemHiberbootState"; + } + + case PlatformInformation: + { + return L"PlatformInformation"; + } + + case PdcInvocation: + { + return L"PdcInvocation"; + } + + case MonitorInvocation: + { + return L"MonitorInvocation"; + } + + case FirmwareTableInformationRegistered: + { + return L"FirmwareTableInformationRegistered"; + } + + case SetShutdownSelectedTime: + { + return L"SetShutdownSelectedTime"; + } + + case SuspendResumeInvocation: + { + return L"SuspendResumeInvocation"; + } + + case PlmPowerRequestCreate: + { + return L"PlmPowerRequestCreate"; + } + + case ScreenOff: + { + return L"ScreenOff"; + } + + case CsDeviceNotification: + { + return L"CsDeviceNotification"; + } + + case PlatformRole: + { + return L"PlatformRole"; + } + + case LastResumePerformance: + { + return L"LastResumePerformance"; + } + + case DisplayBurst: + { + return L"DisplayBurst"; + } + + case ExitLatencySamplingPercentage: + { + return L"ExitLatencySamplingPercentage"; + } + + case RegisterSpmPowerSettings: + { + return L"RegisterSpmPowerSettings"; + } + + case PlatformIdleStates: + { + return L"PlatformIdleStates"; + } + + case ProcessorIdleVeto: + { + return L"ProcessorIdleVeto"; + } + + case PlatformIdleVeto: + { + return L"PlatformIdleVeto"; + } + + case SystemBatteryStatePrecise: + { + return L"SystemBatteryStatePrecise"; + } + + case ThermalEvent: + { + return L"ThermalEvent"; + } + + case PowerRequestActionInternal: + { + return L"PowerRequestActionInternal"; + } + + case BatteryDeviceState: + { + return L"BatteryDeviceState"; + } + + case PowerInformationInternal: + { + return L"PowerInformationInternal"; + } + + case ThermalStandby: + { + return L"ThermalStandby"; + } + + case SystemHiberFileType: + { + return L"SystemHiberFileType"; + } + + case PhysicalPowerButtonPress: + { + return L"PhysicalPowerButtonPress"; + } + + case QueryPotentialDripsConstraint: + { + return L"QueryPotentialDripsConstraint"; + } + + case EnergyTrackerCreate: + { + return L"EnergyTrackerCreate"; + } + + case EnergyTrackerQuery: + { + return L"EnergyTrackerQuery"; + } + + case UpdateBlackBoxRecorder: + { + return L"UpdateBlackBoxRecorder"; + } + + case SessionAllowExternalDmaDevices: + { + return L"SessionAllowExternalDmaDevices"; + } + + default: + { + return L"Unknown Class"; + } + } + + return NULL; +} + +/* EOF */ diff --git a/ntoskrnl/po/drips.c b/ntoskrnl/po/drips.c new file mode 100644 index 0000000000000..988757a00841c --- /dev/null +++ b/ntoskrnl/po/drips.c @@ -0,0 +1,16 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power Manager Deepest run-time idle platform state (DRIPS) management routines + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* EOF */ diff --git a/ntoskrnl/po/events.c b/ntoskrnl/po/events.c deleted file mode 100644 index 98be49b06e1bb..0000000000000 --- a/ntoskrnl/po/events.c +++ /dev/null @@ -1,304 +0,0 @@ -/* - * PROJECT: ReactOS Kernel - * LICENSE: GPL - See COPYING in the top level directory - * FILE: ntoskrnl/po/events.c - * PURPOSE: Power Manager - * PROGRAMMERS: Hervé Poussineau (hpoussin@reactos.org) - */ - -/* INCLUDES ******************************************************************/ - -#include -#define NDEBUG -#include - -/* GLOBALS *******************************************************************/ - -typedef struct _SYS_BUTTON_CONTEXT -{ - PDEVICE_OBJECT DeviceObject; - PIO_WORKITEM WorkItem; - KEVENT Event; - IO_STATUS_BLOCK IoStatusBlock; - ULONG SysButton; -} SYS_BUTTON_CONTEXT, *PSYS_BUTTON_CONTEXT; - -static VOID -NTAPI -PopGetSysButton( - IN PDEVICE_OBJECT DeviceObject, - IN PVOID Context); - -PKWIN32_POWEREVENT_CALLOUT PopEventCallout; -extern PCALLBACK_OBJECT SetSystemTimeCallback; - -/* FUNCTIONS *****************************************************************/ - -VOID -NTAPI -PoNotifySystemTimeSet(VOID) -{ - KIRQL OldIrql; - - /* Check if Win32k registered a notification callback */ - if (PopEventCallout) - { - /* Raise to dispatch */ - KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); - - /* Notify the callback */ - ExNotifyCallback(SetSystemTimeCallback, NULL, NULL); - - /* Lower IRQL back */ - KeLowerIrql(OldIrql); - } -} - -static NTSTATUS -NTAPI -PopGetSysButtonCompletion( - IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp, - IN PVOID Context) -{ - PSYS_BUTTON_CONTEXT SysButtonContext = Context; - ULONG SysButton; - - /* The DeviceObject can be NULL, so use the one we stored */ - DeviceObject = SysButtonContext->DeviceObject; - - /* FIXME: What do do with the sys button event? */ - SysButton = *(PULONG)Irp->AssociatedIrp.SystemBuffer; - { - DPRINT1("A device reported the event 0x%x (", SysButton); - if (SysButton & SYS_BUTTON_POWER) DbgPrint(" POWER"); - if (SysButton & SYS_BUTTON_SLEEP) DbgPrint(" SLEEP"); - if (SysButton & SYS_BUTTON_LID) DbgPrint(" LID"); - if (SysButton == 0) DbgPrint(" WAKE"); - DbgPrint(" )\n"); - - if (SysButton & SYS_BUTTON_POWER) - { - /* FIXME: Read registry for the action we should perform here */ - DPRINT1("Initiating shutdown after power button event\n"); - - ZwShutdownSystem(ShutdownNoReboot); - } - } - - /* Allocate a new workitem to send the next IOCTL_GET_SYS_BUTTON_EVENT */ - SysButtonContext->WorkItem = IoAllocateWorkItem(DeviceObject); - if (!SysButtonContext->WorkItem) - { - DPRINT("IoAllocateWorkItem() failed\n"); - ExFreePoolWithTag(SysButtonContext, 'IWOP'); - return STATUS_SUCCESS; - } - IoQueueWorkItem(SysButtonContext->WorkItem, - PopGetSysButton, - DelayedWorkQueue, - SysButtonContext); - - return STATUS_SUCCESS /* STATUS_CONTINUE_COMPLETION */; -} - -static VOID -NTAPI -PopGetSysButton( - IN PDEVICE_OBJECT DeviceObject, - IN PVOID Context) -{ - PSYS_BUTTON_CONTEXT SysButtonContext = Context; - PIO_WORKITEM CurrentWorkItem = SysButtonContext->WorkItem; - PIRP Irp; - - /* Get button pressed (IOCTL_GET_SYS_BUTTON_EVENT) */ - KeInitializeEvent(&SysButtonContext->Event, NotificationEvent, FALSE); - Irp = IoBuildDeviceIoControlRequest(IOCTL_GET_SYS_BUTTON_EVENT, - DeviceObject, - NULL, - 0, - &SysButtonContext->SysButton, - sizeof(SysButtonContext->SysButton), - FALSE, - &SysButtonContext->Event, - &SysButtonContext->IoStatusBlock); - if (Irp) - { - IoSetCompletionRoutine(Irp, - PopGetSysButtonCompletion, - SysButtonContext, - TRUE, - FALSE, - FALSE); - IoCallDriver(DeviceObject, Irp); - } - else - { - DPRINT1("IoBuildDeviceIoControlRequest() failed\n"); - ExFreePoolWithTag(SysButtonContext, 'IWOP'); - } - - IoFreeWorkItem(CurrentWorkItem); -} - -NTSTATUS -NTAPI -PopAddRemoveSysCapsCallback(IN PVOID NotificationStructure, - IN PVOID Context) -{ - PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification; - PSYS_BUTTON_CONTEXT SysButtonContext; - OBJECT_ATTRIBUTES ObjectAttributes; - HANDLE FileHandle; - PDEVICE_OBJECT DeviceObject; - PFILE_OBJECT FileObject; - PIRP Irp; - IO_STATUS_BLOCK IoStatusBlock; - KEVENT Event; - BOOLEAN Arrival; - ULONG Caps; - NTSTATUS Status; - POP_POLICY_DEVICE_TYPE DeviceType = (POP_POLICY_DEVICE_TYPE)(ULONG_PTR)Context; - - DPRINT("PopAddRemoveSysCapsCallback(%p %p)\n", - NotificationStructure, Context); - - Notification = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION)NotificationStructure; - if (Notification->Version != 1) - return STATUS_REVISION_MISMATCH; - if (Notification->Size != sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION)) - return STATUS_INVALID_PARAMETER; - if (RtlCompareMemory(&Notification->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID)) == sizeof(GUID)) - Arrival = TRUE; - else if (RtlCompareMemory(&Notification->Event, &GUID_DEVICE_INTERFACE_REMOVAL, sizeof(GUID)) == sizeof(GUID)) - Arrival = FALSE; - else - return STATUS_INVALID_PARAMETER; - - if (Arrival && DeviceType == PolicyDeviceBattery) - { - PopCapabilities.SystemBatteriesPresent = TRUE; - return STATUS_SUCCESS; - } - - if (Arrival) - { - DPRINT("Arrival of %wZ\n", Notification->SymbolicLinkName); - - /* Open the device */ - InitializeObjectAttributes(&ObjectAttributes, - Notification->SymbolicLinkName, - OBJ_KERNEL_HANDLE, - NULL, - NULL); - Status = ZwOpenFile(&FileHandle, - FILE_READ_DATA, - &ObjectAttributes, - &IoStatusBlock, - FILE_SHARE_READ | FILE_SHARE_WRITE, - 0); - if (!NT_SUCCESS(Status)) - { - DPRINT1("ZwOpenFile() failed with status 0x%08lx\n", Status); - return Status; - } - Status = ObReferenceObjectByHandle(FileHandle, - FILE_READ_DATA, - IoFileObjectType, - KernelMode, - (PVOID*)&FileObject, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("ObReferenceObjectByHandle() failed with status 0x%08lx\n", Status); - ZwClose(FileHandle); - return Status; - } - DeviceObject = IoGetRelatedDeviceObject(FileObject); - ObDereferenceObject(FileObject); - - /* Get capabilities (IOCTL_GET_SYS_BUTTON_CAPS) */ - KeInitializeEvent(&Event, NotificationEvent, FALSE); - Irp = IoBuildDeviceIoControlRequest(IOCTL_GET_SYS_BUTTON_CAPS, - DeviceObject, - NULL, - 0, - &Caps, - sizeof(Caps), - FALSE, - &Event, - &IoStatusBlock); - if (!Irp) - { - DPRINT1("IoBuildDeviceIoControlRequest() failed\n"); - ZwClose(FileHandle); - return STATUS_INSUFFICIENT_RESOURCES; - } - Status = IoCallDriver(DeviceObject, Irp); - if (Status == STATUS_PENDING) - { - DPRINT("IOCTL_GET_SYS_BUTTON_CAPS pending\n"); - KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL); - Status = IoStatusBlock.Status; - } - if (!NT_SUCCESS(Status)) - { - DPRINT1("Sending IOCTL_GET_SYS_BUTTON_CAPS failed with status 0x%08x\n", Status); - ZwClose(FileHandle); - return STATUS_INSUFFICIENT_RESOURCES; - } - - DPRINT("Device capabilities: 0x%x\n", Caps); - if (Caps & SYS_BUTTON_POWER) - { - DPRINT("POWER button present\n"); - PopCapabilities.PowerButtonPresent = TRUE; - } - - if (Caps & SYS_BUTTON_SLEEP) - { - DPRINT("SLEEP button present\n"); - PopCapabilities.SleepButtonPresent = TRUE; - } - - if (Caps & SYS_BUTTON_LID) - { - DPRINT("LID present\n"); - PopCapabilities.LidPresent = TRUE; - } - - SysButtonContext = ExAllocatePoolWithTag(NonPagedPool, - sizeof(SYS_BUTTON_CONTEXT), - 'IWOP'); - if (!SysButtonContext) - { - DPRINT1("ExAllocatePoolWithTag() failed\n"); - ZwClose(FileHandle); - return STATUS_INSUFFICIENT_RESOURCES; - } - - /* Queue a work item to get sys button event */ - SysButtonContext->WorkItem = IoAllocateWorkItem(DeviceObject); - SysButtonContext->DeviceObject = DeviceObject; - if (!SysButtonContext->WorkItem) - { - DPRINT1("IoAllocateWorkItem() failed\n"); - ZwClose(FileHandle); - ExFreePoolWithTag(SysButtonContext, 'IWOP'); - return STATUS_INSUFFICIENT_RESOURCES; - } - IoQueueWorkItem(SysButtonContext->WorkItem, - PopGetSysButton, - DelayedWorkQueue, - SysButtonContext); - - ZwClose(FileHandle); - return STATUS_SUCCESS; - } - else - { - DPRINT1("Removal of a power capable device not implemented\n"); - return STATUS_NOT_IMPLEMENTED; - } -} diff --git a/ntoskrnl/po/guid.c b/ntoskrnl/po/guid.c deleted file mode 100644 index a3ec3196c0fd7..0000000000000 --- a/ntoskrnl/po/guid.c +++ /dev/null @@ -1,4 +0,0 @@ - -#include -#include -#include diff --git a/ntoskrnl/po/hibersup.c b/ntoskrnl/po/hibersup.c new file mode 100644 index 0000000000000..7ed42fb6f474e --- /dev/null +++ b/ntoskrnl/po/hibersup.c @@ -0,0 +1,32 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power system hibernation infrastructure support + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID +NTAPI +PoSetHiberRange( + _In_ PVOID HiberContext, + _In_ ULONG Flags, + _In_ OUT PVOID StartPage, + _In_ ULONG Length, + _In_ ULONG PageTag) +{ + /* FIXME */ + UNIMPLEMENTED; + NOTHING; +} + +/* EOF */ diff --git a/ntoskrnl/po/i386/.keep b/ntoskrnl/po/i386/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ntoskrnl/po/init.c b/ntoskrnl/po/init.c new file mode 100644 index 0000000000000..df48fc7dafe86 --- /dev/null +++ b/ntoskrnl/po/init.c @@ -0,0 +1,578 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power Manager Initialization infrastructure + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +BOOLEAN PopAcpiPresent; +POP_POWER_ACTION PopAction; +SYSTEM_POWER_CAPABILITIES PopCapabilities; +ADMINISTRATOR_POWER_POLICY PopAdminPowerPolicy; +BOOLEAN PopSimulate = FALSE; + +/* PRIVATE FUNCTIONS **********************************************************/ + +static +VOID +PopInitShutdownList(VOID) +{ + PAGED_CODE(); + + /* Initialize the shutdown trigger event */ + KeInitializeEvent(&PopShutdownEvent, NotificationEvent, FALSE); + + /* Setup the centralized shutdown queue entries list */ + PopShutdownThreadList = NULL; + InitializeListHead(&PopShutdownQueue); + + /* + * Setup the shutdown list protect lock and acknowledge the Power + * Manager the list is ready to be used. + */ + KeInitializeGuardedMutex(&PopShutdownListMutex); + PopShutdownListAvailable = TRUE; +} + +static +VOID +PopInitPolicyManager(VOID) +{ + PAGED_CODE(); + + /* Initialize the power policy lock */ + ExInitializeResourceLite(&PopPowerPolicyLock); + PopPowerPolicyOwnerLockThread = NULL; + + /* Initialize AC/DC power policies */ + PopInitializePowerPolicy(&PopAcPowerPolicy); + PopInitializePowerPolicy(&PopDcPowerPolicy); + + /* + * Always assume that we are being powered up by the PSU than + * a battery when we are firing up the system. A system battery + * presence is determined when we talk to the battery composite driver. + * It is the said driver that has to notify us of batteries. + */ + PopSetDefaultPowerPolicy(PolicyAc); + + /* Initialize the power policy worker protect lock and policy IRPs list */ + KeInitializeSpinLock(&PopPowerPolicyWorkerLock); + InitializeListHead(&PopPowerPolicyIrpQueueList); + + /* Setup the policy worker item thread */ + ExInitializeWorkItem(&PopPowerPolicyWorkItem, + PopPolicyManagerWorker, + NULL); + + /* No policy workers are pending when the Power Manager initializes */ + PopPendingPolicyWorker = FALSE; + + /* + * Register default power policy workers with the Power Manager. + * The Power Manager will invoke the corresponding worker routines + * each time it checks for outstanding policy workers to get deployed. + */ + PopRegisterPowerPolicyWorker(PolicyWorkerNotification, + PopPowerPolicyNotification); + PopRegisterPowerPolicyWorker(PolicyWorkerSystemIdle, + PopPowerPolicySystemIdle); + PopRegisterPowerPolicyWorker(PolicyWorkerTimeChange, + PopPowerPolicyTimeChange); +} + +static +VOID +PopInitAdminPowerPolicyOverrides( + _Out_ PADMINISTRATOR_POWER_POLICY AdminPolicy) +{ + PAGED_CODE(); + + /* Zero out the admin policy parameter to caller */ + RtlZeroMemory(AdminPolicy, sizeof(ADMINISTRATOR_POWER_POLICY)); + + /* Setup the admin power policy overrides */ + AdminPolicy->MinSleep = PowerSystemSleeping1; + AdminPolicy->MaxSleep = PowerSystemHibernate; + AdminPolicy->MinVideoTimeout = 0; + AdminPolicy->MaxVideoTimeout = 0xFFFFFFFF; + AdminPolicy->MinSpindownTimeout = 0; + AdminPolicy->MaxSpindownTimeout = 0xFFFFFFFF; +} + +static +VOID +PopInitPowerSettingCallbacks(VOID) +{ + /* FIXME */ + PAGED_CODE(); + UNIMPLEMENTED; + return; +} + +static +BOOLEAN +PopInitBattery(VOID) +{ + PAGED_CODE(); + + /* Allocate memory space for the composite battery structure */ + PopBattery = PopAllocatePool(sizeof(*PopBattery), + FALSE, + TAG_PO_COMPOSITE_BATTERY); + if (!PopBattery) + { + return FALSE; + } + + /* Initialize the latest update battery state event */ + KeInitializeEvent(&PopFreshBatteryDataEvent, NotificationEvent, FALSE); + + /* + * For now we are not sure if this device is powered up by a battery + * or by a PSU until a power policy device driver informs us of an + * upcoming composite battery of which we must connect to. + */ + PopBattery->Flags |= POP_CB_NO_BATTERY; + PopBattery->Mode = POP_CB_NO_MODE; + PopBattery->PreviousMode = POP_CB_NO_MODE; + PopBattery->DeviceObject = NULL; + PopBattery->Irp = NULL; + return TRUE; +} + +static +VOID +PopInitPoFxManager(VOID) +{ + /* FIXME */ + PAGED_CODE(); + UNIMPLEMENTED; + return; +} + +CODE_SEG("INIT") +NTSTATUS +NTAPI +PopCreatePowerRequestObjectType(VOID) +{ + /* FIXME */ + PAGED_CODE(); + UNIMPLEMENTED; + return STATUS_SUCCESS; +} + +CODE_SEG("INIT") +NTSTATUS +NTAPI +PopCreateThermalRequestObjectType(VOID) +{ + /* FIXME */ + PAGED_CODE(); + UNIMPLEMENTED; + return STATUS_SUCCESS; +} + +CODE_SEG("INIT") +BOOLEAN +NTAPI +PopInitSystemPhase0(VOID) +{ + NTSTATUS Status; + PCHAR CommandLine; + + PAGED_CODE(); + + /* + * Prepare the centralized power capabilities and power actions. + * We will determine the capabilities of this machine as soon as + * we discover its functionalities. + */ + RtlZeroMemory(&PopCapabilities, sizeof(SYSTEM_POWER_CAPABILITIES)); + RtlZeroMemory(&PopAction, sizeof(POP_POWER_ACTION)); + + /* + * Check if the system supports ACPI or the user promptly forced + * disabling of ACPI. Keep in mind that a lack of ACPI support does + * not necessarily mean the system may support APM, a kernel mode + * APM driver has to tell us that therefore we cannot set ApmPresent here. + */ + CommandLine = KeLoaderBlock->LoadOptions; + _strupr(CommandLine); + if (strstr(CommandLine, "NOACPI")) + { + PopAcpiPresent = FALSE; + } + else + { + PopAcpiPresent = KeLoaderBlock->Extension->AcpiTable != NULL ? TRUE : FALSE; + } + + /* Only enable soft off shutdown in presence of ACPI as stated above */ + if (PopAcpiPresent) + { + PopCapabilities.SystemS5 = TRUE; + } + + /* Do not apply any power action as we are initializing the power manager */ + PopApplyPowerAction(PowerActionNone); + + /* Initialize the power IRP skeleton infrastructure */ + InitializeListHead(&PopDispatchWorkerIrpList); + InitializeListHead(&PopQueuedIrpList); + InitializeListHead(&PopQueuedInrushIrpList); + InitializeListHead(&PopIrpThreadList); + InitializeListHead(&PopIrpDataList); + + PopInrushIrp = NULL; + PopPendingIrpDispatcWorkerCount = 0; + PopIrpDispatchWorkerCount = 0; + PopIrpOwnerLockThread = NULL; + PopIrpDispatchWorkerPending = FALSE; + + KeInitializeEvent(&PopIrpDispatchPendingEvent, + NotificationEvent, + FALSE); + + KeInitializeSemaphore(&PopIrpDispatchMasterSemaphore, + 0, + MAXLONG); + + KeInitializeSpinLock(&PopIrpLock); + + Status = PopCreateIrpWorkerThread(&PopMasterDispatchIrp); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Creation of power IRP master dispatcher has FAILED (Status 0x%lx)\n", Status); + return FALSE; + } + + /* Initialize volumes and DOPE support */ + InitializeListHead(&PopVolumeDevices); + KeInitializeGuardedMutex(&PopVolumeLock); + KeInitializeSpinLock(&PopDopeGlobalLock); + + /* Initialize the device notification lock */ + ExInitializeResourceLite(&PopNotifyDeviceLock); + + /* + * Initialize the thermal protect lock and list of thermal zones. Do not + * set the thermal zone state as active until we figure out if the system + * has at least one thermal zone device supported. + */ + KeInitializeSpinLock(&PopThermalZoneLock); + InitializeListHead(&PopThermalZones); + PopApplyThermalZoneState(POP_THERMAL_ZONE_NONE); + + /* Initialize the idle detection skeleton infrastructure */ + InitializeListHead(&PopIdleDetectList); + KeInitializeTimerEx(&PopIdleScanDevicesTimer, NotificationTimer); + KeInitializeDpc(&PopIdleScanDevicesDpc, PopScanForIdleStateDevicesDpcRoutine, NULL); + + /* Initialize the list of power switches and action waiters */ + InitializeListHead(&PopControlSwitches); + InitializeListHead(&PopActionWaiters); + + /* + * Initialize the memory unlock worker thread and corresponding + * completion event. These constructs are specifically used in + * NtSetSystemPowerState in case memory pages/sections cannot be + * grabbed into the hibernation file. + */ + ExInitializeWorkItem(&PopUnlockMemoryWorkItem, + PopUnlockMemoryWorker, + NULL); + + KeInitializeEvent(&PopUnlockMemoryCompleteEvent, + NotificationEvent, + FALSE); + + /* Initialize the shutdown core mechanism */ + PopInitShutdownList(); + + /* Initialize the list of devices that woke the system */ + InitializeListHead(&PopWakeSourceDevicesList); + PopSystemFullWake = 0; // FIXME: Set a proper wake mode for the system + + /* + * Assign the default shutdown power states upon the initialization of the + * power manager. This is to ensure proper behavior when we are shutting + * down the system, in case none of the HALs has registered their power states. + */ + Status = PopRegisterSystemStateHandler(PowerStateShutdownOff, + FALSE, + PopShutdownHandler, + NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("System state handler registration failed (Status 0x%lx)\n", Status); + return FALSE; + } + + /* Initialize the composite battery */ + if (!PopInitBattery()) + { + DPRINT1("Composite battery initialization has failed\n"); + return FALSE; + } + + /* Initialize the power setting callbacks */ + PopInitPowerSettingCallbacks(); + + /* Initialize the power policy and worker manager */ + PopInitPolicyManager(); + + /* Initialize the global power admin overrides */ + PopInitAdminPowerPolicyOverrides(&PopAdminPowerPolicy); + + /* + * Initialize core PPM management at early phase. We do not check + * for function status as the initial core initialization always suceeds. + */ + PpmInitialize(TRUE); + + /* Initialize the power framework (PoFx) */ + PopInitPoFxManager(); + + DPRINT1("Hold on there buster\n"); + //__debugbreak(); + return TRUE; +} + +CODE_SEG("INIT") +BOOLEAN +NTAPI +PopInitSystemPhase1(VOID) +{ + NTSTATUS Status; + PVOID NotificationEntry; + LARGE_INTEGER IdleScanDueTime; + + PAGED_CODE(); + + /* + * Register the policy device drivers of which notifications may arrive. + * Drivers that we care about are the class input, battery, lid, thermal + * zones, system buttons, fans and ACPI memory devices. + */ + Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, + PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, + (PVOID)&GUID_DEVICE_SYS_BUTTON, + IopRootDeviceNode->PhysicalDeviceObject->DriverObject, + PopDevicePolicyCallback, + (PVOID)(ULONG_PTR)PolicyDeviceSystemButton, + &NotificationEntry); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to register PnP notifications for the POWER BUTTON (Status 0x%lx)\n", Status); + return FALSE; + } + + Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, + PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, + (PVOID)&GUID_CLASS_INPUT, + IopRootDeviceNode->PhysicalDeviceObject->DriverObject, + PopDevicePolicyCallback, + (PVOID)(ULONG_PTR)PolicyDeviceSystemButton, + &NotificationEntry); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to register PnP notifications for CLASS INPUT (Status 0x%lx)\n", Status); + return FALSE; + } + + Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, + PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, + (PVOID)&GUID_DEVICE_LID, + IopRootDeviceNode->PhysicalDeviceObject->DriverObject, + PopDevicePolicyCallback, + (PVOID)(ULONG_PTR)PolicyDeviceSystemButton, + &NotificationEntry); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to register PnP notifications for DEVICE LID (Status 0x%lx)\n", Status); + return FALSE; + } + + Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, + PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, + (PVOID)&GUID_DEVICE_MEMORY, + IopRootDeviceNode->PhysicalDeviceObject->DriverObject, + PopDevicePolicyCallback, + (PVOID)(ULONG_PTR)PolicyDeviceMemory, + &NotificationEntry); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to register PnP notifications for DEVICE MEMORY (Status 0x%lx)\n", Status); + return FALSE; + } + + Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, + PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, + (PVOID)&GUID_DEVICE_BATTERY, + IopRootDeviceNode->PhysicalDeviceObject->DriverObject, + PopDevicePolicyCallback, + (PVOID)(ULONG_PTR)PolicyDeviceBattery, + &NotificationEntry); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to register PnP notifications for BATTERY (Status 0x%lx)\n", Status); + return FALSE; + } + + Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, + PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, + (PVOID)&GUID_DEVICE_THERMAL_ZONE, + IopRootDeviceNode->PhysicalDeviceObject->DriverObject, + PopDevicePolicyCallback, + (PVOID)(ULONG_PTR)PolicyDeviceThermalZone, + &NotificationEntry); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to register PnP notifications for THERMAL ZONES (Status 0x%lx)\n", Status); + return FALSE; + } + + Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, + PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, + (PVOID)&GUID_DEVICE_FAN, + IopRootDeviceNode->PhysicalDeviceObject->DriverObject, + PopDevicePolicyCallback, + (PVOID)(ULONG_PTR)PolicyDeviceFan, + &NotificationEntry); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to register PnP notifications for FANS (Status 0x%lx)\n", Status); + return FALSE; + } + + /* Initialize the Power registry database if it was not created before */ + PopCreatePowerPolicyDatabase(); + + /* + * Check if power simulation is triggered for this system. + * Apply some capabilities in this case. + */ + if (PopSimulate) + { + /* Simulate the presence of a battery */ + PopCapabilities.SystemBatteriesPresent = TRUE; + PopCapabilities.BatteryScale[0].Granularity = 100; + PopCapabilities.BatteryScale[0].Capacity = 400; + PopCapabilities.BatteryScale[1].Granularity = 10; + PopCapabilities.BatteryScale[1].Capacity = 0xFFFF; + + /* Set the RTC and latency to some default states for simulation */ + PopCapabilities.RtcWake = PowerSystemSleeping3; + PopCapabilities.DefaultLowLatencyWake = PowerSystemSleeping1; + + /* And simulate the presence of ACPI devices and sleep states */ + PopCapabilities.PowerButtonPresent = TRUE; + PopCapabilities.SleepButtonPresent = TRUE; + PopCapabilities.LidPresent = TRUE; + PopCapabilities.SystemS1 = TRUE; + PopCapabilities.SystemS2 = TRUE; + PopCapabilities.SystemS3 = TRUE; + PopCapabilities.SystemS4 = TRUE; + } + + /* Read the AC/DC, administrator and miscellanea policies and apply them where necessary */ + PopAcquirePowerPolicyLock(); + PopDefaultPolicies(); + PopReleasePowerPolicyLock(); + + /* Initialize the power request object implementation */ + Status = PopCreatePowerRequestObjectType(); + if (!NT_SUCCESS(Status)) + { + /* If this fails, then sacrifice the system later on... */ + return FALSE; + } + + /* Initialize the thermal request implementation as well */ + Status = PopCreateThermalRequestObjectType(); + if (!NT_SUCCESS(Status)) + { + /* If this fails, then sacrifice the system later on... */ + return FALSE; + } + + /* Fire up the idle scan timer for idle detection */ + IdleScanDueTime.QuadPart = Int32x32To64(PopIdleScanIntervalInSeconds, + -10 * 1000 * 1000); + KeSetTimerEx(&PopIdleScanDevicesTimer, + IdleScanDueTime, + 1000, // Recurring interval in 1 second (1000 ms = 1 s) + &PopIdleScanDevicesDpc); + + return TRUE; +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +CODE_SEG("INIT") +BOOLEAN +NTAPI +PoInitSystem( + _In_ ULONG BootPhase) +{ + BOOLEAN Success; + + switch (BootPhase) + { + /* Early phase initialization */ + case 0: + { + Success = PopInitSystemPhase0(); + break; + } + + /* Last phase initialization */ + case 1: + { + Success = PopInitSystemPhase1(); + break; + } + + /* Bail out on any other unknown phases */ + default: + { + KeBugCheckEx(INTERNAL_POWER_ERROR, + 0, + POP_PO_INIT_FAILURE, + BootPhase, + 0); + } + } + + return Success; +} + +CODE_SEG("INIT") +VOID +NTAPI +PoInitializePrcb( + _Inout_ PKPRCB Prcb) +{ + /* Initialize the power state for this processor */ + RtlZeroMemory(&Prcb->PowerState, sizeof(Prcb->PowerState)); + Prcb->PowerState.Idle0KernelTimeLimit = 0xFFFFFFFF; + Prcb->PowerState.CurrentThrottle = POP_CURRENT_THROTTLE_MAX; + Prcb->PowerState.CurrentThrottleIndex = 0; + Prcb->PowerState.IdleFunction = PpmIdle; + + /* Register the performance routine for this processor and the timer */ + KeInitializeDpc(&Prcb->PowerState.PerfDpc, PpmPerfIdleDpcRoutine, Prcb); + KeSetTargetProcessorDpc(&Prcb->PowerState.PerfDpc, Prcb->Number); + KeInitializeTimerEx(&Prcb->PowerState.PerfTimer, SynchronizationTimer); +} + +/* EOF */ diff --git a/ntoskrnl/po/irp.c b/ntoskrnl/po/irp.c new file mode 100644 index 0000000000000..b9a277fd3682e --- /dev/null +++ b/ntoskrnl/po/irp.c @@ -0,0 +1,1517 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power Manager IRP management routines + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES ******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS *******************************************************************/ + +PIRP PopInrushIrp; +LIST_ENTRY PopDispatchWorkerIrpList; +LIST_ENTRY PopQueuedIrpList; +LIST_ENTRY PopQueuedInrushIrpList; +LIST_ENTRY PopIrpThreadList; +LIST_ENTRY PopIrpDataList; +KSPIN_LOCK PopIrpLock; +PKTHREAD PopIrpOwnerLockThread; +KEVENT PopIrpDispatchPendingEvent; +ULONG PopPendingIrpDispatcWorkerCount; +ULONG PopIrpDispatchWorkerCount; +KSEMAPHORE PopIrpDispatchMasterSemaphore; +BOOLEAN PopIrpDispatchWorkerPending; +ULONG PopIrpWatchdogTickIntervalInSeconds = 1; + +/* COMPLETION ROUTINES *******************************************************/ + +static IO_COMPLETION_ROUTINE PopRequestPowerIrpCompletion; + +#if 0 +static IO_COMPLETION_ROUTINE PopThermalIrpCompletion; +static IO_COMPLETION_ROUTINE PopFanIrpCompletion; +#endif + +/* PRIVATE FUNCTIONS *********************************************************/ + +_Function_class_(KDEFERRED_ROUTINE) +VOID +NTAPI +PopIrpWatchdogDpcRoutine( + _In_ PKDPC Dpc, + _In_ PVOID DeferredContext, + _In_ PVOID SystemArgument1, + _In_ PVOID SystemArgument2) +{ + ULONGLONG TimeRemaining; + TRIAGE_9F_POWER Triage; + KLOCK_QUEUE_HANDLE IrpLockHandle; + PPOP_IRP_DATA IrpData = (PPOP_IRP_DATA)DeferredContext; + + /* We do not care for these parameters */ + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(SystemArgument1); + UNREFERENCED_PARAMETER(SystemArgument2); + + /* Toy around with this watchdog with the IRP lock held */ + PopAcquireIrpLock(&IrpLockHandle); + + /* + * Decrease the watchdog time by one second. Log to the debugger information + * about this watchdog if it has reached specific intervals to warn the + * user of an impending death of the system. + */ + TimeRemaining = InterlockedDecrementUL(&IrpData->WatchdogStart); +#if DBG + PopReportWatchdogTime(IrpData); +#endif + + /* Give this system a final breath if the watchdog has expired */ + if (TimeRemaining == 0) + { + /* Setup the triage dump info for debugging purposes */ + RtlZeroMemory(&Triage, sizeof(TRIAGE_9F_POWER)); + Triage.Signature = POP_9F_TRIAGE_SIGNATURE; + Triage.Revision = POP_9F_TRIAGE_REVISION_V1; + Triage.IrpList = PopDispatchWorkerIrpList; + Triage.ThreadList = PopIrpThreadList; + Triage.DelayedWorkerQueue = NULL; // FIXME: We must fill in data once we support KPRIQUEUEs + + /* + * This device has failed to complete its power IRP in time. + * Any major delays on power operations from devices indicate a + * malfunction from their end, which could further badly impact + * system's startup or shutdown. It is the final countdown. + */ + KeBugCheckEx(DRIVER_POWER_STATE_FAILURE, + 0x3, + (ULONG_PTR)IrpData->Pdo, + (ULONG_PTR)&Triage, + (ULONG_PTR)IrpData->Irp); + } + + PopReleaseIrpLock(&IrpLockHandle); +} + +static +POWER_ACTION +PopTranslateDriverIrpPowerAction( + _In_ PPOP_POWER_ACTION PowerAction, + _In_ BOOLEAN EjectDevice) +{ + POWER_ACTION Action; + SYSTEM_POWER_STATE SystemState; + + /* + * The current power action must not be within a hibernation + * path (aka PowerActionHibernate) when a driver is doing a + * power request. It is at our discretion whether the device + * IRP can get into the hibernation path. + */ + Action = PowerAction->Action; + SystemState = PowerAction->SystemState; + ASSERT(Action != PowerActionHibernate); + + /* + * Check if we are incurring in a warm eject operation, and + * specifically for this device it is requesting a power operation. + */ + if (Action == PowerActionWarmEject) + { + /* + * We are doing a warm eject operation. The system can only be + * within appropriate sleep states (S1-S4) at this rate. This is + * to ensure the power transition of the system is consistent. + */ + ASSERT((SystemState >= PowerSystemSleeping1) && + (SystemState <= PowerSystemHibernate)); + + if (EjectDevice) + { + /* The device is the one wanting to do a warm eject operation */ + return PowerActionWarmEject; + } + + /* + * We are doing a warm eject operation, however it is not for the + * current device so we cannot simply translate the power action into + * a warm eject one. In this scenario translate the action into a sleep + * case if the system is about to sleep, or hibernate. + */ + if (SystemState == PowerSystemHibernate) + { + return PowerActionHibernate; + } + else + { + return PowerActionSleep; + } + } + + /* + * No warm eject operation is in progress, simply return the action + * that comes from the global power actions. If the system incurs in + * hibernation however, then translate to hibernate. + */ + if (SystemState == PowerSystemHibernate) + { + return PowerActionHibernate; + } + + return Action; +} + +static +SYSTEM_POWER_STATE_CONTEXT +PopBuildSystemPowerStateContext( + _In_ POWER_STATE_TYPE Type) +{ + SYSTEM_POWER_STATE_CONTEXT StateContext; + + /* + * For drivers, we will always tell the system is at working stage. + * Whereas for system power IRPs we must tell the user whether + * a wake-from-hibernation or fast startup was instantiated. + * + * FIXME: These values are hardcoded, the system state must be obtained + * from the global power actions. + */ + RtlZeroMemory(&StateContext, sizeof(SYSTEM_POWER_STATE_CONTEXT)); + if (Type == DevicePowerState) + { + StateContext.TargetSystemState = PowerSystemWorking; + StateContext.EffectiveSystemState = PowerSystemWorking; + StateContext.CurrentSystemState = PowerSystemWorking; + } + else + { + /* I do not implement that yet */ + ASSERT(FALSE); + } + + return StateContext; +} + +static +VOID +PopInsertIrpForDispatch( + _In_ PIRP Irp, + _In_ PIO_STACK_LOCATION CurrentStack) +{ + PDEVICE_OBJECT DeviceObject; + + /* + * The same thread who wants to insert an IRP worker + * entry must own the lock. Otherwise it is a bug. + */ + POP_ASSERT_IRP_LOCK_THREAD_OWNERSHIP(); + + /* + * Mark this IRP as pending as it will be dispatched + * as soon as a worker thread will process the IRP and + * add an extra reference count to the device so that it + * does not randomly die out on us. + */ + DeviceObject = CurrentStack->DeviceObject; + CurrentStack->Control |= SL_PENDING_RETURNED; + ObReferenceObject(DeviceObject); + + /* + * This is the same inrush IRP that the system is + * processing. Put it in the front of the list, as + * inrush IRPs have the utmost priority. + */ + if (Irp == PopInrushIrp) + { + InsertHeadList(&PopDispatchWorkerIrpList, &Irp->Tail.Overlay.ListEntry); + } + else + { + /* This is a normal IRP, insert it for processing */ + InsertTailList(&PopDispatchWorkerIrpList, &Irp->Tail.Overlay.ListEntry); + } + + /* + * Signal the semaphore of an impeding IRP that is queued for a worker. + * The IRP master dispatcher has to deal with this. + */ + KeReleaseSemaphore(&PopIrpDispatchMasterSemaphore, + IO_NO_INCREMENT, + 1, + FALSE); +} + +_Function_class_(KSTART_ROUTINE) +VOID +NTAPI +PopIrpDispatchWorker( + _In_ PVOID StartContext) +{ + PIRP Irp; + KIRQL OldIrql; + PIO_STACK_LOCATION IrpStack; + PDEVICE_OBJECT DeviceObject; + KLOCK_QUEUE_HANDLE LockHandle; + POP_IRP_THREAD_ENTRY IrpThreadEntry; + PLIST_ENTRY Entry; + + PAGED_CODE(); + UNREFERENCED_PARAMETER(StartContext); + + /* Grab the IRP from the list */ + PopAcquireIrpLock(&LockHandle); + Entry = RemoveHeadList(&PopDispatchWorkerIrpList); + Irp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry); + + /* Setup the IRP thread entry */ + RtlZeroMemory(&IrpThreadEntry, sizeof(POP_IRP_THREAD_ENTRY)); + InitializeListHead(&IrpThreadEntry.Link); + IrpThreadEntry.Thread = KeGetCurrentThread(); + IrpThreadEntry.Irp = Irp; + + /* And insert this worker thread entry into the list, for debugging purposes */ + InsertTailList(&PopIrpThreadList, &IrpThreadEntry.Link); + PopReleaseIrpLock(&LockHandle); + + /* Obtain the stack and device object for dispatching */ + IrpStack = IoGetCurrentIrpStackLocation(Irp); + DeviceObject = IrpStack->DeviceObject; + + /* + * Dispatch the IRP to a raised IRQL if we are safe that + * the device driver is not pageable. If it is, then + * do not raise the IRQL, this is a blocking request. + */ + PopRaiseIrqlToDpc(DeviceObject, &OldIrql); + PopDispatchPowerIrp(Irp, DeviceObject, IrpStack); + PopLowerIrqlBack(OldIrql); + + /* We are done, save up a worker space */ + PopIrpDispatchWorkerCount--; + + /* Dereference the device object that we hold a reference back */ + ObDereferenceObject(DeviceObject); + + /* + * Check if we have any pending IRPs that are waiting + * for a worker to be available. With a worker slot that + * we liberated, signal the master dispatcher to put the + * IRP in service. + */ + if (PopIrpDispatchWorkerPending) + { + PopPendingIrpDispatcWorkerCount--; + KeSetEvent(&PopIrpDispatchPendingEvent, + IO_NO_INCREMENT, + FALSE); + + /* This is the last IRP pending for a worker so we are done */ + if (PopPendingIrpDispatcWorkerCount == 0) + { + PopIrpDispatchWorkerPending = FALSE; + } + } + + /* This worker is about to terminate, remove it from the list */ + PopAcquireIrpLock(&LockHandle); + RemoveEntryList(&IrpThreadEntry.Link); + PopReleaseIrpLock(&LockHandle); + PsTerminateSystemThread(STATUS_SUCCESS); +} + +_Function_class_(KSTART_ROUTINE) +VOID +NTAPI +PopMasterDispatchIrp( + _In_ PVOID StartContext) +{ + NTSTATUS Status; + PVOID WaitObjects[2]; + + PAGED_CODE(); + UNREFERENCED_PARAMETER(StartContext); + + WaitObjects[0] = &PopIrpDispatchMasterSemaphore; + WaitObjects[1] = &PopIrpDispatchPendingEvent; + + for (;;) + { + /* + * Wait for the IRP master dispatcher semaphore or for the + * dispatch pending event to get signaled. In the former case, + * we have an impeding IRP that is queued for a worker. In the + * latter case, an IRP is pending for a worker space to be liberated. + */ + KeWaitForMultipleObjects(2, + WaitObjects, + WaitAny, + Executive, + KernelMode, + FALSE, + NULL, + NULL); + /* + * Determine if we have exhausted every IRP worker currently + * available to service this IRP. + */ + if (PopIrpDispatchWorkerCount < POP_MAX_IRP_WORKERS_COUNT) + { + /* Service it with a power thread */ + Status = PopCreateIrpWorkerThread(PopIrpDispatchWorker); + if (!NT_SUCCESS(Status)) + { + /* Failing to service the IRP on our end is fatal */ + KeBugCheckEx(INTERNAL_POWER_ERROR, 0x1, (ULONG_PTR)Status, 0, 0); + } + + /* And occupy a slot space for this newly created worker */ + PopIrpDispatchWorkerCount++; + } + else + { + /* + * We do not have any space left to setup a worker for this IRP, + * we have to put it in queue until an existing worker thread + * finishes its job and liberates a space for us. + */ + PopIrpDispatchWorkerPending = TRUE; + PopPendingIrpDispatcWorkerCount++; + } + } +} + +static +BOOLEAN +PopShouldForwardIrpAtPassive( + _In_ PIRP Irp) +{ + PDEVICE_OBJECT DeviceObject; + PIO_STACK_LOCATION IrpStack; + + /* + * Forwarding a power IRP at passive level can only happen + * within the following conditions: + * + * - The DO has set DO_POWER_PAGABLE indicating the device driver + * is pageable and not inrush; + * + * - The device or system is not in a fully working state. + * + * Either the system or device that is not fully ON it is not guaranteed + * that the IRP can be dispatched safely at a higher interrupt level. + */ + IrpStack = IoGetCurrentIrpStackLocation(Irp); + DeviceObject = IrpStack->DeviceObject; + if ((IrpStack->MinorFunction == IRP_MN_SET_POWER) && + !(DeviceObject->Flags & DO_POWER_PAGABLE)) + { + if (IrpStack->Parameters.Power.Type == DevicePowerState && + IrpStack->Parameters.Power.State.DeviceState == PowerDeviceD0) + { + return FALSE; + } + + if (IrpStack->Parameters.Power.Type == SystemPowerState && + IrpStack->Parameters.Power.State.SystemState == PowerSystemWorking) + { + return FALSE; + } + } + + return TRUE; +} + +static +BOOLEAN +PopIsDoProcessingPowerIrp( + _In_ PDEVICE_OBJECT DeviceObject) +{ + PEXTENDED_DEVOBJ_EXTENSION DevObjExts; + + /* We must not be above the permitted IRQL */ + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* Is this device processing any power IRPs? */ + DevObjExts = IoGetDevObjExtension(DeviceObject); + if ((DevObjExts->PowerFlags & POP_DOE_DEVICE_IRP_ACTIVE) || + (DevObjExts->PowerFlags & POP_DOE_SYSTEM_IRP_ACTIVE)) + { + return TRUE; + } + + /* This DO is not processing anything */ + return FALSE; +} + +static +ULONG +PopComputeIrpListLength( + _In_ PLIST_ENTRY IrpListHead) +{ + PLIST_ENTRY Entry; + ULONG Count = 0; + + /* The naughty caller passed a NULL list */ + ASSERT(IrpListHead != NULL); + + /* This list is empty */ + if (IsListEmpty(IrpListHead)) + { + return 0; + } + + /* Compute the exact number of list entries this list has */ + for (Entry = IrpListHead->Flink; + Entry != IrpListHead; + Entry = Entry->Flink) + { + Count++; + } + + return Count; +} + +static +VOID +PopAddInrushIrpToQueue( + _In_ PPOP_IRP_DATA IrpData) +{ + PPOP_IRP_QUEUE_ENTRY QueueEntry; + ULONG QueuedIrpsCount; + + /* We must not be above the permitted IRQL */ + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* The thread who enqueues an IRP must own the IRP lock */ + POP_ASSERT_IRP_LOCK_THREAD_OWNERSHIP(); + + /* + * Allocate a IRP queue entry to hold it up into the inrush + * IRP list. We are already in the middle of processing the + * power IRP so this operation must not fail. + */ + QueueEntry = PopAllocatePool(sizeof(*QueueEntry), + FALSE, + TAG_PO_IRP_QUEUE_ENTRY); + NT_ASSERT(QueueEntry != NULL); + + /* Insert the queue entry into the inrush queue list */ + InitializeListHead(&QueueEntry->Link); + QueueEntry->IrpData = IrpData; + InsertTailList(&PopQueuedInrushIrpList, &QueueEntry->Link); + + /* + * We must not enqueue too many inrush IRPs in the list. + * Unlike normal power IRPs the maximum threshold is lower + * because, while the system can only process one inrush IRP + * at a time, all the bloat of inrush IRPs would cause too + * much power consumption. + */ + QueuedIrpsCount = PopComputeIrpListLength(&PopQueuedInrushIrpList); + if (QueuedIrpsCount > POP_MAX_INRUSH_IRP_QUEUE_LIST) + { + /* This is it, crash the system */ + KeBugCheckEx(INTERNAL_POWER_ERROR, + 0x1, + 2, + POP_MAX_INRUSH_IRP_QUEUE_LIST, + 0); + } +} + +static +VOID +PopAddIrpToQueue( + _In_ PPOP_IRP_DATA IrpData) +{ + PPOP_IRP_QUEUE_ENTRY QueueEntry; + ULONG QueuedIrpsCount; + + /* We must not be above the permitted IRQL */ + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* The thread who enqueues an IRP must own the IRP lock */ + POP_ASSERT_IRP_LOCK_THREAD_OWNERSHIP(); + + /* + * Allocate a IRP queue entry to hold it up into the IRP list. + * We are already in the middle of processing the power IRP so + * this operation must not fail. + */ + QueueEntry = PopAllocatePool(sizeof(*QueueEntry), + FALSE, + TAG_PO_IRP_QUEUE_ENTRY); + NT_ASSERT(QueueEntry != NULL); + + /* Insert the queue entry into the queue list */ + InitializeListHead(&QueueEntry->Link); + QueueEntry->IrpData = IrpData; + InsertTailList(&PopQueuedIrpList, &QueueEntry->Link); + + /* + * Same condition case like above. However for normal power + * IRPs, if drivers request too many of them but do not complete + * them in an expected time manner, then one or some drivers + * are buggy. Crash the system. + */ + QueuedIrpsCount = PopComputeIrpListLength(&PopQueuedIrpList); + if (QueuedIrpsCount > POP_MAX_INRUSH_IRP_QUEUE_LIST) + { + KeBugCheckEx(INTERNAL_POWER_ERROR, + 0x1, + 3, + POP_MAX_IRP_QUEUE_LIST, + 0); + } +} + +static +PPOP_IRP_DATA +PopRemoveInrushPowerIrpFromQueue( + _In_opt_ PDEVICE_OBJECT DeviceObject, + _In_ BOOLEAN SearchByDevice) +{ + PLIST_ENTRY Entry; + PPOP_IRP_DATA IrpData; + PPOP_IRP_QUEUE_ENTRY QueueEntry; + PEXTENDED_DEVOBJ_EXTENSION DevObjExts; + + /* We must not be above the permitted IRQL */ + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* The thread who dequeues an IRP must own the IRP lock */ + POP_ASSERT_IRP_LOCK_THREAD_OWNERSHIP(); + + /* The list is empty, consider our job as done */ + if (IsListEmpty(&PopQueuedInrushIrpList)) + { + return NULL; + } + + /* Delist the previous IRP at the top of the head list */ + if (!SearchByDevice) + { + Entry = RemoveHeadList(&PopQueuedInrushIrpList); + QueueEntry = CONTAINING_RECORD(Entry, POP_IRP_QUEUE_ENTRY, Link); + IrpData = QueueEntry->IrpData; + PopFreePool(QueueEntry, TAG_PO_IRP_QUEUE_ENTRY); + return IrpData; + } + + /* Cache the DO extensions, make sure this is really an inrush device */ + ASSERT(DeviceObject != NULL); + DevObjExts = IoGetDevObjExtension(DeviceObject); + ASSERT(DevObjExts->PowerFlags & POP_DOE_HAS_INRUSH_DEVICE); + + /* Iterate over the list of inrush IRPs and look for the appropriate one */ + for (Entry = PopQueuedInrushIrpList.Flink; + Entry != &PopQueuedInrushIrpList; + Entry = Entry->Flink) + { + QueueEntry = CONTAINING_RECORD(Entry, POP_IRP_QUEUE_ENTRY, Link); + IrpData = QueueEntry->IrpData; + if ((IrpData->Pdo == DeviceObject) && + (DevObjExts->PowerFlags & POP_DOE_PENDING_PROCESS)) + { + /* This is the interested IRP, dequeue it and stop looking */ + RemoveEntryList(&QueueEntry->Link); + PopFreePool(QueueEntry, TAG_PO_IRP_QUEUE_ENTRY); + return IrpData; + } + } + + return NULL; +} + +static +PPOP_IRP_DATA +PopRemovePowerIrpFromQueue( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ POWER_STATE_TYPE StateType) +{ + PPOP_IRP_DATA IrpData; + PLIST_ENTRY Entry; + PPOP_IRP_QUEUE_ENTRY QueueEntry; + PEXTENDED_DEVOBJ_EXTENSION DevObjExts; + + /* We must not be above the permitted IRQL */ + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* The thread who dequeues an IRP must own the IRP lock */ + POP_ASSERT_IRP_LOCK_THREAD_OWNERSHIP(); + + /* The list is empty, consider our job as done */ + if (IsListEmpty(&PopQueuedIrpList)) + { + return NULL; + } + + /* Iterate over the list of IRPs and look for the appropriate one */ + DevObjExts = IoGetDevObjExtension(DeviceObject); + for (Entry = PopQueuedIrpList.Flink; + Entry != &PopQueuedIrpList; + Entry = Entry->Flink) + { + /* + * Look for the appropriate IRP and power type the device is + * interested in. Typically the power state type is the one + * coming from the previous IRP that is about to be completed. + * A device driver cannot request a mix of device and system IRPs + * simultaneously. + */ + QueueEntry = CONTAINING_RECORD(Entry, POP_IRP_QUEUE_ENTRY, Link); + IrpData = QueueEntry->IrpData; + if ((IrpData->Pdo == DeviceObject) && + (IrpData->PowerStateType == StateType) && + (DevObjExts->PowerFlags & POP_DOE_PENDING_PROCESS)) + { + /* This is the interested IRP, dequeue it and stop looking */ + RemoveEntryList(&QueueEntry->Link); + PopFreePool(QueueEntry, TAG_PO_IRP_QUEUE_ENTRY); + return IrpData; + } + } + + return NULL; +} + +static +VOID +PopClearDoeActiveFlags( + _In_ POWER_STATE_TYPE StateType, + _Inout_ PEXTENDED_DEVOBJ_EXTENSION DevObjExts) +{ + /* NULL DOEs are illegal here */ + ASSERT(DevObjExts != NULL); + + /* Check if this is a device type of IRP */ + if (StateType == DevicePowerState) + { + /* It was processing a normal device IRP */ + DevObjExts->PowerFlags &= ~POP_DOE_DEVICE_IRP_ACTIVE; + return; + } + + /* If we reach here then this device was processing a system IRP */ + DevObjExts->PowerFlags &= ~POP_DOE_SYSTEM_IRP_ACTIVE; +} + +static +VOID +PopSetDoeActiveFlags( + _In_ POWER_STATE_TYPE StateType, + _Inout_ PEXTENDED_DEVOBJ_EXTENSION DevObjExts) +{ + /* NULL DOEs are illegal here */ + ASSERT(DevObjExts != NULL); + + /* Check if this is a device type of IRP */ + if (StateType == DevicePowerState) + { + /* It is about to process a normal device IRP */ + DevObjExts->PowerFlags |= POP_DOE_DEVICE_IRP_ACTIVE; + return; + } + + /* If we reach here then this device is about to process a system IRP */ + DevObjExts->PowerFlags |= POP_DOE_SYSTEM_IRP_ACTIVE; +} + +static +VOID +PopSetupIrpWatchdog( + _In_ PPOP_IRP_DATA IrpData) +{ + KTIMER Timer; + KDPC WatchdogDpc; + + /* We must be held under a IRP lock when setting up watchdogs */ + POP_ASSERT_IRP_LOCK_THREAD_OWNERSHIP(); + + /* Setup the watchdog DPC and timer */ + KeInitializeTimerEx(&Timer, NotificationTimer); + KeInitializeDpc(&WatchdogDpc, PopIrpWatchdogDpcRoutine, IrpData); + + /* Fill in watchdog data to the IRP data buffer */ + IrpData->WatchdogTimer = Timer; + IrpData->WatchdogDpc = WatchdogDpc; +} + +static +VOID +PopEnableIrpWatchdog( + _In_ PPOP_IRP_DATA IrpData) +{ + LARGE_INTEGER TickInterval; + + /* We must be held under a IRP lock when enabling watchdogs */ + POP_ASSERT_IRP_LOCK_THREAD_OWNERSHIP(); + + /* Set the tick interval for the DPC routine to fire up in 1 second */ + TickInterval.QuadPart = Int32x32To64(PopIrpWatchdogTickIntervalInSeconds, + -10 * 1000 * 1000); + + /* Now enable the watchdog and fire up the timer */ + IrpData->WatchdogEnabled = TRUE; + KeSetTimerEx(&IrpData->WatchdogTimer, + TickInterval, + 1000, // Recurring interval in 1 second (1000 ms = 1 s) + &IrpData->WatchdogDpc); +} + +static +VOID +PopCancelIrpWatchdog( + _In_ PPOP_IRP_DATA IrpData) +{ + /* We must be held under a IRP lock when cancelling watchdogs */ + POP_ASSERT_IRP_LOCK_THREAD_OWNERSHIP(); + + /* Cancel the watchdog now */ + IrpData->WatchdogEnabled = FALSE; + KeCancelTimer(&IrpData->WatchdogTimer); +} + +static +VOID +PopQueuePowerIrp( + _In_ PPOP_IRP_DATA IrpData) +{ + PIRP Irp; + POWER_STATE_TYPE StateType; + SYSTEM_POWER_STATE SystemState; + DEVICE_POWER_STATE DeviceState; + BOOLEAN HasInrushDevice; + PIO_STACK_LOCATION Stack; + KLOCK_QUEUE_HANDLE IrpLockHandle; + PEXTENDED_DEVOBJ_EXTENSION DevObjExts, PdoDevObjExts; + PDEVICE_OBJECT TopTargetDevice, PolicyDeviceOwner, LowestDevice; + + /* Cache the associated DOs and IRP stack for later use */ + TopTargetDevice = IrpData->TargetDevice; + PolicyDeviceOwner = IrpData->Pdo; + Irp = IrpData->Irp; + Stack = IoGetCurrentIrpStackLocation(Irp); + + /* + * Precaution check, make sure the caller did not pass a bogus power state. + * Passing PowerSystemUnspecified or PowerDeviceUnspecified only makes things + * confusing for power management. + */ + if (Stack->Parameters.Power.SystemContext == POP_SYS_CONTEXT_DEVICE_PWR_REQUEST) + { + DeviceState = Stack->Parameters.Power.State.DeviceState; + ASSERT(DeviceState != PowerDeviceUnspecified); + } + else // POP_SYS_CONTEXT_WAKE_REQUEST + { + SystemState = Stack->Parameters.WaitWake.PowerState; + ASSERT(SystemState != PowerSystemUnspecified); + } + + /* + * Go the faster path, if this is a wake request then dispatch the IRP + * to the target top device immediately. We do not enable watchdog here + * because a wake request does not change the power state of a device + * or system. + */ + if (IrpData->MinorFunction == IRP_MN_WAIT_WAKE) + { + IoCallDriver(TopTargetDevice, Irp); + return; + } + + /* This is a Query/Set minor function IRP, we gotta handle the lock */ + PopAcquireIrpLock(&IrpLockHandle); + + /* + * Loop within the device stack and look for devices that are inrush. + * No matter if there are more than one inrush devices because at least + * one is required to consider the whole device stack as inrush. + */ + HasInrushDevice = FALSE; + DevObjExts = IoGetDevObjExtension(TopTargetDevice); + PdoDevObjExts = IoGetDevObjExtension(PolicyDeviceOwner); + while (DevObjExts->AttachedTo) + { + LowestDevice = DevObjExts->AttachedTo; + DevObjExts = IoGetDevObjExtension(LowestDevice); + if (LowestDevice->Flags & DO_POWER_INRUSH) + { + /* + * This device is inrush so we have to mark the entire device + * stack as inrush. We assign the inrush flag to the policy + * device owner as it is responsible for the device stack chain. + */ + HasInrushDevice = TRUE; + PdoDevObjExts->PowerFlags |= POP_DOE_HAS_INRUSH_DEVICE; + break; + } + } + + /* Setup the IRP watchdog but do not enable it yet */ + PopSetupIrpWatchdog(IrpData); + + /* + * RULE A -- Handling power inrush IRPs. + * + * This device wants a surge amount of electricity to power itself up. + * For that we must assert ourselves the following conditions are met + * for this to occur. + * + * - The caller explicitly requests a change in power with IRP_MN_SET_POWER; + * - The caller did submit the correct D-state value to power up the device; + * - The device is not in D0 power state already. + * + * Powering up inrush devices is a serialized process. Only the Power Manager + * can handle an inrush IRP one at a time. + */ + StateType = IrpData->PowerStateType; + if (HasInrushDevice && + IrpData->MinorFunction == IRP_MN_SET_POWER && + DeviceState == PowerDeviceD0 && + PopGetDoePowerState(DevObjExts, FALSE) != PowerDeviceD0) + { + if (PopInrushIrp == NULL) + { + /* + * We have a perfect candidate of an inrush IRP, let the Power Manager + * handle it to the device stack now. + */ + PopSelectInrushIrp(Irp); + goto DispatchNow; + } + else + { + /* + * There is already an inrush IRP of which the Power Manager is currently + * handling for a device. Either it is this device that inquires another + * inrush IRP or it is another device, either way, enqueue this IRP. + */ + PopAddInrushIrpToQueue(IrpData); + PdoDevObjExts->PowerFlags |= POP_DOE_PENDING_PROCESS; + PopReleaseIrpLock(&IrpLockHandle); + return; + } + } + + /* + * RULE B - Handling normal Query/Set power IRPs. + * + * The process of handling these power IRPs is done on a per-request serialization + * basis, unlike inrush devices, the Power Manager can power up multiple devices + * in parallel. Of course a device can only submit one power request at a time so + * if it still has to complete a power IRP and it inquires another power IRP, the + * newly request IRP will be put in queue. + */ + if (PopIsDoProcessingPowerIrp(PolicyDeviceOwner)) + { + PopAddIrpToQueue(IrpData); + PdoDevObjExts->PowerFlags |= POP_DOE_PENDING_PROCESS; + PopReleaseIrpLock(&IrpLockHandle); + return; + } + + /* Time to dispatch this power IRP finally */ +DispatchNow: + PopSetDoeActiveFlags(StateType, PdoDevObjExts); + PopEnableIrpWatchdog(IrpData); + PopReleaseIrpLock(&IrpLockHandle); + IoCallDriver(TopTargetDevice, Irp); +} + +static +VOID +PopDequeuePowerIrp( + _In_ PPOP_IRP_DATA IrpData) +{ + PIRP Irp; + POWER_STATE_TYPE StateType; + KLOCK_QUEUE_HANDLE IrpLockHandle; + PEXTENDED_DEVOBJ_EXTENSION PdoDevObjExts; + BOOLEAN IrpFound = FALSE; + PDEVICE_OBJECT PolicyDeviceOwner; + PPOP_IRP_DATA DequeuedIrpData, DequeuedInrushIrpData, IrpDataToDispatch = NULL; + + /* Cache the IRP and the associated policy DO */ + Irp = IrpData->Irp; + PolicyDeviceOwner = IrpData->Pdo; + + /* This was a wake request, return immediately */ + if (IrpData->MinorFunction == IRP_MN_WAIT_WAKE) + { + return; + } + + /* This is a Query/Set power IRP, acquire the lock now */ + PopAcquireIrpLock(&IrpLockHandle); + + /* Cache the policy DO extensions and power state type */ + PdoDevObjExts = IoGetDevObjExtension(PolicyDeviceOwner); + StateType = IrpData->PowerStateType; + + /* This IRP is in our hands now, disable the watchdog and clear active flags */ + PopCancelIrpWatchdog(IrpData); + PopClearDoeActiveFlags(StateType, PdoDevObjExts); + + /* + * Check if this IRP is an inrush power IRP that the Power Manager was + * currently processing it. We have to look for any queued inrush IRPs + * this device has inquired. Deploy any other inrush IRPs of other devices + * so that they can get a chance for their request to be satisfied. + */ + if (PopInrushIrp == Irp) + { + /* + * RULE A - Dismiss the currently completed inrush IRP and check if this + * device inquired more than one inrush power request. + */ + PopReleaseInrushIrp(Irp); + DequeuedInrushIrpData = PopRemoveInrushPowerIrpFromQueue(PolicyDeviceOwner, TRUE); + if (!DequeuedInrushIrpData) + { + /* + * RULE B - This device no longer has any queued inrush IRPs to be completed. + * It is worth noting this device might no longer be an inrush one so we would + * have to look for any queued normal IRPs this device is still processing but + * we must first give chance to other inrush devices to complete their IRPs. + */ + PdoDevObjExts->PowerFlags &= ~POP_DOE_HAS_INRUSH_DEVICE; + DequeuedInrushIrpData = PopRemoveInrushPowerIrpFromQueue(NULL, FALSE); + if (DequeuedInrushIrpData) + { + /* + * We have an inrush IRP that awaits processing (RULE B is in effect). We must gather + * new data from this IRP as previous device object no longer applies because it does + * not own this IRP. + */ + IrpFound = TRUE; + IrpDataToDispatch = DequeuedInrushIrpData; + PopSelectInrushIrp(DequeuedInrushIrpData->Irp); + StateType = DequeuedInrushIrpData->PowerStateType; + + /* + * And as this policy device owner does not own this IRP but another policy owner owns + * the IRP, we must get the policy DO extensions from that PDO. + */ + PdoDevObjExts = IoGetDevObjExtension(DequeuedInrushIrpData->Pdo); + } + } + else + { + /* This device still has inrush IRPs to complete (RULE A is in effect), select it for processing */ + IrpFound = TRUE; + IrpDataToDispatch = DequeuedInrushIrpData; + PopSelectInrushIrp(DequeuedInrushIrpData->Irp); + } + } + + + /* + * RULE C - If we ever get up to this point there could be that the current + * device was inrush before and we have exhausted all the inrush IRPs of + * all devices (see RULE B). Or it is just a normal device that may have + * inquired multiple power requests. + */ + if (!IrpFound) + { + DequeuedIrpData = PopRemovePowerIrpFromQueue(PolicyDeviceOwner, StateType); + if (!DequeuedIrpData) + { + /* This device does not have any queued IRPs, take away the pending flag */ + PdoDevObjExts->PowerFlags &= ~POP_DOE_PENDING_PROCESS; + PopReleaseIrpLock(&IrpLockHandle); + return; + } + + /* This device still has power IRPs to complete (RULE C is in effect) */ + IrpDataToDispatch = DequeuedIrpData; + } + + /* Deploy the dequeued IRP to the device stack */ + ASSERT(IrpDataToDispatch != NULL); + PopSetDoeActiveFlags(StateType, PdoDevObjExts); + PopEnableIrpWatchdog(IrpDataToDispatch); + IoCallDriver(IrpDataToDispatch->TargetDevice, IrpDataToDispatch->Irp); +} + +static +VOID +PopFreePowerIrp( + _In_ PPOP_IRP_DATA IrpData) +{ + PIRP Irp; + PDEVICE_OBJECT DeviceObject; + KLOCK_QUEUE_HANDLE IrpLockHandle; + + /* Delist the following IRP power data from IRP data list */ + PopAcquireIrpLock(&IrpLockHandle); + RemoveEntryList(&IrpData->Link); + PopReleaseIrpLock(&IrpLockHandle); + + /* Cache the referenced top device object before we are going to free the IRP */ + DeviceObject = IrpData->TargetDevice; + + /* Mark the location of this IRP as completed and free it */ + Irp = IrpData->Irp; + IoSkipCurrentIrpStackLocation(Irp); + PopMarkIrpCurrentLocationComplete(Irp); + IoFreeIrp(Irp); + + /* + * Take one reference away. Note that this device might have more than one + * reference depending on how many power requests the policy device owner + * has issued. + */ + ObDereferenceObject(DeviceObject); + + /* And finally free the IRP power data */ + PopFreePool(IrpData, TAG_PO_IRP_DATA); +} + +static +NTSTATUS +PopAllocatePowerIrp( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ BOOLEAN IsFxDevice, + _In_ UCHAR MinorFunction, + _In_ POWER_STATE PowerState, + _Out_ PPOP_IRP_DATA *IrpData) +{ + PIRP Irp; + PPOP_IRP_DATA LocalIrpData; + PDEVICE_OBJECT TopDeviceObject; + + /* We must not be above the permitted IRQL */ + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* Get the top device object of the device stack and allocate the power IRP */ + TopDeviceObject = IoGetAttachedDeviceReference(DeviceObject); + Irp = IoAllocateIrp(TopDeviceObject->StackSize + 2, FALSE); + if (!Irp) + { + DPRINT1("Failed to allocate memory for the power IRP\n"); + ObDereferenceObject(TopDeviceObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Allocate memory for the power IRP data */ + LocalIrpData = PopAllocatePool(sizeof(*LocalIrpData), + FALSE, + TAG_PO_IRP_DATA); + if (LocalIrpData == NULL) + { + DPRINT1("Failed to allocate memory for the power IRP data\n"); + IoFreeIrp(Irp); + ObDereferenceObject(TopDeviceObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Is this IRP belonging to a framework device? */ + if (IsFxDevice) + { + /* We do not support PoFx yet */ + ASSERT(IsFxDevice == FALSE); + } + + /* Initialize the basic data */ + InitializeListHead(&LocalIrpData->Link); + LocalIrpData->Irp = Irp; + LocalIrpData->Pdo = DeviceObject; + LocalIrpData->TargetDevice = TopDeviceObject; + LocalIrpData->CurrentDevice = TopDeviceObject; + LocalIrpData->WatchdogStart = POP_IRP_WATCHDOG_DUETIME; + LocalIrpData->PowerState = PowerState; + LocalIrpData->MinorFunction = MinorFunction; + LocalIrpData->WatchdogEnabled = FALSE; + LocalIrpData->FxDevice = NULL; + + /* Initialize device and system related IRP data */ + LocalIrpData->Device.CallerCompletion = NULL; + LocalIrpData->Device.CallerContext = NULL; + LocalIrpData->Device.CallerDevice = DeviceObject; + LocalIrpData->Device.SystemWake = FALSE; + + LocalIrpData->System.NotifyDevice = NULL; + LocalIrpData->System.FxDeviceActivated = FALSE; + + /* Insert it into the list and give it to caller */ + ExInterlockedInsertTailList(&PopIrpDataList, + &LocalIrpData->Link, + &PopIrpLock); + *IrpData = LocalIrpData; + return STATUS_SUCCESS; +} + +static +NTSTATUS +NTAPI +PopRequestPowerIrpCompletion( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Context) +{ + PPOP_IRP_DATA IrpData; + KLOCK_QUEUE_HANDLE IrpLockHandle; + PREQUEST_POWER_COMPLETE CompletionRoutine; + + /* We must not be above the permitted IRQL */ + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* + * We do not care about the completion context and DO passed by this + * routine as we already have the IRP power data that has them. + */ + UNREFERENCED_PARAMETER(DeviceObject); + UNREFERENCED_PARAMETER(Context); + + /* Get the IRP power data, make sure this is not NULL */ + PopAcquireIrpLock(&IrpLockHandle); + IrpData = PopFindIrpData(Irp, NULL, SearchByIrp); + ASSERT(IrpData); + PopReleaseIrpLock(&IrpLockHandle); + + /* Dispatch the function completion with the desired power state to the device */ + CompletionRoutine = IrpData->Device.CallerCompletion; + if (CompletionRoutine) + { + CompletionRoutine(IrpData->Device.CallerDevice, + IrpData->MinorFunction, + IrpData->PowerState, + IrpData->Device.CallerContext, + &Irp->IoStatus); + } + + /* + * Dequeue any pending power IRPs that this device has requested or + * from any other device that awaits their power request to be satisfied. + */ + PopDequeuePowerIrp(IrpData); + PopFreePowerIrp(IrpData); + return STATUS_MORE_PROCESSING_REQUIRED; +} + +/* PUBLIC FUNCTIONS **********************************************************/ + +PPOP_IRP_DATA +NTAPI +PopFindIrpData( + _In_opt_ PIRP Irp, + _In_opt_ PDEVICE_OBJECT DeviceObject, + _In_ POP_SEARCH_BY SearchBy) +{ + PLIST_ENTRY Entry; + PPOP_IRP_DATA IrpData = NULL; + + /* We must not be above the permitted IRQL */ + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* We must be held under a IRP lock when looking for IRPs data */ + POP_ASSERT_IRP_LOCK_THREAD_OWNERSHIP(); + + /* Iterate over the IRP data list and look for the appropriate IRP data */ + for (Entry = PopIrpDataList.Flink; + Entry != &PopIrpDataList; + Entry = Entry->Flink) + { + IrpData = CONTAINING_RECORD(Entry, POP_IRP_DATA, Link); + if (SearchBy == SearchByIrp) + { + ASSERT(Irp); + if (IrpData->Irp == Irp) + { + break; + } + } + else // SearchByDevice + { + ASSERT(DeviceObject); + if (IrpData->Pdo == DeviceObject) + { + break; + } + } + } + + return IrpData; +} + +BOOLEAN +NTAPI +PopHasDoOutstandingIrp( + _In_ PDEVICE_OBJECT DeviceObject) +{ + /* Simply call the private helper */ + return PopIsDoProcessingPowerIrp(DeviceObject); +} + +NTSTATUS +NTAPI +PopRequestPowerIrp( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ UCHAR MinorFunction, + _In_ POWER_STATE PowerState, + _In_ BOOLEAN IsFxDevice, + _In_ BOOLEAN NotifyPEP, + _In_opt_ PREQUEST_POWER_COMPLETE CompletionFunction, + _In_opt_ __drv_aliasesMem PVOID Context, + _Outptr_opt_ PIRP *Irp) +{ + NTSTATUS Status; + PIRP PwrIrp; + PPOP_IRP_DATA IrpData; + PIO_STACK_LOCATION Stack; + + /* We must not be above the permitted IRQL */ + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* Bail out on unrelated minor functions */ + if (MinorFunction != IRP_MN_QUERY_POWER + && MinorFunction != IRP_MN_SET_POWER + && MinorFunction != IRP_MN_WAIT_WAKE) + { + DPRINT1("Invalid power minor function (%u)\n", MinorFunction); + return STATUS_INVALID_PARAMETER_2; + } + + /* Initialize power IRP data for this power request */ + Status = PopAllocatePowerIrp(DeviceObject, + IsFxDevice, + MinorFunction, + PowerState, + &IrpData); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to process the power request for DO (0x%p), IRP data allocation failed\n", DeviceObject); + return Status; + } + + /* + * Assign the notify PEP toggle as this IRP travels within + * the device stack a PEP gets notified at IRP completion. + */ + IrpData->NotifyPEP = NotifyPEP; + + /* + * Usually power requests are inquired by bus drivers, the system power + * state is not transitioned, the Power Manager is responsible to do that. + */ + IrpData->SystemTransition = FALSE; + + /* Fill in power request caller context data */ + IrpData->Device.CallerCompletion = CompletionFunction; + IrpData->Device.CallerContext = Context; + IrpData->Device.CallerDevice = DeviceObject; + + /* + * Set an initial status information for the newly created IRP. + * It is just so that the driver does not see bogus data upon + * receiving the IRP. + */ + PwrIrp = IrpData->Irp; + PwrIrp->IoStatus.Status = STATUS_NOT_SUPPORTED; + PwrIrp->IoStatus.Information = 0; + IoSetNextIrpStackLocation(PwrIrp); + + /* Fill in power data to the IRP stack */ + Stack = IoGetNextIrpStackLocation(PwrIrp); + Stack->Parameters.Others.Argument1 = DeviceObject; + Stack->Parameters.Others.Argument2 = (PVOID)(ULONG_PTR)MinorFunction; + Stack->Parameters.Others.Argument3 = (PVOID)(ULONG_PTR)PowerState.DeviceState; + Stack->Parameters.Others.Argument4 = Context; + Stack->DeviceObject = IrpData->TargetDevice; + IoSetNextIrpStackLocation(PwrIrp); + + /* + * Check what kind of minor function this is and fill specific power + * data depending on that function. + */ + Stack = IoGetNextIrpStackLocation(PwrIrp); + Stack->MajorFunction = IRP_MJ_POWER; + Stack->MinorFunction = MinorFunction; + if (MinorFunction == IRP_MN_WAIT_WAKE) + { + /* + * You would think that we will set POP_SYS_CONTEXT_SYSTEM_IRP or + * POP_SYS_CONTEXT_DEVICE_PWR_REQUEST here but consider the following: + * this is a wake request, whether it is for the system or a device. + * Setting the wake context here would come in handy, if a bus driver + * wants to set this IRP as responsible for waking the system, we can + * set the "system wake" toggle within IRP data accordingly. + */ + Stack->Parameters.WaitWake.PowerState = PowerState.SystemState; + Stack->Parameters.Power.SystemContext = POP_SYS_CONTEXT_WAKE_REQUEST; + } + else + { + /* + * This is a query/set power request by the device. For the shutdown type + * we fill in the appropriate power action depending on the current system + * state and current power request operation. + */ + IrpData->PowerStateType = DevicePowerState; + Stack->Parameters.Power.Type = DevicePowerState; + Stack->Parameters.Power.State = PowerState; + Stack->Parameters.Power.SystemContext = POP_SYS_CONTEXT_DEVICE_PWR_REQUEST; + Stack->Parameters.Power.ShutdownType = PopTranslateDriverIrpPowerAction(&PopAction, FALSE); + + /* Fill in system power state context for the IRP */ + Stack->Parameters.Power.SystemPowerStateContext = PopBuildSystemPowerStateContext(DevicePowerState); + } + + /* Give the power IRP to the caller if asked */ + if (Irp != NULL) + { + *Irp = PwrIrp; + } + + /* And setup the dedicated power IRP completion routine */ + IoSetCompletionRoutine(PwrIrp, + PopRequestPowerIrpCompletion, + CompletionFunction, + TRUE, + TRUE, + TRUE); + + /* + * Now enqueue this power request IRP for later processing. The function + * will dispatch the IRP immediately if it is a wake request or if it was + * not processing any IRPs before. + */ + PopQueuePowerIrp(IrpData); + return STATUS_PENDING; +} + +NTSTATUS +NTAPI +PopCreateIrpWorkerThread( + _In_ PKSTART_ROUTINE WorkerRoutine) +{ + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE ThreadHandle; + KPRIORITY Priority = POP_WORKER_THREAD_PRIORITY; + + PAGED_CODE(); + + /* Setup the object attributes for the worker thread */ + InitializeObjectAttributes(&ObjectAttributes, + NULL, + OBJ_KERNEL_HANDLE, + NULL, + NULL); + + /* Give birth to this worker thread */ + Status = PsCreateSystemThread(&ThreadHandle, + THREAD_ALL_ACCESS, + &ObjectAttributes, + NULL, + NULL, + WorkerRoutine, + NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to create an IRP power worker thread (Status 0x%lx)", Status); + return Status; + } + + /* + * The worker thread is alive and sound. We have to re-adjust the thread + * base priority because we are processing power IRPs so we do not want + * to get delayed that much. + */ + Status = ZwSetInformationThread(ThreadHandle, + ThreadBasePriority, + &Priority, + sizeof(KPRIORITY)); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to set the thread base priority of IRP power worker thread (Status 0x%lx)\n", Status); + return Status; + } + + /* The thread servicing the worker routine is already up, close the handle */ + ZwClose(ThreadHandle); + return STATUS_SUCCESS; +} + +NTSTATUS +FASTCALL +PoHandlePowerIrp( + _In_ PIRP Irp) +{ + PPOP_IRP_DATA IrpData; + PIO_STACK_LOCATION IrpStack; + PDEVICE_OBJECT DeviceObject; + KLOCK_QUEUE_HANDLE IrpLockHandle; + + /* We must not be above the permitted IRQL */ + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* Make sure this is a Power IRP as we expected */ + IrpStack = IoGetCurrentIrpStackLocation(Irp); + POP_ASSERT_IRP_IS_POWER(IrpStack); + + /* + * The specific driver does not participate in power management, + * or at least it does not want to. Consider this IRP of this + * device as done, because there is nothing the Power Manager can do. + * + * !!!NOTE!!! -- DO_POWER_NOOP existed in DDK in the past but it got later + * removed. During that time, there is always a naughty driver who used it + * and still uses it. The general rule is that a driver must always participate + * in power management, even if the driver does not do much in terms of power + * handling. + * + * -- https://www-user.tu-chemnitz.de/~heha/oney_wdm/ch08d.htm + * -- https://www.digiater.nl/openvms/decus/vmslt01b/nt/wdmerr.htm + * -- Programming the Windows Drivel Model book by Walter Oney + */ + DeviceObject = IrpStack->DeviceObject; + if (DeviceObject->Flags & DO_POWER_NOOP) + { + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; + } + + /* Update the current device as it currently holds hands on this IRP */ + PopAcquireIrpLock(&IrpLockHandle); + IrpData = PopFindIrpData(Irp, NULL, SearchByIrp); + ASSERT(IrpData != NULL); + IrpData->CurrentDevice = DeviceObject; + PopReleaseIrpLock(&IrpLockHandle); + + /* Check if we should send this IRP at PASSIVE_LEVEL */ + if (PopShouldForwardIrpAtPassive(Irp)) + { + /* + * Blocking (synchronous) request. The driver is pageable and we must + * dispatch the IRP at PASSIVE_LEVEL as ordered. Only if we're at this + * IRQL that is. + */ + if (KeGetCurrentIrql() == PASSIVE_LEVEL) + { + return PopDispatchPowerIrp(Irp, DeviceObject, IrpStack); + } + } + + /* + * What we have gotten here is either a non-blocking request (we can forward + * the IRP to the device in the stack at DISPATCH_LEVEL as per our own liking) + * or the driver is pageable but we are not at the passive interrupt level. + * Insert the IRP so that an IRP worker thread will handle the request asynchronously. + */ + PopAcquireIrpLock(&IrpLockHandle); + PopInsertIrpForDispatch(Irp, IrpStack); + PopReleaseIrpLock(&IrpLockHandle); + return STATUS_PENDING; +} + +/* EOF */ diff --git a/ntoskrnl/po/misc.c b/ntoskrnl/po/misc.c new file mode 100644 index 0000000000000..806a4c237f90a --- /dev/null +++ b/ntoskrnl/po/misc.c @@ -0,0 +1,246 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power Manager miscellaneous utility routines + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +UNICODE_STRING PopPowerRegPath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Power"); +UNICODE_STRING RegAcPolicy = RTL_CONSTANT_STRING(L"AcPolicy"); +UNICODE_STRING RegDcPolicy = RTL_CONSTANT_STRING(L"DcPolicy"); + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID +NTAPI +PopCreatePowerPolicyDatabase(VOID) +{ + NTSTATUS Status; + HANDLE KeyHandle; + OBJECT_ATTRIBUTES PowerKeyAttrs; + + /* Initialize the object attributes for the power key database */ + InitializeObjectAttributes(&PowerKeyAttrs, + &PopPowerRegPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + /* Create the power registry database */ + Status = ZwCreateKey(&KeyHandle, + KEY_READ | KEY_WRITE, + &PowerKeyAttrs, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + NULL); + + /* + * We cannot simply bait an eye on failing to set up the power registry + * database. The power policy settings would never be saved in this case. + */ + ASSERT(NT_SUCCESS(Status)); + ZwClose(KeyHandle); +} + +NTSTATUS +NTAPI +PopReadPowerSettings( + _In_ PUNICODE_STRING PowerValue, + _In_ ULONG ValueType, + _Out_ PKEY_VALUE_PARTIAL_INFORMATION *ReturnedData) +{ + NTSTATUS Status; + OBJECT_ATTRIBUTES PowerKeyAttrs; + ULONG ReturnedLength; + PKEY_VALUE_PARTIAL_INFORMATION BufferKey = NULL; + HANDLE PowerKey = NULL; + + PAGED_CODE(); + + /* Initialize the object attributes for the power key database */ + InitializeObjectAttributes(&PowerKeyAttrs, + &PopPowerRegPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + /* Open the power settings key */ + Status = ZwOpenKey(&PowerKey, + KEY_QUERY_VALUE, + &PowerKeyAttrs); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to open %wZ (Status 0x%lx)\n", PopPowerRegPath, Status); + return Status; + } + + /* + * Let the Configuration Manager figure out how much space is needed + * to allocate a buffer for our needs. + */ + Status = ZwQueryValueKey(PowerKey, + PowerValue, + KeyValuePartialInformation, + NULL, + 0, + &ReturnedLength); + if (Status != STATUS_BUFFER_TOO_SMALL) + { + /* Got entirely something else, this is super bad */ + DPRINT1("Expected STATUS_BUFFER_TOO_SMALL but got 0x%lx. Punt...\n", Status); + ZwClose(PowerKey); + return Status; + } + + /* Allocate chunks of memory based on the space length we got for the buffer */ + BufferKey = PopAllocatePool(ReturnedLength, TRUE, TAG_PO_REGISTRY); + if (BufferKey == NULL) + { + /* Not enough memory, bail out */ + DPRINT1("Failed to allocate memory for the key buffer!\n"); + ZwClose(PowerKey); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Query the actual value now */ + Status = ZwQueryValueKey(PowerKey, + PowerValue, + KeyValuePartialInformation, + BufferKey, + ReturnedLength, + &ReturnedLength); + + /* We no longer need the power key */ + ZwClose(PowerKey); + + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to query data from %wZ value (Status 0x%lx)\n", PowerValue, Status); + PopFreePool(BufferKey, TAG_PO_REGISTRY); + return Status; + } + + /* Is this the value type the caller requested? */ + if (BufferKey->Type != ValueType) + { + DPRINT1("The caller requested an invalid value (requested value %lu, got %lu)\n", ValueType, BufferKey->Type); + PopFreePool(BufferKey, TAG_PO_REGISTRY); + return STATUS_INVALID_PARAMETER; + } + + /* Return the queried information to the caller */ + *ReturnedData = BufferKey; + return Status; +} + +PVOID +NTAPI +PopAllocatePool( + _In_ SIZE_T PoolSize, + _In_ BOOLEAN Paged, + _In_ ULONG Tag) +{ + PVOID Buffer; + BOOLEAN UseDefaultTag = FALSE; + + /* Avoid zero pool allocations */ + ASSERT(PoolSize != 0); + + /* Use the default tag if none was provided */ + if (Tag == 0) + { + UseDefaultTag = TRUE; + } + + Buffer = ExAllocatePoolZero(Paged ? PagedPool : NonPagedPool, + PoolSize, + UseDefaultTag ? TAG_PO : Tag); + if (Buffer == NULL) + { + return NULL; + } + + return Buffer; +} + +VOID +NTAPI +PopFreePool( + _In_ _Post_invalid_ PVOID PoolBuffer, + _In_ ULONG Tag) +{ + ASSERT(PoolBuffer != NULL); + ExFreePoolWithTag(PoolBuffer, Tag); +} + +VOID +NTAPI +PoRundownDeviceObject( + _In_ PDEVICE_OBJECT DeviceObject) +{ + /* This device object is being freed, does it still process power IRPs? */ + if (PopHasDoOutstandingIrp(DeviceObject)) + { + /* + * This device still processes a power IRP and has not even + * finished on doing so. Any power IRPs that this device owns + * are going to be orphaned. This is bad because we will not be able + * to pass such IRPs to the responsible device driver, thereby ending + * up with "phantom" power requests. Kill the system... + */ + KeBugCheckEx(DRIVER_POWER_STATE_FAILURE, + 0x1, + (ULONG_PTR)DeviceObject, + 0, + 0); + } + + /* Cancel any idle detection for this device */ + PoRegisterDeviceForIdleDetection(DeviceObject, 0, 0, PowerDeviceUnspecified); + + /* Remove the power volumes of this device */ + /* FIXME: To be enabled once Mm supports pageable sections */ +#if 0 + MmLockPageableSectionByHandle(ExPageLockHandle); +#endif + PopRemoveVolumeDevice(DeviceObject); +#if 0 + MmUnlockPageableImageSection(ExPageLockHandle); +#endif +} + +ULONG +NTAPI +PopQueryActiveProcessors(VOID) +{ + KAFFINITY ProcessorAffinity; + ULONG ProcessorsCount; + + /* Query the active processors and count them based on the set mask bits */ + ProcessorsCount = 0; + ProcessorAffinity = KeQueryActiveProcessors(); + while (ProcessorAffinity) + { + /* This bit is set so we have a processor, count it */ + if (ProcessorAffinity & 1) + { + ProcessorsCount++; + } + + /* Go to the next bit */ + ProcessorAffinity >>= 1; + } + + return ProcessorsCount; +} + +/* EOF */ diff --git a/ntoskrnl/po/notif.c b/ntoskrnl/po/notif.c new file mode 100644 index 0000000000000..81b5c53c6d5da --- /dev/null +++ b/ntoskrnl/po/notif.c @@ -0,0 +1,64 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power notifications routines + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +ERESOURCE PopNotifyDeviceLock; + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* PUBLIC FUNCTIONS ***********************************************************/ + +NTSTATUS +NTAPI +PoCancelDeviceNotify( + _In_ PVOID NotifyBlock) +{ + /* FIXME */ + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +NTAPI +PoRegisterDeviceNotify( + _Out_ PVOID Unknown0, + _In_ ULONG Unknown1, + _In_ ULONG Unknown2, + _In_ ULONG Unknown3, + _In_ PVOID Unknown4, + _In_ PVOID Unknown5) +{ + /* FIXME */ + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + +VOID +NTAPI +PoNotifySystemTimeSet(VOID) +{ + KIRQL OldIrql; + + /* Notify the system time set callback only if Win32k registered one */ + if (PopEventCallout) + { + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); + ExNotifyCallback(SetSystemTimeCallback, NULL, NULL); + PopRequestPolicyWorker(PolicyWorkerTimeChange); + PopCheckForPendingWorkers(); + KeLowerIrql(OldIrql); + } +} + +/* EOF */ diff --git a/ntoskrnl/po/ntapi.c b/ntoskrnl/po/ntapi.c new file mode 100644 index 0000000000000..2cf95a9f6da18 --- /dev/null +++ b/ntoskrnl/po/ntapi.c @@ -0,0 +1,891 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power Manager NT API system calls + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +WORK_QUEUE_ITEM PopUnlockMemoryWorkItem; +KEVENT PopUnlockMemoryCompleteEvent; + +/* PRIVATE FUNCTIONS **********************************************************/ + +_Use_decl_annotations_ +VOID +NTAPI +PopUnlockMemoryWorker( + _In_ PVOID Parameter) +{ + UNIMPLEMENTED; + return; +} + +static +BOOLEAN +PopIsCallerPrivileged( + _In_ POWER_INFORMATION_LEVEL Level, + _In_ PVOID InputBuffer, + _In_ KPROCESSOR_MODE PreviousMode) +{ + LUID Privilege; + + PAGED_CODE(); + + /* If the caller was coming from the kernel, he has absolute privileges */ + if (PreviousMode == KernelMode) + { + return TRUE; + } + + /* + * This is coming from UM of which we cannot trust it, and we might have an + * input buffer. Ensure that the caller has the appropriate privilege for + * whatever operation he wants to do. + */ + if (InputBuffer) + { + Privilege = (Level == SystemReserveHiberFile) ? SeCreatePagefilePrivilege : SeShutdownPrivilege; + if (!SeSinglePrivilegeCheck(Privilege, PreviousMode)) + { + return FALSE; + } + } + + /* + * This is a query operation (understood by InputBuffer being NULL) or + * the caller has the required privilege, allow him access. + */ + return TRUE; +} + +/* INFORMATION CLASSES ********************************************************/ + +static const INFORMATION_CLASS_INFO PoPowerInformationClass[] = +{ + /* SystemPowerPolicyAc */ + IQS_NONE, + + /* SystemPowerPolicyDc */ + IQS_NONE, + + /* VerifySystemPolicyAc */ + IQS_NONE, + + /* VerifySystemPolicyDc */ + IQS_NONE, + + /* SystemPowerCapabilities */ + IQS_SAME(SYSTEM_POWER_CAPABILITIES, ULONG, ICIF_QUERY), + + /* SystemBatteryState */ + IQS_SAME(SYSTEM_BATTERY_STATE, ULONG, ICIF_QUERY), + + /* SystemPowerStateHandler */ + IQS_NONE, + + /* ProcessorStateHandler */ + IQS_NONE, + + /* SystemPowerPolicyCurrent */ + IQS_NONE, + + /* AdministratorPowerPolicy */ + IQS_NONE, + + /* SystemReserveHiberFile */ + IQS_NONE, + + /* ProcessorInformation */ + IQS_SAME(PROCESSOR_POWER_INFORMATION, ULONG, ICIF_QUERY), + + /* SystemPowerInformation */ + IQS_NONE, + + /* ProcessorStateHandler2 */ + IQS_NONE, + + /* LastWakeTime */ + IQS_NONE, + + /* LastSleepTime */ + IQS_NONE, + + /* SystemExecutionState */ + IQS_NONE, + + /* SystemPowerStateNotifyHandler */ + IQS_NONE, + + /* ProcessorPowerPolicyAc */ + IQS_NONE, + + /* ProcessorPowerPolicyDc */ + IQS_NONE, + + /* VerifyProcessorPowerPolicyAc */ + IQS_NONE, + + /* VerifyProcessorPowerPolicyDc */ + IQS_NONE, + + /* ProcessorPowerPolicyCurrent */ + IQS_NONE, + + /* SystemPowerStateLogging */ + IQS_NONE, + + /* SystemPowerLoggingEntry */ + IQS_NONE, + + /* SetPowerSettingValue */ + IQS_NONE, + + /* NotifyUserPowerSetting */ + IQS_NONE, + + /* PowerInformationLevelUnused0 */ + IQS_NONE, // Not used + + /* SystemMonitorHiberBootPowerOff */ + IQS_NONE, + + /* SystemVideoState */ + IQS_NONE, + + /* TraceApplicationPowerMessage */ + IQS_NONE, + + /* TraceApplicationPowerMessageEnd */ + IQS_NONE, + + /* ProcessorPerfStates */ + IQS_NONE, + + /* ProcessorIdleStates */ + IQS_NONE, + + /* ProcessorCap */ + IQS_NONE, + + /* SystemWakeSource */ + IQS_NONE, + + /* SystemHiberFileInformation */ + IQS_NONE, + + /* TraceServicePowerMessage */ + IQS_NONE, + + /* ProcessorLoad */ + IQS_NONE, + + /* PowerShutdownNotification */ + IQS_NONE, + + /* MonitorCapabilities */ + IQS_NONE, + + /* SessionPowerInit */ + IQS_NONE, + + /* SessionDisplayState */ + IQS_NONE, + + /* PowerRequestCreate */ + IQS_NONE, + + /* PowerRequestAction */ + IQS_NONE, + + /* GetPowerRequestList */ + IQS_NONE, + + /* ProcessorInformationEx */ + IQS_NONE, + + /* NotifyUserModeLegacyPowerEvent */ + IQS_NONE, + + /* GroupPark */ + IQS_NONE, + + /* ProcessorIdleDomains */ + IQS_NONE, + + /* WakeTimerList */ + IQS_NONE, + + /* SystemHiberFileSize */ + IQS_NONE, + + /* ProcessorIdleStatesHv */ + IQS_NONE, + + /* ProcessorPerfStatesHv */ + IQS_NONE, + + /* ProcessorPerfCapHv */ + IQS_NONE, + + /* ProcessorSetIdle */ + IQS_NONE, + + /* LogicalProcessorIdling */ + IQS_NONE, + + /* UserPresence */ + IQS_NONE, + + /* PowerSettingNotificationName */ + IQS_NONE, + + /* GetPowerSettingValue */ + IQS_NONE, + + /* IdleResiliency */ + IQS_NONE, + + /* SessionRITState */ + IQS_NONE, + + /* SessionConnectNotification */ + IQS_NONE, + + /* SessionPowerCleanup */ + IQS_NONE, + + /* SessionLockState */ + IQS_NONE, + + /* SystemHiberbootState */ + IQS_NONE, + + /* PlatformInformation */ + IQS_SAME(POWER_PLATFORM_INFORMATION, ULONG, ICIF_QUERY), + + /* PdcInvocation */ + IQS_NONE, + + /* MonitorInvocation */ + IQS_NONE, + + /* FirmwareTableInformationRegistered */ + IQS_NONE, + + /* SetShutdownSelectedTime */ + IQS_NONE, + + /* SuspendResumeInvocation */ + IQS_NONE, + + /* PlmPowerRequestCreate */ + IQS_NONE, + + /* ScreenOff */ + IQS_NONE, + + /* CsDeviceNotification */ + IQS_NONE, + + /* PlatformRole */ + IQS_NONE, + + /* LastResumePerformance */ + IQS_NONE, + + /* DisplayBurst */ + IQS_NONE, + + /* ExitLatencySamplingPercentage */ + IQS_NONE, + + /* RegisterSpmPowerSettings */ + IQS_NONE, + + /* PlatformIdleStates */ + IQS_NONE, + + /* ProcessorIdleVeto */ + IQS_NONE, // Deprecated + + /* PlatformIdleVeto */ + IQS_NONE, // Deprecated + + /* SystemBatteryStatePrecise */ + IQS_NONE, + + /* ThermalEvent */ + IQS_NONE, + + /* PowerRequestActionInternal */ + IQS_NONE, + + /* BatteryDeviceState */ + IQS_NONE, + + /* PowerInformationInternal */ + IQS_NONE, + + /* ThermalStandby */ + IQS_NONE, + + /* SystemHiberFileType */ + IQS_NONE, + + /* PhysicalPowerButtonPress */ + IQS_NONE, + + /* QueryPotentialDripsConstraint */ + IQS_NONE, + + /* EnergyTrackerCreate */ + IQS_NONE, + + /* EnergyTrackerQuery */ + IQS_NONE, + + /* UpdateBlackBoxRecorder */ + IQS_NONE, + + /* SessionAllowExternalDmaDevices */ + IQS_NONE, +}; + +/* SYSTEM CALLS ***************************************************************/ + +NTSTATUS +NTAPI +NtInitiatePowerAction( + _In_ POWER_ACTION SystemAction, + _In_ SYSTEM_POWER_STATE MinSystemState, + _In_ ULONG Flags, + _In_ BOOLEAN Asynchronous) +{ + /* FIXME */ + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +NTAPI +NtPowerInformation( + _In_ POWER_INFORMATION_LEVEL PowerInformationLevel, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferLength) +{ + NTSTATUS Status; + KPROCESSOR_MODE PreviousMode; + SYSTEM_BATTERY_STATE LocalBatteryState = {0}; + PSYSTEM_BATTERY_STATE BatteryState; + POWER_PLATFORM_INFORMATION LocalPlatformInfo; + PPOWER_PLATFORM_INFORMATION PlatformInfo; + PSYSTEM_POWER_CAPABILITIES PowerCapabilities; + PVOID LocalBuffer = NULL; + + PAGED_CODE(); + +#if DBG + DPRINT("NtPowerInformation(PowerInformationLevel %S, InputBuffer 0x%p, " + "InputBufferLength 0x%x, OutputBuffer 0x%p, OutputBufferLength 0x%x)\n", + PopGetPowerInformationLevelName(PowerInformationLevel), + InputBuffer, InputBufferLength, + OutputBuffer, OutputBufferLength); +#endif + + /* Bail out if the caller is doing something special and has no required privilege */ + PreviousMode = ExGetPreviousMode(); + if (!PopIsCallerPrivileged(PowerInformationLevel, InputBuffer, PreviousMode)) + { + DPRINT1("The caller has no required privilege for this operation, bail out\n"); + return STATUS_PRIVILEGE_NOT_HELD; + } + + /* Probe the parameters depending on whether the caller queries or sets something */ + if (InputBuffer) + { + /* It wants to set new power data, make sure the length was provided */ + ASSERT(InputBufferLength > 0); + + /* And probe the actual buffer data and class validity */ + Status = DefaultSetInfoBufferCheck(PowerInformationLevel, + PoPowerInformationClass, + RTL_NUMBER_OF(PoPowerInformationClass), + InputBuffer, + InputBufferLength, + PreviousMode); + if (NT_SUCCESS(Status)) + { + if (PreviousMode == UserMode) + { + /* + * We are not done here. We must allocate a local buffer to hold the + * input data so that we are safe from anybody who frees InputBuffer. + */ + LocalBuffer = PopAllocatePool(InputBufferLength, + TRUE, + TAG_PO_INPUT_INFO_CLASS_BUFFER); + if (LocalBuffer == NULL) + { + DPRINT1("Buffer allocation for input data failed\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + /* Copy the power input data now */ + RtlCopyMemory(LocalBuffer, InputBuffer, InputBufferLength); + } + } + else + { + /* + * The caller is coming from the kernel. We do not need to + * allocate any buffer because we trust the kernel. + */ + LocalBuffer = InputBuffer; + } + } + } + else + { + /* The caller wants to query something from the Power Manager */ + ASSERT(OutputBufferLength > 0); + + /* Probe the parameter and class validity */ + Status = DefaultQueryInfoBufferCheck(PowerInformationLevel, + PoPowerInformationClass, + RTL_NUMBER_OF(PoPowerInformationClass), + ICIF_PROBE_READ_WRITE, + OutputBuffer, + OutputBufferLength, + NULL, + NULL, + PreviousMode); + } + + /* Probing failed, bail out */ + if (!NT_SUCCESS(Status)) + { +#if DBG + DPRINT1("Information verification class failed (Status -> 0x%lx, PowerInformationLevel -> %S)\n", + Status, PopGetPowerInformationLevelName(PowerInformationLevel)); +#endif + return Status; + } + + /* Do the Query/Set power operations with the policy lock held */ + PopAcquirePowerPolicyLock(); + + switch (PowerInformationLevel) + { + case SystemPowerCapabilities: + { + PowerCapabilities = (PSYSTEM_POWER_CAPABILITIES)OutputBuffer; + + /* The caller provided an input buffer on a Query class, bail out */ + if (InputBuffer) + { + DPRINT1("InputBuffer provided on SystemPowerCapabilities class when it should not be\n"); + Status = STATUS_INVALID_PARAMETER; + goto Quit; + } + + /* This is not the right output buffer length, bail out */ + if (OutputBufferLength < sizeof(SYSTEM_POWER_CAPABILITIES)) + { + DPRINT1("OutputBufferLength too small (length %lu)\n", OutputBufferLength); + Status = STATUS_BUFFER_TOO_SMALL; + goto Quit; + } + + /* FIXME: We should filter the capabilities if the system has legacy stuff */ + _SEH2_TRY + { + RtlCopyMemory(PowerCapabilities, + &PopCapabilities, + sizeof(SYSTEM_POWER_CAPABILITIES)); + + Status = STATUS_SUCCESS; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + + break; + } + + case SystemBatteryState: + { + BatteryState = (PSYSTEM_BATTERY_STATE)OutputBuffer; + + /* The caller provided an input buffer on a Query class, bail out */ + if (InputBuffer) + { + DPRINT1("InputBuffer provided on SystemBatteryState class when it should not be\n"); + Status = STATUS_INVALID_PARAMETER; + goto Quit; + } + + /* This is not the right output buffer length, bail out */ + if (OutputBufferLength < sizeof(SYSTEM_BATTERY_STATE)) + { + DPRINT1("OutputBufferLength too small (length %lu)\n", OutputBufferLength); + Status = STATUS_BUFFER_TOO_SMALL; + goto Quit; + } + + /* Query the current state of the composite battery */ + PopQueryBatteryState(&LocalBatteryState); + + /* Copy the current state of the composite battery to the caller */ + _SEH2_TRY + { + RtlCopyMemory(BatteryState, + &LocalBatteryState, + sizeof(LocalBatteryState)); + + Status = STATUS_SUCCESS; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + + break; + } + + case PlatformInformation: + { + PlatformInfo = (PPOWER_PLATFORM_INFORMATION)OutputBuffer; + + /* The caller provided an input buffer on a Query class, bail out */ + if (InputBuffer) + { + DPRINT1("InputBuffer provided on PlatformInformation class when it should not be\n"); + Status = STATUS_INVALID_PARAMETER; + goto Quit; + } + + /* This is not the right output buffer length, bail out */ + if (OutputBufferLength < sizeof(POWER_PLATFORM_INFORMATION)) + { + DPRINT1("OutputBufferLength too small (length %lu)\n", OutputBufferLength); + Status = STATUS_BUFFER_TOO_SMALL; + goto Quit; + } + + /* Determine the AoAc capability from the global Power Manager variable */ + LocalPlatformInfo.AoAc = PopAoAcPresent; + + _SEH2_TRY + { + RtlCopyMemory(PlatformInfo, + &LocalPlatformInfo, + sizeof(POWER_PLATFORM_INFORMATION)); + + Status = STATUS_SUCCESS; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + + break; + } + + default: + { +#if DBG + DPRINT1("%S information class is UNIMPLEMENTED\n", PopGetPowerInformationLevelName(PowerInformationLevel)); +#endif + Status = STATUS_NOT_IMPLEMENTED; + break; + } + } + +Quit: + PopReleasePowerPolicyLock(); + + if (LocalBuffer && PreviousMode == UserMode) + { + PopFreePool(LocalBuffer, TAG_PO_INPUT_INFO_CLASS_BUFFER); + } + + return Status; +} + +NTSTATUS +NTAPI +NtGetDevicePowerState( + _In_ HANDLE Device, + _Out_ PDEVICE_POWER_STATE PowerState) +{ + NTSTATUS Status; + PFILE_OBJECT FileObject; + KPROCESSOR_MODE PreviousMode; + PDEVICE_OBJECT DeviceObject; + DEVICE_POWER_STATE DeviceState; + PEXTENDED_DEVOBJ_EXTENSION DevObjExts; + + PAGED_CODE(); + + /* The caller must supply a variable to the output argument */ + ASSERT(PowerState); + + /* Ensure that we can return the device power state to the caller */ + PreviousMode = ExGetPreviousMode(); + if (PreviousMode != KernelMode) + { + _SEH2_TRY + { + ProbeForWriteUlong(PowerState); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + /* Return the exception code */ + _SEH2_YIELD(return _SEH2_GetExceptionCode()); + } + _SEH2_END; + } + + /* Reference the device so that we can get its device extensions */ + Status = ObReferenceObjectByHandle(Device, + 0, + IoFileObjectType, + PreviousMode, + (PVOID*)&FileObject, + NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to reference the device (Status 0x%lx)\n", Status); + return Status; + } + + /* + * Get the device object and put a reference on it so that it does not + * die in our arms. We no longer care about the file object. + */ + DeviceObject = IoGetRelatedDeviceObject(FileObject); + ObReferenceObject(DeviceObject); + ObDereferenceObject(FileObject); + + /* Get the extensions and retrieve the power state of this device */ + DevObjExts = IoGetDevObjExtension(DeviceObject); + DeviceState = PopGetDoePowerState(DevObjExts, FALSE); + + /* Return the current power state to the caller safely */ + _SEH2_TRY + { + *PowerState = DeviceState; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + + ObDereferenceObject(DeviceObject); + return Status; +} + +BOOLEAN +NTAPI +NtIsSystemResumeAutomatic(VOID) +{ + /* FIXME */ + UNIMPLEMENTED; + return FALSE; +} + +NTSTATUS +NTAPI +NtRequestWakeupLatency( + _In_ LATENCY_TIME Latency) +{ + /* On Windows Vista and later versions of Windows, this function is a NOP */ + UNREFERENCED_PARAMETER(Latency); + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +NtSetThreadExecutionState( + _In_ EXECUTION_STATE esFlags, + _Out_ EXECUTION_STATE *PreviousFlags) +{ + /* FIXME */ + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +NTAPI +NtSetSystemPowerState( + _In_ POWER_ACTION SystemAction, + _In_ SYSTEM_POWER_STATE MinSystemState, + _In_ ULONG Flags) +{ + NTSTATUS Status; + ULONG FreedPagesCount; + KPROCESSOR_MODE PreviousMode; + POP_POWER_ACTION Action; + + /* Look for any invalid argument and bail out */ + 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("Invalid parameters found:\n"); + DPRINT1(" SystemAction: 0x%x\n", SystemAction); + DPRINT1(" MinSystemState: 0x%x\n", MinSystemState); + DPRINT1(" Flags: 0x%x\n", Flags); + return STATUS_INVALID_PARAMETER; + } + + /* Is this called from user mode? */ + PreviousMode = ExGetPreviousMode(); + if (PreviousMode != KernelMode) + { + /* Make sure that the caller has the required shutdown privilege */ + if (!SeSinglePrivilegeCheck(SeShutdownPrivilege, PreviousMode)) + { + DPRINT1("Privilege not held for setting a system power state\n"); + return STATUS_PRIVILEGE_NOT_HELD; + } + + /* Turn this execution into kernel as this is an invasive operation */ + return ZwSetSystemPowerState(SystemAction, MinSystemState, Flags); + } + + /* Disable lazy registry flushing */ + CmSetLazyFlushState(FALSE); + + /* Setup the power action */ + RtlZeroMemory(&Action, sizeof(Action)); + Action.Action = SystemAction; + Action.Flags = Flags; + + /* Notify any registered callbacks of an impeding system state change */ + ExNotifyCallback(PowerStateCallback, (PVOID)PO_CB_SYSTEM_STATE_LOCK, NULL); + + /* Do not allow swaping worker threads at this operation */ + ExSwapinWorkerThreads(FALSE); + + /* Lock the entire power policy manager and make our action global */ + PopAcquirePowerPolicyLock(); + PopAction = Action; + + /* Process action requests */ + Status = STATUS_CANCELLED; + while (TRUE) + { + /* No power action was inquired */ + if (Action.Action == PowerActionNone) + { + break; + } + + /* Check if this action is of shutdown type */ + if (Status == STATUS_CANCELLED) + { + if ((PopAction.Action == PowerActionShutdown) || + (PopAction.Action == PowerActionShutdownReset) || + (PopAction.Action == PowerActionShutdownOff)) + { + PopAction.Shutdown = TRUE; + } + + Status = STATUS_SUCCESS; + } + + /* Stop processin action requests if we are at an invalid status */ + if (!NT_SUCCESS(Status)) + { + break; + } + + /* Flush all volumes and the registry */ + PopFlushVolumes(PopAction.Shutdown); + + /* Flush dirty cache pages */ + CcRosFlushDirtyPages(MAXULONG, &FreedPagesCount, FALSE, FALSE); + + /* Now execute the graceful shutdown */ + PopAction.IrpMinor = IRP_MN_SET_POWER; + if (PopAction.Shutdown) + { + /* If we are not running in the system context then queue the shutdown worker */ + if (PsGetCurrentProcess() != PsInitialSystemProcess) + { + ExInitializeWorkItem(&PopShutdownWorkItem, + &PopGracefulShutdown, + NULL); + ExQueueWorkItem(&PopShutdownWorkItem, CriticalWorkQueue); + + KeSuspendThread(KeGetCurrentThread()); + Status = STATUS_SYSTEM_SHUTDOWN; + goto Exit; + } + else + { + /* We are running within the system context, invoke shutdown directly */ + PopGracefulShutdown(NULL); + } + } + + /* There is A LOOOOOOOT OF STUFF TO IMPLEMENT HERE, consider it a stub at the moment */ + DPRINT1("System is still up and running, you may not have chosen a yet supported power option: %u\n", PopAction.Action); + break; + } + +Exit: + /* Release the policy manager lock and enable lazy registry flushing */ + PopReleasePowerPolicyLock(); + CmSetLazyFlushState(TRUE); + return Status; +} + +NTSTATUS +NTAPI +NtRequestDeviceWakeup( + _In_ HANDLE DeviceHandle) +{ + /* The following function is not implemented in Windows */ + UNIMPLEMENTED; + UNREFERENCED_PARAMETER(DeviceHandle); + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +NTAPI +NtCancelDeviceWakeupRequest( + _In_ HANDLE DeviceHandle) +{ + /* The following function is not implemented in Windows */ + UNIMPLEMENTED; + UNREFERENCED_PARAMETER(DeviceHandle); + return STATUS_NOT_IMPLEMENTED; +} + +/* EOF */ diff --git a/ntoskrnl/po/poapi.c b/ntoskrnl/po/poapi.c new file mode 100644 index 0000000000000..86324d880c0a2 --- /dev/null +++ b/ntoskrnl/po/poapi.c @@ -0,0 +1,612 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power Manager public API routines + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS **********************************************************/ + +static +BOOLEAN +PopIsDeviceRegisteredForIdleDetection( + _In_ PDEVICE_OBJECT_POWER_EXTENSION TargetDope, + _Out_opt_ PLIST_ENTRY *ReturnedDopeEntry) +{ + PLIST_ENTRY Entry; + PDEVICE_OBJECT_POWER_EXTENSION Dope; + + PAGED_CODE(); + + /* Passing a NULL DOPE is illegal */ + ASSERT(TargetDope); + + /* Iterate over the list of idle detect registered devices */ + for (Entry = PopIdleDetectList.Flink; + Entry != &PopIdleDetectList; + Entry = Entry->Flink) + { + /* Is this the DOPE we are looking for? */ + Dope = CONTAINING_RECORD(Entry, DEVICE_OBJECT_POWER_EXTENSION, IdleList); + if (Dope == TargetDope) + { + /* This is it, return the DOPE entry to the caller if asked */ + if (ReturnedDopeEntry != NULL) + { + *ReturnedDopeEntry = Entry; + } + + return TRUE; + } + } + + return FALSE; +} + +static +BOOLEAN +PopIsCallerRunningAtRightIrql( + _In_ POWER_STATE_TYPE Type, + _In_ POWER_STATE State) +{ + /* + * First rule, we must be above the operational IRQL that PoSetPowerState + * allows for execution. Second rule, if the device slightly powers up + * from sleeping (it is not D0) or it powers down, the IRQL must not + * be above APC_LEVEL. + */ + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + if (Type == DevicePowerState && + State.DeviceState != PowerDeviceD0) + { + if (KeGetCurrentIrql() > APC_LEVEL) + { + return FALSE; + } + } + + return TRUE; +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +NTSTATUS +NTAPI +PoCallDriver( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ __drv_aliasesMem PIRP Irp) +{ + PIO_STACK_LOCATION IrpStack; + + /* We must not be above the permitted IRQL */ + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* The passed IRP must be a Power IRP */ + IrpStack = IoGetNextIrpStackLocation(Irp); + POP_ASSERT_IRP_IS_POWER(IrpStack); + + /* Forward that IRP to the device */ + return IoCallDriver(DeviceObject, Irp); +} + +NTSTATUS +NTAPI +PoRequestPowerIrp( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ UCHAR MinorFunction, + _In_ POWER_STATE PowerState, + _In_opt_ PREQUEST_POWER_COMPLETE CompletionFunction, + _In_opt_ __drv_aliasesMem PVOID Context, + _Outptr_opt_ PIRP *Irp) +{ + /* Invoke the private helper to do the deed */ + return PopRequestPowerIrp(DeviceObject, + MinorFunction, + PowerState, + FALSE, + FALSE, + CompletionFunction, + Context, + Irp); +} + +VOID +NTAPI +PoStartNextPowerIrp( + _Inout_ PIRP Irp) +{ + UNREFERENCED_PARAMETER(Irp); + NOTHING; +} + +PVOID +NTAPI +PoRegisterSystemState( + _Inout_opt_ PVOID StateHandle, + _In_ EXECUTION_STATE Flags) +{ + /* FIXME */ + UNIMPLEMENTED; + return NULL; +} + +VOID +NTAPI +PoUnregisterSystemState( + _Inout_ PVOID StateHandle) +{ + /* FIXME */ + UNIMPLEMENTED; + NOTHING; +} + +VOID +NTAPI +PoSetSystemState( + _In_ EXECUTION_STATE Flags) +{ + /* We must not be above the permitted IRQL */ + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* The caller did not supply any of the execution state flags, bail out */ + if (Flags & ~(ES_SYSTEM_REQUIRED | + ES_DISPLAY_REQUIRED | + ES_USER_PRESENT)) + { + DPRINT1("Invalid flag mask was supplied (Flag %lu)\n", Flags); + ASSERT(FALSE); + return; + } + + /* + * Also make sure the caller does not play us for absolute fools by + * supplying ES_CONTINUOUS to a function of which the system state + * cannot be persisted as per the documentation. + */ + if (Flags & ES_CONTINUOUS) + { + DPRINT1("ES_CONTINUOUS was set when it must not be\n"); + ASSERT(FALSE); + return; + } + + /* Notify the Power Manager of the current busy state of the system */ + PopIndicateSystemStateActivity(Flags); +} + +PULONG +NTAPI +PoRegisterDeviceForIdleDetection( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ ULONG ConservationIdleTime, + _In_ ULONG PerformanceIdleTime, + _In_ DEVICE_POWER_STATE State) +{ + KIRQL OldIrql; + PLIST_ENTRY DopeEntry; + PEXTENDED_DEVOBJ_EXTENSION DeviceExtension; + PDEVICE_OBJECT_POWER_EXTENSION Dope; + BOOLEAN IsDeviceDisk = FALSE; + + PAGED_CODE(); + + /* The caller has to pass a DO here */ + ASSERT(DeviceObject); + + /* The caller wants to cancel the idle detection for this device */ + if (!ConservationIdleTime && !PerformanceIdleTime) + { + /* Grab the DOPE from the device and remove it from the idle detect list */ + PopAcquireDopeLock(&OldIrql); + DeviceExtension = IoGetDevObjExtension(DeviceObject); + Dope = DeviceExtension->Dope; + if (Dope) + { + /* Remove the DOPE entry only if the device was registered to begin with */ + if (PopIsDeviceRegisteredForIdleDetection(Dope, &DopeEntry)) + { + /* + * This device is about to disable its idle detection but make sure + * no active busy references are currently in force. Supposedly this + * is the case, the caller has forgot to end its busy periods with + * PoEndDeviceBusy for each PoStartDeviceBusy call it has instantiated. + */ + ASSERT(Dope->BusyReference == 0); + + /* Unlink it from the global idle detect list */ + RemoveEntryList(DopeEntry); + + /* Scrub the idle and busy counters */ + Dope->IdleCount = 0; + Dope->BusyCount = 0; + Dope->TotalBusyCount = 0; + + /* Reset the idle time values */ + Dope->ConservationIdleTime = 0; + Dope->PerformanceIdleTime = 0; + + /* Reset the power state values of this DOPE */ + Dope->IdleState = PowerDeviceUnspecified; + Dope->CurrentState = PowerDeviceUnspecified; + + /* And finally, reset the idle list link */ + InitializeListHead(&Dope->IdleList); + } + } + + PopReleaseDopeLock(OldIrql); + return NULL; + } + + /* + * The naughty caller thinks that passing PowerDeviceUnspecified as power + * state to be requested when either of the two idle timers have fired is + * correct. It will just make things worse as the supplied power state will + * be passed from the power manager to the device driver to set a new power + * state, of an unspecified type. This will put the device into a limbo + * situation as nobody will know if the device is turned ON or whatever. + */ + if (State == PowerDeviceUnspecified) + { + DPRINT1("WARNING: The DO (0x%p) attempted to set state PowerDeviceUnspecified\n", DeviceObject); + ASSERT(State != PowerDeviceUnspecified); + return NULL; + } + + /* + * The caller wants to register default idle time values that the power + * manager it provides. This is supported only for devices that is a disk + * device or a mass storage device. Ignore the request for unsupported devices. + */ + if (ConservationIdleTime == -1 && PerformanceIdleTime == -1) + { + if (!(DeviceObject->DeviceType & FILE_DEVICE_DISK) && + !(DeviceObject->DeviceType & FILE_DEVICE_MASS_STORAGE)) + { + DPRINT("Default idle times requested for an unsupported device type (%lu) of DO (0x%p)\n", DeviceObject->DeviceType, DeviceObject); + return NULL; + } + + /* This is a disk/mass storage, we will mark it as such in the DOPE later */ + IsDeviceDisk = TRUE; + } + + /* Get the DOPE of this device */ + Dope = PopGetDope(DeviceObject); + if (!Dope) + { + DPRINT1("Failed to get DOPE or allocate one for DO (0x%p), bailing out\n", DeviceObject); + return NULL; + } + + + /* Lock the global DOPE database as this device will get its DOPE modified */ + PopAcquireDopeLock(&OldIrql); + + /* Fill in idle detection datum */ + Dope->IdleCount = 0; + Dope->ConservationIdleTime = ConservationIdleTime; + Dope->PerformanceIdleTime = PerformanceIdleTime; + Dope->IdleState = State; + Dope->IdleType = (IsDeviceDisk == TRUE) ? DeviceIdleDisk : DeviceIdleNormal; + + /* + * Check that if the device has already registered for idle detection + * and it just wanted to adjust the idle time and state values. Insert it + * to the registered devices for idle detection if this is for the first time. + */ + if (!PopIsDeviceRegisteredForIdleDetection(Dope, NULL)) + { + /* + * At the time of registering for idle detection, this device was in fully + * operational mode (this has to be in this state in order to do that). + */ + Dope->CurrentState = PowerDeviceD0; + InsertTailList(&PopIdleDetectList, &Dope->IdleList); + } + + /* + * As this device has registered with the power manager for idle time + * detection, once the idle times have fired up, the device will incur + * in a power state change as per the request of whom actually wanted + * this device to be registered. Deploy any pending policy workers that + * have been left behind. + */ + PopReleaseDopeLock(OldIrql); + PopCheckForPendingWorkers(); + + return (PULONG)&Dope->IdleCount; +} + +POWER_STATE +NTAPI +PoSetPowerState( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ POWER_STATE_TYPE Type, + _In_ POWER_STATE State) +{ + POWER_STATE PrevState; + KLOCK_QUEUE_HANDLE IrpLockHandle; + PEXTENDED_DEVOBJ_EXTENSION DeviceExtension; + + /* + * Unlike other conventional PoXxx functions, this one can allow execution + * not above DISPATCH_LEVEL if a device decides to power down or slightly up. + * Assert this condition. + */ + ASSERT(PopIsCallerRunningAtRightIrql(Type, State)); + + /* + * Acquire the IRP lock here, so we avoid unexpected power transitions. + * Especially if this device happens to power down or up out of a sudden + * while we are here (though unlikely), so we can set a new power state safely. + */ + PopAcquireIrpLock(&IrpLockHandle); + DeviceExtension = IoGetDevObjExtension(DeviceObject); + + /* Apply new power state depending on type and if it is necessary */ + if (Type == SystemPowerState) + { + PrevState.SystemState = PopGetDoePowerState(DeviceExtension, TRUE); + if (PrevState.SystemState != State.SystemState) + { + /* The system is transitioning into a new state, apply it */ +#if DBG + DPRINT1("System is transitioning to a new power state %s -> %s (DO 0x%p)\n", + PopTranslateSystemPowerStateToString(PrevState.SystemState), PopTranslateSystemPowerStateToString(State.SystemState), DeviceObject); +#endif + PopSetDoePowerState(DeviceExtension, State, TRUE); + } + + } + else // DevicePowerState + { + PrevState.DeviceState = PopGetDoePowerState(DeviceExtension, FALSE); + if (PrevState.DeviceState != State.DeviceState) + { + /* This device is transitioning into a new state, apply it */ +#if DBG + DPRINT1("Device is transitioning to a new power state %s -> %s (DO 0x%p)\n", + PopTranslateDevicePowerStateToString(PrevState.DeviceState), PopTranslateDevicePowerStateToString(State.DeviceState), DeviceObject); +#endif + PopSetDoePowerState(DeviceExtension, State, FALSE); + } + } + + PopReleaseIrpLock(&IrpLockHandle); + return PrevState; +} + +NTSTATUS +NTAPI +PoRegisterPowerSettingCallback( + _In_opt_ PDEVICE_OBJECT DeviceObject, + _In_ LPCGUID SettingGuid, + _In_ PPOWER_SETTING_CALLBACK Callback, + _In_opt_ PVOID Context, + _Outptr_opt_ PVOID *Handle) +{ + /* FIXME */ + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +NTAPI +PoUnregisterPowerSettingCallback( + _Inout_ PVOID Handle) +{ + /* FIXME */ + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + +VOID +NTAPI +PoSetSystemWake( + _Inout_ PIRP Irp) +{ + PPOP_IRP_DATA IrpData; + PIO_STACK_LOCATION IrpSp; + KLOCK_QUEUE_HANDLE IrpLockHandle; + + /* We must not be above the permitted IRQL */ + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* This has to be a wake power IRP, otherwise the IRP we got is bogus */ + IrpSp = IoGetCurrentIrpStackLocation(Irp); + POP_ASSERT_IRP_IS_WAKE(IrpSp); + + /* + * The IRP of the device that contributed to the waking of the system is + * this one, so set it as "woke IRP". + */ + PopAcquireIrpLock(&IrpLockHandle); + IrpData = PopFindIrpData(Irp, NULL, SearchByIrp); + ASSERT(IrpData != NULL); + IrpData->Device.SystemWake = TRUE; + PopReleaseIrpLock(&IrpLockHandle); +} + +BOOLEAN +NTAPI +PoGetSystemWake( + _In_ PIRP Irp) +{ + PPOP_IRP_DATA IrpData; + PIO_STACK_LOCATION IrpSp; + KLOCK_QUEUE_HANDLE IrpLockHandle; + + /* We must not be above the permitted IRQL */ + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* This has to be a wake power IRP, otherwise the IRP we got is bogus */ + IrpSp = IoGetCurrentIrpStackLocation(Irp); + POP_ASSERT_IRP_IS_WAKE(IrpSp); + + /* Grab the IRP power data based on the given IRP */ + PopAcquireIrpLock(&IrpLockHandle); + IrpData = PopFindIrpData(Irp, NULL, SearchByIrp); + PopReleaseIrpLock(&IrpLockHandle); + + /* Does this IRP contributed to the waking of the system? */ + if (IrpData->Device.SystemWake) + { + /* That's the guy */ + return TRUE; + } + + return FALSE; +} + +VOID +NTAPI +PoSetDeviceBusyEx( + _Inout_ PULONG IdlePointer) +{ + /* Bail out on NULL idle pointers */ + ASSERT(IdlePointer); + + /* This device is about to get busy so increment the busy count by one */ + InterlockedIncrement((volatile LONG *)&IdlePointer + POP_BUSY_COUNT_OFFSET); +} + +VOID +NTAPI +PoStartDeviceBusy( + _Inout_ PULONG IdlePointer) +{ + /* Bail out on NULL idle pointers */ + ASSERT(IdlePointer); + + /* This device is about to get busy so keep an active reference */ + InterlockedIncrement((volatile LONG *)&IdlePointer + POP_BUSY_REFERENCE_OFFSET); +} + +VOID +NTAPI +PoEndDeviceBusy( + _Inout_ PULONG IdlePointer) +{ + /* Bail out on NULL idle pointers */ + ASSERT(IdlePointer); + + /* This device is no longer busy, take a reference away */ + InterlockedDecrement((volatile LONG *)&IdlePointer + POP_BUSY_REFERENCE_OFFSET); +} + +BOOLEAN +NTAPI +PoQueryWatchdogTime( + _In_ PDEVICE_OBJECT Pdo, + _Out_ PULONG SecondsRemaining) +{ + PPOP_IRP_DATA IrpData; + KLOCK_QUEUE_HANDLE IrpLockHandle; + + /* We must not be above the permitted IRQL */ + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* Assume the watchdog of this power IRP is not enabled yet */ + *SecondsRemaining = 0; + + /* Look for the appropriate IRP power data that belongs to this PDO */ + PopAcquireIrpLock(&IrpLockHandle); + IrpData = PopFindIrpData(NULL, Pdo, SearchByDevice); + PopReleaseIrpLock(&IrpLockHandle); + + /* + * No IRP power data was found for this PDO, therefore no power IRPs + * were issued from this PDO. + */ + if (!IrpData) + { + return FALSE; + } + + /* A power IRP watchdog is currently in force, return the watchdog counter */ + if (IrpData->WatchdogEnabled == TRUE) + { + *SecondsRemaining = IrpData->WatchdogStart; + return TRUE; + } + + return FALSE; +} + +NTSTATUS +NTAPI +PoSetPowerRequest( + _Inout_ PVOID PowerRequest, + _In_ POWER_REQUEST_TYPE Type) +{ + /* FIXME */ + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + +VOID +NTAPI +PoDeletePowerRequest( + _Inout_ PVOID PowerRequest) +{ + /* FIXME */ + UNIMPLEMENTED; + NOTHING; +} + +NTSTATUS +NTAPI +PoCreatePowerRequest( + _Outptr_ PVOID *PowerRequest, + _In_ PDEVICE_OBJECT DeviceObject, + _In_opt_ PCOUNTED_REASON_CONTEXT Context) +{ + /* FIXME */ + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +NTAPI +PoClearPowerRequest( + _Inout_ PVOID PowerRequest, + _In_ POWER_REQUEST_TYPE Type) +{ + /* FIXME */ + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + +NTKERNELAPI +NTSTATUS +PoCreateThermalRequest( + _Outptr_ PVOID *ThermalRequest, + _In_ PDEVICE_OBJECT TargetDeviceObject, + _In_ PDEVICE_OBJECT PolicyDeviceObject, + _In_ PCOUNTED_REASON_CONTEXT Context, + _In_ ULONG Flags) +{ + /* FIXME */ + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + +_IRQL_requires_max_(APC_LEVEL) +NTKERNELAPI +VOID +PoDeleteThermalRequest( + _Inout_ PVOID ThermalRequest) +{ + /* FIXME */ + UNIMPLEMENTED; + NOTHING; +} + +/* EOF */ diff --git a/ntoskrnl/po/pocs.c b/ntoskrnl/po/pocs.c new file mode 100644 index 0000000000000..19a5670883ab8 --- /dev/null +++ b/ntoskrnl/po/pocs.c @@ -0,0 +1,481 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power Manager control switches (lid, power buttons, etc) mechanisms + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +LIST_ENTRY PopControlSwitches; + +/* COMPLETION ROUTINE *********************************************************/ + +static IO_COMPLETION_ROUTINE PopControlSwitchIrpCompletion; + +/* PRIVATE FUNCTIONS **********************************************************/ + +static +VOID +PopPerformButtonAction( + _In_ PPOP_CONTROL_SWITCH ControlSwitch, + _In_ ULONG Event) +{ + /* Performing a power action from an unknown switch type is a serious bug */ + ASSERT(ControlSwitch->SwitchType != SwitchNone); + + /* + * Do not be fooled by the event being 0 as if no actual event was being + * generated. When 0 is returned it means a wake button (or any other button + * such as a key from the keyboard) generated a system wake request. + * But this event could also indicate a lid was either closed or opened, + * because our dummy ACPI driver does not understand the event was indeed + * coming from a lid so we have to be cautious differentiating the event + * by ourselves. + */ + if (Event == 0) + { + if (ControlSwitch->SwitchType == SwitchLid) + { + /* + * Yes, this is an event generated by a lid indeed. Acknowledge the + * Power Manager as lid either being closed or opened. + */ + ControlSwitch->Switch.Lid.Opened = !ControlSwitch->Switch.Lid.Opened; + + /* FIXME: Do more lid related stuff (alert callbacks, lid power setting, etc.) */ + UNIMPLEMENTED; + } + else + { + /* A different button key (or just the power button) woke the system */ + /* FIXME: Do stuff here (reset all the switch triggers, alert the presence of a user to Win32k, etc) */ + UNIMPLEMENTED; + } + } + + /* The power button was pressed, handle it to appropriate switch */ + if (Event == SYS_BUTTON_POWER && + ControlSwitch->SwitchType == SwitchButtonPower) + { + /* FIXME: See the big NOTE below */ + ControlSwitch->Switch.Button.Triggered = TRUE; + ZwShutdownSystem(ShutdownNoReboot); + } + + /* + * NOTE to myself: The function's implementation needs to be completed once + * I am ready to write code for power actions support (in act.c) that is + * responsible to issue power actions due to requests by PO itself or outside + * requestors (e.g. from NtInitiatePowerAction). + */ +} + +static +VOID +PopDisableControlSwitchCaps( + _In_ POP_SWITCH_TYPE SwitchType) +{ + /* We should already know what is the capability of this switch */ + ASSERT(SwitchType != SwitchNone); + + /* Disable the capability based on the switch */ + switch (SwitchType) + { + case SwitchLid: + { + PopCapabilities.LidPresent = FALSE; + break; + } + + case SwitchButtonPower: + { + PopCapabilities.PowerButtonPresent = FALSE; + break; + } + + case SwitchButtonSleep: + { + PopCapabilities.SleepButtonPresent = FALSE; + break; + } + + /* We should not reach here */ + DEFAULT_UNREACHABLE; + } +} + +static +PLIST_ENTRY +PopGetControlSwitchEntryFromList( + _In_ PPOP_CONTROL_SWITCH TargetControlSwitch) +{ + PLIST_ENTRY Entry; + PPOP_CONTROL_SWITCH ControlSwitch; + + /* Passing a NULL control switch is illegal */ + ASSERT(TargetControlSwitch); + + /* Iterate over the control switches and look for what we want */ + for (Entry = PopControlSwitches.Flink; + Entry != &PopControlSwitches; + Entry = Entry->Flink) + { + ControlSwitch = CONTAINING_RECORD(Entry, POP_CONTROL_SWITCH, Link); + if (TargetControlSwitch == ControlSwitch) + { + return Entry; + } + } + + return NULL; +} + +static +VOID +PopControlSwitchCleanup( + _In_ PPOP_CONTROL_SWITCH ControlSwitch) +{ + PIRP Irp; + PLIST_ENTRY Entry; + PDEVICE_OBJECT DeviceObject; + + /* The policy lock must be owned as we delist this control switch */ + POP_ASSERT_POWER_POLICY_LOCK_OWNERSHIP(); + + /* + * Ensure the control is already in the global switches list, otherwise + * something is seriously wrong. Remove it from the list. + */ + Entry = PopGetControlSwitchEntryFromList(ControlSwitch); + NT_ASSERT(Entry != NULL); + RemoveEntryList(Entry); + + /* If this control switch was processing an IRP, free it */ + Irp = ControlSwitch->Irp; + if (Irp) + { + IoFreeIrp(Irp); + } + + /* Disable the power capability so that everyone knows the switch has gone away */ + PopDisableControlSwitchCaps(ControlSwitch->SwitchType); + + /* Dereference the device policy object */ + DeviceObject = ControlSwitch->DeviceObject; + ObDereferenceObject(DeviceObject); + + /* Free the control switch */ + PopFreePool(ControlSwitch, TAG_PO_CONTROL_SWITCH); +} + +static +NTSTATUS +NTAPI +PopControlSwitchIrpCompletion( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_ PVOID Context) +{ + NTSTATUS Status; + UCHAR Mode; + ULONG IrpData; + PPOP_DEVICE_POLICY_WORKITEM_DATA WorkItemData; + PPOP_CONTROL_SWITCH ControlSwitch = (PPOP_CONTROL_SWITCH)Context; + + /* We do not care about these as we already have them in the control switch */ + UNREFERENCED_PARAMETER(DeviceObject); + UNREFERENCED_PARAMETER(Irp); + + /* Check if our I/O request has succeeded */ + Status = ControlSwitch->Irp->IoStatus.Status; + if (!NT_SUCCESS(Status)) + { + /* It failed, invoke the CS handler to cleanup this switch */ + DPRINT1("The control switch I/O request has failed (Status 0x%08lx)\n", Status); + ControlSwitch->Flags |= POP_CS_CLEANUP; + goto InvokeHandler; + } + + /* Process this request depending on what we actually asked */ + Mode = ControlSwitch->Mode; + switch (Mode) + { + case POP_CS_QUERY_CAPS_MODE: + { + /* Query the capabilities of this switch and set the appropriate button type */ + IrpData = *(PULONG)Irp->AssociatedIrp.SystemBuffer; + DPRINT1("Captured control switch capabilities: 0x%x\n", IrpData); + if (IrpData & SYS_BUTTON_POWER) + { + DPRINT1("POWER button present\n"); + PopCapabilities.PowerButtonPresent = TRUE; + ControlSwitch->SwitchType = SwitchButtonPower; + } + + if (IrpData & SYS_BUTTON_SLEEP) + { + DPRINT1("SLEEP button present\n"); + PopCapabilities.SleepButtonPresent = TRUE; + ControlSwitch->SwitchType = SwitchButtonSleep; + } + + if (IrpData & SYS_BUTTON_LID) + { + DPRINT1("LID present\n"); + PopCapabilities.LidPresent = TRUE; + ControlSwitch->SwitchType = SwitchLid; + } + + break; + } + + case POP_CS_QUERY_EVENT_MODE: + { + /* Perform a power action, depending on the inquired button event */ + IrpData = *(PULONG)Irp->AssociatedIrp.SystemBuffer; + PopPerformButtonAction(ControlSwitch, IrpData); + break; + } + + default: // POP_CS_NO_MODE + { + /* We do not know any other modes, kill the system */ + KeBugCheckEx(INTERNAL_POWER_ERROR, + 0, + POP_INVALID_CONTROL_SWITCH_MODE, + (ULONG_PTR)Mode, + (ULONG_PTR)ControlSwitch); + } + } + + /* Free the current IRP so that the handler can issue a new one */ + IoFreeIrp(ControlSwitch->Irp); + ControlSwitch->Irp = NULL; + +InvokeHandler: + /* Allocate a policy workitem data so that we can invoke the CS handler */ + WorkItemData = PopAllocatePool(sizeof(POP_DEVICE_POLICY_WORKITEM_DATA), + FALSE, + TAG_PO_POLICY_DEVICE_WORKITEM_DATA); + NT_ASSERT(WorkItemData != NULL); + + WorkItemData->PolicyData = ControlSwitch; + WorkItemData->PolicyType = PolicyDeviceSystemButton; + ExInitializeWorkItem(&WorkItemData->WorkItem, + PopControlSwitchHandler, + WorkItemData); + + /* Enqueue the control switch back to the handler */ + ExQueueWorkItem(&WorkItemData->WorkItem, DelayedWorkQueue); + return STATUS_MORE_PROCESSING_REQUIRED; +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +PPOP_CONTROL_SWITCH +NTAPI +PopGetControlSwitchByDevice( + _In_ PDEVICE_OBJECT DeviceObject) +{ + PLIST_ENTRY Entry; + PPOP_CONTROL_SWITCH ControlSwitch; + + /* We must expect a DO here */ + ASSERT(DeviceObject); + + /* Iterate over the control switches and return the one we wanted */ + for (Entry = PopControlSwitches.Flink; + Entry != &PopControlSwitches; + Entry = Entry->Flink) + { + ControlSwitch = CONTAINING_RECORD(Entry, POP_CONTROL_SWITCH, Link); + if (ControlSwitch->DeviceObject == DeviceObject) + { + return ControlSwitch; + } + } + + return NULL; +} + +NTSTATUS +NTAPI +PopCreateControlSwitch( + _In_ PDEVICE_OBJECT DeviceObject, + _Out_ PPOP_CONTROL_SWITCH *ControlSwitch) +{ + PPOP_CONTROL_SWITCH LocalControlSwitch; + + PAGED_CODE(); + + /* Allocate memory for the control switch we are going to create */ + LocalControlSwitch = PopAllocatePool(sizeof(POP_CONTROL_SWITCH), + FALSE, + TAG_PO_CONTROL_SWITCH); + if (LocalControlSwitch == NULL) + { + DPRINT1("Failed to allocate pool of memory for the control switch\n"); + *ControlSwitch = NULL; + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* + * Fill in the necessary datum. Albeit the control switch is pretty much created + * at this point the control switch handler is responsible to initialize it with + * real datum through communication between the power manager and the associated + * device driver of which it handles the control switch. + */ + InitializeListHead(&LocalControlSwitch->Link); + LocalControlSwitch->Irp = NULL; + LocalControlSwitch->Flags |= POP_CS_INITIALIZING; + LocalControlSwitch->Mode = POP_CS_NO_MODE; + LocalControlSwitch->DeviceObject = DeviceObject; + LocalControlSwitch->SwitchType = SwitchNone; + + *ControlSwitch = LocalControlSwitch; + return STATUS_SUCCESS; +} + +VOID +NTAPI +PopSetButtonPowerAction( + _Inout_ PPOWER_ACTION_POLICY Button, + _In_ POWER_ACTION Action) +{ + PAGED_CODE(); + + /* Punish the caller for bogus power actions */ + ASSERT((Action >= PowerActionNone) && (Action <= PowerActionDisplayOff)); + + /* Setup the actions for this button */ + Button->Action = Action; + Button->Flags = 0; + Button->EventCode = 0; +} + +_Use_decl_annotations_ +VOID +NTAPI +PopControlSwitchHandler( + _In_ PVOID Parameter) +{ + ULONG IoControlCode; + PIRP Irp; + PIO_STACK_LOCATION IrpStack; + PDEVICE_OBJECT DeviceObject; + PPOP_CONTROL_SWITCH ControlSwitch; + PPOP_DEVICE_POLICY_WORKITEM_DATA WorkItemData = (PPOP_DEVICE_POLICY_WORKITEM_DATA)Parameter; + + PAGED_CODE(); + + /* We entered into a device policy handler, acquire the policy lock */ + PopAcquirePowerPolicyLock(); + + /* + * Ensure we got the right policy device as this handler only processes + * control switches and not anything else. + */ + ASSERT(WorkItemData->PolicyType == PolicyDeviceSystemButton); + ControlSwitch = WorkItemData->PolicyData; + + /* + * This control switch asked for cleanup. This could be caused by the ACPI + * driver disabling that switch or we got an unexpected error. Tear the + * switch apart. + */ + if (ControlSwitch->Flags & POP_CS_CLEANUP) + { + PopControlSwitchCleanup(ControlSwitch); + PopReleasePowerPolicyLock(); + PopFreePool(WorkItemData, TAG_PO_POLICY_DEVICE_WORKITEM_DATA); + return; + } + + /* + * Ensure this control switch does not have an outstanding IRP that still needs + * to be completed. The CS IRP completion handler is responsible to free it once + * it has got data from the ACPI driver of which the IRP is no longer needed. + */ + ASSERT(ControlSwitch->Irp == NULL); + + /* Allocate a new fresh IRP to satisfy the required request */ + DeviceObject = ControlSwitch->DeviceObject; + Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); + if (!Irp) + { + /* + * Failing this request on our end is fatal. Most likely the I/O manager + * tried so hard to allocate an IRP for us it failed on doing so, due to + * a serious low memory condition. Not something that we can do to salvage + * the system so kill it. + */ + KeBugCheckEx(INTERNAL_POWER_ERROR, + 0, + POP_DEVICE_POLICY_IRP_ALLOC_FAILED, + PolicyDeviceSystemButton, + (ULONG_PTR)DeviceObject); + } + + /* + * This is a newly fresh created control switch of which we do not know what are + * its true capabilities as of yet. Insert it into the global list of control switches + * and set a query capabilities command mode so that on IRP completion the Power Manager + * knows what data it looked for. + */ + if (ControlSwitch->Flags & POP_CS_INITIALIZING) + { + InsertTailList(&PopControlSwitches, &ControlSwitch->Link); + ControlSwitch->Mode = POP_CS_QUERY_CAPS_MODE; + IoControlCode = IOCTL_GET_SYS_BUTTON_CAPS; + ControlSwitch->Flags &= ~POP_CS_INITIALIZING; + goto DispatchIrp; + } + + /* + * We already know the capabilities of this control switch. Query a button event + * from this switch and wait on it until the event gets triggered. The IRP completion + * routine will handle it. + */ + ControlSwitch->Mode = POP_CS_QUERY_EVENT_MODE; + IoControlCode = IOCTL_GET_SYS_BUTTON_EVENT; + +DispatchIrp: + /* Setup the IRP stack parameters based on the requested operation */ + IrpStack = IoGetNextIrpStackLocation(Irp); + IrpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL; + IrpStack->Parameters.DeviceIoControl.IoControlCode = IoControlCode; + IrpStack->Parameters.DeviceIoControl.InputBufferLength = sizeof(ULONG); + IrpStack->Parameters.DeviceIoControl.OutputBufferLength = sizeof(ULONG); + + /* + * Register the IRP completion CS handler and give the IRP to the control switch, + * so that when the ACPI driver returns back our IRP the completion handler will + * further process the CS operation event. + */ + ControlSwitch->Irp = Irp; + IoSetCompletionRoutine(Irp, + PopControlSwitchIrpCompletion, + ControlSwitch, + TRUE, + TRUE, + FALSE); + + /* + * We are going to leave the device policy handler soon, release the lock + * before dispatching the IRP to the ACPI driver. + */ + PopReleasePowerPolicyLock(); + IoCallDriver(DeviceObject, Irp); + + /* Free the work item data as we no longer need it */ + PopFreePool(WorkItemData, TAG_PO_POLICY_DEVICE_WORKITEM_DATA); +} + +/* EOF */ diff --git a/ntoskrnl/po/pofx/comp.c b/ntoskrnl/po/pofx/comp.c new file mode 100644 index 0000000000000..e8c9393ba34d0 --- /dev/null +++ b/ntoskrnl/po/pofx/comp.c @@ -0,0 +1,16 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Component management routines + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* EOF */ diff --git a/ntoskrnl/po/pofx/device.c b/ntoskrnl/po/pofx/device.c new file mode 100644 index 0000000000000..43de82fb8191b --- /dev/null +++ b/ntoskrnl/po/pofx/device.c @@ -0,0 +1,16 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power device handling mechanism routines + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* EOF */ diff --git a/ntoskrnl/po/pofx/fxapi.c b/ntoskrnl/po/pofx/fxapi.c new file mode 100644 index 0000000000000..d2b34e9d6fe5d --- /dev/null +++ b/ntoskrnl/po/pofx/fxapi.c @@ -0,0 +1,16 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power Manager Framework API (PoFx) support routines + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* PUBLIC FUNCTIONS ***********************************************************/ + +/* EOF */ diff --git a/ntoskrnl/po/pofx/plugin.c b/ntoskrnl/po/pofx/plugin.c new file mode 100644 index 0000000000000..84359d8b159ee --- /dev/null +++ b/ntoskrnl/po/pofx/plugin.c @@ -0,0 +1,16 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Platform Extension Plug-in (PEP) routines support + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* EOF */ diff --git a/ntoskrnl/po/policy.c b/ntoskrnl/po/policy.c new file mode 100644 index 0000000000000..2354d69d78846 --- /dev/null +++ b/ntoskrnl/po/policy.c @@ -0,0 +1,784 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power Policy Manager & Policy Worker Manager + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +ULONG PopShutdownPowerOffPolicy; +ERESOURCE PopPowerPolicyLock; +LIST_ENTRY PopPowerPolicyIrpQueueList; +WORK_QUEUE_ITEM PopPowerPolicyWorkItem; +PKTHREAD PopPowerPolicyOwnerLockThread; +KSPIN_LOCK PopPowerPolicyWorkerLock; +BOOLEAN PopPendingPolicyWorker; +SYSTEM_POWER_POLICY PopAcPowerPolicy; +SYSTEM_POWER_POLICY PopDcPowerPolicy; +PSYSTEM_POWER_POLICY PopDefaultPowerPolicy; +PKWIN32_POWEREVENT_CALLOUT PopEventCallout = NULL; +POP_POLICY_WORKER PopPolicyWorker[PolicyWorkerMax] = {0}; + +/* PRIVATE FUNCTIONS **********************************************************/ + +static +VOID +PopReadShutdownPolicy(VOID) +{ + NTSTATUS Status; + HANDLE KeyHandle; + ULONG Length; + UNICODE_STRING KeyString; + OBJECT_ATTRIBUTES ObjectAttributes; + 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); + + /* Default the policy value to 0 just in case if opening the key fails */ + PopShutdownPowerOffPolicy = 0; + + /* 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); + } +} + +static +NTSTATUS +PopRemovePolicyDevice( + _In_ PDEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationStructure, + _In_ POWER_POLICY_DEVICE_TYPE PolicyDeviceType) +{ + NTSTATUS Status; + PVOID PolicyData; + BOOLEAN MustDereference = TRUE; + PDEVICE_OBJECT PolicyDeviceObject; + PWORKER_THREAD_ROUTINE PolicyWorkerRoutine; + PPOP_DEVICE_POLICY_WORKITEM_DATA PolicyWorkItemData; + PPOP_CONTROL_SWITCH ControlSwitch; + + PAGED_CODE(); + + /* The power policy lock must be owned */ + POP_ASSERT_POWER_POLICY_LOCK_OWNERSHIP(); + + /* + * We received notification of the removal of the composite battery. There is + * no need to reference the object twice as the battery device driver is always + * the same and it does not change. + */ + if (PolicyDeviceType == PolicyDeviceBattery) + { + /* + * Of course the Power Manager must not be played by absolute fools that a + * battery removal is happening when a battery was not even used in the first place... + */ + ASSERT((PopBattery->Flags & POP_CB_NO_BATTERY) != 0); + + /* Cache the battery device object that we referenced before */ + PolicyDeviceObject = PopBattery->DeviceObject; + + /* The CB handler will dereference the battery device by itself, no need to do it twice */ + MustDereference = FALSE; + } + else + { + /* + * We are opening a device that has already been opened with a reference but + * on device removal we are going to de-reference the extra reference we held + * (and on the policy device handler it will totally de-reference the last count) + * so it should not be a problem anyway. + */ + Status = PopGetPolicyDeviceObject(NotificationStructure->SymbolicLinkName, + &PolicyDeviceObject); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to open the device for removal\n"); + return Status; + } + } + + /* Allocate a policy device workitem */ + PolicyWorkItemData = PopAllocatePool(sizeof(POP_DEVICE_POLICY_WORKITEM_DATA), + FALSE, + TAG_PO_POLICY_DEVICE_WORKITEM_DATA); + if (PolicyWorkItemData == NULL) + { + DPRINT1("Failed to allocate pool of memory for the policy device workitem data\n"); + ObDereferenceObject(PolicyDeviceObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (PolicyDeviceType == PolicyDeviceSystemButton) + { + /* Grab the target control switch that we are going to remove */ + ControlSwitch = PopGetControlSwitchByDevice(PolicyDeviceObject); + ASSERT(ControlSwitch != NULL); + ControlSwitch->Flags |= POP_CS_CLEANUP; + PolicyData = ControlSwitch; + PolicyWorkerRoutine = PopControlSwitchHandler; + } + else if (PolicyDeviceType == PolicyDeviceMemory) + { + DPRINT1("Policy memory device not currently implemented yet\n"); + ASSERT(FALSE); + } + else if (PolicyDeviceType == PolicyDeviceBattery) + { + PopBattery->Flags |= POP_CB_REMOVE_BATTERY; + PolicyData = NULL; + PolicyWorkerRoutine = PopCompositeBatteryHandler; + } + else if (PolicyDeviceType == PolicyDeviceThermalZone) + { + DPRINT1("Policy thermal zone device not currently implemented yet\n"); + ASSERT(FALSE); + } + else if (PolicyDeviceType == PolicyDeviceFan) + { + DPRINT1("Policy fan device not currently implemented yet\n"); + ASSERT(FALSE); + } + else + { + /* We do not support device removals for unknown devices */ + DPRINT1("Got an unsupported policy device (%lu), expect weird system behaviors\n", PolicyDeviceType); + PopFreePool(PolicyWorkItemData, TAG_PO_POLICY_DEVICE_WORKITEM_DATA); + ObDereferenceObject(PolicyDeviceObject); + return STATUS_NOT_SUPPORTED; + } + + /* Queue the appropriate policy device handler to remove this device */ + PolicyWorkItemData->PolicyData = PolicyData; + PolicyWorkItemData->PolicyType = PolicyDeviceType; + ExInitializeWorkItem(&PolicyWorkItemData->WorkItem, + PolicyWorkerRoutine, + PolicyWorkItemData); + ExQueueWorkItem(&PolicyWorkItemData->WorkItem, DelayedWorkQueue); + + if (MustDereference) + { + ObDereferenceObject(PolicyDeviceObject); + } + + return STATUS_SUCCESS; +} + +static +NTSTATUS +PopAddPolicyDevice( + _In_ PDEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationStructure, + _In_ POWER_POLICY_DEVICE_TYPE PolicyDeviceType) +{ + NTSTATUS Status; + PVOID PolicyData; + PDEVICE_OBJECT PolicyDeviceObject; + PWORKER_THREAD_ROUTINE PolicyWorkerRoutine; + PPOP_DEVICE_POLICY_WORKITEM_DATA PolicyWorkItemData; + PPOP_CONTROL_SWITCH ControlSwitch; + + PAGED_CODE(); + + /* The power policy lock must be owned */ + POP_ASSERT_POWER_POLICY_LOCK_OWNERSHIP(); + + DPRINT1("Connecting with policy device %wZ\n", NotificationStructure->SymbolicLinkName); + + /* + * This is an upcoming composite battery. There is only one instance of a + * composite battery driver that takes care of all batteries present in a system. + * With that said, if we get to this point and there is already a battery in use + * then we have gotten a candiate of a new battery. The tag must be recalculated + * (the Battery Class driver will do that). There is no need to open the same + * device object twice. Let the battery handler get acknowledged of a new battery + * taking place. + */ + if (PolicyDeviceType == PolicyDeviceBattery && + !(PopBattery->Flags & POP_CB_NO_BATTERY)) + { + PopMarkNewBatteryPending(); + return STATUS_SUCCESS; + } + else + { + /* Get a device object of this policy device to gather info of what it is */ + Status = PopGetPolicyDeviceObject(NotificationStructure->SymbolicLinkName, + &PolicyDeviceObject); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to connect with the policy device\n"); + return Status; + } + } + + /* Allocate a policy device workitem, we will use it later below */ + PolicyWorkItemData = PopAllocatePool(sizeof(POP_DEVICE_POLICY_WORKITEM_DATA), + FALSE, + TAG_PO_POLICY_DEVICE_WORKITEM_DATA); + if (PolicyWorkItemData == NULL) + { + DPRINT1("Failed to allocate pool of memory for the policy device workitem data\n"); + ObDereferenceObject(PolicyDeviceObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* + * Check what kind of policy device it is and initialize critical + * datum with the power manager. + */ + switch (PolicyDeviceType) + { + case PolicyDeviceSystemButton: + { + /* This is a control switch, let the CS manager setup data for it */ + Status = PopCreateControlSwitch(PolicyDeviceObject, &ControlSwitch); + PolicyData = ControlSwitch; + PolicyWorkerRoutine = PopControlSwitchHandler; + break; + } + + case PolicyDeviceBattery: + { + /* + * There is an upcoming composite battery for the first time, + * let the CB manager handle it. + */ + Status = PopAddCompositeBattery(PolicyDeviceObject, NotificationStructure->SymbolicLinkName); + PolicyData = NULL; + PolicyWorkerRoutine = PopCompositeBatteryHandler; + break; + } + + case PolicyDeviceThermalZone: + { + DPRINT1("Policy thermal zone device not currently implemented yet\n"); + ASSERT(FALSE); + Status = STATUS_NOT_IMPLEMENTED; + break; + } + + case PolicyDeviceMemory: + { + DPRINT1("Policy memory device not currently implemented yet\n"); + ASSERT(FALSE); + Status = STATUS_NOT_IMPLEMENTED; + break; + } + + case PolicyDeviceFan: + { + DPRINT1("Policy fan device not currently implemented yet\n"); + ASSERT(FALSE); + Status = STATUS_NOT_IMPLEMENTED; + break; + } + + default: + { + /* + * We got an unknown policy device of which we do not have any + * infrastructure for it currently implemented. As a consequence + * the system may not work properly due to foreign devices not + * attached with the power manager. + */ + DPRINT1("Got an unsupported policy device (%lu), expect weird system behaviors\n", PolicyDeviceType); + PopFreePool(PolicyWorkItemData, TAG_PO_POLICY_DEVICE_WORKITEM_DATA); + ObDereferenceObject(PolicyDeviceObject); + return STATUS_NOT_SUPPORTED; + } + } + + /* Bail out if the creation of policy device data has failed */ + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to create data for the policy device (Status 0x%08lx, policy device %lu)\n", Status, PolicyDeviceType); + PopFreePool(PolicyWorkItemData, TAG_PO_POLICY_DEVICE_WORKITEM_DATA); + ObDereferenceObject(PolicyDeviceObject); + return Status; + } + + /* + * We are almost done, fill in device policy data into the allocated workitem + * so that this little cute boy gets dispatched to the appropriate policy device + * handler routine. + */ + PolicyWorkItemData->PolicyData = PolicyData; + PolicyWorkItemData->PolicyType = PolicyDeviceType; + ExInitializeWorkItem(&PolicyWorkItemData->WorkItem, + PolicyWorkerRoutine, + PolicyWorkItemData); + + /* Dispatch the policy device now */ + ExQueueWorkItem(&PolicyWorkItemData->WorkItem, DelayedWorkQueue); + return STATUS_SUCCESS; +} + +static +VOID +PopDispatchPowerEvent( + _In_ PWIN32_POWEREVENT_PARAMETERS Win32PwrEventParams) +{ + PVOID Session; + ULONG SessionId; + ULONG ProcessFlags; + KAPC_STATE ApcState; + NTSTATUS Status; + + /* If we get here and the power callout was not registered yet, just quit */ + if (!PopEventCallout) + { + return; + } + + /* Ensure the power callout is within the session space in order to be usable */ + //ASSERT(MmIsSessionAddress(PopEventCallout) == TRUE); + + /* + * FIXME: Unfortunately ReactOS currently does not support multiple + * sessions. As a consequence we have to dispatch the power event + * to only the active single session. This is not a problem per se + * but when ReactOS will ever get multi-sessions support, this code + * needs adaptations as it currently only dispatches power events to + * only single session. For GDI events (PsW32GdiOn && PsW32GdiOff), these + * shall be dispatched to the console session only, everything else have + * to be broadcasted to all sessions. + */ + + /* Dispatch the power event directly if the process is within the session space */ + ProcessFlags = PsGetCurrentProcess()->Flags; + SessionId = PsGetCurrentProcessSessionId(); + if (ProcessFlags & PSF_PROCESS_IN_SESSION_BIT) + { + PopEventCallout(Win32PwrEventParams); + return; + } + + /* It is not within the session space, get the session */ + Session = MmGetSessionById(SessionId); + if (!Session) + { + /* + * We cannot dispatch the power event to a session that does not + * even exist. This would probably mean the process is within + * a bogus session that was never inserted, so alert the debugger + * in such case. + */ + ASSERT(Session != NULL); + return; + } + + /* Attach to that session in order to dispatch the power event */ + Status = MmAttachSession(Session, &ApcState); + if (!NT_SUCCESS(Status)) + { + /* Failing to attach signifies a major problem, bail out */ + ASSERT(Status == STATUS_SUCCESS); + return; + } + + /* Dispatch the power event */ + PopEventCallout(Win32PwrEventParams); + MmDetachSession(Session, &ApcState); + MmQuitNextSession(Session); +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +NTSTATUS +NTAPI +PopGetPolicyDeviceObject( + _In_ PUNICODE_STRING DeviceName, + _Out_ PDEVICE_OBJECT *DeviceObject) +{ + NTSTATUS Status; + HANDLE FileHandle; + PFILE_OBJECT FileObject; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; + + PAGED_CODE(); + + /* Open this policy device */ + InitializeObjectAttributes(&ObjectAttributes, + DeviceName, + OBJ_KERNEL_HANDLE, + NULL, + NULL); + Status = ZwOpenFile(&FileHandle, + SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_NONALERT); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to open the policy device (Status 0x%08lx)\n", Status); + return Status; + } + + /* Reference the policy device and get a DO of it */ + Status = ObReferenceObjectByHandle(FileHandle, + SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, + IoFileObjectType, + KernelMode, + (PVOID*)&FileObject, + NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to reference the policy device file object (Status 0x%08lx)\n", Status); + ZwClose(FileHandle); + return Status; + } + + /* + * Put a reference on the returned device object so that we can + * safely discard the file object of which we no longer care about it. + */ + *DeviceObject = IoGetRelatedDeviceObject(FileObject); + ObReferenceObject(*DeviceObject); + ObDereferenceObject(FileObject); + ZwClose(FileHandle); + return STATUS_SUCCESS; +} + +VOID +NTAPI +PopPowerPolicyNotification(VOID) +{ + /* FIXME */ + UNIMPLEMENTED; + NOTHING; +} + +VOID +NTAPI +PopPowerPolicySystemIdle(VOID) +{ + /* FIXME */ + UNIMPLEMENTED; + NOTHING; +} + +VOID +NTAPI +PopPowerPolicyTimeChange(VOID) +{ + WIN32_POWEREVENT_PARAMETERS Params; + + /* Invoke the Win32k power callout to dispatch the time change event */ + Params.EventNumber = PsW32SystemTime; + Params.Code = 0; + PopDispatchPowerEvent(&Params); +} + +VOID +NTAPI +PopInitializePowerPolicy( + _Out_ PSYSTEM_POWER_POLICY PowerPolicy) +{ + ULONG PolicyIndex; + + PAGED_CODE(); + + /* + * Zero out the structure and set the policy revision. Up to this day + * the Revision 1 is universal across all editions of Windows. This is + * unlikely to change, unless a huge revamp occurs in the power manager. + */ + RtlZeroMemory(PowerPolicy, sizeof(SYSTEM_POWER_POLICY)); + PowerPolicy->Revision = POP_SYSTEM_POWER_POLICY_REVISION_V1; + + /* Let Winlogon behave normally for this policy (no logon lock on sleep) */ + PowerPolicy->WinLogonFlags = 0; + + /* Set the default power actions and states for this policy */ + PowerPolicy->LidOpenWake = PowerSystemWorking; + PowerPolicy->LidClose.Action = PowerActionNone; + PowerPolicy->ReducedLatencySleep = PowerSystemSleeping1; + PowerPolicy->MinSleep = PowerSystemSleeping1; + PowerPolicy->MaxSleep = PowerSystemSleeping3; + PopSetButtonPowerAction(&PowerPolicy->PowerButton, PowerActionShutdownOff); + PopSetButtonPowerAction(&PowerPolicy->SleepButton, PowerActionSleep); + + /* Setup the default minimum sleep state for all the discharge policies */ + for (PolicyIndex = 0; PolicyIndex < NUM_DISCHARGE_POLICIES; PolicyIndex++) + { + PowerPolicy->DischargePolicy[PolicyIndex].MinSystemState = PowerSystemSleeping1; + } + + /* Set the default fan throttle constants */ + PowerPolicy->FanThrottleTolerance = 100; + PowerPolicy->ForcedThrottle = 100; + PowerPolicy->OverThrottled.Action = PowerActionNone; + + /* + * Let the system be notified of 25 resolutions of a system + * power state change event. + */ + PowerPolicy->BroadcastCapacityResolution = 25; +} + +VOID +NTAPI +PopDefaultPolicies(VOID) +{ + /* + * Read the shutdown policy. This policy will tell us whether the system must + * be powered off after shutdown or not. + */ + PopReadShutdownPolicy(); + + /* FIXME: There is lotta stuff to do here... */ + UNIMPLEMENTED; + return; +} + +VOID +NTAPI +PopRegisterPowerPolicyWorker( + _In_ POP_POWER_POLICY_WORKER_TYPES WorkerType, + _In_ PPOP_POLICY_WORKER_FUNC WorkerFunction) +{ + PAGED_CODE(); + + /* Do not let the caller on tricking us with an unknown policy type */ + ASSERT((WorkerType >= PolicyWorkerNotification) && (WorkerType <= PolicyWorkerMax)); + + /* Register a policy worker of this type */ + PopPolicyWorker[WorkerType].Pending = FALSE; + PopPolicyWorker[WorkerType].Thread = NULL; + PopPolicyWorker[WorkerType].WorkerFunction = WorkerFunction; +} + +VOID +NTAPI +PopRequestPolicyWorker( + _In_ POP_POWER_POLICY_WORKER_TYPES WorkerType) +{ + BOOLEAN PendingWorker; + KIRQL Irql; + + /* + * Check if this kind of worker type has not been requested before. + * By rule there can be only one worker enqueued for each type. + * It does not matter if somebody wants to enqueue a worker that is + * already pending, because once a policy worker fires up it will + * address the latest changes that will take effect on the system. + */ + PopAcquirePowerPolicyWorkerLock(&Irql); + PendingWorker = PopPolicyWorker[WorkerType].Pending; + if (!PendingWorker) + { + /* This type of worker was never requested, set the pending mark */ + PopPolicyWorker[WorkerType].Pending = TRUE; + + /* + * For debugging purposes attach the current calling thread to + * this policy worker so that we know which thread requested it before. + */ + PopPolicyWorker[WorkerType].Thread = KeGetCurrentThread(); + } + + PopReleasePowerPolicyWorkerLock(Irql); +} + +VOID +NTAPI +PopCheckForPendingWorkers(VOID) +{ + ULONG WorkerIndex; + KIRQL Irql; + BOOLEAN PendingFound = FALSE; + + /* + * The current calling thread owns the policy lock. This means the + * thread is currently doing changes at certain power policies. + * We will dispatch any outstanding workers as soon as the calling + * thread leaves the policy manager. + */ + if (PopPowerPolicyOwnerLockThread == KeGetCurrentThread()) + { + return; + } + + /* Do not allow any further worker requests, check for any pending work */ + PopAcquirePowerPolicyWorkerLock(&Irql); + for (WorkerIndex = PolicyWorkerNotification; + WorkerIndex < PolicyWorkerMax; + WorkerIndex++) + { + /* Is this worker pending? */ + if (PopPolicyWorker[WorkerIndex].Pending) + { + /* It is, stop locking */ + PendingFound = TRUE; + break; + } + } + + /* If no outstanding work was found then just quit */ + if (!PendingFound) + { + PopReleasePowerPolicyWorkerLock(Irql); + return; + } + + /* + * At least one policy worker has been requested and currently + * pending for dispatch. Check that the policy manager worker + * thread is not already running. Enqueue it if need be. + */ + if (!PopPendingPolicyWorker) + { + PopPendingPolicyWorker = TRUE; + ExQueueWorkItem(&PopPowerPolicyWorkItem, DelayedWorkQueue); + } + + /* Allow worker requests now */ + PopReleasePowerPolicyWorkerLock(Irql); +} + +_Use_decl_annotations_ +VOID +NTAPI +PopPolicyManagerWorker( + _In_ PVOID Parameter) +{ + ULONG WorkerIndex; + KIRQL Irql; + + /* + * Iterate over the registered policy workers and look for any of these + * that are currently pending for dispatch. + */ + PopAcquirePowerPolicyWorkerLock(&Irql); + for (WorkerIndex = PolicyWorkerNotification; + WorkerIndex < PolicyWorkerMax; + WorkerIndex++) + { + /* Process this policy worker only if it has been requested */ + if (PopPolicyWorker[WorkerIndex].Pending) + { + /* Clear the pending mark, we will serve this policy worker now */ + PopPolicyWorker[WorkerIndex].Pending = FALSE; + PopPolicyWorker[WorkerIndex].Thread = NULL; + PopReleasePowerPolicyWorkerLock(Irql); + + /* Dispatch it */ + PopPolicyWorker[WorkerIndex].WorkerFunction(); + + /* Look for the next policy worker */ + PopAcquirePowerPolicyWorkerLock(&Irql); + } + } + + /* The policy manager worker thread has finished its job */ + PopPendingPolicyWorker = FALSE; + PopReleasePowerPolicyWorkerLock(Irql); +} + +NTSTATUS +NTAPI +PopDevicePolicyCallback( + _In_ PVOID NotificationStructure, + _In_ PVOID Context) +{ + NTSTATUS Status; + BOOLEAN Arrival; + POWER_POLICY_DEVICE_TYPE PolicyDeviceType; + PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification; + + PAGED_CODE(); + + /* Passing a NULL policy notification or policy context is not what we expect here */ + if (!NotificationStructure || !Context) + { + DPRINT1("Got NULL policy notification or policy context\n"); + return STATUS_INVALID_PARAMETER; + } + + /* Begin validating the notification structure */ + Notification = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION)NotificationStructure; + PolicyDeviceType = (POWER_POLICY_DEVICE_TYPE)(ULONG_PTR)Context; + if (Notification->Version != 1) + { + DPRINT1("The policy notification has an invalid version (must be 1, got %u)\n", Notification->Version); + return STATUS_REVISION_MISMATCH; + } + + if (Notification->Size != sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION)) + { + DPRINT1("The policy notification has a malformed size structure (must be %u, got %u)\n", + sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION), Notification->Size); + return STATUS_INVALID_PARAMETER; + } + + /* + * Now check whether we have an upcoming policy device that we must connect + * to or detach the said policy device with the power manager. Any events + * other than arrival or removal notifications are bogus. + */ + if (RtlCompareMemory(&Notification->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID)) == sizeof(GUID)) + { + Arrival = TRUE; + PopAcquirePowerPolicyLock(); + Status = PopAddPolicyDevice(Notification, PolicyDeviceType); + PopReleasePowerPolicyLock(); + } + else if (RtlCompareMemory(&Notification->Event, &GUID_DEVICE_INTERFACE_REMOVAL, sizeof(GUID)) == sizeof(GUID)) + { + Arrival = FALSE; + PopAcquirePowerPolicyLock(); + Status = PopRemovePolicyDevice(Notification, PolicyDeviceType); + PopReleasePowerPolicyLock(); + } + else + { + DPRINT1("Got a bogus policy notification event, must be either be ARRIVAL or REMOVAL\n"); + return STATUS_INVALID_PARAMETER; + } + + /* Bail out if the policy device processing has failed */ + if (!NT_SUCCESS(Status)) + { + DPRINT1("The %s of policy device %wZ has failed (Status 0x%lx)\n", + Arrival ? "arrival" : "removal", Notification->SymbolicLinkName, Status); + return Status; + } + + return STATUS_SUCCESS; +} + +/* EOF */ diff --git a/ntoskrnl/po/poreq.c b/ntoskrnl/po/poreq.c new file mode 100644 index 0000000000000..1807c6c36e88d --- /dev/null +++ b/ntoskrnl/po/poreq.c @@ -0,0 +1,16 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power request object implementation base support routines + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* EOF */ diff --git a/ntoskrnl/po/posett.c b/ntoskrnl/po/posett.c new file mode 100644 index 0000000000000..8632ece32aa91 --- /dev/null +++ b/ntoskrnl/po/posett.c @@ -0,0 +1,16 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power Manager notification change registering helpers + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* EOF */ diff --git a/ntoskrnl/po/poshtdwn.c b/ntoskrnl/po/poshtdwn.c deleted file mode 100644 index b3fbb8f0c2a03..0000000000000 --- a/ntoskrnl/po/poshtdwn.c +++ /dev/null @@ -1,403 +0,0 @@ -/* - * 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 -#ifdef NEWCC -#include -#endif - -#define NDEBUG -#include - -#include "inbv/logo.h" - -/* GLOBALS *******************************************************************/ - -ULONG PopShutdownPowerOffPolicy; -KEVENT PopShutdownEvent; -PPOP_SHUTDOWN_WAIT_ENTRY PopShutdownThreadList; -LIST_ENTRY PopShutdownQueue; -KGUARDED_MUTEX PopShutdownListMutex; -BOOLEAN PopShutdownListAvailable; - - -/* PRIVATE FUNCTIONS *********************************************************/ - -VOID -NTAPI -PopInitShutdownList(VOID) -{ - PAGED_CODE(); - - /* Initialize the global shutdown event */ - KeInitializeEvent(&PopShutdownEvent, NotificationEvent, FALSE); - - /* Initialize the shutdown lists */ - PopShutdownThreadList = NULL; - InitializeListHead(&PopShutdownQueue); - - /* Initialize the shutdown list lock */ - KeInitializeGuardedMutex(&PopShutdownListMutex); - - /* The list is available now */ - PopShutdownListAvailable = TRUE; -} - -NTSTATUS -NTAPI -PoRequestShutdownWait( - _In_ PETHREAD Thread) -{ - PPOP_SHUTDOWN_WAIT_ENTRY ShutDownWaitEntry; - NTSTATUS Status; - PAGED_CODE(); - - /* Allocate a new shutdown wait entry */ - ShutDownWaitEntry = ExAllocatePoolWithTag(PagedPool, sizeof(*ShutDownWaitEntry), 'LSoP'); - if (ShutDownWaitEntry == NULL) - { - return STATUS_NO_MEMORY; - } - - /* Reference the thread and save it in the wait entry */ - ObReferenceObject(Thread); - ShutDownWaitEntry->Thread = Thread; - - /* Acquire the shutdown list lock */ - KeAcquireGuardedMutex(&PopShutdownListMutex); - - /* Check if the list is still available */ - if (PopShutdownListAvailable) - { - /* Insert the item in the list */ - ShutDownWaitEntry->NextEntry = PopShutdownThreadList; - PopShutdownThreadList = ShutDownWaitEntry; - - /* We are successful */ - Status = STATUS_SUCCESS; - } - else - { - /* We cannot proceed, cleanup and return failure */ - ObDereferenceObject(Thread); - ExFreePoolWithTag(ShutDownWaitEntry, 'LSoP'); - Status = STATUS_UNSUCCESSFUL; - } - - /* Release the list lock */ - KeReleaseGuardedMutex(&PopShutdownListMutex); - - /* Return the status */ - return Status; -} - -VOID -NTAPI -PopProcessShutDownLists(VOID) -{ - PPOP_SHUTDOWN_WAIT_ENTRY ShutDownWaitEntry; - PWORK_QUEUE_ITEM WorkItem; - PLIST_ENTRY ListEntry; - - /* First signal the shutdown event */ - KeSetEvent(&PopShutdownEvent, IO_NO_INCREMENT, FALSE); - - /* Acquire the shutdown list lock */ - KeAcquireGuardedMutex(&PopShutdownListMutex); - - /* Block any further attempts to register a shutdown event */ - PopShutdownListAvailable = FALSE; - - /* Release the list lock, since we are exclusively using the lists now */ - KeReleaseGuardedMutex(&PopShutdownListMutex); - - /* Process the shutdown queue */ - while (!IsListEmpty(&PopShutdownQueue)) - { - /* Get the head entry */ - ListEntry = RemoveHeadList(&PopShutdownQueue); - WorkItem = CONTAINING_RECORD(ListEntry, WORK_QUEUE_ITEM, List); - - /* Call the shutdown worker routine */ - WorkItem->WorkerRoutine(WorkItem->Parameter); - } - - /* Now process the shutdown thread list */ - while (PopShutdownThreadList != NULL) - { - /* Get the top entry and remove it from the list */ - ShutDownWaitEntry = PopShutdownThreadList; - PopShutdownThreadList = PopShutdownThreadList->NextEntry; - - /* Wait for the thread to finish and dereference it */ - KeWaitForSingleObject(ShutDownWaitEntry->Thread, 0, 0, 0, 0); - ObDereferenceObject(ShutDownWaitEntry->Thread); - - /* Finally free the entry */ - ExFreePoolWithTag(ShutDownWaitEntry, 'LSoP'); - } -} - -VOID -NTAPI -PopShutdownHandler(VOID) -{ - /* Stop all interrupts */ - KeRaiseIrqlToDpcLevel(); - _disable(); - - /* Do we have boot video */ - if (InbvIsBootDriverInstalled()) - { - /* Yes we do, cleanup for shutdown screen */ - if (!InbvCheckDisplayOwnership()) InbvAcquireDisplayOwnership(); - InbvResetDisplay(); - // InbvEnableDisplayString(TRUE); - DisplayShutdownBitmap(); - } - else - { - /* Do it in text-mode */ - DisplayShutdownText(); - } - - /* Hang the system */ - for (;;) HalHaltSystem(); -} - -VOID -NTAPI -PopShutdownSystem(IN POWER_ACTION SystemAction) -{ - /* Note should notify caller of NtPowerInformation(PowerShutdownNotification) */ - - /* Unload symbols */ - DPRINT("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); - PopSetSystemPowerState(PowerSystemShutdown, SystemAction); - 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, SystemAction); - 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) -{ - PEPROCESS Process = NULL; - - /* Process the registered waits and work items */ - PopProcessShutDownLists(); - - /* Loop every process */ - Process = PsGetNextProcess(Process); - while (Process) - { - /* Make sure this isn't the idle or initial process */ - if ((Process != PsInitialSystemProcess) && (Process != PsIdleProcess)) - { - /* Print it */ - DPRINT1("%15s is still RUNNING (%p)\n", Process->ImageFileName, Process->UniqueProcessId); - } - - /* Get the next process */ - Process = PsGetNextProcess(Process); - } - - /* First, the HAL handles any "end of boot" special functionality */ - DPRINT("HAL shutting down\n"); - HalEndOfBoot(); - - /* Shut down the Shim cache if enabled */ - ApphelpCacheShutdown(); - - /* In this step, the I/O manager does first-chance shutdown notification */ - DPRINT("I/O manager shutting down in phase 0\n"); - IoShutdownSystem(0); - - /* In this step, all workers are killed and hives are flushed */ - DPRINT("Configuration Manager shutting down\n"); - CmShutdownSystem(); - - /* Shut down the Executive */ - DPRINT("Executive shutting down\n"); - ExShutdownSystem(); - - /* Note that modified pages should be written here (MiShutdownSystem) */ - MmShutdownSystem(0); - - /* Flush all user files before we start shutting down IO */ - /* This is where modified pages are written back by the IO manager */ - CcShutdownSystem(); - - /* In this step, the I/O manager does last-chance shutdown notification */ - DPRINT("I/O manager shutting down in phase 1\n"); - IoShutdownSystem(1); - CcWaitForCurrentLazyWriterActivity(); - - /* FIXME: Calling Mm shutdown phase 1 here to get page file dereference - * but it shouldn't be called here. Only phase 2 should be called. - */ - MmShutdownSystem(1); - - /* Note that here, we should broadcast the power IRP to devices */ - - /* In this step, the HAL disables any wake timers */ - DPRINT("Disabling wake timers\n"); - HalSetWakeEnable(FALSE); - - /* And finally the power request is sent */ - DPRINT("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 **********************************************************/ - -/* - * @unimplemented - */ -NTSTATUS -NTAPI -PoQueueShutdownWorkItem( - _In_ PWORK_QUEUE_ITEM WorkItem) -{ - NTSTATUS Status; - - /* Acquire the shutdown list lock */ - KeAcquireGuardedMutex(&PopShutdownListMutex); - - /* Check if the list is (already/still) available */ - if (PopShutdownListAvailable) - { - /* Insert the item into the list */ - InsertTailList(&PopShutdownQueue, &WorkItem->List); - Status = STATUS_SUCCESS; - } - else - { - /* We are already in shutdown */ - Status = STATUS_SYSTEM_SHUTDOWN; - } - - /* Release the list lock */ - KeReleaseGuardedMutex(&PopShutdownListMutex); - - return Status; -} - -/* - * @implemented - */ -NTSTATUS -NTAPI -PoRequestShutdownEvent(OUT PVOID *Event) -{ - NTSTATUS Status; - PAGED_CODE(); - - /* Initialize to NULL */ - if (Event) *Event = NULL; - - /* Request a shutdown wait */ - Status = PoRequestShutdownWait(PsGetCurrentThread()); - if (!NT_SUCCESS(Status)) - { - return Status; - } - - /* Return the global shutdown event */ - if (Event) *Event = &PopShutdownEvent; - return STATUS_SUCCESS; -} - diff --git a/ntoskrnl/po/power.c b/ntoskrnl/po/power.c deleted file mode 100644 index c2f998e7688f5..0000000000000 --- a/ntoskrnl/po/power.c +++ /dev/null @@ -1,1149 +0,0 @@ -/* - * PROJECT: ReactOS Kernel - * LICENSE: GPL - See COPYING in the top level directory - * FILE: ntoskrnl/po/power.c - * PURPOSE: Power Manager - * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) - * Hervé Poussineau (hpoussin@reactos.com) - */ - -/* INCLUDES ******************************************************************/ - -#include -#define NDEBUG -#include - -/* GLOBALS *******************************************************************/ - -typedef struct _POWER_STATE_TRAVERSE_CONTEXT -{ - SYSTEM_POWER_STATE SystemPowerState; - POWER_ACTION PowerAction; - PDEVICE_OBJECT PowerDevice; -} POWER_STATE_TRAVERSE_CONTEXT, *PPOWER_STATE_TRAVERSE_CONTEXT; - -PDEVICE_NODE PopSystemPowerDeviceNode = NULL; -BOOLEAN PopAcpiPresent = FALSE; -POP_POWER_ACTION PopAction; -WORK_QUEUE_ITEM PopShutdownWorkItem; -SYSTEM_POWER_CAPABILITIES PopCapabilities; - -/* PRIVATE FUNCTIONS *********************************************************/ - -static WORKER_THREAD_ROUTINE PopPassivePowerCall; -_Use_decl_annotations_ -static -VOID -NTAPI -PopPassivePowerCall( - PVOID Parameter) -{ - PIRP Irp = Parameter; - PIO_STACK_LOCATION IoStack; - - ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); - - _Analysis_assume_(Irp != NULL); - IoStack = IoGetNextIrpStackLocation(Irp); - - (VOID)IoCallDriver(IoStack->DeviceObject, Irp); -} - -_IRQL_requires_max_(DISPATCH_LEVEL) -_IRQL_requires_same_ -static -NTSTATUS -PopPresentIrp( - _In_ PIO_STACK_LOCATION NextStack, - _In_ PIRP Irp) -{ - NTSTATUS Status; - BOOLEAN CallAtPassiveLevel; - PDEVICE_OBJECT DeviceObject; - PWORK_QUEUE_ITEM WorkQueueItem; - - ASSERT(NextStack->MajorFunction == IRP_MJ_POWER); - - DeviceObject = NextStack->DeviceObject; - - /* Determine whether the IRP must be handled at PASSIVE_LEVEL. - * Only SET_POWER to working state can happen at raised IRQL. */ - CallAtPassiveLevel = TRUE; - if ((NextStack->MinorFunction == IRP_MN_SET_POWER) && - !(DeviceObject->Flags & DO_POWER_PAGABLE)) - { - if (NextStack->Parameters.Power.Type == DevicePowerState && - NextStack->Parameters.Power.State.DeviceState == PowerDeviceD0) - { - CallAtPassiveLevel = FALSE; - } - if (NextStack->Parameters.Power.Type == SystemPowerState && - NextStack->Parameters.Power.State.SystemState == PowerSystemWorking) - { - CallAtPassiveLevel = FALSE; - } - } - - if (CallAtPassiveLevel) - { - /* We need to fit a work item into the DriverContext below */ - C_ASSERT(sizeof(Irp->Tail.Overlay.DriverContext) >= sizeof(WORK_QUEUE_ITEM)); - - if (KeGetCurrentIrql() == PASSIVE_LEVEL) - { - /* Already at passive, call next driver directly */ - return IoCallDriver(DeviceObject, Irp); - } - - /* Need to schedule a work item and return pending */ - NextStack->Control |= SL_PENDING_RETURNED; - - WorkQueueItem = (PWORK_QUEUE_ITEM)&Irp->Tail.Overlay.DriverContext; - ExInitializeWorkItem(WorkQueueItem, - PopPassivePowerCall, - Irp); - ExQueueWorkItem(WorkQueueItem, DelayedWorkQueue); - - return STATUS_PENDING; - } - - /* Direct call. Raise IRQL in debug to catch invalid paged memory access. */ -#if DBG - { - KIRQL OldIrql; - KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); -#endif - - Status = IoCallDriver(DeviceObject, Irp); - -#if DBG - KeLowerIrql(OldIrql); - } -#endif - - return Status; -} - -static IO_COMPLETION_ROUTINE PopRequestPowerIrpCompletion; - -static -NTSTATUS -NTAPI -PopRequestPowerIrpCompletion( - _In_ PDEVICE_OBJECT DeviceObject, - _In_ PIRP Irp, - _In_reads_opt_(_Inexpressible_("varies")) PVOID Context) -{ - PIO_STACK_LOCATION Stack; - PREQUEST_POWER_COMPLETE CompletionRoutine; - POWER_STATE PowerState; - - Stack = IoGetCurrentIrpStackLocation(Irp); - CompletionRoutine = Context; - - PowerState.DeviceState = (ULONG_PTR)Stack->Parameters.Others.Argument3; - - if (CompletionRoutine) - { - CompletionRoutine(Stack->Parameters.Others.Argument1, - (UCHAR)(ULONG_PTR)Stack->Parameters.Others.Argument2, - PowerState, - Stack->Parameters.Others.Argument4, - &Irp->IoStatus); - } - - IoSkipCurrentIrpStackLocation(Irp); - IoFreeIrp(Irp); - ObDereferenceObject(DeviceObject); - - return STATUS_MORE_PROCESSING_REQUIRED; -} - -VOID -NTAPI -PopCleanupPowerState(IN PPOWER_STATE PowerState) -{ - //UNIMPLEMENTED; -} - -NTSTATUS -PopSendQuerySystemPowerState(PDEVICE_OBJECT DeviceObject, SYSTEM_POWER_STATE SystemState, POWER_ACTION PowerAction) -{ - KEVENT Event; - IO_STATUS_BLOCK IoStatusBlock; - PIO_STACK_LOCATION IrpSp; - PIRP Irp; - NTSTATUS Status; - - KeInitializeEvent(&Event, - NotificationEvent, - FALSE); - - Irp = IoBuildSynchronousFsdRequest(IRP_MJ_POWER, - DeviceObject, - NULL, - 0, - NULL, - &Event, - &IoStatusBlock); - if (!Irp) return STATUS_INSUFFICIENT_RESOURCES; - - IrpSp = IoGetNextIrpStackLocation(Irp); - IrpSp->MinorFunction = IRP_MN_QUERY_POWER; - IrpSp->Parameters.Power.Type = SystemPowerState; - IrpSp->Parameters.Power.State.SystemState = SystemState; - IrpSp->Parameters.Power.ShutdownType = PowerAction; - - Status = PoCallDriver(DeviceObject, Irp); - if (Status == STATUS_PENDING) - { - KeWaitForSingleObject(&Event, - Executive, - KernelMode, - FALSE, - NULL); - Status = IoStatusBlock.Status; - } - - return Status; -} - -NTSTATUS -PopSendSetSystemPowerState(PDEVICE_OBJECT DeviceObject, SYSTEM_POWER_STATE SystemState, POWER_ACTION PowerAction) -{ - KEVENT Event; - IO_STATUS_BLOCK IoStatusBlock; - PIO_STACK_LOCATION IrpSp; - PIRP Irp; - NTSTATUS Status; - - KeInitializeEvent(&Event, - NotificationEvent, - FALSE); - - Irp = IoBuildSynchronousFsdRequest(IRP_MJ_POWER, - DeviceObject, - NULL, - 0, - NULL, - &Event, - &IoStatusBlock); - if (!Irp) return STATUS_INSUFFICIENT_RESOURCES; - - IrpSp = IoGetNextIrpStackLocation(Irp); - IrpSp->MinorFunction = IRP_MN_SET_POWER; - IrpSp->Parameters.Power.Type = SystemPowerState; - IrpSp->Parameters.Power.State.SystemState = SystemState; - IrpSp->Parameters.Power.ShutdownType = PowerAction; - - Status = PoCallDriver(DeviceObject, Irp); - if (Status == STATUS_PENDING) - { - KeWaitForSingleObject(&Event, - Executive, - KernelMode, - FALSE, - NULL); - Status = IoStatusBlock.Status; - } - - return Status; -} - -NTSTATUS -PopQuerySystemPowerStateTraverse(PDEVICE_NODE DeviceNode, - PVOID Context) -{ - PPOWER_STATE_TRAVERSE_CONTEXT PowerStateContext = Context; - PDEVICE_OBJECT TopDeviceObject; - NTSTATUS Status; - - DPRINT("PopQuerySystemPowerStateTraverse(%p, %p)\n", DeviceNode, Context); - - if (DeviceNode == IopRootDeviceNode) - return STATUS_SUCCESS; - - if (DeviceNode->Flags & DNF_LEGACY_DRIVER) - return STATUS_SUCCESS; - - TopDeviceObject = IoGetAttachedDeviceReference(DeviceNode->PhysicalDeviceObject); - - Status = PopSendQuerySystemPowerState(TopDeviceObject, - PowerStateContext->SystemPowerState, - PowerStateContext->PowerAction); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Device '%wZ' failed IRP_MN_QUERY_POWER\n", &DeviceNode->InstancePath); - } - ObDereferenceObject(TopDeviceObject); - -#if 0 - return Status; -#else - return STATUS_SUCCESS; -#endif -} - -NTSTATUS -PopSetSystemPowerStateTraverse(PDEVICE_NODE DeviceNode, - PVOID Context) -{ - PPOWER_STATE_TRAVERSE_CONTEXT PowerStateContext = Context; - PDEVICE_OBJECT TopDeviceObject; - NTSTATUS Status; - - DPRINT("PopSetSystemPowerStateTraverse(%p, %p)\n", DeviceNode, Context); - - if (DeviceNode == IopRootDeviceNode) - return STATUS_SUCCESS; - - if (DeviceNode->PhysicalDeviceObject == PowerStateContext->PowerDevice) - return STATUS_SUCCESS; - - if (DeviceNode->Flags & DNF_LEGACY_DRIVER) - return STATUS_SUCCESS; - - TopDeviceObject = IoGetAttachedDeviceReference(DeviceNode->PhysicalDeviceObject); - if (TopDeviceObject == PowerStateContext->PowerDevice) - { - ObDereferenceObject(TopDeviceObject); - return STATUS_SUCCESS; - } - - Status = PopSendSetSystemPowerState(TopDeviceObject, - PowerStateContext->SystemPowerState, - PowerStateContext->PowerAction); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Device '%wZ' failed IRP_MN_SET_POWER\n", &DeviceNode->InstancePath); - } - - ObDereferenceObject(TopDeviceObject); - -#if 0 - return Status; -#else - return STATUS_SUCCESS; -#endif -} - -NTSTATUS -NTAPI -PopSetSystemPowerState(SYSTEM_POWER_STATE PowerState, POWER_ACTION PowerAction) -{ - PDEVICE_OBJECT DeviceObject; - PDEVICE_OBJECT Fdo; - NTSTATUS Status; - DEVICETREE_TRAVERSE_CONTEXT Context; - POWER_STATE_TRAVERSE_CONTEXT PowerContext; - - Status = IopGetSystemPowerDeviceObject(&DeviceObject); - if (!NT_SUCCESS(Status)) - { - DPRINT1("No system power driver available\n"); - Fdo = NULL; - } - else - { - Fdo = IoGetAttachedDeviceReference(DeviceObject); - if (Fdo == DeviceObject) - { - DPRINT("An FDO was not attached\n"); - return STATUS_UNSUCCESSFUL; - } - } - - /* Set up context */ - PowerContext.PowerAction = PowerAction; - PowerContext.SystemPowerState = PowerState; - PowerContext.PowerDevice = Fdo; - - /* Query for system power change */ - IopInitDeviceTreeTraverseContext(&Context, - IopRootDeviceNode, - PopQuerySystemPowerStateTraverse, - &PowerContext); - - Status = IopTraverseDeviceTree(&Context); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Query system power state failed; changing state anyway\n"); - } - - /* Set system power change */ - IopInitDeviceTreeTraverseContext(&Context, - IopRootDeviceNode, - PopSetSystemPowerStateTraverse, - &PowerContext); - - IopTraverseDeviceTree(&Context); - - if (!PopAcpiPresent) return STATUS_NOT_IMPLEMENTED; - - if (Fdo != NULL) - { - if (PowerAction != PowerActionShutdownReset) - PopSendSetSystemPowerState(Fdo, PowerState, PowerAction); - - ObDereferenceObject(Fdo); - } - - return Status; -} - -CODE_SEG("INIT") -BOOLEAN -NTAPI -PoInitSystem(IN ULONG BootPhase) -{ - PVOID NotificationEntry; - PCHAR CommandLine; - BOOLEAN ForceAcpiDisable = FALSE; - - /* Check if this is phase 1 init */ - if (BootPhase == 1) - { - NTSTATUS Status; - /* Register power button notification */ - Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, - PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, - (PVOID)&GUID_DEVICE_SYS_BUTTON, - IopRootDeviceNode->PhysicalDeviceObject->DriverObject, - PopAddRemoveSysCapsCallback, - (PVOID)(ULONG_PTR)PolicyDeviceSystemButton, - &NotificationEntry); - if (!NT_SUCCESS(Status)) - return FALSE; - - /* Register lid notification */ - Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, - PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, - (PVOID)&GUID_DEVICE_LID, - IopRootDeviceNode->PhysicalDeviceObject->DriverObject, - PopAddRemoveSysCapsCallback, - (PVOID)(ULONG_PTR)PolicyDeviceSystemButton, - &NotificationEntry); - if (!NT_SUCCESS(Status)) - return FALSE; - - /* Register battery notification */ - Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, - PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, - (PVOID)&GUID_DEVICE_BATTERY, - IopRootDeviceNode->PhysicalDeviceObject->DriverObject, - PopAddRemoveSysCapsCallback, - (PVOID)(ULONG_PTR)PolicyDeviceBattery, - &NotificationEntry); - - return NT_SUCCESS(Status); - } - - /* Initialize the power capabilities */ - RtlZeroMemory(&PopCapabilities, sizeof(SYSTEM_POWER_CAPABILITIES)); - - /* Get the Command Line */ - CommandLine = KeLoaderBlock->LoadOptions; - - /* Upcase it */ - _strupr(CommandLine); - - /* Check for ACPI disable */ - if (strstr(CommandLine, "NOACPI")) ForceAcpiDisable = TRUE; - - if (ForceAcpiDisable) - { - /* Set the ACPI State to False if it's been forced that way */ - PopAcpiPresent = FALSE; - } - else - { - /* Otherwise check if the LoaderBlock has a ACPI Table */ - PopAcpiPresent = KeLoaderBlock->Extension->AcpiTable != NULL ? TRUE : FALSE; - } - - /* Enable shutdown by power button */ - if (PopAcpiPresent) - PopCapabilities.SystemS5 = TRUE; - - /* Initialize volume support */ - InitializeListHead(&PopVolumeDevices); - KeInitializeGuardedMutex(&PopVolumeLock); - - /* Initialize support for dope */ - KeInitializeSpinLock(&PopDopeGlobalLock); - - /* Initialize support for shutdown waits and work-items */ - PopInitShutdownList(); - - return TRUE; -} - -VOID -NTAPI -PopPerfIdle(PPROCESSOR_POWER_STATE PowerState) -{ - DPRINT1("PerfIdle function: %p\n", PowerState); -} - -VOID -NTAPI -PopPerfIdleDpc(IN PKDPC Dpc, - IN PVOID DeferredContext, - IN PVOID SystemArgument1, - IN PVOID SystemArgument2) -{ - /* Call the Perf Idle function */ - PopPerfIdle(&((PKPRCB)DeferredContext)->PowerState); -} - -VOID -FASTCALL -PopIdle0(IN PPROCESSOR_POWER_STATE PowerState) -{ - /* FIXME: Extremly naive implementation */ - HalProcessorIdle(); -} - -CODE_SEG("INIT") -VOID -NTAPI -PoInitializePrcb(IN PKPRCB Prcb) -{ - /* Initialize the Power State */ - RtlZeroMemory(&Prcb->PowerState, sizeof(Prcb->PowerState)); - Prcb->PowerState.Idle0KernelTimeLimit = 0xFFFFFFFF; - Prcb->PowerState.CurrentThrottle = 100; - Prcb->PowerState.CurrentThrottleIndex = 0; - Prcb->PowerState.IdleFunction = PopIdle0; - - /* Initialize the Perf DPC and Timer */ - KeInitializeDpc(&Prcb->PowerState.PerfDpc, PopPerfIdleDpc, Prcb); - KeSetTargetProcessorDpc(&Prcb->PowerState.PerfDpc, Prcb->Number); - KeInitializeTimerEx(&Prcb->PowerState.PerfTimer, SynchronizationTimer); -} - -/* PUBLIC FUNCTIONS **********************************************************/ - -/* - * @unimplemented - */ -NTSTATUS -NTAPI -PoCancelDeviceNotify(IN PVOID NotifyBlock) -{ - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - -/* - * @unimplemented - */ -NTSTATUS -NTAPI -PoRegisterDeviceNotify(OUT PVOID Unknown0, - IN ULONG Unknown1, - IN ULONG Unknown2, - IN ULONG Unknown3, - IN PVOID Unknown4, - IN PVOID Unknown5) -{ - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - -/* - * @unimplemented - */ -VOID -NTAPI -PoShutdownBugCheck(IN BOOLEAN LogError, - IN ULONG BugCheckCode, - IN ULONG_PTR BugCheckParameter1, - IN ULONG_PTR BugCheckParameter2, - IN ULONG_PTR BugCheckParameter3, - IN ULONG_PTR BugCheckParameter4) -{ - DPRINT1("PoShutdownBugCheck called\n"); - - /* FIXME: Log error if requested */ - /* FIXME: Initiate a shutdown */ - - /* Bugcheck the system */ - KeBugCheckEx(BugCheckCode, - BugCheckParameter1, - BugCheckParameter2, - BugCheckParameter3, - BugCheckParameter4); -} - -/* - * @unimplemented - */ -VOID -NTAPI -PoSetHiberRange(IN PVOID HiberContext, - IN ULONG Flags, - IN OUT PVOID StartPage, - IN ULONG Length, - IN ULONG PageTag) -{ - UNIMPLEMENTED; - return; -} - -/* - * @implemented - */ -_IRQL_requires_max_(DISPATCH_LEVEL) -NTSTATUS -NTAPI -PoCallDriver( - _In_ PDEVICE_OBJECT DeviceObject, - _Inout_ __drv_aliasesMem PIRP Irp) -{ - PIO_STACK_LOCATION NextStack; - - ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); - - ASSERT(DeviceObject); - ASSERT(Irp); - - NextStack = IoGetNextIrpStackLocation(Irp); - ASSERT(NextStack->MajorFunction == IRP_MJ_POWER); - - /* Set DeviceObject for PopPresentIrp */ - NextStack->DeviceObject = DeviceObject; - - /* Only QUERY_POWER and SET_POWER use special handling */ - if (NextStack->MinorFunction != IRP_MN_SET_POWER && - NextStack->MinorFunction != IRP_MN_QUERY_POWER) - { - return IoCallDriver(DeviceObject, Irp); - } - - /* Call the next driver, either directly or at PASSIVE_LEVEL */ - return PopPresentIrp(NextStack, Irp); -} - -/* - * @unimplemented - */ -PULONG -NTAPI -PoRegisterDeviceForIdleDetection(IN PDEVICE_OBJECT DeviceObject, - IN ULONG ConservationIdleTime, - IN ULONG PerformanceIdleTime, - IN DEVICE_POWER_STATE State) -{ - UNIMPLEMENTED; - return NULL; -} - -/* - * @unimplemented - */ -PVOID -NTAPI -PoRegisterSystemState(IN PVOID StateHandle, - IN EXECUTION_STATE Flags) -{ - UNIMPLEMENTED; - return NULL; -} - -/* - * @implemented - */ -NTSTATUS -NTAPI -PoRequestPowerIrp( - _In_ PDEVICE_OBJECT DeviceObject, - _In_ UCHAR MinorFunction, - _In_ POWER_STATE PowerState, - _In_opt_ PREQUEST_POWER_COMPLETE CompletionFunction, - _In_opt_ __drv_aliasesMem PVOID Context, - _Outptr_opt_ PIRP *pIrp) -{ - PDEVICE_OBJECT TopDeviceObject; - PIO_STACK_LOCATION Stack; - PIRP Irp; - - if (MinorFunction != IRP_MN_QUERY_POWER - && MinorFunction != IRP_MN_SET_POWER - && MinorFunction != IRP_MN_WAIT_WAKE) - return STATUS_INVALID_PARAMETER_2; - - /* Always call the top of the device stack */ - TopDeviceObject = IoGetAttachedDeviceReference(DeviceObject); - - Irp = IoAllocateIrp(TopDeviceObject->StackSize + 2, FALSE); - if (!Irp) - { - ObDereferenceObject(TopDeviceObject); - return STATUS_INSUFFICIENT_RESOURCES; - } - - Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; - Irp->IoStatus.Information = 0; - - IoSetNextIrpStackLocation(Irp); - - Stack = IoGetNextIrpStackLocation(Irp); - Stack->Parameters.Others.Argument1 = DeviceObject; - Stack->Parameters.Others.Argument2 = (PVOID)(ULONG_PTR)MinorFunction; - Stack->Parameters.Others.Argument3 = (PVOID)(ULONG_PTR)PowerState.DeviceState; - Stack->Parameters.Others.Argument4 = Context; - Stack->DeviceObject = TopDeviceObject; - IoSetNextIrpStackLocation(Irp); - - Stack = IoGetNextIrpStackLocation(Irp); - Stack->MajorFunction = IRP_MJ_POWER; - Stack->MinorFunction = MinorFunction; - if (MinorFunction == IRP_MN_WAIT_WAKE) - { - Stack->Parameters.WaitWake.PowerState = PowerState.SystemState; - } - else - { - Stack->Parameters.Power.Type = DevicePowerState; - Stack->Parameters.Power.State = PowerState; - } - - if (pIrp != NULL) - *pIrp = Irp; - - IoSetCompletionRoutine(Irp, PopRequestPowerIrpCompletion, CompletionFunction, TRUE, TRUE, TRUE); - PoCallDriver(TopDeviceObject, Irp); - - /* Always return STATUS_PENDING. The completion routine - * will call CompletionFunction and complete the Irp. - */ - return STATUS_PENDING; -} - -/* - * @unimplemented - */ -POWER_STATE -NTAPI -PoSetPowerState(IN PDEVICE_OBJECT DeviceObject, - IN POWER_STATE_TYPE Type, - IN POWER_STATE State) -{ - POWER_STATE ps; - - ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); - - ps.SystemState = PowerSystemWorking; // Fully on - ps.DeviceState = PowerDeviceD0; // Fully on - - return ps; -} - -/* - * @unimplemented - */ -VOID -NTAPI -PoSetSystemState(IN EXECUTION_STATE Flags) -{ - UNIMPLEMENTED; -} - -/* - * @unimplemented - */ -VOID -NTAPI -PoStartNextPowerIrp(IN PIRP Irp) -{ - UNIMPLEMENTED_ONCE; -} - -/* - * @unimplemented - */ -VOID -NTAPI -PoUnregisterSystemState(IN PVOID StateHandle) -{ - UNIMPLEMENTED; -} - -/* - * @unimplemented - */ -NTSTATUS -NTAPI -NtInitiatePowerAction(IN POWER_ACTION SystemAction, - IN SYSTEM_POWER_STATE MinSystemState, - IN ULONG Flags, - IN BOOLEAN Asynchronous) -{ - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - -/* - * @unimplemented - */ -NTSTATUS -NTAPI -NtPowerInformation(IN POWER_INFORMATION_LEVEL PowerInformationLevel, - IN PVOID InputBuffer OPTIONAL, - IN ULONG InputBufferLength, - OUT PVOID OutputBuffer OPTIONAL, - IN ULONG OutputBufferLength) -{ - NTSTATUS Status; - KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); - - PAGED_CODE(); - - DPRINT("NtPowerInformation(PowerInformationLevel 0x%x, InputBuffer 0x%p, " - "InputBufferLength 0x%x, OutputBuffer 0x%p, OutputBufferLength 0x%x)\n", - PowerInformationLevel, - InputBuffer, InputBufferLength, - OutputBuffer, OutputBufferLength); - - if (PreviousMode != KernelMode) - { - _SEH2_TRY - { - ProbeForRead(InputBuffer, InputBufferLength, 1); - ProbeForWrite(OutputBuffer, OutputBufferLength, sizeof(ULONG)); - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - _SEH2_YIELD(return _SEH2_GetExceptionCode()); - } - _SEH2_END; - } - - switch (PowerInformationLevel) - { - case SystemBatteryState: - { - PSYSTEM_BATTERY_STATE BatteryState = (PSYSTEM_BATTERY_STATE)OutputBuffer; - - if (InputBuffer != NULL) - return STATUS_INVALID_PARAMETER; - if (OutputBufferLength < sizeof(SYSTEM_BATTERY_STATE)) - return STATUS_BUFFER_TOO_SMALL; - - _SEH2_TRY - { - /* Just zero the struct */ - RtlZeroMemory(BatteryState, sizeof(*BatteryState)); - BatteryState->EstimatedTime = MAXULONG; - BatteryState->BatteryPresent = PopCapabilities.SystemBatteriesPresent; -// BatteryState->AcOnLine = TRUE; -// BatteryState->MaxCapacity = ; -// BatteryState->RemainingCapacity = ; - - Status = STATUS_SUCCESS; - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - Status = _SEH2_GetExceptionCode(); - } - _SEH2_END; - - break; - } - - case SystemPowerCapabilities: - { - PSYSTEM_POWER_CAPABILITIES PowerCapabilities = (PSYSTEM_POWER_CAPABILITIES)OutputBuffer; - - if (InputBuffer != NULL) - return STATUS_INVALID_PARAMETER; - if (OutputBufferLength < sizeof(SYSTEM_POWER_CAPABILITIES)) - return STATUS_BUFFER_TOO_SMALL; - - _SEH2_TRY - { - RtlCopyMemory(PowerCapabilities, - &PopCapabilities, - sizeof(SYSTEM_POWER_CAPABILITIES)); - - Status = STATUS_SUCCESS; - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - Status = _SEH2_GetExceptionCode(); - } - _SEH2_END; - - break; - } - - case ProcessorInformation: - { - PPROCESSOR_POWER_INFORMATION PowerInformation = (PPROCESSOR_POWER_INFORMATION)OutputBuffer; - - if (InputBuffer != NULL) - return STATUS_INVALID_PARAMETER; - if (OutputBufferLength < sizeof(PROCESSOR_POWER_INFORMATION)) - return STATUS_BUFFER_TOO_SMALL; - - /* FIXME: return structures for all processors */ - - _SEH2_TRY - { - /* FIXME: some values are hardcoded */ - PowerInformation->Number = 0; - PowerInformation->MaxMhz = 1000; - PowerInformation->CurrentMhz = KeGetCurrentPrcb()->MHz; - PowerInformation->MhzLimit = 1000; - PowerInformation->MaxIdleState = 0; - PowerInformation->CurrentIdleState = 0; - - Status = STATUS_SUCCESS; - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - Status = _SEH2_GetExceptionCode(); - } - _SEH2_END; - - break; - } - - default: - Status = STATUS_NOT_IMPLEMENTED; - DPRINT1("PowerInformationLevel 0x%x is UNIMPLEMENTED! Have a nice day.\n", - PowerInformationLevel); - break; - } - - return Status; -} - -NTSTATUS -NTAPI -NtGetDevicePowerState(IN HANDLE Device, - IN PDEVICE_POWER_STATE PowerState) -{ - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - -BOOLEAN -NTAPI -NtIsSystemResumeAutomatic(VOID) -{ - UNIMPLEMENTED; - return FALSE; -} - -NTSTATUS -NTAPI -NtRequestWakeupLatency(IN LATENCY_TIME Latency) -{ - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - -NTSTATUS -NTAPI -NtSetThreadExecutionState(IN EXECUTION_STATE esFlags, - OUT EXECUTION_STATE *PreviousFlags) -{ - PKTHREAD Thread = KeGetCurrentThread(); - KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); - EXECUTION_STATE PreviousState; - PAGED_CODE(); - - /* Validate flags */ - if (esFlags & ~(ES_CONTINUOUS | ES_USER_PRESENT)) - { - /* Fail the request */ - return STATUS_INVALID_PARAMETER; - } - - /* Check for user parameters */ - if (PreviousMode != KernelMode) - { - /* Protect the probes */ - _SEH2_TRY - { - /* Check if the pointer is valid */ - ProbeForWriteUlong(PreviousFlags); - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - /* It isn't -- fail */ - _SEH2_YIELD(return _SEH2_GetExceptionCode()); - } - _SEH2_END; - } - - /* Save the previous state, always masking in the continous flag */ - PreviousState = Thread->PowerState | ES_CONTINUOUS; - - /* Check if we need to update the power state */ - if (esFlags & ES_CONTINUOUS) Thread->PowerState = (UCHAR)esFlags; - - /* Protect the write back to user mode */ - _SEH2_TRY - { - /* Return the previous flags */ - *PreviousFlags = PreviousState; - } - _SEH2_EXCEPT(ExSystemExceptionFilter()) - { - /* Something's wrong, fail */ - _SEH2_YIELD(return _SEH2_GetExceptionCode()); - } - _SEH2_END; - - /* All is good */ - return STATUS_SUCCESS; -} - -NTSTATUS -NTAPI -NtSetSystemPowerState(IN POWER_ACTION SystemAction, - IN SYSTEM_POWER_STATE MinSystemState, - IN ULONG Flags) -{ - KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); - POP_POWER_ACTION Action = {0}; - NTSTATUS Status; - ULONG Dummy; - - /* Check for invalid parameter combinations */ - if ((MinSystemState >= PowerSystemMaximum) || - (MinSystemState <= PowerSystemUnspecified) || - (SystemAction > PowerActionWarmEject) || - (SystemAction < PowerActionReserved) || - (Flags & ~(POWER_ACTION_QUERY_ALLOWED | - POWER_ACTION_UI_ALLOWED | - POWER_ACTION_OVERRIDE_APPS | - POWER_ACTION_LIGHTEST_FIRST | - POWER_ACTION_LOCK_CONSOLE | - POWER_ACTION_DISABLE_WAKES | - POWER_ACTION_CRITICAL))) - { - DPRINT1("NtSetSystemPowerState: Bad parameters!\n"); - DPRINT1(" SystemAction: 0x%x\n", SystemAction); - DPRINT1(" MinSystemState: 0x%x\n", MinSystemState); - DPRINT1(" Flags: 0x%x\n", Flags); - return STATUS_INVALID_PARAMETER; - } - - /* Check for user caller */ - if (PreviousMode != KernelMode) - { - /* Check for shutdown permission */ - if (!SeSinglePrivilegeCheck(SeShutdownPrivilege, PreviousMode)) - { - /* Not granted */ - DPRINT1("ERROR: Privilege not held for shutdown\n"); - return STATUS_PRIVILEGE_NOT_HELD; - } - - /* Do it as a kernel-mode caller for consistency with system state */ - return ZwSetSystemPowerState(SystemAction, MinSystemState, Flags); - } - - /* Read policy settings (partial shutdown vs. full shutdown) */ - if (SystemAction == PowerActionShutdown) PopReadShutdownPolicy(); - - /* Disable lazy flushing of registry */ - DPRINT("Stopping lazy flush\n"); - CmSetLazyFlushState(FALSE); - - /* Setup the power action */ - Action.Action = SystemAction; - Action.Flags = Flags; - - /* Notify callbacks */ - DPRINT("Notifying callbacks\n"); - ExNotifyCallback(PowerStateCallback, (PVOID)3, NULL); - - /* Swap in any worker thread stacks */ - DPRINT("Swapping worker threads\n"); - ExSwapinWorkerThreads(FALSE); - - /* Make our action global */ - PopAction = Action; - - /* Start power loop */ - Status = STATUS_CANCELLED; - while (TRUE) - { - /* Break out if there's nothing to do */ - if (Action.Action == PowerActionNone) break; - - /* Check for first-pass or restart */ - if (Status == STATUS_CANCELLED) - { - /* Check for shutdown action */ - if ((PopAction.Action == PowerActionShutdown) || - (PopAction.Action == PowerActionShutdownReset) || - (PopAction.Action == PowerActionShutdownOff)) - { - /* Set the action */ - PopAction.Shutdown = TRUE; - } - - /* Now we are good to go */ - Status = STATUS_SUCCESS; - } - - /* Check if we're still in an invalid status */ - if (!NT_SUCCESS(Status)) break; - - /* Flush all volumes and the registry */ - DPRINT("Flushing volumes\n"); - PopFlushVolumes(PopAction.Shutdown); - -#ifndef NEWCC - /* Flush dirty cache pages */ - /* XXX: Is that still mandatory? As now we'll wait on lazy writer to complete? */ - CcRosFlushDirtyPages(MAXULONG, &Dummy, TRUE, FALSE); - DPRINT("Cache flushed %lu pages\n", Dummy); -#else - Dummy = 0; -#endif - - /* Set IRP for drivers */ - PopAction.IrpMinor = IRP_MN_SET_POWER; - if (PopAction.Shutdown) - { - DPRINT("Queueing shutdown thread\n"); - /* Check if we are running in the system context */ - if (PsGetCurrentProcess() != PsInitialSystemProcess) - { - /* We're not, so use a worker thread for shutdown */ - ExInitializeWorkItem(&PopShutdownWorkItem, - &PopGracefulShutdown, - NULL); - - ExQueueWorkItem(&PopShutdownWorkItem, CriticalWorkQueue); - - /* Spend us -- when we wake up, the system is good to go down */ - KeSuspendThread(KeGetCurrentThread()); - Status = STATUS_SYSTEM_SHUTDOWN; - goto Exit; - - } - else - { - /* Do the shutdown inline */ - PopGracefulShutdown(NULL); - } - } - - /* You should not have made it this far */ - // ASSERTMSG("System is still up and running?!\n", FALSE); - DPRINT1("System is still up and running, you may not have chosen a yet supported power option: %u\n", PopAction.Action); - break; - } - -Exit: - /* We're done, return */ - return Status; -} diff --git a/ntoskrnl/po/ppm/amd64/.keep b/ntoskrnl/po/ppm/amd64/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ntoskrnl/po/ppm/arm/.keep b/ntoskrnl/po/ppm/arm/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ntoskrnl/po/ppm/cpustat.c b/ntoskrnl/po/ppm/cpustat.c new file mode 100644 index 0000000000000..8e38b4ec77fc8 --- /dev/null +++ b/ntoskrnl/po/ppm/cpustat.c @@ -0,0 +1,16 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Processor Power Management C/P states management + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* EOF */ \ No newline at end of file diff --git a/ntoskrnl/po/ppm/eng.c b/ntoskrnl/po/ppm/eng.c new file mode 100644 index 0000000000000..0f5249f87f986 --- /dev/null +++ b/ntoskrnl/po/ppm/eng.c @@ -0,0 +1,18 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Processor Power Management core engine infrastructure + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* PUBLIC FUNCTIONS ***********************************************************/ + +/* EOF */ \ No newline at end of file diff --git a/ntoskrnl/po/ppm/i386/.keep b/ntoskrnl/po/ppm/i386/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ntoskrnl/po/ppm/idle.c b/ntoskrnl/po/ppm/idle.c new file mode 100644 index 0000000000000..84be0772c9d14 --- /dev/null +++ b/ntoskrnl/po/ppm/idle.c @@ -0,0 +1,27 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Processor Power Management idle processor handling support + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID +FASTCALL +PpmIdle( + _In_ PPROCESSOR_POWER_STATE PowerState) +{ + /* FIXME */ + HalProcessorIdle(); +} + +/* EOF */ diff --git a/ntoskrnl/po/ppm/init.c b/ntoskrnl/po/ppm/init.c new file mode 100644 index 0000000000000..a2361dccf01ef --- /dev/null +++ b/ntoskrnl/po/ppm/init.c @@ -0,0 +1,29 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Processor Power Management Initialization Code + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* PUBLIC FUNCTIONS ***********************************************************/ + +CODE_SEG("INIT") +NTSTATUS +NTAPI +PpmInitialize( + _In_ BOOLEAN EarlyPhase) +{ + /* FIXME */ + UNREFERENCED_PARAMETER(EarlyPhase); + return STATUS_SUCCESS; +} + +/* EOF */ diff --git a/ntoskrnl/po/ppm/perf.c b/ntoskrnl/po/ppm/perf.c new file mode 100644 index 0000000000000..81c0022f22346 --- /dev/null +++ b/ntoskrnl/po/ppm/perf.c @@ -0,0 +1,31 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Processor Power Management performance handling + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* PUBLIC FUNCTIONS ***********************************************************/ + +VOID +NTAPI +PpmPerfIdleDpcRoutine( + _In_ PKDPC Dpc, + _In_ PVOID DeferredContext, + _In_ PVOID SystemArgument1, + _In_ PVOID SystemArgument2) +{ + /* FIXME */ + UNIMPLEMENTED; + NOTHING; +} + +/* EOF */ \ No newline at end of file diff --git a/ntoskrnl/po/ppm/policy.c b/ntoskrnl/po/ppm/policy.c new file mode 100644 index 0000000000000..2bff929d8713c --- /dev/null +++ b/ntoskrnl/po/ppm/policy.c @@ -0,0 +1,16 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Processor Power Management processor policies + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* EOF */ \ No newline at end of file diff --git a/ntoskrnl/po/shtdwn.c b/ntoskrnl/po/shtdwn.c new file mode 100644 index 0000000000000..aeb2597f11e12 --- /dev/null +++ b/ntoskrnl/po/shtdwn.c @@ -0,0 +1,422 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power shutdown mechanism routines + * COPYRIGHT: Copyright ReactOS Portable Systems Group + * Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +#include "inbv/logo.h" + +/* GLOBALS ********************************************************************/ + +KEVENT PopShutdownEvent; +PPOP_SHUTDOWN_WAIT_ENTRY PopShutdownThreadList; +LIST_ENTRY PopShutdownQueue; +WORK_QUEUE_ITEM PopShutdownWorkItem; +KGUARDED_MUTEX PopShutdownListMutex; +BOOLEAN PopShutdownListAvailable; +BOOLEAN PopShutdownCleanly = FALSE; + +/* PRIVATE FUNCTIONS **********************************************************/ + +static +NTSTATUS +PopRequestShutdownEvent( + _In_ PETHREAD Thread) +{ + PPOP_SHUTDOWN_WAIT_ENTRY ShutDownWaitEntry; + + PAGED_CODE(); + + /* Allocate memory pool for the shutdown wait entry */ + ShutDownWaitEntry = PopAllocatePool(sizeof(*ShutDownWaitEntry), + TRUE, + TAG_PO_SHUTDOWN_EVENT); + if (ShutDownWaitEntry == NULL) + { + return STATUS_NO_MEMORY; + } + + /* + * Put a reference on this thread so that it does not simply die in + * our arms when we are going to process shutdown events. + */ + ObReferenceObject(Thread); + ShutDownWaitEntry->Thread = Thread; + + /* + * If there is already a shutdown action in progress then we simply + * cannot put this entry for later processing anymore. + */ + PopAcquireShutdownLock(); + if (!PopShutdownListAvailable) + { + ObDereferenceObject(Thread); + PopFreePool(ShutDownWaitEntry, TAG_PO_SHUTDOWN_EVENT); + PopReleaseShutdownLock(); + return STATUS_UNSUCCESSFUL; + } + + /* It is still available, put the entry in the list */ + ShutDownWaitEntry->NextEntry = PopShutdownThreadList; + PopShutdownThreadList = ShutDownWaitEntry; + PopReleaseShutdownLock(); + return STATUS_SUCCESS; +} + +static +VOID +PopProcessShutDownLists(VOID) +{ + PLIST_ENTRY Entry; + PWORK_QUEUE_ITEM WorkItem; + PPOP_SHUTDOWN_WAIT_ENTRY ShutDownWaitEntry; + + /* Signal the shutdown evern so that everybody knows the system is shutting down */ + KeSetEvent(&PopShutdownEvent, IO_NO_INCREMENT, FALSE); + + /* + * Block any further shutdown event requests on an impeding shutdown procedure + * of the system. It is not problem if we release the lock that early because + * up to that point we have total control of the queued shutdown events in the list. + */ + PopAcquireShutdownLock(); + PopShutdownListAvailable = FALSE; + PopReleaseShutdownLock(); + + /* Process any queued shutdown work items */ + while (!IsListEmpty(&PopShutdownQueue)) + { + Entry = RemoveHeadList(&PopShutdownQueue); + WorkItem = CONTAINING_RECORD(Entry, WORK_QUEUE_ITEM, List); + WorkItem->WorkerRoutine(WorkItem->Parameter); + } + + /* Process any inserted threads into the shutdown list */ + while (PopShutdownThreadList != NULL) + { + ShutDownWaitEntry = PopShutdownThreadList; + PopShutdownThreadList = PopShutdownThreadList->NextEntry; + + /* Wait for the thread to finish its operations before dereferencing it */ + KeWaitForSingleObject(ShutDownWaitEntry->Thread, 0, 0, 0, 0); + ObDereferenceObject(ShutDownWaitEntry->Thread); + PopFreePool(ShutDownWaitEntry, TAG_PO_SHUTDOWN_EVENT); + } +} + +static +ULONG +PopEnumLiveProcesses(VOID) +{ + ULONG ProcessCount = 0; + PEPROCESS Process = NULL; + + /* Loop every active process that could not be killed */ + Process = PsGetNextProcess(Process); + while (Process) + { + /* Make sure this isn't the idle or initial process */ + if ((Process != PsInitialSystemProcess) && (Process != PsIdleProcess)) + { + /* We still have an active process, count it */ + DPRINT1("%15s is still RUNNING (%p)\n", Process->ImageFileName, Process->UniqueProcessId); + ProcessCount++; + } + + /* Get the next process */ + Process = PsGetNextProcess(Process); + } + + return ProcessCount; +} + +static +VOID +PopShutdownSystem( + _In_ POWER_ACTION SystemAction) +{ + NTSTATUS Status; + + /* Unload the debug symbols before shutdown */ + DbgUnLoadImageSymbols(NULL, (PVOID)-1, 0); + + /* Shutdown the system depending on what the caller asked */ + switch (SystemAction) + { + case PowerActionShutdownReset: + { + /* It asked for a reboot, handle it to the dedicated state handler */ + PopInvokeSystemStateHandler(PowerStateShutdownReset, NULL); + + /* That did not work, we must reboot the system with the help of HAL instead */ + HalReturnToFirmware(HalRebootRoutine); + break; + } + + case PowerActionShutdown: + { + /* + * The caller wants to shutdown the system but not powering it OFF. + * In this case we must forcibly register the default state handler to + * ours, thereby ignoring the one registered by HAL. + */ + if (PopShutdownPowerOffPolicy) + { + if (PopDefaultPowerStateHandlers[PowerStateShutdownOff].Handler != PopShutdownHandler) + { + Status = PopRegisterSystemStateHandler(PowerStateShutdownOff, + FALSE, + PopShutdownHandler, + NULL); + NT_ASSERT(NT_SUCCESS(Status)); + } + } + + /* + * Invoke the state handler. This should not fail supposedly we registered + * the system state with our handler. But in case we fail the request we + * must power down... + */ + PopInvokeSystemStateHandler(PowerStateShutdownOff, NULL); + HalReturnToFirmware(HalPowerDownRoutine); + break; + } + + case PowerActionShutdownOff: + { + /* The caller wants to shutdown the system completely */ + PopInvokeSystemStateHandler(PowerStateShutdownOff, NULL); + + /* That did not work, we must shutdown the system with the help of HAL instead */ + HalReturnToFirmware(HalPowerDownRoutine); + break; + } + + default: + { + /* I do not know what the caller asked, so better reboot the machine */ + HalReturnToFirmware(HalRebootRoutine); + break; + } + } + + /* Anything else should not happen */ + KeBugCheckEx(INTERNAL_POWER_ERROR, 5, 0, 0, 0); +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +_Use_decl_annotations_ +VOID +NTAPI +PopGracefulShutdown( + _In_ PVOID Parameter) +{ + ULONG ProcessCount; + + /* + * If somebody issued a bugcheck within the power actions, at this + * point we must crash the system now! + */ + if (PopAction.ShutdownBugCode) + { + KeBugCheckEx(PopAction.ShutdownBugCode->Code, + PopAction.ShutdownBugCode->Parameter1, + PopAction.ShutdownBugCode->Parameter2, + PopAction.ShutdownBugCode->Parameter3, + PopAction.ShutdownBugCode->Parameter4); + } + + /* + * If a clean shutdown procedure was requested we must shutdown every + * subsystem of the Executive layer of the kernel. + */ + if (PopShutdownCleanly) + { + /* Process the registered shutdown events and queued work items */ + PopProcessShutDownLists(); + + /* + * Invoke the Process Manager to terminate any non-critical user mode + * processes that are still present in the process space, and shutdown + * the Process Manager. + * + * Normally any user mode app is given a shutdown notification and respond to it + * with no haste, but if ever a naughty application. + */ + DPRINT1("Process manager shutting down\n"); + PsShutdownSystem(); + + /* Make sure that no other processes are alive other than the system ones */ + ProcessCount = PopEnumLiveProcesses(); + ASSERT(ProcessCount == 0); + } + + /* Invoke the "End Of Boot" HAL routine as the system is shutting down */ + DPRINT1("Executing HAL End of Boot procedure\n"); + HalEndOfBoot(); + + /* Shutdown the Shim cache manager if enabled */ + DPRINT1("Shim cache manager shutting down\n"); + ApphelpCacheShutdown(); + + /* + * Shutdown the I/O manager in Phase 0. At this phase every driver is notified + * with a shutdown IRP to let them know that shutdown is imminent so that they + * can prepare packing up stuff. + */ + DPRINT1("I/O manager shutting down in phase 0\n"); + IoShutdownSystem(0); + + /* Flush the registry hives and shutdown the Configuration Manager */ + DPRINT1("Configuration Manager shutting down\n"); + CmShutdownSystem(); + + /* Punt the hard-error related stuff initialized by the Executive and shutdown it */ + DPRINT1("Executive shutting down\n"); + ExShutdownSystem(); + + /* + * Shutdown the Memory Manager in Phase 0. At this phase all the paging files are + * closed and their corresponding page file names freed. + */ + DPRINT1("Memory manager shutting down in phase 0\n"); + MmShutdownSystem(0); + + /* Wait on the Cache Controller to process the last batch of lazy writer activity */ + DPRINT1("Waiting on Cc to finish the current lazy writer activity\n"); + CcWaitForCurrentLazyWriterActivity(); + + /* Flush all user files and shutdown the Cache Controller */ + DPRINT1("Cache Controller shutting down\n"); + CcShutdownSystem(); + + /* Process the last I/O operations and shutdown that manager completely */ + DPRINT1("I/O manager shutting down in phase 1\n"); + IoShutdownSystem(1); + + /* FIXME: Must broadcast the power IRP to all devices here */ + + /* Shutdown the Object Manager */ + if (PopShutdownCleanly) + { + DPRINT1("Object Manager shutting down\n"); + ObShutdownSystem(); + } + + /* Disable any wake timer alarms */ + DPRINT1("Disabling HAL wake alarms\n"); + HalSetWakeEnable(FALSE); + + /* Perform the final shutdown of the Memory Manager */ + DPRINT1("Memory manager shutting down in phase 2\n"); + MmShutdownSystem(2); + + /* Handle the power action to shutdown the system */ + DPRINT1("System shutdown imminent... (action %lx)\n", PopAction.Action); + PopShutdownSystem(PopAction.Action); +} + +NTSTATUS +NTAPI +PopShutdownHandler( + _In_opt_ PVOID Context, + _In_opt_ PENTER_STATE_SYSTEM_HANDLER SystemHandler, + _In_opt_ PVOID SystemContext, + _In_ LONG NumberProcessors, + _In_opt_ LONG volatile *Number) +{ + /* Disable interrupts and make sure the shutdown screen is enacted on the first processor */ + _disable(); + if (KeGetCurrentPrcb()->Number == 0) + { + /* Display the shutdown screen only if boot video is installed */ + if (InbvIsBootDriverInstalled()) + { + if (!InbvCheckDisplayOwnership()) + { + InbvAcquireDisplayOwnership(); + } + + InbvResetDisplay(); + DisplayShutdownBitmap(); + } + else + { + /* No boot video is installed, display the shutdown screen in text-mode */ + DisplayShutdownText(); + } + } + + /* Hang the system */ + for (;;) + { + HalHaltSystem(); + } +} + +NTSTATUS +NTAPI +PoQueueShutdownWorkItem( + _In_ PWORK_QUEUE_ITEM WorkItem) +{ + PAGED_CODE(); + + /* + * Do not enqueue a shutdown work item if a shutdown action + * is already in progress. + */ + PopAcquireShutdownLock(); + if (!PopShutdownListAvailable) + { + DPRINT1("Cannot enqueue new shutdown work items, shutdown is in progress\n"); + PopReleaseShutdownLock(); + return STATUS_SYSTEM_SHUTDOWN; + } + + InsertTailList(&PopShutdownQueue, &WorkItem->List); + PopReleaseShutdownLock(); + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +PoRequestShutdownEvent( + _Out_ PVOID *Event) +{ + NTSTATUS Status; + + PAGED_CODE(); + + /* Initialize the pointer to NULL */ + if (Event) + { + *Event = NULL; + } + + /* Invoke the private helper to do the job */ + Status = PopRequestShutdownEvent(PsGetCurrentThread()); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to request a shutdown event (Status 0x%lx)\n", Status); + return Status; + } + + /* Give to the caller the global shutdown event */ + if (Event) + { + *Event = &PopShutdownEvent; + } + + return STATUS_SUCCESS; +} + +/* EOF */ diff --git a/ntoskrnl/po/state.c b/ntoskrnl/po/state.c new file mode 100644 index 0000000000000..2796035770eb4 --- /dev/null +++ b/ntoskrnl/po/state.c @@ -0,0 +1,645 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power states and Idle management for System and Devices infrastructure + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +KDPC PopIdleScanDevicesDpc; +KTIMER PopIdleScanDevicesTimer; +LIST_ENTRY PopIdleDetectList; +ULONG PopIdleScanIntervalInSeconds = 1; +POWER_STATE_HANDLER PopDefaultPowerStateHandlers[PowerStateMaximum] = {0}; + +/* PRIVATE FUNCTIONS **********************************************************/ + +static +VOID +PopSystemRequired(VOID) +{ + /* FIXME */ + UNIMPLEMENTED; + return; +} + +static +VOID +PopDisplayRequired(VOID) +{ + /* FIXME */ + UNIMPLEMENTED; + return; +} + +static +VOID +PopUserPresent(VOID) +{ + /* FIXME */ + UNIMPLEMENTED; + return; +} + +static +VOID +PopWaitForAllSystemStateDpcs( + _In_ PPOP_POWER_STATE_HANDLER_COMMAND_CONTEXT StateCommandContext, + _In_ ULONG ProcessorsCount) +{ + /* Keep waiting until every processor has their DPC ready */ + for (;;) + { + if (StateCommandContext->DpcReadyForProcess == ProcessorsCount) + { + break; + } + } +} + +static +VOID +PopHandlePshc( + _In_ PPOP_POWER_STATE_HANDLER_COMMAND_CONTEXT StateCommandContext, + _Inout_ PPOP_POWER_STATE_HANDLER_PROCESSOR_CONTEXT ProcessorContext) +{ + NTSTATUS Status, FpStatus; + KFLOATING_SAVE FloatingSave; + POP_POWER_HANDLER_COMMAND Command; + + /* Wait until the boot processor instructs a new command */ + for (;;) + { + if (StateCommandContext->ExecutingCommand != ProcessorContext->CurrentCommand) + { + break; + } + + YieldProcessor(); + } + + /* Process the following command */ + Command = StateCommandContext->ExecutingCommand; + switch (Command) + { + case SaveFloatingPointContext: + { + /* Save the floating point context of this processor */ + FpStatus = KeSaveFloatingPointState(&FloatingSave); + if (NT_SUCCESS(FpStatus)) + { + ProcessorContext->FpContext = FloatingSave; + ProcessorContext->FpStatus = FpStatus; + } + + break; + } + + case InvokePowerStateHandler: + { + /* Invoke the state handler on this processor */ + Status = StateCommandContext->StateHandler->Handler(StateCommandContext->ContextData.StateHandlerData.Context, + StateCommandContext->ContextData.StateHandlerData.SystemHandler, + StateCommandContext->ContextData.StateHandlerData.SystemContext, + StateCommandContext->ContextData.StateHandlerData.NumberProcessors, + &StateCommandContext->ContextData.StateHandlerData.Number); + + /* Cache the state handler status to the processor context */ + ProcessorContext->Status = Status; + break; + } + + case RestoreFloatingPointContext: + { + /* Restore the floating point context if we saved it */ + if (NT_SUCCESS(ProcessorContext->FpStatus)) + { + KeRestoreFloatingPointState(&ProcessorContext->FpContext); + } + + break; + } + + case QuitDpc: + { + /* There is nothing to do here */ + break; + } + + default: + { + /* I do not know any other PSHC commands */ + ASSERT(FALSE); + break; + } + } + + /* + * This processor has done processing the command, cache it as the last + * command and acknowledge the boot processor that we have handled it. + */ + ProcessorContext->CurrentCommand = Command; + InterlockedIncrementUL(&StateCommandContext->ProcessorHandledCommand); +} + +static +VOID +PopSendPshc( + _In_ POP_POWER_HANDLER_COMMAND Command, + _In_ ULONG ProcessorsCount, + _In_ PPOP_POWER_STATE_HANDLER_COMMAND_CONTEXT StateCommandContext, + _Inout_ PPOP_POWER_STATE_HANDLER_PROCESSOR_CONTEXT ProcessorContext) +{ + /* Setup a new command to the global state command context */ + InterlockedExchangeUL(&StateCommandContext->ExecutingCommand, Command); + + /* Initialize the handled command counter */ + InterlockedExchangeUL(&StateCommandContext->ProcessorHandledCommand, 0); + + /* Handle the power state handler command */ + PopHandlePshc(StateCommandContext, ProcessorContext); + + /* Now wait for every processor to handle this command */ + for (;;) + { + if (StateCommandContext->ProcessorHandledCommand == ProcessorsCount) + { + break; + } + + YieldProcessor(); + } +} + +_Function_class_(KDEFERRED_ROUTINE) +VOID +NTAPI +PopProcessSystemStateCommand( + _In_ PKDPC Dpc, + _In_ PVOID DeferredContext, + _In_ PVOID SystemArgument1, + _In_ PVOID SystemArgument2) +{ + POP_POWER_STATE_HANDLER_PROCESSOR_CONTEXT ProcessorContext; + PPOP_POWER_STATE_HANDLER_COMMAND_CONTEXT StateCommandContext = (PPOP_POWER_STATE_HANDLER_COMMAND_CONTEXT)DeferredContext; + + /* We do not care for these parameters */ + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(SystemArgument1); + UNREFERENCED_PARAMETER(SystemArgument2); + + /* Setup the processor command context that is bound to this processor */ + RtlZeroMemory(&ProcessorContext, sizeof(ProcessorContext)); + + /* Is the boot processor initializing DPCs for other processors? */ + if (StateCommandContext->InitializingDpcs) + { + /* + * Tell the boot processor that we are ready on our end by incrementing the + * count of DPCs that are ready for processing power state handler commands. + */ + InterlockedIncrementUL(&StateCommandContext->DpcReadyForProcess); + } + + /* + * Handle any upcoming power state handler commnad (PSHC) on this processor + * from the invoking boot processor. Cease the execution when the boot processor + * told us the system handler invocation is done and no longer progressing. + */ + for (;;) + { + if (ProcessorContext.CurrentCommand == QuitDpc) + { + break; + } + + PopHandlePshc(StateCommandContext, &ProcessorContext); + } +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +NTSTATUS +NTAPI +PopInvokeSystemStateHandler( + _In_ POWER_STATE_HANDLER_TYPE HandlerType, + _In_opt_ PPOP_HIBER_CONTEXT HiberContext) +{ + NTSTATUS Status; + KIRQL OldIrql; + KDPC StateHandlerDpc; + ULONG ProcessorsCount, ProcessorIndex; + POP_POWER_STATE_HANDLER_PROCESSOR_CONTEXT ProcessorContext; + POP_POWER_STATE_HANDLER_COMMAND_CONTEXT StateCommandContext; + + /* Nobody else must be raising the IRQL with this function called */ + ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); + + /* The caller submitted a bogus handler type, bail out */ + if (HandlerType < PowerStateSleeping1 || HandlerType > PowerStateShutdownOff) + { + DPRINT1("Unknown state type handler (%lu), quitting...\n", HandlerType); + return STATUS_INVALID_PARAMETER; + } + + /* + * Cache the state handler and check if it has been registered + * with the Power Manager, otherwise do not bother. + */ + RtlZeroMemory(&StateCommandContext, sizeof(StateCommandContext)); + StateCommandContext.StateHandler = &PopDefaultPowerStateHandlers[HandlerType]; + if (!StateCommandContext.StateHandler->Handler) + { + DPRINT1("No state handler registered for this type (%lu), quitting...\n", HandlerType); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + /* + * Query the number of active processors of which we must invoke + * a power state handler command to each of them. + */ + ProcessorsCount = PopQueryActiveProcessors(); + + /* TODO: Grab the hibernation context and do something with it (once I write support for it) */ + StateCommandContext.HiberContext = HiberContext; + + /* + * Have the function calling thread be running on the boot processor and + * increase the IRQL to dispatch level. We do this because the boot processor + * is the one invoking the power state handler command to every other active + * processor of the system. + */ + KeSetSystemAffinityThread(1); + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); + + /* + * If this is a MP machine we have to invoke the system state + * handler to every processor. + */ + if (ProcessorsCount > 1) + { + /* + * Setup a state handler command DPC which we will enqueue it to every + * existing processor active in the system. This ensures that each + * processor executes the system state handler separately and handles + * their own processor context data by themselves. + */ + StateCommandContext.InitializingDpcs = TRUE; + KeInitializeDpc(&StateHandlerDpc, PopProcessSystemStateCommand, &StateCommandContext); + KeSetImportanceDpc(&StateHandlerDpc, HighImportance); + + /* + * Loop every CPU and enqueue a DPC to each of them. This will make each + * processor spinning until the boot processor (that is us) invokes a + * power state handler command to every processor. + */ + for (ProcessorIndex = 0; + ProcessorIndex < ProcessorsCount; + ProcessorIndex++) + { + /* + * Of course make sure that we do not insert the DPC for the boot + * processor, thereby hurting ourselves by spinning it forever. + */ + if (ProcessorIndex != KeGetCurrentPrcb()->Number) + { + /* Assign the DPC to the target processor */ + KeSetTargetProcessorDpc(&StateHandlerDpc, ProcessorIndex); + + /* Insert the DPC to the target processor in queue */ + KeInsertQueueDpc(&StateHandlerDpc, NULL, NULL); + } + } + + /* Wait for all the processor DPCs to be ready */ + PopWaitForAllSystemStateDpcs(&StateCommandContext, ProcessorsCount); + + /* All DPCs ready, we are no longer initializing */ + StateCommandContext.InitializingDpcs = FALSE; + } + + /* Setup a state handler processor context for the boot processor */ + RtlZeroMemory(&ProcessorContext, sizeof(ProcessorContext)); + + /* + * Now that processors are ready to receive PSHC commands, send a "save floating + * point state" command to every other processor, including the boot one. + */ + PopSendPshc(SaveFloatingPointContext, + ProcessorsCount, + &StateCommandContext, + &ProcessorContext); + + /* Check if we have a hibernation context or is the system sleeping/shutting down */ + if (HiberContext) + { + DPRINT1("Not implemented yet my fam buddy\n"); + ASSERT(FALSE); + } + else + { + /* Setup system state arguments (FIXME: this is not enough though...) */ + StateCommandContext.ContextData.StateHandlerData.NumberProcessors = KeNumberProcessors; + StateCommandContext.ContextData.StateHandlerData.Number = KeNumberProcessors; + + /* + * The system is either sleeping or shutting down, invoke the system + * state handler to every existing active processor. + */ + PopSendPshc(InvokePowerStateHandler, + ProcessorsCount, + &StateCommandContext, + &ProcessorContext); + + /* Cache the returned status from the system state handler */ + Status = ProcessorContext.Status; + } + + /* Restore the saved floating point state context */ + PopSendPshc(RestoreFloatingPointContext, + ProcessorsCount, + &StateCommandContext, + &ProcessorContext); + + /* Invoke every processor to quit their DPCs as the operation is done */ + PopSendPshc(QuitDpc, + ProcessorsCount, + &StateCommandContext, + &ProcessorContext); + + KeLowerIrql(OldIrql); + return Status; +} + +_Function_class_(KDEFERRED_ROUTINE) +VOID +NTAPI +PopScanForIdleStateDevicesDpcRoutine( + _In_ PKDPC Dpc, + _In_ PVOID DeferredContext, + _In_ PVOID SystemArgument1, + _In_ PVOID SystemArgument2) +{ + NTSTATUS Status; + KIRQL OldIrql; + PLIST_ENTRY Entry; + POWER_STATE State; + PDEVICE_OBJECT DeviceObject; + POP_DEVICE_IDLE_TYPE IdleType; + ULONG IdleTreshold, NewIdleCount; + PDEVICE_OBJECT_POWER_EXTENSION Dope; + + /* We do not care for these parameters */ + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(SystemArgument1); + UNREFERENCED_PARAMETER(SystemArgument2); + + /* Begin iterating over the global idle detect list for any registered devices */ + PopAcquireDopeLock(&OldIrql); + for (Entry = PopIdleDetectList.Flink; + Entry != &PopIdleDetectList; + Entry = Entry->Flink) + { + /* Capture the DOPE of this device */ + Dope = CONTAINING_RECORD(Entry, DEVICE_OBJECT_POWER_EXTENSION, IdleList); + ASSERT(Dope != NULL); + + /* Cache the device object */ + DeviceObject = Dope->DeviceObject; + + /* + * FIRST RULE of the thumb: look for active busy references the caller + * is still holding onto this device. A reference above 0 means the caller + * has an active outstanding instance call of PoStartDeviceBusy, and this + * could go for as much as the caller wishes, until it explicitly tells the + * power manager that the device stopped being busy. We do not touch the busy + * count here. + */ + if (Dope->BusyReference > 0) + { + /* + * The act of disabling the idle counter as per MSDN documentation actually means + * resetting the said counter back to 0 if the device used to be idling before. + */ + if (Dope->IdleCount > 0) + { + Dope->IdleCount = 0; + } + + DPRINT("The device object (0x%p) with DOPE (0x%p) is busy", DeviceObject, Dope); + continue; + } + + /* + * SECOND RULE of the thumb: this device does not have active busy references + * but it is being held busy for a brief period of time. The function which is + * responsible for that is PoSetDeviceBusyEx. + */ + if (Dope->BusyCount > 0) + { + /* + * As this device was beind held for a short period of time, now it is + * time to decrease the busy count by one. The caller is responsible to + * keep it busy with multiple PoSetDeviceBusyEx requests. + */ + Dope->BusyCount--; + + /* + * If this device used to be idling before at the time of declaring itself + * as busy, reset its idle counter. + */ + if (Dope->IdleCount > 0) + { + Dope->IdleCount = 0; + } + + DPRINT("The device object (0x%p) with DOPE (0x%p) is busy for a brief period of time", DeviceObject, Dope); + continue; + } + + /* This device is not busy, increment the idle counter */ + NewIdleCount = InterlockedIncrementUL(&Dope->IdleCount); + + /* Obtain the idle time treshold based on the device type */ + IdleType = Dope->IdleType; + if (IdleType == DeviceIdleNormal) + { + /* + * Grab the treshold from the registered conservation or performance + * idle time, depending on the power policy the power manager has + * currently enforced. + */ + if (PopDefaultPowerPolicy == &PopDcPowerPolicy) + { + /* The system runs on batteries, so favor the conservation idle time */ + IdleTreshold = Dope->ConservationIdleTime; + } + else + { + /* + * The system runs on AC power (typically from PSU or its batteries + * are charging), favor the performance idle time. + */ + IdleTreshold = Dope->PerformanceIdleTime; + } + } + else if (IdleType == DeviceIdleDisk) + { + /* + * This device is a disk or mass storage device, grab the treshold from + * the default spin-down idle time of the currently enforced power policy. + */ + IdleTreshold = PopDefaultPowerPolicy->SpindownTimeout; + } + else + { + /* The Power Manager does not know of this device, bail out */ + DPRINT1("The device (0x%p) with DOPE (0x%p) is of an unknown type (%lu), crash is imminent\n", + DeviceObject, Dope, IdleType); + KeBugCheckEx(INTERNAL_POWER_ERROR, + 0x200, + POP_IDLE_DETECT_UNKNOWN_DEVICE, + (ULONG_PTR)DeviceObject, + (ULONG_PTR)Dope); + } + + /* Send a power IRP to this device if it is idling for long enough */ + if (IdleTreshold && (NewIdleCount == IdleTreshold)) + { + State.DeviceState = Dope->IdleState; + Status = PopRequestPowerIrp(DeviceObject, + IRP_MN_SET_POWER, + State, + FALSE, + FALSE, + NULL, + NULL, + NULL); + NT_ASSERT(Status == STATUS_PENDING); + Dope->CurrentState = Dope->IdleState; + } + } + + PopReleaseDopeLock(OldIrql); +} + +ULONG +NTAPI +PopGetDoePowerState( + _In_ PEXTENDED_DEVOBJ_EXTENSION DevObjExts, + _In_ BOOLEAN GetSystem) +{ + ULONG PowerFlags; + + if (GetSystem) + { + PowerFlags = (DevObjExts->PowerFlags & POP_DOE_SYSTEM_POWER_FLAG_BIT); + } + else + { + PowerFlags = ((DevObjExts->PowerFlags & POP_DOE_DEVICE_POWER_FLAG_BIT) >> 4); + } + + return PowerFlags; +} + +VOID +NTAPI +PopSetDoePowerState( + _In_ PEXTENDED_DEVOBJ_EXTENSION DevObjExts, + _In_ POWER_STATE NewState, + _In_ BOOLEAN SetSystem) +{ + SYSTEM_POWER_STATE SystemState; + DEVICE_POWER_STATE DeviceState; + + if (SetSystem) + { + SystemState = NewState.SystemState; + DevObjExts->PowerFlags |= SystemState & POP_DOE_SYSTEM_POWER_FLAG_BIT; + } + else + { + DeviceState = NewState.DeviceState; + DevObjExts->PowerFlags |= ((DeviceState << 4) & POP_DOE_DEVICE_POWER_FLAG_BIT); + } +} + +NTSTATUS +NTAPI +PopRegisterSystemStateHandler( + _In_ POWER_STATE_HANDLER_TYPE Type, + _In_ BOOLEAN RtcWake, + _In_ PENTER_STATE_HANDLER Handler, + _In_opt_ PVOID Context) +{ + PAGED_CODE(); + + /* Caller was trying to give an invalid handler type, bail out */ + if (Type < PowerStateSleeping1 || Type >= PowerStateMaximum) + { + DPRINT1("Invalid power state handler type was given (Type %d)\n", Type); + return STATUS_INVALID_PARAMETER_1; + } + + /* We know the type but no state handler given? Bail out! */ + if (!Handler) + { + DPRINT1("No power state handler given\n"); + return STATUS_INVALID_PARAMETER_3; + } + + /* Register the system state handler with the Power Manager now */ + PopDefaultPowerStateHandlers[Type].Type = Type; + PopDefaultPowerStateHandlers[Type].RtcWake = RtcWake; + PopDefaultPowerStateHandlers[Type].Handler = Handler; + PopDefaultPowerStateHandlers[Type].Context = Context; + + return STATUS_SUCCESS; +} + +VOID +NTAPI +PopIndicateSystemStateActivity( + _In_ EXECUTION_STATE StateActivity) +{ + /* We must not be above the permitted IRQL */ + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* + * We got acknowledgement from the caller that the system is currently busy, + * handle this on an execution state basis. For system required scenario we + * will reset the system idle counter. + */ + if (StateActivity & ES_SYSTEM_REQUIRED) + { + PopSystemRequired(); + } + + /* + * For display required we must tell GDI to not dim the display + * by resetting the display idle counter. + */ + if (StateActivity & ES_DISPLAY_REQUIRED) + { + PopDisplayRequired(); + } + + /* + * Tell GDI the physical presence of a user, wake the system and + * reset the system idle counter. + */ + if (StateActivity & ES_USER_PRESENT) + { + PopUserPresent(); + } +} + +/* EOF */ diff --git a/ntoskrnl/po/thermreq.c b/ntoskrnl/po/thermreq.c new file mode 100644 index 0000000000000..2f26bbb23a514 --- /dev/null +++ b/ntoskrnl/po/thermreq.c @@ -0,0 +1,18 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power Manager thermal request implementation support + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* PUBLIC FUNCTIONS ***********************************************************/ + +/* EOF */ diff --git a/ntoskrnl/po/thermzn.c b/ntoskrnl/po/thermzn.c new file mode 100644 index 0000000000000..6f4f171410307 --- /dev/null +++ b/ntoskrnl/po/thermzn.c @@ -0,0 +1,22 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Thermal zones manager infrastructure + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +KSPIN_LOCK PopThermalZoneLock; +LIST_ENTRY PopThermalZones; +ULONG PopCoolingSystemMode = 0; + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* EOF */ diff --git a/ntoskrnl/po/thrtmgr.c b/ntoskrnl/po/thrtmgr.c new file mode 100644 index 0000000000000..ac03e0205bd7b --- /dev/null +++ b/ntoskrnl/po/thrtmgr.c @@ -0,0 +1,16 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Throttle manager infrastructure + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS **********************************************************/ + +/* EOF */ diff --git a/ntoskrnl/po/povolume.c b/ntoskrnl/po/voldope.c similarity index 50% rename from ntoskrnl/po/povolume.c rename to ntoskrnl/po/voldope.c index 1897c045942dd..9d5eeacfef936 100644 --- a/ntoskrnl/po/povolume.c +++ b/ntoskrnl/po/voldope.c @@ -1,151 +1,32 @@ /* - * 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 + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power Manager volumes and Device Object Power Extension (DOPE) support routines + * COPYRIGHT: Copyright 2010 ReactOS Portable Systems Group + * Copyright 2023 George BiÈ™oc */ -/* INCLUDES ******************************************************************/ +/* 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; +/* GLOBALS ********************************************************************/ KGUARDED_MUTEX PopVolumeLock; -LIST_ENTRY PopVolumeDevices; KSPIN_LOCK PopDopeGlobalLock; +LIST_ENTRY PopVolumeDevices; +ULONG PopVolumeFlushPolicy = 0; +PDEVICE_NODE PopSystemPowerDeviceNode = NULL; -/* 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), - TAG_PO_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, TAG_PO_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) */ - Dope = PopGetDope(DeviceObject); - if (Dope) - { - /* Make sure we can flush safely */ - KeAcquireGuardedMutex(&PopVolumeLock); - - /* Add this volume into the list of power-manager volumes */ - if (!Dope->Volume.Flink) InsertTailList(&PopVolumeDevices, &Dope->Volume); - - /* Allow flushes to go through */ - KeReleaseGuardedMutex(&PopVolumeLock); - } -} - -VOID -NTAPI -PoRemoveVolumeDevice(IN PDEVICE_OBJECT DeviceObject) -{ - PDEVICE_OBJECT_POWER_EXTENSION Dope; - PEXTENDED_DEVOBJ_EXTENSION DeviceExtension; - KIRQL OldIrql; - PAGED_CODE(); - - /* If the device already has the dope, return it */ - DeviceExtension = IoGetDevObjExtension(DeviceObject); - if (!DeviceExtension->Dope) - { - /* no dope */ - return; - } - - /* Make sure we can flush safely */ - KeAcquireGuardedMutex(&PopVolumeLock); - - /* Get dope from device */ - Dope = (PDEVICE_OBJECT_POWER_EXTENSION)DeviceExtension->Dope; - - if (Dope->Volume.Flink) - { - /* Remove from volume from list */ - RemoveEntryList(&Dope->Volume); - } - - /* Allow flushes to go through */ - KeReleaseGuardedMutex(&PopVolumeLock); - - /* Now remove dope from device object */ - KeAcquireSpinLock(&PopDopeGlobalLock, &OldIrql); - - /* remove from dev obj */ - DeviceExtension->Dope = NULL; - - /* Release lock */ - KeReleaseSpinLock(&PopDopeGlobalLock, OldIrql); - - /* Free dope */ - ExFreePoolWithTag(Dope, TAG_PO_DOPE); -} +/* PRIVATE FUNCTIONS **********************************************************/ +_Function_class_(KSTART_ROUTINE) VOID NTAPI -PopFlushVolumeWorker(IN PVOID Context) +PopMasterFlushVolume( + _In_ PVOID Context) { PPOP_FLUSH_VOLUME FlushContext = (PPOP_FLUSH_VOLUME)Context; PDEVICE_OBJECT_POWER_EXTENSION Dope; @@ -158,34 +39,33 @@ PopFlushVolumeWorker(IN PVOID Context) HANDLE VolumeHandle; IO_STATUS_BLOCK IoStatusBlock; - /* Acquire the flush lock since we're messing with the list */ - KeAcquireGuardedMutex(&PopVolumeLock); + /* Do not allow anybody to add power volumes (other than us) while we do a flush operation */ + PopAcquireVolumeLock(); - /* Loop the flush list */ + /* Iterate over the flush list so that we can flush volume entries */ while (!IsListEmpty(&FlushContext->List)) { - /* Grab the next (ie: current) entry and remove it */ + /* Grab the entry from the flush context and add it */ NextEntry = FlushContext->List.Flink; RemoveEntryList(NextEntry); - - /* Add it back on the volume list */ InsertTailList(&PopVolumeDevices, NextEntry); - /* Done touching the volume list */ - KeReleaseGuardedMutex(&PopVolumeLock); + /* We are done touching the volume list */ + PopReleaseVolumeLock(); - /* Get the dope from the volume link */ + /* Grab the DOPE from the volume link and volume name for flushing */ 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 */ - DPRINT("Opening: %wZ\n", &NameInfo->Name); + /* + * We queried the volume name, this is required to open a + * file handle of the said volume so that we can flush its data. + */ + DPRINT("Opening volume: %wZ\n", &NameInfo->Name); InitializeObjectAttributes(&ObjectAttributes, &NameInfo->Name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, @@ -204,50 +84,160 @@ PopFlushVolumeWorker(IN PVOID Context) 0); if (NT_SUCCESS(Status)) { - /* Flush it and close it */ + /* Flush it finally */ DPRINT("Sending flush to: %p\n", VolumeHandle); ZwFlushBuffersFile(VolumeHandle, &IoStatusBlock); ZwClose(VolumeHandle); } } - /* Acquire the flush lock again since we'll touch the list */ - KeAcquireGuardedMutex(&PopVolumeLock); + /* We are going to touch the volume list so protect it */ + PopAcquireVolumeLock(); } - /* One more flush completed... if it was the last, signal the caller */ - if (!--FlushContext->Count) KeSetEvent(&FlushContext->Wait, IO_NO_INCREMENT, FALSE); + /* If this was the last flush then signal such event to the caller */ + if (!--FlushContext->Count) + { + KeSetEvent(&FlushContext->Wait, IO_NO_INCREMENT, FALSE); + } - /* Serialize with flushers */ - KeReleaseGuardedMutex(&PopVolumeLock); + /* Give everybody access to the volume list now */ + PopReleaseVolumeLock(); +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +PDEVICE_OBJECT_POWER_EXTENSION +NTAPI +PopGetDope( + _In_ PDEVICE_OBJECT DeviceObject) +{ + PEXTENDED_DEVOBJ_EXTENSION DeviceExtension; + PDEVICE_OBJECT_POWER_EXTENSION Dope; + KIRQL OldIrql; + + PAGED_CODE(); + + /* This device already has a DOPE, just return that instead */ + DeviceExtension = IoGetDevObjExtension(DeviceObject); + if (DeviceExtension->Dope) + { + return (PDEVICE_OBJECT_POWER_EXTENSION)DeviceExtension->Dope; + } + + /* Allocate memory for the DOPE */ + Dope = PopAllocatePool(sizeof(DEVICE_OBJECT_POWER_EXTENSION), FALSE, TAG_PO_DOPE); + if (Dope == NULL) + { + /* Bail out */ + return NULL; + } + + /* Initialize the necessary data to this DOPE */ + Dope->DeviceObject = DeviceObject; + Dope->IdleState = PowerDeviceUnspecified; + Dope->CurrentState = PowerDeviceUnspecified; + InitializeListHead(&Dope->IdleList); + + /* Initialize the busy counters */ + Dope->BusyCount = 0; + Dope->BusyReference = 0; + Dope->TotalBusyCount = 0; + + /* + * Acquire the DOPE lock so that we can safely assign this DOPE + * to the device. Note that if somebody else already went fast + * enough to assign it before us, then we have discard whatever + * we allocated. + */ + PopAcquireDopeLock(&OldIrql); + if (!DeviceExtension->Dope) + { + /* OK, we are good to assign our DOPE to this device */ + DeviceExtension->Dope = (PVOID)Dope; + Dope = NULL; + } + + /* Someone assigned a DOPE before us, trash our DOPE */ + PopReleaseDopeLock(OldIrql); + if (Dope) + { + PopFreePool(Dope, TAG_PO_DOPE); + } + + return (PDEVICE_OBJECT_POWER_EXTENSION)DeviceExtension->Dope; } VOID NTAPI -PopFlushVolumes(IN BOOLEAN ShuttingDown) +PopRemoveVolumeDevice( + _In_ PDEVICE_OBJECT DeviceObject) +{ + PDEVICE_OBJECT_POWER_EXTENSION Dope; + PEXTENDED_DEVOBJ_EXTENSION DeviceExtension; + KIRQL OldIrql; + + PAGED_CODE(); + + /* This device never had a DOPE, consider our job as done */ + DeviceExtension = IoGetDevObjExtension(DeviceObject); + if (!DeviceExtension->Dope) + { + return; + } + + /* Remove the volume while guarded with lock guarded */ + PopAcquireVolumeLock(); + Dope = (PDEVICE_OBJECT_POWER_EXTENSION)DeviceExtension->Dope; + if (Dope->Volume.Flink) + { + RemoveEntryList(&Dope->Volume); + } + + /* Release the lock */ + PopReleaseVolumeLock(); + + /* Now tear the DOPE from this DO apart */ + PopAcquireDopeLock(&OldIrql); + DeviceExtension->Dope = NULL; + PopReleaseDopeLock(OldIrql); + + /* And free it from memory */ + PopFreePool(Dope, TAG_PO_DOPE); +} + +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; + PDEVICE_OBJECT_POWER_EXTENSION Dope; NTSTATUS Status; HANDLE ThreadHandle; ULONG ThreadCount; + ULONG VolumeCount = 0; + POP_FLUSH_VOLUME FlushContext = {{0}}; + static UNICODE_STRING RegistryName = RTL_CONSTANT_STRING(L"\\Registry"); - /* Setup the flush context */ + /* Prepare the flush context */ InitializeListHead(&FlushContext.List); KeInitializeEvent(&FlushContext.Wait, NotificationEvent, FALSE); - /* What to flush */ - FlushPolicy = ShuttingDown ? 1 | 2 : PopFlushPolicy; - if ((FlushPolicy & 1)) + /* + * Determine what should we flush exactly. During a shutdown power + * action we flush the registry and every volume that is writable. + * Note that if the system was not shutting down but is put to sleep + * or goes to hibernation path, only flush what the flush policy allows. + */ + FlushPolicy = ShuttingDown ? POP_FLUSH_REGISTRY | POP_FLUSH_NON_REM_DEVICES : PopVolumeFlushPolicy; + + /* A flush of the registry is requested, do the deed */ + if ((FlushPolicy & POP_FLUSH_REGISTRY)) { - /* Registry flush requested, so open it */ - DPRINT("Opening registry\n"); InitializeObjectAttributes(&ObjectAttributes, &RegistryName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, @@ -256,57 +246,68 @@ PopFlushVolumes(IN BOOLEAN ShuttingDown) Status = ZwOpenKey(&RegistryHandle, KEY_READ, &ObjectAttributes); if (NT_SUCCESS(Status)) { - /* Flush the registry */ - DPRINT("Flushing registry\n"); ZwFlushKey(RegistryHandle); ZwClose(RegistryHandle); } } - /* Serialize with other flushes */ - KeAcquireGuardedMutex(&PopVolumeLock); + /* Block any incoming volumes as we do a flush */ + PopAcquireVolumeLock(); - /* Scan the volume list */ + /* Iterate over the volume list */ NextEntry = PopVolumeDevices.Flink; while (NextEntry != &PopVolumeDevices) { - /* Get the dope from the link */ + /* Grab the DOPE from the volume link */ Dope = CONTAINING_RECORD(NextEntry, DEVICE_OBJECT_POWER_EXTENSION, Volume); - /* Grab the next entry now, since we'll be modifying the list */ + /* Grab the next volume entry */ NextEntry = NextEntry->Flink; - /* Make sure the object is mounted, writable, exists, and is not a floppy */ + /* + * The device object owning this volume has to meet the following + * criteria for a successful flush: + * + * - The volume of the DO is mounted; + * - The DO points to a real device; + * - The DO currently exists and is not a floppy device; + * - The volume is writable. + */ 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 */ + /* + * At least one of the conditions above is not met, + * skip this volume. + */ continue; } - /* Remove it from the dope and add it to the flush context list */ + /* + * The caller requested to skip non-removable devices. + * We will simply ignore volume flushes for these devices. + */ + if (!(FlushPolicy & POP_FLUSH_NON_REM_DEVICES)) + { + if (!(Dope->DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)) + { + continue; + } + } + + /* Insert the volume to the flust context list and count it too */ 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; - } - - /* Check if there were no volumes at all */ + /* No volumes have been found, just quit */ if (!VolumeCount) { - /* Nothing to do */ - KeReleaseGuardedMutex(&PopVolumeLock); + PopReleaseVolumeLock(); return; } @@ -318,58 +319,79 @@ PopFlushVolumes(IN BOOLEAN ShuttingDown) NULL, NULL); - /* We will ourselves become a flusher thread */ + /* + * We will become a flusher thread ourselves so setup the + * thread count context now. + */ FlushContext.Count = 1; ThreadCount--; - /* Look for any extra ones we might need */ + /* We still have other volumes to process so create threads for them */ while (ThreadCount > 0) { - /* Create a new one */ ThreadCount--; - DPRINT("Creating flush thread\n"); Status = PsCreateSystemThread(&ThreadHandle, THREAD_ALL_ACCESS, &ObjectAttributes, 0L, NULL, - PopFlushVolumeWorker, + PopMasterFlushVolume, &FlushContext); if (NT_SUCCESS(Status)) { - /* One more created... */ FlushContext.Count++; ZwClose(ThreadHandle); } } /* Allow flushes to go through */ - KeReleaseGuardedMutex(&PopVolumeLock); + PopReleaseVolumeLock(); - /* Enter the flush work */ - DPRINT("Local flush\n"); - PopFlushVolumeWorker(&FlushContext); - - /* Wait for all flushes to be over */ - DPRINT("Waiting for flushes\n"); + /* Jump into the flusher and wait for other flushes to be over */ + PopMasterFlushVolume(&FlushContext); KeWaitForSingleObject(&FlushContext.Wait, Executive, KernelMode, FALSE, NULL); - DPRINT("Flushes have completed\n"); } VOID NTAPI -PoInitializeDeviceObject(IN OUT PDEVOBJ_EXTENSION DeviceObjectExtension) +PoInitializeDeviceObject( + _Inout_ PDEVOBJ_EXTENSION DeviceObjectExtension) { PEXTENDED_DEVOBJ_EXTENSION DeviceExtension = (PVOID)DeviceObjectExtension; - PAGED_CODE(); - /* Initialize the power flags */ - DeviceExtension->PowerFlags = PowerSystemUnspecified & 0xF; - DeviceExtension->PowerFlags |= ((PowerDeviceUnspecified << 4) & 0xF0); + PAGED_CODE(); - /* The device object is not on drugs yet */ + /* Initialize the power flags and DOPE */ + DeviceExtension->PowerFlags = PowerSystemUnspecified & POP_DOE_SYSTEM_POWER_FLAG_BIT; + DeviceExtension->PowerFlags |= ((PowerDeviceUnspecified << 4) & POP_DOE_DEVICE_POWER_FLAG_BIT); DeviceExtension->Dope = NULL; } -/* PUBLIC FUNCTIONS **********************************************************/ +VOID +NTAPI +PoVolumeDevice( + _In_ PDEVICE_OBJECT DeviceObject) +{ + PDEVICE_OBJECT_POWER_EXTENSION Dope; + + PAGED_CODE(); + + /* + * Retrieve the DOPE from this device. It will allocate a newly + * fresh DOPE if the device never had one. + */ + Dope = PopGetDope(DeviceObject); + if (Dope) + { + /* Insert this volume now */ + PopAcquireVolumeLock(); + if (!Dope->Volume.Flink) + { + InsertTailList(&PopVolumeDevices, &Dope->Volume); + } + + PopReleaseVolumeLock(); + } +} +/* EOF */ diff --git a/ntoskrnl/po/wakesrc.c b/ntoskrnl/po/wakesrc.c new file mode 100644 index 0000000000000..794be593707c3 --- /dev/null +++ b/ntoskrnl/po/wakesrc.c @@ -0,0 +1,25 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power Manager system wake source management + * COPYRIGHT: Copyright 2023 George BiÈ™oc + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +LIST_ENTRY PopWakeSourceDevicesList; +ULONG PopSystemFullWake; +#if 0 +KSEMAPHORE PopWakeSourceResetSemaphore; +KEVENT PopWakeSourceResetComplete; +#endif + +/* PUBLIC FUNCTIONS ***********************************************************/ + +/* EOF */ diff --git a/ntoskrnl/ps/kill.c b/ntoskrnl/ps/kill.c index 4a74649248660..6e8051568e638 100644 --- a/ntoskrnl/ps/kill.c +++ b/ntoskrnl/ps/kill.c @@ -86,8 +86,15 @@ PspTerminateProcess(IN PEPROCESS Process, "Process: %p ExitStatus: %d\n", Process, ExitStatus); PSREFTRACE(Process); - /* Check if this is a Critical Process */ - if (Process->BreakOnTermination) + /* + * Check if this is a Critical Process. Bugcheck the system if a non-authorized + * caller attempts to terminate it. However if a clean shutdown of the kernel + * was inquired and the system is indeed shutting down as ExitStatus indicates + * then we have to terminate it. + */ + if (Process->BreakOnTermination && + !PopShutdownCleanly && + ExitStatus != STATUS_SYSTEM_SHUTDOWN) { /* Break to debugger */ PspCatchCriticalBreak("Terminating critical process 0x%p (%s)\n", @@ -132,7 +139,7 @@ PsTerminateProcess(IN PEPROCESS Process, VOID NTAPI -PspShutdownProcessManager(VOID) +PsShutdownSystem(VOID) { PEPROCESS Process = NULL; @@ -509,7 +516,7 @@ PspExitThread(IN NTSTATUS ExitStatus) ExWaitForRundownProtectionRelease(&Thread->RundownProtect); /* Cleanup the power state */ - PopCleanupPowerState((PPOWER_STATE)&Thread->Tcb.PowerState); + //PopCleanupPowerState((PPOWER_STATE)&Thread->Tcb.PowerState); /* Call the WMI Callback for Threads */ //WmiTraceThread(Thread, NULL, FALSE); @@ -1095,7 +1102,7 @@ PspExitProcess(IN BOOLEAN LastThread, } /* Cleanup the power state */ - PopCleanupPowerState((PPOWER_STATE)&Process->Pcb.PowerState); + //PopCleanupPowerState((PPOWER_STATE)&Process->Pcb.PowerState); /* Clear the security port */ if (!Process->SecurityPort) diff --git a/sdk/include/ddk/ntpoapi.h b/sdk/include/ddk/ntpoapi.h index 7856fff043d6f..4c5d0bae77f40 100644 --- a/sdk/include/ddk/ntpoapi.h +++ b/sdk/include/ddk/ntpoapi.h @@ -127,7 +127,10 @@ typedef enum { PowerActionShutdown, PowerActionShutdownReset, PowerActionShutdownOff, - PowerActionWarmEject + PowerActionWarmEject, +#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) || defined(__REACTOS__) + PowerActionDisplayOff +#endif } POWER_ACTION, *PPOWER_ACTION; #if (NTDDI_VERSION >= NTDDI_WINXP) || !defined(_BATCLASS_) @@ -410,6 +413,49 @@ typedef struct _POWER_ACTION_POLICY { ULONG EventCode; } POWER_ACTION_POLICY, *PPOWER_ACTION_POLICY; +typedef struct _SYSTEM_POWER_LEVEL { + BOOLEAN Enable; + UCHAR Spare[3]; + ULONG BatteryLevel; + POWER_ACTION_POLICY PowerPolicy; + SYSTEM_POWER_STATE MinSystemState; +} SYSTEM_POWER_LEVEL, *PSYSTEM_POWER_LEVEL; + +#define DISCHARGE_POLICY_CRITICAL 0 +#define DISCHARGE_POLICY_LOW 1 +#define NUM_DISCHARGE_POLICIES 4 + +typedef struct _SYSTEM_POWER_POLICY { + ULONG Revision; + POWER_ACTION_POLICY PowerButton; + POWER_ACTION_POLICY SleepButton; + POWER_ACTION_POLICY LidClose; + SYSTEM_POWER_STATE LidOpenWake; + ULONG Reserved; + POWER_ACTION_POLICY Idle; + ULONG IdleTimeout; + UCHAR IdleSensitivity; + UCHAR DynamicThrottle; + UCHAR Spare2[2]; + SYSTEM_POWER_STATE MinSleep; + SYSTEM_POWER_STATE MaxSleep; + SYSTEM_POWER_STATE ReducedLatencySleep; + ULONG WinLogonFlags; + ULONG Spare3; + ULONG DozeS4Timeout; + ULONG BroadcastCapacityResolution; + SYSTEM_POWER_LEVEL DischargePolicy[NUM_DISCHARGE_POLICIES]; + ULONG VideoTimeout; + BOOLEAN VideoDimDisplay; + ULONG VideoReserved[3]; + ULONG SpindownTimeout; + BOOLEAN OptimizeForPower; + UCHAR FanThrottleTolerance; + UCHAR ForcedThrottle; + UCHAR MinThrottle; + POWER_ACTION_POLICY OverThrottled; +} SYSTEM_POWER_POLICY, *PSYSTEM_POWER_POLICY; + /* POWER_ACTION_POLICY.Flags constants */ #define POWER_ACTION_QUERY_ALLOWED 0x00000001 #define POWER_ACTION_UI_ALLOWED 0x00000002 diff --git a/sdk/include/ndk/iotypes.h b/sdk/include/ndk/iotypes.h index 3e78ef20979f6..a971df086289c 100644 --- a/sdk/include/ndk/iotypes.h +++ b/sdk/include/ndk/iotypes.h @@ -1103,6 +1103,112 @@ typedef struct _EXTENDED_DRIVER_EXTENSION PFS_FILTER_CALLBACKS FsFilterCallbacks; } EXTENDED_DRIVER_EXTENSION, *PEXTENDED_DRIVER_EXTENSION; +// +// I/O hibernation dump events +// +typedef enum _DUMP_EVENTS +{ + DUMP_EVENT_NONE = 0, + DUMP_EVENT_HIBER_RESUME = 1, + DUMP_EVENT_HIBER_RESUME_END = 2 +} DUMP_EVENTS, *PDUMP_EVENTS; + +// +// I/O crash dump initialization context +// +typedef struct _DUMP_INITIALIZATION_CONTEXT +{ + ULONG Length; + ULONG Reserved; + PVOID MemoryBlock; + PVOID CommonBuffer[2]; + LARGE_INTEGER PhysicalAddress[2]; + VOID (*StallRoutine)(ULONG arg1); + UCHAR (*OpenRoutine)(LARGE_INTEGER arg1); + LONG (*WriteRoutine)(PLARGE_INTEGER arg1, PMDL arg2); + VOID (*FinishRoutine)(); + PADAPTER_OBJECT AdapterObject; + PVOID MappedRegisterBase; + PVOID PortConfiguration; + BOOLEAN CrashDump; + BOOLEAN MarkMemoryOnly; + BOOLEAN HiberResume; + UCHAR Reserved1; + ULONG MaximumTransferSize; + ULONG CommonBufferSize; + PVOID TargetAddress; + LONG (*WritePendingRoutine)(LONG arg1, PLARGE_INTEGER arg2, PMDL arg3, PVOID arg4); + ULONG PartitionStyle; + union + { + struct + { + ULONG Signature; + ULONG CheckSum; + } Mbr; + struct + { + GUID DiskId; + } Gpt; + } DiskInfo; + LONG (*ReadRoutine)(LONG arg1, PLARGE_INTEGER arg2, PMDL arg3); + LONG (*GetDriveTelemetryRoutine)(ULONG arg1, ULONG arg2, PVOID arg3, ULONG arg4); + ULONG LogSectionTruncateSize; + ULONG Parameters[16]; + VOID (*GetTransferSizesRoutine)(PULONG arg1, PULONG arg2); + VOID (*DumpNotifyRoutine)(DUMP_EVENTS arg1, PVOID arg2, ULONG arg3); +} DUMP_INITIALIZATION_CONTEXT, *PDUMP_INITIALIZATION_CONTEXT; + +// +// I/O crash dump stack context +// +typedef struct _DUMP_STACK_CONTEXT +{ + DUMP_INITIALIZATION_CONTEXT Init; + LARGE_INTEGER PartitionOffset; + PVOID DumpPointers; + ULONG PointersLength; + PUSHORT ModulePrefix; + LIST_ENTRY DriverList; + STRING InitMsg; + STRING ProgMsg; + STRING DoneMsg; + PVOID FileObject; + DEVICE_USAGE_NOTIFICATION_TYPE UsageTypes; +} DUMP_STACK_CONTEXT, *PDUMP_STACK_CONTEXT; + +// +// PnP Device Triage Completion Queue +// +typedef struct _TRIAGE_PNP_DEVICE_COMPLETION_QUEUE +{ + LIST_ENTRY DispatchedList; +} TRIAGE_PNP_DEVICE_COMPLETION_QUEUE, *PTRIAGE_PNP_DEVICE_COMPLETION_QUEUE; + +// +// Power 0x9F Failure Triage (see https://idebug.blogspot.com/2011/12/bug-check-0x9f-driverpowerstatefailure.html) +// +typedef struct _TRIAGE_9F_POWER +{ + ULONG Signature; + UCHAR Revision; + LIST_ENTRY IrpList; + LIST_ENTRY ThreadList; + PVOID DelayedWorkerQueue; // FIXME: This must be _TRIAGE_EX_WORK_QUEUE once we support KPRIQUEUEs +} TRIAGE_9F_POWER, *PTRIAGE_9F_POWER; + +// +// PnP 0x9F Failure Triage (see https://bsodtutorials.wordpress.com/2020/03/15/the-complete-guide-to-debugging-a-stop-0x9f-part-3) +// +typedef struct _TRIAGE_9F_PNP +{ + ULONG Signature; + UCHAR Revision; + PTRIAGE_PNP_DEVICE_COMPLETION_QUEUE CompletionQueue; + PVOID DelayedWorkQueue; // FIXME: This must be _TRIAGE_EX_WORK_QUEUE once we support KPRIQUEUEs + PVOID DelayedIoWorkQueue; // FIXME: This must be _TRIAGE_EX_WORK_QUEUE once we support KPRIQUEUEs +} TRIAGE_9F_PNP, *PTRIAGE_9F_PNP; + #endif // !NTOS_MODE_USER // diff --git a/sdk/include/ndk/pofuncs.h b/sdk/include/ndk/pofuncs.h index e4f85e2e95a3a..cd620beba5e3e 100644 --- a/sdk/include/ndk/pofuncs.h +++ b/sdk/include/ndk/pofuncs.h @@ -63,7 +63,7 @@ NTSTATUS NTAPI NtGetDevicePowerState( _In_ HANDLE Device, - _In_ PDEVICE_POWER_STATE PowerState + _Out_ PDEVICE_POWER_STATE PowerState ); NTSYSCALLAPI diff --git a/sdk/include/ndk/umtypes.h b/sdk/include/ndk/umtypes.h index 3037151b6b0fd..63dc524aeb464 100644 --- a/sdk/include/ndk/umtypes.h +++ b/sdk/include/ndk/umtypes.h @@ -188,6 +188,36 @@ typedef struct _OBJECT_ATTRIBUTES PVOID SecurityQualityOfService; } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; +#if (NTDDI_VERSION >= NTDDI_WIN7) || defined(__REACTOS__) +typedef enum _REQUESTER_TYPE +{ + KernelRequester, + UserProcessRequester, + UserSharedServiceRequester +} REQUESTER_TYPE; + +typedef struct _DIAGNOSTIC_BUFFER +{ + ULONG Size; + REQUESTER_TYPE CallerType; + union + { + struct + { + ULONG ProcessImageNameOffset; + ULONG ProcessId; + ULONG ServiceTag; + }; + struct + { + ULONG DeviceDescriptionOffset; + ULONG DevicePathOffset; + }; + } DUMMYUNIONNAME; + ULONG ReasonOffset; +} DIAGNOSTIC_BUFFER, *PDIAGNOSTIC_BUFFER; +#endif + // // ClientID Structure // diff --git a/sdk/include/reactos/mc/bugcodes.mc b/sdk/include/reactos/mc/bugcodes.mc index ec64c19b96a9e..5c1097f7e6028 100644 --- a/sdk/include/reactos/mc/bugcodes.mc +++ b/sdk/include/reactos/mc/bugcodes.mc @@ -1217,6 +1217,14 @@ If this is the first time you have seen this error screen, read If problems continue, remove all nonfree software from your computer. . +MessageId=0x9F +Severity=Success +Facility=System +SymbolicName=DRIVER_POWER_STATE_FAILURE +Language=English +DRIVER_POWER_STATE_FAILURE +. + MessageId=0xA0 Severity=Success Facility=System diff --git a/sdk/include/xdk/iotypes.h b/sdk/include/xdk/iotypes.h index 4a00f1137cf44..06f1ca0b4ac0a 100644 --- a/sdk/include/xdk/iotypes.h +++ b/sdk/include/xdk/iotypes.h @@ -253,6 +253,7 @@ typedef struct _WAIT_CONTEXT_BLOCK { #define DO_BUS_ENUMERATED_DEVICE 0x00001000 #define DO_POWER_PAGABLE 0x00002000 #define DO_POWER_INRUSH 0x00004000 +#define DO_POWER_NOOP 0x00008000 /* DEVICE_OBJECT.Characteristics */ #define FILE_REMOVABLE_MEDIA 0x00000001 @@ -3284,9 +3285,9 @@ typedef struct _IO_STACK_LOCATION { struct { union { ULONG SystemContext; -#if (NTDDI_VERSION >= NTDDI_VISTA) +#if (NTDDI_VERSION >= NTDDI_VISTA) || defined(__REACTOS__) SYSTEM_POWER_STATE_CONTEXT SystemPowerStateContext; -#endif // (NTDDI_VERSION >= NTDDI_VISTA) +#endif // (NTDDI_VERSION >= NTDDI_VISTA) || defined(__REACTOS__) }; POWER_STATE_TYPE POINTER_ALIGNMENT Type; POWER_STATE POINTER_ALIGNMENT State; diff --git a/sdk/include/xdk/ketypes.h b/sdk/include/xdk/ketypes.h index e6ae528d1d24b..8056ece542254 100644 --- a/sdk/include/xdk/ketypes.h +++ b/sdk/include/xdk/ketypes.h @@ -628,6 +628,15 @@ typedef struct _KLOCK_QUEUE_HANDLE { KIRQL OldIrql; } KLOCK_QUEUE_HANDLE, *PKLOCK_QUEUE_HANDLE; +#if (NTDDI_VERSION >= NTDDI_WIN7) || defined(__REACTOS__) +typedef struct _KAFFINITY_EX { + USHORT Count; + USHORT Size; + ULONG Reserved; + ULONG Bitmap[1]; +} KAFFINITY_EX, *PKAFFINITY_EX; +#endif + #if defined(_AMD64_) typedef ULONG64 KSPIN_LOCK_QUEUE_NUMBER; diff --git a/sdk/include/xdk/pofuncs.h b/sdk/include/xdk/pofuncs.h index 5b40e2eadd63d..d6296fd3fb86f 100644 --- a/sdk/include/xdk/pofuncs.h +++ b/sdk/include/xdk/pofuncs.h @@ -95,24 +95,24 @@ PoQueueShutdownWorkItem( #endif $endif (_NTIFS_) $if (_WDMDDK_) -#if (NTDDI_VERSION >= NTDDI_VISTA) +#if (NTDDI_VERSION >= NTDDI_VISTA) || defined(__REACTOS__) _IRQL_requires_max_(DISPATCH_LEVEL) -NTKRNLVISTAAPI +NTKERNELAPI VOID NTAPI PoSetSystemWake( _Inout_ struct _IRP *Irp); _IRQL_requires_max_(DISPATCH_LEVEL) -NTKRNLVISTAAPI +NTKERNELAPI BOOLEAN NTAPI PoGetSystemWake( _In_ struct _IRP *Irp); _IRQL_requires_max_(APC_LEVEL) -NTKRNLVISTAAPI +NTKERNELAPI NTSTATUS NTAPI PoRegisterPowerSettingCallback( @@ -123,23 +123,23 @@ PoRegisterPowerSettingCallback( _Outptr_opt_ PVOID *Handle); _IRQL_requires_max_(APC_LEVEL) -NTKRNLVISTAAPI +NTKERNELAPI NTSTATUS NTAPI PoUnregisterPowerSettingCallback( _Inout_ PVOID Handle); -#endif /* (NTDDI_VERSION >= NTDDI_VISTA) */ +#endif /* (NTDDI_VERSION >= NTDDI_VISTA) || defined(__REACTOS__) */ -#if (NTDDI_VERSION >= NTDDI_VISTASP1) +#if (NTDDI_VERSION >= NTDDI_VISTASP1) || defined(__REACTOS__) NTKERNELAPI VOID NTAPI PoSetDeviceBusyEx( _Inout_ PULONG IdlePointer); -#endif /* (NTDDI_VERSION >= NTDDI_VISTASP1) */ +#endif /* (NTDDI_VERSION >= NTDDI_VISTASP1) || defined(__REACTOS__) */ -#if (NTDDI_VERSION >= NTDDI_WIN7) +#if (NTDDI_VERSION >= NTDDI_WIN7) || defined(__REACTOS__) NTKERNELAPI VOID @@ -193,5 +193,25 @@ PoCreatePowerRequest( _In_ PDEVICE_OBJECT DeviceObject, _In_opt_ PCOUNTED_REASON_CONTEXT Context); -#endif /* (NTDDI_VERSION >= NTDDI_WIN7) */ +#endif /* (NTDDI_VERSION >= NTDDI_WIN7) || defined(__REACTOS__) */ + +#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) || defined(__REACTOS__) +_IRQL_requires_max_(APC_LEVEL) +NTKERNELAPI +NTSTATUS +PoCreateThermalRequest( + _Outptr_ PVOID *ThermalRequest, + _In_ PDEVICE_OBJECT TargetDeviceObject, + _In_ PDEVICE_OBJECT PolicyDeviceObject, + _In_ PCOUNTED_REASON_CONTEXT Context, + _In_ ULONG Flags); + +_IRQL_requires_max_(APC_LEVEL) +NTKERNELAPI +VOID +PoDeleteThermalRequest( + _Inout_ PVOID ThermalRequest); + +#endif /* (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) || defined(__REACTOS__) */ +$endif (_WDMDDK_) diff --git a/sdk/include/xdk/potypes.h b/sdk/include/xdk/potypes.h index 9524ee0b7c4b1..1f5f5ddf57e87 100644 --- a/sdk/include/xdk/potypes.h +++ b/sdk/include/xdk/potypes.h @@ -135,7 +135,10 @@ typedef enum { PowerActionShutdown, PowerActionShutdownReset, PowerActionShutdownOff, - PowerActionWarmEject + PowerActionWarmEject, +#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) || defined(__REACTOS__) + PowerActionDisplayOff +#endif } POWER_ACTION, *PPOWER_ACTION; typedef enum _DEVICE_POWER_STATE { @@ -163,7 +166,23 @@ typedef enum _POWER_STATE_TYPE { DevicePowerState } POWER_STATE_TYPE, *PPOWER_STATE_TYPE; -#if (NTDDI_VERSION >= NTDDI_VISTA) +#if (NTDDI_VERSION >= NTDDI_VISTA) || defined(__REACTOS__) +typedef enum _POWER_POLICY_DEVICE_TYPE +{ + PolicyDeviceSystemButton = 0, + PolicyDeviceThermalZone, + PolicyDeviceBattery, + PolicyDeviceMemory, + PolicyInitiatePowerActionAPI, + PolicySetPowerStateAPI, + PolicyImmediateDozeS4, + PolicySystemIdle, + PolicyDeviceWakeAlarm, + PolicyDeviceFan, + PolicyCsBatterySaver, + PolicyDeviceMax +} POWER_POLICY_DEVICE_TYPE; + typedef struct _SYSTEM_POWER_STATE_CONTEXT { _ANONYMOUS_UNION union { _ANONYMOUS_STRUCT struct { @@ -180,7 +199,7 @@ typedef struct _SYSTEM_POWER_STATE_CONTEXT { } SYSTEM_POWER_STATE_CONTEXT, *PSYSTEM_POWER_STATE_CONTEXT; #endif -#if (NTDDI_VERSION >= NTDDI_WIN7) +#if (NTDDI_VERSION >= NTDDI_WIN7) || defined(__REACTOS__) typedef struct _COUNTED_REASON_CONTEXT { ULONG Version; ULONG Flags; @@ -196,6 +215,40 @@ typedef struct _COUNTED_REASON_CONTEXT { } COUNTED_REASON_CONTEXT, *PCOUNTED_REASON_CONTEXT; #endif +// +// Thermal Information (Extended version) +// +#if (NTDDI_VERSION >= NTDDI_WINBLUE) || defined(__REACTOS__) +typedef struct _THERMAL_INFORMATION_EX +{ + ULONG ThermalStamp; + ULONG ThermalConstant1; + ULONG ThermalConstant2; + ULONG SamplingPeriod; + ULONG CurrentTemperature; + ULONG PassiveTripPoint; + ULONG CriticalTripPoint; + UCHAR ActiveTripPointCount; + ULONG ActiveTripPoint[10]; + ULONG S4TransitionTripPoint; + ULONG MinimumThrottle; +} THERMAL_INFORMATION_EX, *PTHERMAL_INFORMATION_EX; + +// +// Thermal zone policy +// +typedef struct _THERMAL_POLICY +{ + ULONG Version; + BOOLEAN WaitForUpdate; + BOOLEAN Hibernate; + BOOLEAN Critical; + ULONG ActivationReasons; + ULONG PassiveLimit; + ULONG ActiveLevel; +} THERMAL_POLICY, *PTHERMAL_POLICY; +#endif + #define IOCTL_QUERY_DEVICE_POWER_STATE \ CTL_CODE(FILE_DEVICE_BATTERY, 0x0, METHOD_BUFFERED, FILE_READ_ACCESS) @@ -321,6 +374,15 @@ typedef struct _POWER_PLATFORM_INFORMATION BOOLEAN AoAc; } POWER_PLATFORM_INFORMATION, *PPOWER_PLATFORM_INFORMATION; +typedef struct _ADMINISTRATOR_POWER_POLICY { + SYSTEM_POWER_STATE MinSleep; + SYSTEM_POWER_STATE MaxSleep; + ULONG MinVideoTimeout; + ULONG MaxVideoTimeout; + ULONG MinSpindownTimeout; + ULONG MaxSpindownTimeout; +} ADMINISTRATOR_POWER_POLICY, *PADMINISTRATOR_POWER_POLICY; + #if (NTDDI_VERSION >= NTDDI_WINXP) || !defined(_BATCLASS_) typedef struct { ULONG Granularity; diff --git a/sdk/include/xdk/winnt_old.h b/sdk/include/xdk/winnt_old.h index 157c3724e8096..fb09555cba474 100644 --- a/sdk/include/xdk/winnt_old.h +++ b/sdk/include/xdk/winnt_old.h @@ -3905,7 +3905,10 @@ typedef enum { PowerActionShutdown, PowerActionShutdownReset, PowerActionShutdownOff, - PowerActionWarmEject + PowerActionWarmEject, +#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) || defined(__REACTOS__) + PowerActionDisplayOff +#endif } POWER_ACTION, *PPOWER_ACTION; typedef enum _DEVICE_POWER_STATE { diff --git a/sdk/lib/drivers/ntoskrnl_vista/CMakeLists.txt b/sdk/lib/drivers/ntoskrnl_vista/CMakeLists.txt index ae8970d20cda0..0b8cf9a217b1d 100644 --- a/sdk/lib/drivers/ntoskrnl_vista/CMakeLists.txt +++ b/sdk/lib/drivers/ntoskrnl_vista/CMakeLists.txt @@ -9,7 +9,6 @@ list(APPEND SOURCE etw.c fsrtl.c io.c - po.c ke.c ${REACTOS_SOURCE_DIR}/sdk/lib/rtl/utf8.c) diff --git a/sdk/lib/drivers/ntoskrnl_vista/po.c b/sdk/lib/drivers/ntoskrnl_vista/po.c deleted file mode 100644 index 3598332a87b81..0000000000000 --- a/sdk/lib/drivers/ntoskrnl_vista/po.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * PROJECT: ReactOS Kernel - Vista+ APIs - * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) - * PURPOSE: Po functions of Vista+ - * COPYRIGHT: 2020 Victor Perevertkin (victor.perevertkin@reactos.org) - */ - -#include -#include - -NTKRNLVISTAAPI -NTSTATUS -NTAPI -PoRegisterPowerSettingCallback( - _In_opt_ PDEVICE_OBJECT DeviceObject, - _In_ LPCGUID SettingGuid, - _In_ PPOWER_SETTING_CALLBACK Callback, - _In_opt_ PVOID Context, - _Outptr_opt_ PVOID *Handle) -{ - return STATUS_NOT_IMPLEMENTED; -} - -_IRQL_requires_max_(APC_LEVEL) -NTKRNLVISTAAPI -NTSTATUS -NTAPI -PoUnregisterPowerSettingCallback( - _Inout_ PVOID Handle) -{ - return STATUS_NOT_IMPLEMENTED; -} - -_IRQL_requires_max_(DISPATCH_LEVEL) -NTKRNLVISTAAPI -BOOLEAN -NTAPI -PoQueryWatchdogTime( - _In_ PDEVICE_OBJECT Pdo, - _Out_ PULONG SecondsRemaining) -{ - return FALSE; -} - -_IRQL_requires_max_(DISPATCH_LEVEL) -NTKRNLVISTAAPI -VOID -NTAPI -PoSetSystemWake( - _Inout_ struct _IRP *Irp) -{ - -} - -_IRQL_requires_max_(DISPATCH_LEVEL) -NTKRNLVISTAAPI -BOOLEAN -NTAPI -PoGetSystemWake( - _In_ struct _IRP *Irp) -{ - return FALSE; -}