diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 747ec2f47..09e60c053 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -46,6 +46,7 @@ # Energy Efficiency /pofx/WDF/ @microsoft/ee-devs +/powerlimit/ @microsoft/ee-devs /simbatt/ @microsoft/ee-devs /thermal/ @microsoft/ee-devs diff --git a/exclusions.csv b/exclusions.csv index 6694a8533..73a96d345 100644 --- a/exclusions.csv +++ b/exclusions.csv @@ -3,3 +3,5 @@ audio\acx\samples\audiocodec\driver,*,,22621,Only NI: error C1083: Cannot open i general\dchu\osrfx2_dchu_extension_loose,*|x64,,22621,Only NI: Only x64: Fails to build general\dchu\osrfx2_dchu_extension_tight,*|x64,,22621,Only NI: Only x64: Fails to build prm,*,,22621,Only NI: Not supported on NI. +powerlimit\plclient,*,,22621,Only build on Ge and above. +powerlimit\plpolicy,*,,22621,Only build on Ge and above. diff --git a/powerlimit/plclient/README.md b/powerlimit/plclient/README.md new file mode 100644 index 000000000..8ee710115 --- /dev/null +++ b/powerlimit/plclient/README.md @@ -0,0 +1,17 @@ +--- +page_type: sample +description: "Demonstrates a simulated power limit device." +languages: +- cpp +products: +- windows +- windows-wdk +--- + +# plclient - Simulated Power Limit Client Driver + +This sample is a driver for a simulated power limit client device. + +## Universal Windows Driver Compliant + +The plclient sample provides the source code for a power limit device that supports power limit management by the operating system. diff --git a/powerlimit/plclient/plclient.asl b/powerlimit/plclient/plclient.asl new file mode 100644 index 000000000..ce0458df7 --- /dev/null +++ b/powerlimit/plclient/plclient.asl @@ -0,0 +1,10 @@ +DefinitionBlock ("ACPITABL.DAT", "SSDT", 0x02, "MSFT", "simulatr", 0x1) { + Device (\_SB.SOC0) { + Name (_HID, "PLCL0001") + Name (_UID, 1) + } + Device (\_SB.GPU1) { + Name (_HID, "PLCL0001") + Name (_UID, 2) + } +} \ No newline at end of file diff --git a/powerlimit/plclient/plclient.c b/powerlimit/plclient/plclient.c new file mode 100644 index 000000000..416765902 --- /dev/null +++ b/powerlimit/plclient/plclient.c @@ -0,0 +1,451 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + plclient.c + +Abstract: + + This module implements power limit related operations for the simulated power + limit client driver. + +--*/ + +//-------------------------------------------------------------------- Includes + +#include "plclient.h" + +//--------------------------------------------------------------------- Pragmas + +#pragma alloc_text(PAGE, InitPowerLimitValues) +#pragma alloc_text(PAGE, CleanupPowerLimitValues) +#pragma alloc_text(PAGE, PLCQueryAttributes) +#pragma alloc_text(PAGE, PLCSetLimits) +#pragma alloc_text(PAGE, PLCQueryLimitValues) + +//------------------------------------------------------------------- Functions + +_Use_decl_annotations_ +NTSTATUS +InitPowerLimitValues ( + PFDO_DATA DevExt + ) + +/*++ + +Routine Description: + + This routine initializes simulated limit values and attributes for the supplied + device extension. + +Parameters Description: + + DevExt - Supplies a pointer to the device extension to be udpated. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + ULONG DomainId; + ULONG Index; + PPOWER_LIMIT_ATTRIBUTES LimitAttributes; + ULONG LimitCount; + PPOWER_LIMIT_VALUE LimitValues; + NTSTATUS Status; + ULONG Type; + + PAGED_CODE(); + + LimitAttributes = NULL; + LimitValues = NULL; + LimitCount = PLCLIENT_DEFAULT_DOMAIN_COUNT * PLCLIENT_DEFAULT_LIMIT_COUNT_PER_DOMAIN; + LimitAttributes = ExAllocatePool2(POOL_FLAG_PAGED, + LimitCount * sizeof(POWER_LIMIT_ATTRIBUTES), + PLCLIENT_TAG); + + LimitValues = ExAllocatePool2(POOL_FLAG_PAGED, + LimitCount * sizeof(POWER_LIMIT_VALUE), + PLCLIENT_TAG); + + if ((LimitAttributes == NULL) || (LimitValues == NULL)) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto InitPowerLimitValuesEnd; + } + + for (DomainId = 0; DomainId < PLCLIENT_DEFAULT_DOMAIN_COUNT; DomainId += 1) { + for (Type = 0; Type < PLCLIENT_DEFAULT_LIMIT_COUNT_PER_DOMAIN; Type += 1) { + Index = (PLCLIENT_DEFAULT_LIMIT_COUNT_PER_DOMAIN * DomainId) + Type; + + // + // Set init attributes. + // + + LimitAttributes[Index].Type = Type; + LimitAttributes[Index].DomainId = DomainId; + LimitAttributes[Index].MaxValue = PLCLIENT_DEFAULT_MAX_VALUE; + LimitAttributes[Index].MinValue = PLCLIENT_DEFAULT_MIN_VALUE; + LimitAttributes[Index].DefaultACValue = POWER_LIMIT_VALUE_NO_CONTROL; + LimitAttributes[Index].DefaultDCValue = POWER_LIMIT_VALUE_NO_CONTROL; + + if (Type == PowerLimitContinuous) { + LimitAttributes[Index].MinTimeParameter = PLCLIENT_DEFAULT_MIN_VALUE; + LimitAttributes[Index].MaxTimeParameter = PLCLIENT_DEFAULT_MAX_VALUE; + LimitAttributes[Index].Flags.SupportTimeParameter = 1; + } + + // + // Set init values. + // + + LimitValues[Index].Type = Type; + LimitValues[Index].DomainId = DomainId; + LimitValues[Index].TargetValue = POWER_LIMIT_VALUE_NO_CONTROL; + LimitValues[Index].TimeParameter = POWER_LIMIT_VALUE_NO_CONTROL; + } + } + + DevExt->LimitCount = LimitCount; + DevExt->LimitAttributes = LimitAttributes; + DevExt->LimitValues = LimitValues; + LimitAttributes = NULL; + LimitValues = NULL; + Status = STATUS_SUCCESS; + +InitPowerLimitValuesEnd: + if (LimitAttributes != NULL) { + ExFreePoolWithTag(LimitAttributes, PLCLIENT_TAG); + LimitAttributes = NULL; + } + + if (LimitValues != NULL) { + ExFreePoolWithTag(LimitValues, PLCLIENT_TAG); + LimitValues = NULL; + } + + return Status; +} + +_Use_decl_annotations_ +VOID +CleanupPowerLimitValues ( + PFDO_DATA DevExt + ) + +/*++ + +Routine Description: + + This routine cleans up simulated limit values and attributes for the supplied + device extension. + +Parameters Description: + + DevExt - Supplies a pointer to the device extension to be udpated. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + PAGED_CODE(); + + if (DevExt == NULL) { + goto CleanupPowerLimitValuesEnd; + } + + if (DevExt->LimitAttributes != NULL) { + ExFreePoolWithTag(DevExt->LimitAttributes, PLCLIENT_TAG); + DevExt->LimitAttributes = NULL; + } + + if (DevExt->LimitValues != NULL) { + ExFreePoolWithTag(DevExt->LimitValues, PLCLIENT_TAG); + DevExt->LimitValues = NULL; + } + + DevExt->LimitCount = 0; + +CleanupPowerLimitValuesEnd: + return; +} + +_Use_decl_annotations_ +NTSTATUS +PLCQueryAttributes ( + PVOID Context, + ULONG BufferCount, + PVOID Buffer, + PULONG AttributeCount + ) + +/*++ + +Routine Description: + + This is the callback function which returns power limit attributes. + +Parameters Description: + + Context - Supplies a pointer to the device handle. + + BufferCount - Supplies count of Buffer entries. + + Buffer - Supplies a pointer to the buffer to store power limit attributes. + + AttributeCount - Supplies a pointer to save the number of attributes. + +Return Value: + + Returns STATUS_BUFFER_TOO_SMALL if the supplied buffer is not big enough, otherwise + other NTSTATUS values. + +--*/ + +{ + + PFDO_DATA DevExt; + WDFDEVICE DeviceHandle; + BOOLEAN ReleaseLock; + NTSTATUS Status; + + PAGED_CODE(); + + ReleaseLock = FALSE; + if (Context == NULL) { + Status = STATUS_INVALID_PARAMETER; + goto QueryAttributesEnd; + } + + DeviceHandle = (WDFDEVICE)Context; + DevExt = GetDeviceExtension(DeviceHandle); + AcquireGlobalMutex(); + ReleaseLock = TRUE; + if (BufferCount < DevExt->LimitCount) { + if (AttributeCount != NULL) { + *AttributeCount = DevExt->LimitCount; + } + + Status = STATUS_BUFFER_TOO_SMALL; + goto QueryAttributesEnd; + } + + RtlCopyMemory(Buffer, + DevExt->LimitAttributes, + sizeof(POWER_LIMIT_ATTRIBUTES) * DevExt->LimitCount); + + Status = STATUS_SUCCESS; + +QueryAttributesEnd: + if (ReleaseLock != FALSE) { + ReleaseGlobalMutex(); + } + + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +PLCSetLimits ( + PVOID Context, + ULONG ValueCount, + PVOID Values + ) + +/*++ + +Routine Description: + + This is the callback function which takes requests to set power limit values. + +Parameters Description: + + Context - Supplies a pointer to the device handle. + + ValueCount - Supplies count of Value entries. + + Values - Supplies a pointer to the buffer contains values to be updated. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + PPOWER_LIMIT_ATTRIBUTES Attributes; + PFDO_DATA DevExt; + WDFDEVICE DeviceHandle; + ULONG Index; + BOOLEAN ReleaseLock; + NTSTATUS Status; + BOOLEAN Valid; + PPOWER_LIMIT_VALUE ValueBuffer; + ULONG ValueIndex; + + PAGED_CODE(); + + ReleaseLock = FALSE; + if ((Context == NULL) || (ValueCount == 0) || (Values == NULL)) { + Status = STATUS_INVALID_PARAMETER; + goto SetLimitsEnd; + } + + ValueBuffer = (PPOWER_LIMIT_VALUE)Values; + DeviceHandle = (WDFDEVICE)Context; + DevExt = GetDeviceExtension(DeviceHandle); + AcquireGlobalMutex(); + ReleaseLock = TRUE; + + // + // Sanity check on proposed values before update. + // + + if (DevExt->LimitCount < ValueCount) { + Status = STATUS_INVALID_PARAMETER; + goto SetLimitsEnd; + } + + // + // N.B. On a production driver, those values should be used as power limit targets + // for the hardware. + // + + for (Index = 0; Index < ValueCount; Index += 1) { + Valid = FALSE; + for (ValueIndex = 0; ValueIndex < DevExt->LimitCount; ValueIndex += 1) { + if ((ValueBuffer[Index].Type != DevExt->LimitAttributes[ValueIndex].Type) || + (ValueBuffer[Index].DomainId != DevExt->LimitAttributes[ValueIndex].DomainId)) { + + continue; + } + + Attributes = &DevExt->LimitAttributes[ValueIndex]; + if ((ValueBuffer[Index].TargetValue == POWER_LIMIT_VALUE_NO_CONTROL) || + ((ValueBuffer[Index].TargetValue >= Attributes->MinValue) && + (ValueBuffer[Index].TargetValue <= Attributes->MaxValue))) { + + Valid = TRUE; + } + + if (ValueBuffer[Index].TimeParameter != POWER_LIMIT_VALUE_NO_CONTROL) { + if ((Attributes->Flags.SupportTimeParameter != 0) && + (ValueBuffer[Index].TimeParameter >= Attributes->MinTimeParameter) && + (ValueBuffer[Index].TimeParameter <= Attributes->MaxTimeParameter)) { + + Valid = TRUE; + } + } + + break; + } + + // + // N.B. Bail out if this proposed value is not valid. + // + + if (Valid == FALSE) { + Status = STATUS_INVALID_PARAMETER; + goto SetLimitsEnd; + } + } + + for (Index = 0; Index < ValueCount; Index += 1) { + for (ValueIndex = 0; ValueIndex < DevExt->LimitCount; ValueIndex += 1) { + if ((ValueBuffer[Index].Type != DevExt->LimitValues[ValueIndex].Type) || + (ValueBuffer[Index].DomainId != DevExt->LimitValues[ValueIndex].DomainId)) { + + continue; + } + + DevExt->LimitValues[ValueIndex].TargetValue = ValueBuffer[Index].TargetValue; + DevExt->LimitValues[ValueIndex].TimeParameter = ValueBuffer[Index].TimeParameter; + break; + } + } + + Status = STATUS_SUCCESS; + +SetLimitsEnd: + if (ReleaseLock != FALSE) { + ReleaseGlobalMutex(); + } + + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +PLCQueryLimitValues ( + PVOID Context, + ULONG ValueCount, + PVOID Values + ) + +/*++ + +Routine Description: + + This is the callback function which returns power limit values. + +Parameters Description: + + Context - Supplies a pointer to the device handle. + + ValueCount - Supplies count of Value entries. + + Values - Supplies a pointer to the buffer to store power limit values. + +Return Value: + + Returns STATUS_BUFFER_TOO_SMALL if the supplied buffer is not big enough, otherwise + other NTSTATUS values. + +--*/ + +{ + + PFDO_DATA DevExt; + WDFDEVICE DeviceHandle; + BOOLEAN ReleaseLock; + NTSTATUS Status; + + PAGED_CODE(); + + ReleaseLock = FALSE; + if ((Context == NULL) || (ValueCount == 0) || (Values == NULL)){ + Status = STATUS_INVALID_PARAMETER; + goto QueryLimitsEnd; + } + + DeviceHandle = (WDFDEVICE)Context; + DevExt = GetDeviceExtension(DeviceHandle); + AcquireGlobalMutex(); + ReleaseLock = TRUE; + if (ValueCount < DevExt->LimitCount) { + Status = STATUS_BUFFER_TOO_SMALL; + goto QueryLimitsEnd; + } + + RtlCopyMemory(Values, + DevExt->LimitValues, + sizeof(POWER_LIMIT_VALUE) * DevExt->LimitCount); + + Status = STATUS_SUCCESS; + +QueryLimitsEnd: + if (ReleaseLock != FALSE) { + ReleaseGlobalMutex(); + } + + return Status; +} diff --git a/powerlimit/plclient/plclient.h b/powerlimit/plclient/plclient.h new file mode 100644 index 000000000..6e9a7f3fc --- /dev/null +++ b/powerlimit/plclient/plclient.h @@ -0,0 +1,95 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + plclient.h + +Abstract: + + This is the header file for the simulated power limit client driver. + +--*/ + +#pragma once + +//-------------------------------------------------------------------- Includes + +#include +#include +#include +#include +#include +#include +#include +#include "powerlimitclient_drvinterface.h" + +//----------------------------------------------------------------------- Types + +typedef struct { + ULONG LimitCount; + PPOWER_LIMIT_ATTRIBUTES LimitAttributes; + PPOWER_LIMIT_VALUE LimitValues; +} FDO_DATA, *PFDO_DATA; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_DATA, GetDeviceExtension); + +//----------------------------------------------------------------------- Debug + +#define DebugPrint(l, m, ...) DbgPrintEx(DPFLTR_POWER_ID, l, "[plclient]: "m, __VA_ARGS__) +#define DebugEnter() DebugPrint(PLCLIENT_PRINT_TRACE, "Entering %s", __FUNCTION__) +#define DebugExit() DebugPrint(PLCLIENT_PRINT_TRACE, "Leaving " __FUNCTION__ "\n") +#define DebugExitStatus(_status_) DebugPrint(PLCLIENT_PRINT_TRACE, "Leaving " __FUNCTION__ ": Status=0x%08x\n", _status_) + +#define PLCLIENT_PRINT_ERROR DPFLTR_ERROR_LEVEL +#define PLCLIENT_PRINT_TRACE DPFLTR_TRACE_LEVEL +#define PLCLIENT_PRINT_INFO DPFLTR_INFO_LEVEL + +#define PLCLIENT_TAG 'PLCL' + +//--------------------------------------------------------------------- Globals + +extern WDFWAITLOCK GlobalMutex; + +//------------------------------------------------------------------ Prototypes + +FORCEINLINE +VOID +AcquireGlobalMutex ( + VOID + ) +{ + + WdfWaitLockAcquire(GlobalMutex, 0); + return; +} + +FORCEINLINE +VOID +ReleaseGlobalMutex ( + VOID + ) +{ + + WdfWaitLockRelease(GlobalMutex); + return; +} + +// +// plclient.c +// + +QUERY_POWER_LIMIT_ATTRIBUTES PLCQueryAttributes; +SET_POWER_LIMIT PLCSetLimits; +QUERY_POWER_LIMIT PLCQueryLimitValues; + +NTSTATUS +InitPowerLimitValues ( + _Inout_ PFDO_DATA DevExt + ); + +VOID +CleanupPowerLimitValues ( + _Inout_opt_ PFDO_DATA DevExt + ); diff --git a/powerlimit/plclient/plclient.inf b/powerlimit/plclient/plclient.inf new file mode 100644 index 000000000..136b67619 --- /dev/null +++ b/powerlimit/plclient/plclient.inf @@ -0,0 +1,83 @@ +;/*++ +; +;Copyright (c) Microsoft Corporation All rights Reserved +; +;Module Name: +; +; plclient.inf +; +;Abstract: +; +; INF file for installing simulate power limit client driver. +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=System +ClassGuid={4D36E97D-E325-11CE-BFC1-08002BE10318} +Provider=%ProviderString% +DriverVer=08/29/2023, 1.00.0000 +CatalogFile=plclient.cat +PnpLockdown=1 + +[DestinationDirs] +DefaultDestDir = 12 + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +plclient.sys = 1,, + +;******************************************** +; Simulated Power Limit Client Install Section +;******************************************** + +[Manufacturer] +%StdMfg%=Standard,NTamd64 +%StdMfg%=Standard,NTarm64 + +[Standard.NTamd64] +%PlCl.DeviceDesc% = PlCl_Device, ACPI\PLCL0001 +%PlCl.DeviceDesc% = PlCl_Device, root\PLCL0001 + +[Standard.NTarm64] +%PlCl.DeviceDesc% = PlCl_Device, ACPI\PLCL0001 +%PlCl.DeviceDesc% = PlCl_Device, root\PLCL0001 + +[PlCl_Device.NT] +CopyFiles=PlCl_Device_Drivers + +[PlCl_Device.NT.HW] +AddReg=PlCl_Device.NT.AddReg + +[PlCl_Device.NT.AddReg] +HKR,,DeviceCharacteristics,0x10001,0x0100 ; Use same security checks on relative opens +HKR,,Security,,"D:P(A;;GA;;;BA)(A;;GA;;;SY)" ; Allow generic-all access to Built-in administrators and Local system + +[PlCl_Device_Drivers] +plclient.sys + +;-------------- Service installation + +[PlCl_Device.NT.Services] +AddService = plclient,%SPSVCINST_ASSOCSERVICE%,PlCl_Service_Inst + +; -------------- plclient driver install sections + +[PlCl_Service_Inst] +DisplayName = %PlCl.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\plclient.sys +LoadOrderGroup = Extended Base + +[Strings] +SPSVCINST_ASSOCSERVICE= 0x00000002 +ProviderString = "TODO-Set-Provider" +StdMfg = "(Standard system devices)" +DiskId1 = "Simulate Power Limit Client Installation Disk #1" +PlCl.DeviceDesc = "Simulate Power Limit Client Device" +PlCl.SVCDESC = "Simulate Power Limit Client Driver" diff --git a/powerlimit/plclient/plclient.rc b/powerlimit/plclient/plclient.rc new file mode 100644 index 000000000..326c61c2a --- /dev/null +++ b/powerlimit/plclient/plclient.rc @@ -0,0 +1,11 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Simulate Power Limit Client Driver" +#define VER_INTERNALNAME_STR "plclient.sys" +#define VER_ORIGINALFILENAME_STR "plclient.sys" + +#include "common.ver" diff --git a/powerlimit/plclient/plclient.sln b/powerlimit/plclient/plclient.sln new file mode 100644 index 000000000..0401b7991 --- /dev/null +++ b/powerlimit/plclient/plclient.sln @@ -0,0 +1,35 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34701.34 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plclient", "plclient.vcxproj", "{D6B30052-9124-44DB-A421-4DEE110B91E2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Debug|ARM64.Build.0 = Debug|ARM64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Debug|x64.ActiveCfg = Debug|x64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Debug|x64.Build.0 = Debug|x64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Debug|x64.Deploy.0 = Debug|x64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Release|ARM64.ActiveCfg = Release|ARM64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Release|ARM64.Build.0 = Release|ARM64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Release|ARM64.Deploy.0 = Release|ARM64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Release|x64.ActiveCfg = Release|x64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Release|x64.Build.0 = Release|x64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Release|x64.Deploy.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {810BCAEA-5B6F-43EC-AF83-6B849DF63425} + EndGlobalSection +EndGlobal diff --git a/powerlimit/plclient/plclient.vcxproj b/powerlimit/plclient/plclient.vcxproj new file mode 100644 index 000000000..b1ecc40d4 --- /dev/null +++ b/powerlimit/plclient/plclient.vcxproj @@ -0,0 +1,123 @@ + + + + + Debug + x64 + + + Release + x64 + + + Debug + ARM64 + + + Release + ARM64 + + + + {D6B30052-9124-44DB-A421-4DEE110B91E2} + {1bc93793-694f-48fe-9372-81e2b05556fd} + v4.5 + 12.0 + Debug + x64 + plclient + + + + Windows10 + true + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + Windows10 + false + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + Windows10 + true + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + Windows10 + false + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + + + + + + + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + + sha256 + + + + + sha256 + + + + + sha256 + + + + + sha256 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/powerlimit/plclient/plclient.vcxproj.filters b/powerlimit/plclient/plclient.vcxproj.filters new file mode 100644 index 000000000..391468bc5 --- /dev/null +++ b/powerlimit/plclient/plclient.vcxproj.filters @@ -0,0 +1,47 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {8E41214B-6785-4CFE-B992-037D68949A14} + inf;inv;inx;mof;mc; + + + + + Driver Files + + + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/powerlimit/plclient/powerlimitclient_drvinterface.h b/powerlimit/plclient/powerlimitclient_drvinterface.h new file mode 100644 index 000000000..d4c71bda9 --- /dev/null +++ b/powerlimit/plclient/powerlimitclient_drvinterface.h @@ -0,0 +1,58 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + powerlimitclient_drvinterface.h + +Abstract: + + This module contains the interfaces used to communicate with the simulate + power limit client driver stack. + +--*/ + +//--------------------------------------------------------------------- Pragmas + +#pragma once + +//--------------------------------------------------------------------- Defines + +// +// IOCTLs to control the client driver +// + +#define POWERLIMITCLIENT_IOCTL(_index_) \ + CTL_CODE(FILE_DEVICE_UNKNOWN, _index_, METHOD_BUFFERED, FILE_WRITE_DATA) + +// +// IOCTL_POWERLIMIT_CLIENT_QUERY_LIMIT_COUNT +// - Output: ULONG, number of supported power limit parameters. +// + +#define IOCTL_POWERLIMIT_CLIENT_QUERY_LIMIT_COUNT POWERLIMITCLIENT_IOCTL(0x800) + +// +// IOCTL_POWERLIMIT_CLIENT_QUERY_ATTRIBUTES +// - Output: POWER_LIMIT_ATTRIBUTES[], attributes of supported power limit parameters. +// + +#define IOCTL_POWERLIMIT_CLIENT_QUERY_ATTRIBUTES POWERLIMITCLIENT_IOCTL(0x801) + +// +// IOCTL_POWERLIMIT_CLIENT_QUERY_LIMITS +// - Output: POWER_LIMIT_VALUE[], values of supported power limit parameters. +// + +#define IOCTL_POWERLIMIT_CLIENT_QUERY_LIMITS POWERLIMITCLIENT_IOCTL(0x802) + +// +// Each domain supports PowerLimitContinuous/Burst/BurstTimeParameter. +// + +#define PLCLIENT_DEFAULT_LIMIT_COUNT_PER_DOMAIN 3UL +#define PLCLIENT_DEFAULT_DOMAIN_COUNT 2UL +#define PLCLIENT_DEFAULT_LIMIT_COUNT PLCLIENT_DEFAULT_LIMIT_COUNT_PER_DOMAIN * PLCLIENT_DEFAULT_DOMAIN_COUNT +#define PLCLIENT_DEFAULT_MAX_VALUE 50000UL +#define PLCLIENT_DEFAULT_MIN_VALUE 1000UL diff --git a/powerlimit/plclient/wdf.c b/powerlimit/plclient/wdf.c new file mode 100644 index 000000000..f5d0eb9b0 --- /dev/null +++ b/powerlimit/plclient/wdf.c @@ -0,0 +1,477 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + wdf.c + +Abstract: + + The module implements WDF boilerplate for the simulate power limit client. + +--*/ + +//-------------------------------------------------------------------- Includes + +#include "plclient.h" + +//--------------------------------------------------------------------- Globals + +WDFWAITLOCK GlobalMutex; + +//------------------------------------------------------------------ Prototypes + +DRIVER_INITIALIZE DriverEntry; +EVT_WDF_DRIVER_DEVICE_ADD EvtDriverDeviceAdd; +EVT_WDF_DRIVER_UNLOAD EvtDriverUnload; +EVT_WDF_OBJECT_CONTEXT_DESTROY EvtDeviceDestroy; +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL EvtIoDeviceControl; + +//--------------------------------------------------------------------- Pragmas + +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, EvtDriverDeviceAdd) +#pragma alloc_text(PAGE, EvtDriverUnload) +#pragma alloc_text(PAGE, EvtIoDeviceControl) +#pragma alloc_text(PAGE, EvtDeviceDestroy) + +//------------------------------------------------------------------- Functions + +_Use_decl_annotations_ +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + DriverEntry initializes the driver and is the first routine called by the + system after the driver is loaded. DriverEntry configures and creates a WDF + driver object. + +Parameters Description: + + DriverObject - Supplies a pointer to the driver object. + + RegistryPath - Supplies a pointer to a unicode string representing the path + to the driver-specific key in the registry. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + WDF_DRIVER_CONFIG DriverConfig; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(RegistryPath); + + // + // Initiialize the DriverConfig data that controls the attributes that are + // global to this driver. + // + + DebugEnter(); + WDF_DRIVER_CONFIG_INIT(&DriverConfig, EvtDriverDeviceAdd); + DriverConfig.EvtDriverUnload = EvtDriverUnload; + + // + // Create the driver object + // + + Status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &DriverConfig, + WDF_NO_HANDLE); + + if (!NT_SUCCESS(Status)) { + DebugPrint(PLCLIENT_PRINT_ERROR, + "WdfDriverCreate() Failed. Status 0x%x\n", + Status); + + goto DriverEntryEnd; + } + + // + // Initialize global mutex. + // + + Status = WdfWaitLockCreate(WDF_NO_OBJECT_ATTRIBUTES, &GlobalMutex); + if (!NT_SUCCESS(Status)) { + DebugPrint(PLCLIENT_PRINT_ERROR, + "WdfWaitLockCreate() Failed! 0x%x\n", + Status); + + goto DriverEntryEnd; + } + +DriverEntryEnd: + DebugExitStatus(Status); + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +EvtDriverDeviceAdd ( + WDFDRIVER Driver, + PWDFDEVICE_INIT DeviceInit + ) + +/*++ + +Routine Description: + + This routine is called by the framework in response to AddDevice call from + the PnP manager. A WDF device object is created and initialized to represent + a new instance of the power limit client device. + +Arguments: + + Driver - Supplies a handle to the WDF Driver object. + + DeviceInit - Supplies a pointer to a framework-allocated WDFDEVICE_INIT structure. + +Return Value: + + NTSTATUS + +--*/ + +{ + + PFDO_DATA DevExt; + WDF_OBJECT_ATTRIBUTES DeviceAttributes; + WDFDEVICE DeviceHandle; + POWER_LIMIT_INTERFACE PowerLimitInterface; + WDFQUEUE Queue; + WDF_IO_QUEUE_CONFIG QueueConfig; + WDF_QUERY_INTERFACE_CONFIG QueryInterfaceConfig; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + DevExt = NULL; + + DebugEnter(); + + // + // Initialize attributes and a context area for the device object. + // + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&DeviceAttributes, FDO_DATA); + DeviceAttributes.EvtDestroyCallback = &EvtDeviceDestroy; + + // + // Create a framework device object. This call will in turn create + // a WDM device object, attach to the lower stack, and set the + // appropriate flags and attributes. + // + + Status = WdfDeviceCreate(&DeviceInit, &DeviceAttributes, &DeviceHandle); + if (!NT_SUCCESS(Status)) { + DebugPrint(PLCLIENT_PRINT_ERROR, + "WdfDeviceCreate() Failed. 0x%x\n", + Status); + + goto DriverDeviceAddEnd; + } + + // + // Configure a default queue for IO requests. This queue processes requests + // to read the simulated state. + // + // N.B. Those IOCTLs supplies another approach to validate device driver + // interface, which are not needed by production code. + // + + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&QueueConfig, + WdfIoQueueDispatchSequential); + + QueueConfig.EvtIoDeviceControl = EvtIoDeviceControl; + Status = WdfIoQueueCreate(DeviceHandle, + &QueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &Queue); + + if (!NT_SUCCESS(Status)) { + DebugPrint(PLCLIENT_PRINT_ERROR, + "WdfIoQueueCreate() Failed. 0x%x\n", + Status); + + goto DriverDeviceAddEnd; + } + + // + // Initialize the device extension. + // + + DevExt = GetDeviceExtension(DeviceHandle); + Status = InitPowerLimitValues(DevExt); + if (!NT_SUCCESS(Status)) { + DebugPrint(PLCLIENT_PRINT_ERROR, + "InitPowerLimitValues() Failed. 0x%x\n", + Status); + + goto DriverDeviceAddEnd; + } + + // + // Create a device interface for this device to advertise the simulated + // power limit client IO interface. + // + + Status = WdfDeviceCreateDeviceInterface( + DeviceHandle, + &GUID_DEVINTERFACE_POWER_LIMIT, + NULL); + + if (!NT_SUCCESS(Status)) { + goto DriverDeviceAddEnd; + } + + // + // Create a driver interface for this device to advertise the power limit + // interface. + // + + RtlZeroMemory(&PowerLimitInterface, sizeof(PowerLimitInterface)); + PowerLimitInterface.Version = 1; + PowerLimitInterface.Size = sizeof(PowerLimitInterface); + PowerLimitInterface.Context = DeviceHandle; + PowerLimitInterface.InterfaceReference = WdfDeviceInterfaceReferenceNoOp; + PowerLimitInterface.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp; + PowerLimitInterface.DomainCount = PLCLIENT_DEFAULT_DOMAIN_COUNT; + PowerLimitInterface.QueryAttributes = PLCQueryAttributes; + PowerLimitInterface.SetPowerLimit = PLCSetLimits; + PowerLimitInterface.QueryPowerLimit = PLCQueryLimitValues; + WDF_QUERY_INTERFACE_CONFIG_INIT(&QueryInterfaceConfig, + (PINTERFACE)&PowerLimitInterface, + &GUID_POWER_LIMIT_INTERFACE, + NULL); + + Status = WdfDeviceAddQueryInterface(DeviceHandle, &QueryInterfaceConfig); + if (!NT_SUCCESS(Status)) { + DebugPrint(PLCLIENT_PRINT_ERROR, + "WdfDeviceAddQueryInterface() Failed. 0x%x\n", + Status); + + goto DriverDeviceAddEnd; + } + +DriverDeviceAddEnd: + if (!NT_SUCCESS(Status)) { + CleanupPowerLimitValues(DevExt); + } + + DebugExitStatus(Status); + return Status; +} + +_Use_decl_annotations_ +VOID +EvtDeviceDestroy ( + WDFOBJECT Object + ) + +/*++ + +Routine Description: + + This routine destroys the device's data. + +Arguments: + + Object - Supplies the WDF reference to the device that is being removed. + +Return Value: + + None. + +--*/ + +{ + + PFDO_DATA DevExt; + + PAGED_CODE(); + + DebugEnter(); + DevExt = GetDeviceExtension(Object); + CleanupPowerLimitValues(DevExt); + DebugExit(); + return; +} + +_Use_decl_annotations_ +VOID +EvtIoDeviceControl ( + WDFQUEUE Queue, + WDFREQUEST Request, + size_t OutputBufferLength, + size_t InputBufferLength, + ULONG IoControlCode + ) + +/*++ + +Routine Description: + + Handles requests to read the simulated device state. + +Arguments: + + Queue - Supplies a handle to the framework queue object that is associated + with the I/O request. + + Request - Supplies a handle to a framework request object. This one + represents the IRP_MJ_DEVICE_CONTROL IRP received by the framework. + + OutputBufferLength - Supplies the length, in bytes, of the request's output + buffer, if an output buffer is available. + + InputBufferLength - Supplies the length, in bytes, of the request's input + buffer, if an input buffer is available. + + IoControlCode - Supplies the Driver-defined or system-defined I/O control + code (IOCTL) that is associated with the request. + +Return Value: + + VOID + +--*/ + +{ + + ULONG BytesReturned; + WDFDEVICE Device; + PFDO_DATA DevExt; + PVOID OutputBuffer; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(InputBufferLength); + + PAGED_CODE(); + + Device = WdfIoQueueGetDevice(Queue); + DevExt = GetDeviceExtension(Device); + DebugPrint(PLCLIENT_PRINT_TRACE, + "EvtIoDeviceControl: 0x%08x\n", + IoControlCode); + + BytesReturned = 0; + OutputBuffer = NULL; + if (OutputBufferLength > 0) { + Status = WdfRequestRetrieveOutputBuffer(Request, + OutputBufferLength, + &OutputBuffer, + NULL); + + if (!NT_SUCCESS(Status)) { + goto DeviceIoControlEnd; + } + } + + Status = STATUS_NOT_SUPPORTED; + switch(IoControlCode) { + case IOCTL_POWERLIMIT_CLIENT_QUERY_LIMIT_COUNT: + if (OutputBufferLength == sizeof(ULONG)) { + AcquireGlobalMutex(); + *((PULONG)OutputBuffer) = DevExt->LimitCount; + BytesReturned = sizeof(ULONG); + ReleaseGlobalMutex(); + Status = STATUS_SUCCESS; + + } else { + Status = STATUS_BUFFER_OVERFLOW; + } + + break; + + case IOCTL_POWERLIMIT_CLIENT_QUERY_ATTRIBUTES: + if (OutputBufferLength == sizeof(POWER_LIMIT_ATTRIBUTES) * DevExt->LimitCount) { + AcquireGlobalMutex(); + RtlCopyMemory(OutputBuffer, DevExt->LimitAttributes, OutputBufferLength); + ReleaseGlobalMutex(); + BytesReturned = (ULONG)OutputBufferLength; + Status = STATUS_SUCCESS; + + } else { + Status = STATUS_BUFFER_OVERFLOW; + } + + break; + + case IOCTL_POWERLIMIT_CLIENT_QUERY_LIMITS: + if (OutputBufferLength == sizeof(POWER_LIMIT_VALUE) * DevExt->LimitCount) { + AcquireGlobalMutex(); + RtlCopyMemory(OutputBuffer, DevExt->LimitValues, OutputBufferLength); + ReleaseGlobalMutex(); + BytesReturned = (ULONG)OutputBufferLength; + Status = STATUS_SUCCESS; + + } else { + Status = STATUS_BUFFER_OVERFLOW; + } + + break; + + default: + break; + } + +DeviceIoControlEnd: + WdfRequestCompleteWithInformation(Request, Status, BytesReturned); + DebugExitStatus(Status); + return; +} + +_Use_decl_annotations_ +VOID +EvtDriverUnload ( + WDFDRIVER Driver + ) + +/*++ + +Routine Description: + + EvtDriverUnload is called when the driver is being unloaded to clean up + driver state. + +Arguments: + + Driver - Supplies a handle to the WDF Driver object. + +Return Value: + + None + +--*/ + +{ + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + DebugEnter(); + + // + // N.B. Does nothing since we don't have anything to clean up, just print + // some debug info. + // + + DebugExit(); + return; +} diff --git a/powerlimit/plpolicy/README.md b/powerlimit/plpolicy/README.md new file mode 100644 index 000000000..f23c22e3c --- /dev/null +++ b/powerlimit/plpolicy/README.md @@ -0,0 +1,38 @@ +--- +page_type: sample +description: "Demonstrates a simulated power policy device." +languages: +- cpp +products: +- windows +- windows-wdk +--- + +# plpolicy - Simulated Power Limit Policy Driver + +This sample is a driver for a simulated power limit policy device. + +## Universal Windows Driver Compliant + +The plpolicy sample provides the source code for a power limit controller that supplies power limit management to the operating system. + +This driver supplies IOCTLs to receive commands from other modules and plumb cooresponding information to the hardware through OS kernel +space APIs. + +IOCTL_POWERLIMIT_POLICY_REGISTER: Other module supplies the BIOS name of the target device to control the power limit. + +IOCTL_POWERLIMIT_POLICY_QUERY_ATTRIBUTES: Query the target device for supported power limits and attributes. + +IOCTL_POWERLIMIT_POLICY_QUERY_VALUES: Query the target device for active power limit values. + +IOCTL_POWERLIMIT_POLICY_SET_VALUES: Set power limit values to the target device. + +For a production driver, those IOCTLs are not needed. Instead, the policy driver should: +1. During the init phase, subscribes to GUID_DEVINTERFACE_POWER_LIMIT notifications. Then query client's BIOS name in the callback, and + compares it with the target client device. Then create power limit request through PoCreatePowerLimitRequest, query attributes through + PoQueryPowerLimitAttributes. + +2. At runtime, the policy driver should collect input signals and calcualte power limit target(s), then send targets to the client through + PoSetPowerLimitValue. If necessary, the policy driver can query the current power limit of client through PoQueryPowerLimitValue. + +3. If the power limit control is no longer needed, delete the power limit request through PoDeletePowerLimitRequest. diff --git a/powerlimit/plpolicy/plpolicy.c b/powerlimit/plpolicy/plpolicy.c new file mode 100644 index 000000000..e5db18cd1 --- /dev/null +++ b/powerlimit/plpolicy/plpolicy.c @@ -0,0 +1,611 @@ +/*++ + +Copyright (c) Microsoft Corporation + +Module Name: + + plpolicy.c + +Abstract: + + This module implements power limit related operations for the simulated power + limit policy driver. + +--*/ + +//-------------------------------------------------------------------- Includes + +#include "plpolicy.h" + +//------------------------------------------------------------------ Prototypes + +NTSTATUS +GetDeviceObjectFromInterfaceName ( + _In_ PUNICODE_STRING Name, + _Outptr_ PFILE_OBJECT *File, + _Outptr_ PDEVICE_OBJECT *Device + ); + +PDEVICE_REGISTRATION +FindRegistrationByName ( + _In_ PUNICODE_STRING TargetDeviceName, + _In_ PFDO_DATA DevExt + ); + +PDEVICE_REGISTRATION +FindRegistrationById ( + _In_ PFDO_DATA DevExt, + _In_ ULONG RequestId + ); + +//--------------------------------------------------------------------- Globals + +WDFWAITLOCK GlobalMutex; + +//--------------------------------------------------------------------- Pragmas + +#pragma alloc_text(PAGE, RegisterRequest) +#pragma alloc_text(PAGE, UnregisterRequest) +#pragma alloc_text(PAGE, QueryAttributes) +#pragma alloc_text(PAGE, QueryLimitValues) +#pragma alloc_text(PAGE, SetLimitValues) +#pragma alloc_text(PAGE, GetDeviceObjectFromInterfaceName) +#pragma alloc_text(PAGE, FindRegistrationByName) +#pragma alloc_text(PAGE, FindRegistrationById) + +//------------------------------------------------------------------- Functions + +_Use_decl_annotations_ +NTSTATUS +RegisterRequest ( + PFDO_DATA DevExt, + PUNICODE_STRING TargetDeviceName, + PDEVICE_OBJECT PolicyDeviceObject, + PULONG RequestId + ) + +/*++ + +Routine Description: + + This routine registers a power limit request for the specified device. + +Arguments: + + DevExt - Supplies a pointer to the device extension. + + TargetDeviceName - Supplies the name of the target device. + + PolicyDeviceObject - Supplies a pointer to the policy driver's device object. + + RequestId - Supplies a pointer to the registered request. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + PFILE_OBJECT FileObject; + COUNTED_REASON_CONTEXT ReasContext; + PDEVICE_REGISTRATION Registration; + ULONG SizeNeeded; + NTSTATUS Status; + PDEVICE_OBJECT TargetDeviceObject; + + PAGED_CODE(); + + FileObject = NULL; + TargetDeviceObject = NULL; + Registration = NULL; + AcquireGlobalMutex(); + if (FindRegistrationByName(TargetDeviceName, DevExt) != NULL) { + Status = STATUS_SUCCESS; + goto RegisterEnd; + } + + Status = GetDeviceObjectFromInterfaceName(TargetDeviceName, + &FileObject, + &TargetDeviceObject); + + if (!NT_SUCCESS(Status)) { + goto RegisterEnd; + } + + SizeNeeded = sizeof(DEVICE_REGISTRATION) + TargetDeviceName->MaximumLength; + Registration = ExAllocatePool2(POOL_FLAG_PAGED, SizeNeeded, PLPOLICY_TAG); + if (Registration == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto RegisterEnd; + } + + RtlZeroMemory(Registration, SizeNeeded); + Registration->TargetDeviceName.Length = TargetDeviceName->Length; + Registration->TargetDeviceName.MaximumLength = TargetDeviceName->MaximumLength; + Registration->TargetDeviceName.Buffer = OffsetToPtr(Registration, + sizeof(DEVICE_REGISTRATION)); + + RtlCopyMemory(Registration->TargetDeviceName.Buffer, + TargetDeviceName->Buffer, + TargetDeviceName->MaximumLength); + + ReasContext.Version = DIAGNOSTIC_REASON_VERSION; + ReasContext.Flags = DIAGNOSTIC_REASON_SIMPLE_STRING; + RtlInitUnicodeString(&ReasContext.SimpleString, L"Simulated Power Limit Policy Device"); + Status = PoCreatePowerLimitRequest(&Registration->PowerLimitRequest, + TargetDeviceObject, + PolicyDeviceObject, + &ReasContext); + + if (!NT_SUCCESS(Status)) { + goto RegisterEnd; + } + + Registration->Initialized = TRUE; + Registration->RequestId = DevExt->RequestCount; + *RequestId = DevExt->RequestCount; + InsertTailList(&DevExt->RequestHeader, &Registration->Link); + DevExt->RequestCount += 1; + Registration = NULL; + Status = STATUS_SUCCESS; + +RegisterEnd: + ReleaseGlobalMutex(); + if (FileObject != NULL) { + ObDereferenceObject(FileObject); + } + + if (TargetDeviceObject != NULL) { + ObDereferenceObject(TargetDeviceObject); + } + + if (Registration != NULL) { + ExFreePoolWithTag(Registration, PLPOLICY_TAG); + } + + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +UnregisterRequest ( + PFDO_DATA DevExt, + ULONG RequestId + ) + +/*++ + +Routine Description: + + This routine unregisters a power limit request for the specified device. + +Arguments: + + DevExt - Supplies a pointer to the device extension. + + RequestId - Supplies the Id of the request. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + PDEVICE_REGISTRATION Registration; + NTSTATUS Status; + + PAGED_CODE(); + + AcquireGlobalMutex(); + Registration = FindRegistrationById(DevExt, RequestId); + if (Registration == NULL) { + Status = STATUS_OBJECT_NAME_NOT_FOUND; + goto UnregisterEnd; + } + + RemoveEntryList(&Registration->Link); + PoDeletePowerLimitRequest(Registration->PowerLimitRequest); + ExFreePoolWithTag(Registration, PLPOLICY_TAG); + Status = STATUS_SUCCESS; + +UnregisterEnd: + ReleaseGlobalMutex(); + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +QueryAttributes ( + PPOWER_LIMIT_ATTRIBUTES Buffer, + PFDO_DATA DevExt, + ULONG RequestId, + ULONG BufferCount + ) + +/*++ + +Routine Description: + + This routine returns supported power limit parameter's attributes. + +Arguments: + + Buffer - Supplies a pointer to save attributes. + + DevExt - Supplies a pointer to the device extension. + + RequestId - Supplies the Id of the request. + + BufferCount - Supplies the count of buffer. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + ULONG AttributeCount; + PDEVICE_REGISTRATION Registration; + NTSTATUS Status; + + PAGED_CODE(); + + AcquireGlobalMutex(); + Registration = FindRegistrationById(DevExt, RequestId); + if (Registration == NULL) { + Status = STATUS_OBJECT_NAME_NOT_FOUND; + goto QueryAttributesEnd; + } + + Status = PoQueryPowerLimitAttributes(Registration->PowerLimitRequest, + BufferCount, + Buffer, + &AttributeCount); + +QueryAttributesEnd: + ReleaseGlobalMutex(); + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +QueryLimitValues ( + PPOWER_LIMIT_VALUE Buffer, + PFDO_DATA DevExt, + ULONG RequestId, + ULONG BufferCount + ) + +/*++ + +Routine Description: + + This routine checks values of power limits. + +Arguments: + + Buffer - Supplies a pointer to save values. + + DevExt - Supplies a pointer to the device extension. + + RequestId - Supplies the Id of the request. + + BufferCount - Supplies the count of buffer. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + PDEVICE_REGISTRATION Registration; + NTSTATUS Status; + + PAGED_CODE(); + + AcquireGlobalMutex(); + Registration = FindRegistrationById(DevExt, RequestId); + if (Registration == NULL) { + Status = STATUS_OBJECT_NAME_NOT_FOUND; + goto QueryLimitValuesEnd; + } + + Status = PoQueryPowerLimitValue(Registration->PowerLimitRequest, + BufferCount, + Buffer); + +QueryLimitValuesEnd: + ReleaseGlobalMutex(); + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +SetLimitValues ( + PFDO_DATA DevExt, + ULONG RequestId, + ULONG BufferCount, + PPOWER_LIMIT_VALUE Buffer + ) + +/*++ + +Routine Description: + + This routine sets values of power limits. + +Arguments: + + DevExt - Supplies a pointer to the device extension. + + RequestId - Supplies the Id of the request. + + BufferCount - Supplies the count of buffer. + + Buffer - Supplies a pointer to proposed control values. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + COUNTED_REASON_CONTEXT ReasContext; + PDEVICE_REGISTRATION Registration; + NTSTATUS Status; + + PAGED_CODE(); + + AcquireGlobalMutex(); + Registration = FindRegistrationById(DevExt, RequestId); + if (Registration == NULL) { + Status = STATUS_OBJECT_NAME_NOT_FOUND; + goto QueryLimitValuesEnd; + } + + ReasContext.Version = DIAGNOSTIC_REASON_VERSION; + ReasContext.Flags = DIAGNOSTIC_REASON_SIMPLE_STRING; + RtlInitUnicodeString(&ReasContext.SimpleString, L"Sim PL Policy"); + Status = PoSetPowerLimitValue(Registration->PowerLimitRequest, + &ReasContext, + BufferCount, + Buffer); + +QueryLimitValuesEnd: + ReleaseGlobalMutex(); + return Status; +} + +NTSTATUS +GetDeviceObjectFromInterfaceName ( + _In_ PUNICODE_STRING Name, + _Outptr_ PFILE_OBJECT *File, + _Outptr_ PDEVICE_OBJECT *Device + ) + +/*++ + +Routine Description: + + This routine retrieves the device object for the named interface. + +Arguments: + + Name - Supplies a pointer to the name of the device interface. + + File - Supplies a pointer to a location to receive the file object. + + Device - Supplies a pointer to a location to receive the device object. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + OBJECT_ATTRIBUTES Attributes; + PDEVICE_OBJECT DeviceObject; + PFILE_OBJECT FileObject; + HANDLE Handle; + IO_STATUS_BLOCK IoStatus; + NTSTATUS Status; + + PAGED_CODE(); + + DeviceObject = NULL; + FileObject = NULL; + Handle = NULL; + InitializeObjectAttributes(&Attributes, + Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + // + // Open a handle to the device. + // + + Status = ZwCreateFile(&Handle, + FILE_ALL_ACCESS, + &Attributes, + &IoStatus, + NULL, + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + 0, + NULL, + 0); + + if (!NT_SUCCESS(Status)) { + Handle = NULL; + goto GetInterfaceDeviceObjectEnd; + } + + // + // Retrieve the file object associated with the device handle. + // + + Status = ObReferenceObjectByHandle(Handle, + 0, + *IoFileObjectType, + KernelMode, + &FileObject, + NULL); + + if (!NT_SUCCESS(Status)) { + FileObject = NULL; + goto GetInterfaceDeviceObjectEnd; + } + + // + // Get the device object associated with the file object. + // + // N.B. The device object must be referenced before dereferencing the file + // object. + // + + DeviceObject = IoGetRelatedDeviceObject(FileObject); + if (DeviceObject == NULL) { + Status = STATUS_UNSUCCESSFUL; + goto GetInterfaceDeviceObjectEnd; + } + + ObReferenceObject(DeviceObject); + *File = FileObject; + *Device = DeviceObject; + FileObject = NULL; + Status = STATUS_SUCCESS; + +GetInterfaceDeviceObjectEnd: + if (FileObject != NULL) { + ObDereferenceObject(FileObject); + } + + if (Handle != NULL) { + ZwClose(Handle); + } + + return Status; +} + +_Use_decl_annotations_ +PDEVICE_REGISTRATION +FindRegistrationByName ( + PUNICODE_STRING TargetDeviceName, + PFDO_DATA DevExt + ) + +/*++ + +Routine Description: + + This routine searches for an existing registration matching the given + device name. + +Arguments: + + TargetDeviceName - Supplies the PDO name of the device. + + DevExt - Supplies a pointer to the device extension. + +Return Value: + + A pointer to the device registration, if it is found. Otherwise, NULL. + +--*/ + +{ + + LONG Comparison; + PLIST_ENTRY Link; + PDEVICE_REGISTRATION Registration; + + Registration = NULL; + if (DevExt->RequestCount == 0) { + goto FindRegistrationByNameEnd; + } + + for (Link = DevExt->RequestHeader.Flink; + Link != &DevExt->RequestHeader; + Link = Link->Flink) { + + Registration = CONTAINING_RECORD(Link, DEVICE_REGISTRATION, Link); + Comparison = RtlCompareUnicodeString(TargetDeviceName, + &Registration->TargetDeviceName, + FALSE); + + if (Comparison == 0) { + break; + } + + Registration = NULL; + } + +FindRegistrationByNameEnd: + return Registration; +} + +_Use_decl_annotations_ +PDEVICE_REGISTRATION +FindRegistrationById ( + PFDO_DATA DevExt, + ULONG RequestId + ) + +/*++ + +Routine Description: + + This routine searches for an existing registration matching the given + device name. + +Arguments: + + TargetDeviceName - Supplies the PDO name of the device. + + RequestId - Supplies the ID of the power limit request. + +Return Value: + + A pointer to the device registration, if it is found. Otherwise, NULL. + +--*/ + +{ + + PLIST_ENTRY Link; + PDEVICE_REGISTRATION Registration; + + Registration = NULL; + if (DevExt->RequestCount == 0) { + goto FindRegistrationByIdEnd; + } + + for (Link = DevExt->RequestHeader.Flink; + Link != &DevExt->RequestHeader; + Link = Link->Flink) { + + Registration = CONTAINING_RECORD(Link, DEVICE_REGISTRATION, Link); + if (Registration->RequestId == RequestId) { + break; + } + + Registration = NULL; + } + +FindRegistrationByIdEnd: + return Registration; +} \ No newline at end of file diff --git a/powerlimit/plpolicy/plpolicy.h b/powerlimit/plpolicy/plpolicy.h new file mode 100644 index 000000000..ae767b63e --- /dev/null +++ b/powerlimit/plpolicy/plpolicy.h @@ -0,0 +1,128 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + plpolicy.h + +Abstract: + + This is the header file for the simulated power limit policy driver. + +--*/ + +#pragma once + +//-------------------------------------------------------------------- Includes + +#include +#include +#include +#include +#include +#include +#include +#include "powerlimitpolicy_drvinterface.h" + +//----------------------------------------------------------------------- Types + +typedef struct _DEVICE_REGISTRATION { + LIST_ENTRY Link; + BOOLEAN Initialized; + ULONG RequestId; + PVOID PowerLimitRequest; + UNICODE_STRING TargetDeviceName; +} DEVICE_REGISTRATION, *PDEVICE_REGISTRATION; + +typedef struct _FDO_DATA { + ULONG RequestCount; + LIST_ENTRY RequestHeader; +} FDO_DATA, *PFDO_DATA; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_DATA, GetDeviceExtension); + +//----------------------------------------------------------------------- Debug + +#define DebugPrint(l, m, ...) DbgPrintEx(DPFLTR_POWER_ID, l, "[plpolicy]: "m, __VA_ARGS__) +#define DebugEnter() DebugPrint(PLPOLICY_PRINT_TRACE, "Entering " __FUNCTION__ "\n") +#define DebugExit() DebugPrint(PLPOLICY_PRINT_TRACE, "Leaving " __FUNCTION__ "\n") +#define DebugExitStatus(_status_) DebugPrint(PLPOLICY_PRINT_TRACE, "Leaving " __FUNCTION__ ": Status=0x%x\n", _status_) + +#define PLPOLICY_PRINT_ERROR DPFLTR_ERROR_LEVEL +#define PLPOLICY_PRINT_TRACE DPFLTR_TRACE_LEVEL +#define PLPOLICY_PRINT_INFO DPFLTR_INFO_LEVEL + +#define PLPOLICY_TAG 'PLPO' + +#define OffsetToPtr(Base, Offset) ((PVOID)((PUCHAR)(Base) + (Offset))) + +//--------------------------------------------------------------------- Globals + +extern WDFWAITLOCK GlobalMutex; + +//------------------------------------------------------------------ Prototypes + +FORCEINLINE +VOID +AcquireGlobalMutex ( + VOID + ) +{ + + WdfWaitLockAcquire(GlobalMutex, 0); + return; +} + +FORCEINLINE +VOID +ReleaseGlobalMutex ( + VOID + ) +{ + + WdfWaitLockRelease(GlobalMutex); + return; +} + +// +// plpolicy.c +// + +NTSTATUS +RegisterRequest ( + _Inout_ PFDO_DATA DevExt, + _In_ PUNICODE_STRING TargetDeviceName, + _In_ PDEVICE_OBJECT PolicyDeviceObject, + _Out_ PULONG RequestId + ); + +NTSTATUS +UnregisterRequest ( + _Inout_ PFDO_DATA DevExt, + _In_ ULONG RequestId + ); + +NTSTATUS +QueryAttributes ( + _Out_ PPOWER_LIMIT_ATTRIBUTES Buffer, + _Inout_ PFDO_DATA DevExt, + _In_ ULONG RequestId, + _In_ ULONG BufferCount + ); + +NTSTATUS +QueryLimitValues ( + _Out_ PPOWER_LIMIT_VALUE Buffer, + _Inout_ PFDO_DATA DevExt, + _In_ ULONG RequestId, + _In_ ULONG BufferCount + ); + +NTSTATUS +SetLimitValues ( + _Inout_ PFDO_DATA DevExt, + _In_ ULONG RequestId, + _In_ ULONG BufferCount, + _In_ PPOWER_LIMIT_VALUE Buffer + ); diff --git a/powerlimit/plpolicy/plpolicy.inf b/powerlimit/plpolicy/plpolicy.inf new file mode 100644 index 000000000..ac5dc127d --- /dev/null +++ b/powerlimit/plpolicy/plpolicy.inf @@ -0,0 +1,82 @@ +;/*++ +; +;Copyright (c) Microsoft Corporation All rights Reserved +; +;Module Name: +; +; plpolicy.inf +; +;Abstract: +; +; INF file for installing simulate power limit policy driver. +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=System +ClassGuid={4D36E97D-E325-11CE-BFC1-08002BE10318} +Provider=%ProviderString% +DriverVer=08/29/2023,1.00.0000 +CatalogFile=plpolicy.cat +PnpLockdown=1 + +[DestinationDirs] +DefaultDestDir = 12 + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +plpolicy.sys = 1,, + +;***************************************** +; Thermal Request Proxy Install Section +;***************************************** + +[Manufacturer] +%StdMfg%=Standard,NTamd64 +%StdMfg%=Standard,NTarm64 + +[Standard.NTamd64] +%plpolicy.DeviceDesc%=PLPolicy_Device,ACPI\PLPO0001 +%plpolicy.DeviceDesc%=PLPolicy_Device,root\PLPO0001 + +[Standard.NTarm64] +%plpolicy.DeviceDesc%=PLPolicy_Device,ACPI\PLPO0001 +%plpolicy.DeviceDesc%=PLPolicy_Device,root\PLPO0001 + +[PLPolicy_Device.NT] +CopyFiles=PLPolicy_Device.NT.Copy + +[PLPolicy_Device.NT.HW] +AddReg=PLPolicy_Device.NT.AddReg + +[PLPolicy_Device.NT.AddReg] +HKR,,DeviceCharacteristics,0x10001,0x0100 ; Use same security checks on relative opens +HKR,,Security,,"D:P(A;;GA;;;BA)(A;;GA;;;SY)" ; Allow generic-all access to Built-in administrators and Local system + +[PLPolicy_Device.NT.Copy] +plpolicy.sys + +;-------------- Service installation + +[PLPolicy_Device.NT.Services] +AddService = plpolicy,%SPSVCINST_ASSOCSERVICE%,PLPolicy_Service_Inst + +[PLPolicy_Service_Inst] +DisplayName = %plpolicy.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\plpolicy.sys +LoadOrderGroup = System Reserved + +[Strings] +SPSVCINST_ASSOCSERVICE= 0x00000002 +ProviderString = "TODO-Set-Provider" +StdMfg = "(Standard system devices)" +ClassName = "System devices" +DiskId1 = "Simulate Power Limit Policy Installation Disk #1" +plpolicy.DeviceDesc = "Simulate Power Limit Policy Device" +plpolicy.SVCDESC = "Simulate Power Limit Policy Driver" diff --git a/powerlimit/plpolicy/plpolicy.rc b/powerlimit/plpolicy/plpolicy.rc new file mode 100644 index 000000000..0dbbb5ff3 --- /dev/null +++ b/powerlimit/plpolicy/plpolicy.rc @@ -0,0 +1,10 @@ +#include +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Simulate Power Limit Policy Driver" +#define VER_INTERNALNAME_STR "plpolicy.sys" +#define VER_ORIGINALFILENAME_STR "plpolicy.sys" + +#include "common.ver" diff --git a/powerlimit/plpolicy/plpolicy.sln b/powerlimit/plpolicy/plpolicy.sln new file mode 100644 index 000000000..564d8c590 --- /dev/null +++ b/powerlimit/plpolicy/plpolicy.sln @@ -0,0 +1,49 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34701.34 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plpolicy", "plpolicy.vcxproj", "{F69B9212-A156-4EF1-8478-11228DE91DD3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plclient", "..\plclient\plclient.vcxproj", "{D6B30052-9124-44DB-A421-4DEE110B91E2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F69B9212-A156-4EF1-8478-11228DE91DD3}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F69B9212-A156-4EF1-8478-11228DE91DD3}.Debug|ARM64.Build.0 = Debug|ARM64 + {F69B9212-A156-4EF1-8478-11228DE91DD3}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {F69B9212-A156-4EF1-8478-11228DE91DD3}.Debug|x64.ActiveCfg = Debug|x64 + {F69B9212-A156-4EF1-8478-11228DE91DD3}.Debug|x64.Build.0 = Debug|x64 + {F69B9212-A156-4EF1-8478-11228DE91DD3}.Debug|x64.Deploy.0 = Debug|x64 + {F69B9212-A156-4EF1-8478-11228DE91DD3}.Release|ARM64.ActiveCfg = Release|ARM64 + {F69B9212-A156-4EF1-8478-11228DE91DD3}.Release|ARM64.Build.0 = Release|ARM64 + {F69B9212-A156-4EF1-8478-11228DE91DD3}.Release|ARM64.Deploy.0 = Release|ARM64 + {F69B9212-A156-4EF1-8478-11228DE91DD3}.Release|x64.ActiveCfg = Release|x64 + {F69B9212-A156-4EF1-8478-11228DE91DD3}.Release|x64.Build.0 = Release|x64 + {F69B9212-A156-4EF1-8478-11228DE91DD3}.Release|x64.Deploy.0 = Release|x64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Debug|ARM64.Build.0 = Debug|ARM64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Debug|x64.ActiveCfg = Debug|x64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Debug|x64.Build.0 = Debug|x64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Debug|x64.Deploy.0 = Debug|x64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Release|ARM64.ActiveCfg = Release|ARM64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Release|ARM64.Build.0 = Release|ARM64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Release|ARM64.Deploy.0 = Release|ARM64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Release|x64.ActiveCfg = Release|x64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Release|x64.Build.0 = Release|x64 + {D6B30052-9124-44DB-A421-4DEE110B91E2}.Release|x64.Deploy.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {ED084CB6-1513-41AF-9AF9-545395180D32} + EndGlobalSection +EndGlobal diff --git a/powerlimit/plpolicy/plpolicy.vcxproj b/powerlimit/plpolicy/plpolicy.vcxproj new file mode 100644 index 000000000..bb0b948ab --- /dev/null +++ b/powerlimit/plpolicy/plpolicy.vcxproj @@ -0,0 +1,123 @@ + + + + + Debug + x64 + + + Release + x64 + + + Debug + ARM64 + + + Release + ARM64 + + + + {F69B9212-A156-4EF1-8478-11228DE91DD3} + {1bc93793-694f-48fe-9372-81e2b05556fd} + v4.5 + 12.0 + Debug + x64 + plpolicy + + + + Windows10 + true + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + Windows10 + false + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + Windows10 + true + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + Windows10 + false + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + + + + + + + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + + sha256 + + + + + sha256 + + + + + sha256 + + + + + sha256 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/powerlimit/plpolicy/plpolicy.vcxproj.filters b/powerlimit/plpolicy/plpolicy.vcxproj.filters new file mode 100644 index 000000000..b23178606 --- /dev/null +++ b/powerlimit/plpolicy/plpolicy.vcxproj.filters @@ -0,0 +1,47 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {8E41214B-6785-4CFE-B992-037D68949A14} + inf;inv;inx;mof;mc; + + + + + Driver Files + + + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/powerlimit/plpolicy/powerlimitpolicy_drvinterface.h b/powerlimit/plpolicy/powerlimitpolicy_drvinterface.h new file mode 100644 index 000000000..cd630ef66 --- /dev/null +++ b/powerlimit/plpolicy/powerlimitpolicy_drvinterface.h @@ -0,0 +1,85 @@ +/*++ + +Copyright (c) Microsoft Corporation + +Module Name: + + powerlimitpolicy_drvinterface.h + +Abstract: + + This module contains the interfaces used to communicate with the simulate + power limit policy driver stack. + +--*/ + +//--------------------------------------------------------------------- Pragmas + +#pragma once + +//--------------------------------------------------------------------- Defines + +// +// Simulated power limit policy interface +// + +// {dbdc0da1-563c-4e20-8408-d4e3c1069ea3} +DEFINE_GUID(GUID_DEVINTERFACE_POWERLIMIT_POLICY, +0xdbdc0da1, 0x563c, 0x4e20, 0x84, 0x08, 0xd4, 0xe3, 0xc1, 0x06, 0x9e, 0xa3); + +// +// IOCTLs to control the policy driver +// + +#define POWERLIMIT_POLICY_IOCTL(_index_) \ + CTL_CODE(FILE_DEVICE_UNKNOWN, _index_, METHOD_BUFFERED, FILE_WRITE_DATA) + +// +// IOCTL_POWERLIMIT_POLICY_REGISTER +// - Input: WCHAR[], contains interface name of the target device +// - Output: ULONG, Id of the created power limit +// + +#define IOCTL_POWERLIMIT_POLICY_REGISTER POWERLIMIT_POLICY_IOCTL(0x800) + +// +// IOCTL_POWERLIMIT_POLICY_UNREGISTER +// - Input: ULONG, Id of the power limit to unregister +// + +#define IOCTL_POWERLIMIT_POLICY_UNREGISTER POWERLIMIT_POLICY_IOCTL(0x801) + +// +// IOCTL_POWERLIMIT_POLICY_QUERY_ATTRIBUTES +// - Input: POWERLIMIT_POLICY_ATTRIBUTES +// - Output: POWERLIMIT_POLICY_ATTRIBUTES +// + +#define IOCTL_POWERLIMIT_POLICY_QUERY_ATTRIBUTES POWERLIMIT_POLICY_IOCTL(0x802) + +// +// IOCTL_POWERLIMIT_POLICY_QUERY_VALUES +// - Input: POWERLIMIT_POLICY_VALUES +// - Output: POWERLIMIT_POLICY_VALUES +// + +#define IOCTL_POWERLIMIT_POLICY_QUERY_VALUES POWERLIMIT_POLICY_IOCTL(0x803) + +// +// IOCTL_POWERLIMIT_POLICY_SET_VALUES +// - Input: POWERLIMIT_POLICY_VALUES, values to be updated for target power limit request +// + +#define IOCTL_POWERLIMIT_POLICY_SET_VALUES POWERLIMIT_POLICY_IOCTL(0x804) + +typedef struct _POWERLIMIT_POLICY_ATTRIBUTES { + ULONG RequestId; + ULONG BufferCount; + POWER_LIMIT_ATTRIBUTES Buffer[ANYSIZE_ARRAY]; +} POWERLIMIT_POLICY_ATTRIBUTES, *PPOWERLIMIT_POLICY_ATTRIBUTES; + +typedef struct _POWERLIMIT_POLICY_VALUES { + ULONG RequestId; + ULONG BufferCount; + POWER_LIMIT_VALUE Buffer[ANYSIZE_ARRAY]; +} POWERLIMIT_POLICY_VALUES, *PPOWERLIMIT_POLICY_VALUES; diff --git a/powerlimit/plpolicy/sources b/powerlimit/plpolicy/sources new file mode 100644 index 000000000..43789553a --- /dev/null +++ b/powerlimit/plpolicy/sources @@ -0,0 +1,33 @@ +TARGETNAME=plpolicy +TARGETTYPE=DRIVER +TARGET_DESTINATION=powertests\powerlimit\policy + +# Specifies the file is needed for boot +# and for performance issues must have +# an embedded signature. +## MBS Team 2/5/2014 +## Removing boot_loader_critical, instead doing kit_self_signed, because you have test_code=1 +BOOT_LOADER_CRITICAL=1 + +NO_PDB_PATHS=1 + +MSC_WARNING_LEVEL=/W4 /WX + +KMDF_VERSION_MAJOR=1 + +C_DEFINES= $(C_DEFINES) -DUNICODE -D_UNICODE + +INCLUDES=$(INCLUDES); \ + $(MINWIN_PRIV_SDK_INC_PATH); \ + $(MINWIN_PRIV_SDK_INC_PATH)\hals; \ + ..\inc + +SOURCES= \ + plpolicy.c \ + plpolicy.rc \ + wdf.c \ + +PASS0_BINPLACE=\ + plpolicy.inf \ + +MUI_VERIFY_NO_LOC_RESOURCE=1 diff --git a/powerlimit/plpolicy/wdf.c b/powerlimit/plpolicy/wdf.c new file mode 100644 index 000000000..14760ac73 --- /dev/null +++ b/powerlimit/plpolicy/wdf.c @@ -0,0 +1,620 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + wdf.c + +Abstract: + + The module implements WDF boilerplate for the simulate power limit policy. + +--*/ + +//-------------------------------------------------------------------- Includes + +#include "plpolicy.h" + +//------------------------------------------------------------------ Prototypes + +DRIVER_INITIALIZE DriverEntry; +EVT_WDF_DRIVER_DEVICE_ADD EvtDriverDeviceAdd; +EVT_WDF_DRIVER_UNLOAD EvtDriverUnload; +EVT_WDF_OBJECT_CONTEXT_DESTROY EvtDeviceDestroy; +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL EvtIoDeviceControl; + +NTSTATUS +GetDeviceName ( + _Out_ PUNICODE_STRING DeviceName, + _In_reads_bytes_(BufferLength) PWCHAR Buffer, + _In_ SIZE_T BufferLength + ); + +//--------------------------------------------------------------------- Pragmas + +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, EvtDriverDeviceAdd) +#pragma alloc_text(PAGE, EvtDriverUnload) +#pragma alloc_text(PAGE, EvtIoDeviceControl) +#pragma alloc_text(PAGE, EvtDeviceDestroy) +#pragma alloc_text(PAGE, GetDeviceName) + +//------------------------------------------------------------------- Functions + +_Use_decl_annotations_ +NTSTATUS +DriverEntry ( + PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + DriverEntry initializes the driver and is the first routine called by the + system after the driver is loaded. DriverEntry configures and creates a WDF + driver object. + +Parameters Description: + + DriverObject - Supplies a pointer to the driver object. + + RegistryPath - Supplies a pointer to a unicode string representing the + path to the driver-specific key in the registry. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + WDF_DRIVER_CONFIG DriverConfig; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(RegistryPath); + + // + // Initiialize the DriverConfig data that controls the attributes that are + // global to this driver. + // + + DebugEnter(); + WDF_DRIVER_CONFIG_INIT(&DriverConfig, EvtDriverDeviceAdd); + DriverConfig.EvtDriverUnload = EvtDriverUnload; + + // + // Create a framework driver object to represent this driver. + // + + Status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &DriverConfig, + WDF_NO_HANDLE); + + if (!NT_SUCCESS(Status)) { + DebugPrint(PLPOLICY_PRINT_ERROR, + "WdfDriverCreate() Failed! 0x%x\n", + Status); + + goto DriverEntryEnd; + } + + // + // Initialize global mutex. + // + + Status = WdfWaitLockCreate(WDF_NO_OBJECT_ATTRIBUTES, &GlobalMutex); + if (!NT_SUCCESS(Status)) { + DebugPrint(PLPOLICY_PRINT_ERROR, + "WdfSpinLockCreate() Failed! 0x%x\n", + Status); + + goto DriverEntryEnd; + } + + Status = STATUS_SUCCESS; + +DriverEntryEnd: + DebugExitStatus(Status); + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +EvtDriverDeviceAdd ( + WDFDRIVER Driver, + PWDFDEVICE_INIT DeviceInit + ) + +/*++ + +Routine Description: + + EvtDriverDeviceAdd is called by the framework in response to AddDevice + call from the PnP manager. A WDF device object is created and initailized to + represent the simulation control interface. + +Arguments: + + Driver - Supplies a handle to a framework driver object created in + DriverEntry + + DeviceInit - Supplies a pointer to a framework-allocated WDFDEVICE_INIT + structure. + +Return Value: + + NTSTATUS + +--*/ + +{ + + PFDO_DATA DevExt; + WDF_OBJECT_ATTRIBUTES DeviceAttributes; + WDFDEVICE DeviceHandle; + WDF_IO_QUEUE_CONFIG IoQueueConfig; + WDFQUEUE Queue; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + DebugEnter(); + + // + // Initialize attributes and a context area for the device object. + // + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&DeviceAttributes, FDO_DATA); + DeviceAttributes.EvtDestroyCallback = &EvtDeviceDestroy; + + + // + // Create a framework device object. This call will in turn create + // a WDM device object, attach to the lower stack, and set the + // appropriate flags and attributes. + // + + Status = WdfDeviceCreate(&DeviceInit, &DeviceAttributes, &DeviceHandle); + if (!NT_SUCCESS(Status)) { + DebugPrint(PLPOLICY_PRINT_ERROR, + "WdfDeviceCreate() Failed! 0x%x\n", + Status); + + goto EvtDriverDeviceAddEnd; + } + + // + // Configure a default queue for IO requests. This queue processes requests + // to read/write the simulated state. + // + + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&IoQueueConfig, + WdfIoQueueDispatchSequential); + + IoQueueConfig.EvtIoDeviceControl = EvtIoDeviceControl; + Status = WdfIoQueueCreate(DeviceHandle, + &IoQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &Queue); + + if (!NT_SUCCESS(Status)) { + DebugPrint(PLPOLICY_PRINT_ERROR, + "WdfIoQueueCreate() Failed! 0x%x\n", + Status); + + goto EvtDriverDeviceAddEnd; + } + + // + // Create device interface for this device. The interface will be + // enabled by the framework when StartDevice returns successfully. + // Clients of this driver will open this interface and send ioctls. + // + + Status = WdfDeviceCreateDeviceInterface(DeviceHandle, + &GUID_DEVINTERFACE_POWERLIMIT_POLICY, + NULL); + + if (!NT_SUCCESS(Status)) { + DebugPrint(PLPOLICY_PRINT_ERROR, + "WdfDeviceCreateDeviceInterface() Failed! 0x%x\n", + Status); + + goto EvtDriverDeviceAddEnd; + } + + // + // Ignore failures to initialize the device; the test cases will fail in + // this case. + // + + DevExt = GetDeviceExtension(DeviceHandle); + InitializeListHead(&DevExt->RequestHeader); + DevExt->RequestCount = 0; + Status = STATUS_SUCCESS; + +EvtDriverDeviceAddEnd: + DebugExitStatus(Status); + return Status; +} + +_Use_decl_annotations_ +VOID +EvtDeviceDestroy ( + WDFOBJECT Object + ) + +/*++ + +Routine Description: + + This routine destroys the device's data. + +Arguments: + + Object - Supplies the WDF reference to the device that is being removed. + +Return Value: + + None. + +--*/ + +{ + + PFDO_DATA DevExt; + PLIST_ENTRY Link; + PDEVICE_REGISTRATION Registration; + + PAGED_CODE(); + + DebugEnter(); + DevExt = GetDeviceExtension(Object); + while (IsListEmpty(&DevExt->RequestHeader) == FALSE) { + Link = DevExt->RequestHeader.Flink; + Registration = CONTAINING_RECORD(Link, DEVICE_REGISTRATION, Link); + RemoveEntryList(&Registration->Link); + PoDeletePowerLimitRequest(Registration->PowerLimitRequest); + ExFreePoolWithTag(Registration, PLPOLICY_TAG); + } + + DebugExit(); + return; +} + +_Use_decl_annotations_ +VOID +EvtIoDeviceControl ( + WDFQUEUE Queue, + WDFREQUEST Request, + size_t OutputBufferLength, + size_t InputBufferLength, + ULONG IoControlCode + ) + +/*++ + +Routine Description: + + This routine processes an IOCTL sent to the PEP. + +Arguments: + + Queue - Supplies a handle to the WDF queue object. + + Request - Supplies a handle to the WDF request object for this request. + + OuputBufferLength - Supplies the length of the output buffer, in bytes. + + InputBufferLength - Supplies the length of the input buffer, in bytes. + + IoControlCode - Supplies the IOCTL code being processes. + +Return Value: + + None. + +--*/ + +{ + + PPOWERLIMIT_POLICY_ATTRIBUTES Attributes; + ULONG BytesWritten; + PFDO_DATA DevExt; + WDFDEVICE Device; + UNICODE_STRING DeviceName; + PVOID InputBuffer; + PVOID OutputBuffer; + PPOWERLIMIT_POLICY_ATTRIBUTES QueryAttributeInput; + PPOWERLIMIT_POLICY_VALUES QueryValueInput; + ULONG RequestId; + PPOWERLIMIT_POLICY_VALUES SetInput; + ULONG SizeNeeded; + NTSTATUS Status; + PPOWERLIMIT_POLICY_VALUES Values; + + DebugPrint(PLPOLICY_PRINT_TRACE, + "EvtIoDeviceControl: 0x%08x\n", + IoControlCode); + + BytesWritten = 0; + OutputBuffer = NULL; + InputBuffer = NULL; + if (InputBufferLength > 0) { + Status = WdfRequestRetrieveInputBuffer(Request, + InputBufferLength, + &InputBuffer, + NULL); + + if (!NT_SUCCESS(Status)) { + goto DeviceControlEnd; + } + } + + if (OutputBufferLength > 0) { + Status = WdfRequestRetrieveOutputBuffer(Request, + OutputBufferLength, + &OutputBuffer, + NULL); + + if (!NT_SUCCESS(Status)) { + goto DeviceControlEnd; + } + } + + Device = WdfIoQueueGetDevice(Queue); + DevExt = GetDeviceExtension(Device); + switch (IoControlCode) { + case IOCTL_POWERLIMIT_POLICY_REGISTER: + if ((InputBufferLength == 0) || (InputBuffer == NULL)) { + Status = STATUS_INVALID_PARAMETER; + goto DeviceControlEnd; + } + + if ((OutputBufferLength != sizeof(ULONG)) || (OutputBuffer == NULL)) { + Status = STATUS_INVALID_PARAMETER; + goto DeviceControlEnd; + } + + Status = GetDeviceName(&DeviceName, + (PWCHAR)InputBuffer, + InputBufferLength); + + if (!NT_SUCCESS(Status)) { + goto DeviceControlEnd; + } + + Status = RegisterRequest(DevExt, + &DeviceName, + WdfDeviceWdmGetDeviceObject(Device), + &RequestId); + + if (NT_SUCCESS(Status)) { + *(PULONG)OutputBuffer = RequestId; + BytesWritten = sizeof(ULONG); + } + + break; + + case IOCTL_POWERLIMIT_POLICY_UNREGISTER: + if (InputBufferLength < sizeof(ULONG)) { + Status = STATUS_INVALID_PARAMETER; + goto DeviceControlEnd; + } + + RequestId = *(PULONG)InputBuffer; + Status = UnregisterRequest(DevExt, RequestId); + + break; + + case IOCTL_POWERLIMIT_POLICY_QUERY_ATTRIBUTES: + if (InputBufferLength < sizeof(POWERLIMIT_POLICY_ATTRIBUTES)) { + Status = STATUS_INVALID_PARAMETER; + goto DeviceControlEnd; + } + + QueryAttributeInput = (PPOWERLIMIT_POLICY_ATTRIBUTES)InputBuffer; + SizeNeeded = FIELD_OFFSET(POWERLIMIT_POLICY_ATTRIBUTES, + Buffer[QueryAttributeInput->BufferCount]); + + if ((OutputBufferLength < SizeNeeded) || (InputBufferLength < SizeNeeded)) { + Status = STATUS_INVALID_PARAMETER; + goto DeviceControlEnd; + } + + Attributes = ExAllocatePool2(POOL_FLAG_PAGED, SizeNeeded, PLPOLICY_TAG); + if (Attributes == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto DeviceControlEnd; + } + + Attributes->RequestId = QueryAttributeInput->RequestId; + Attributes->BufferCount = QueryAttributeInput->BufferCount; + Status = QueryAttributes(Attributes->Buffer, + DevExt, + QueryAttributeInput->RequestId, + QueryAttributeInput->BufferCount); + + if (NT_SUCCESS(Status)) { + RtlCopyMemory(OutputBuffer, Attributes, SizeNeeded); + BytesWritten = SizeNeeded; + } + + ExFreePoolWithTag(Attributes, PLPOLICY_TAG); + + break; + + case IOCTL_POWERLIMIT_POLICY_QUERY_VALUES: + if (InputBufferLength < sizeof(POWERLIMIT_POLICY_VALUES)) { + Status = STATUS_INVALID_PARAMETER; + goto DeviceControlEnd; + } + + QueryValueInput = (PPOWERLIMIT_POLICY_VALUES)InputBuffer; + SizeNeeded = FIELD_OFFSET(POWERLIMIT_POLICY_VALUES, + Buffer[QueryValueInput->BufferCount]); + + if ((OutputBufferLength < SizeNeeded) || (InputBufferLength < SizeNeeded)) { + Status = STATUS_INVALID_PARAMETER; + goto DeviceControlEnd; + } + + Values = ExAllocatePool2(POOL_FLAG_PAGED, SizeNeeded, PLPOLICY_TAG); + if (Values == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto DeviceControlEnd; + } + + // + // The input buffer contains which limits to read, copy them over before + // calling kernel API. + // + + RtlCopyMemory(Values, QueryValueInput, SizeNeeded); + Status = QueryLimitValues(Values->Buffer, + DevExt, + QueryValueInput->RequestId, + QueryValueInput->BufferCount); + + if (NT_SUCCESS(Status)) { + RtlCopyMemory(OutputBuffer, Values, SizeNeeded); + BytesWritten = SizeNeeded; + } + + ExFreePoolWithTag(Values, PLPOLICY_TAG); + + break; + + case IOCTL_POWERLIMIT_POLICY_SET_VALUES: + if (InputBufferLength < sizeof(POWERLIMIT_POLICY_VALUES)) { + Status = STATUS_INVALID_PARAMETER; + goto DeviceControlEnd; + } + + SetInput = (PPOWERLIMIT_POLICY_VALUES)InputBuffer; + SizeNeeded = FIELD_OFFSET(POWERLIMIT_POLICY_VALUES, + Buffer[SetInput->BufferCount]); + + if (InputBufferLength < SizeNeeded) { + Status = STATUS_INVALID_PARAMETER; + goto DeviceControlEnd; + } + + Status = SetLimitValues(DevExt, + SetInput->RequestId, + SetInput->BufferCount, + SetInput->Buffer); + + break; + + default: + Status = STATUS_NOT_SUPPORTED; + break; + } + +DeviceControlEnd: + WdfRequestCompleteWithInformation(Request, Status, BytesWritten); + DebugExitStatus(Status); + return; +} + +VOID +EvtDriverUnload ( + WDFDRIVER Driver + ) + +/*++ + +Routine Description: + + This routine is called at driver unload to clean up any lingering thermal + requests. + +Arguments: + + Driver - Supplies a pointer to the WDF driver object. + +Return Value: + + None. + +--*/ + +{ + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + DebugEnter(); + + // + // N.B. Does nothing since we don't have anything to clean up, just print + // some debug info. + // + + DebugExit(); + return; +} + +_Use_decl_annotations_ +NTSTATUS +GetDeviceName ( + PUNICODE_STRING DeviceName, + PWCHAR Buffer, + SIZE_T BufferLength + ) + +/*++ + +Routine Description: + + This routine extracts a device name from an IOCTL input buffer, being + careful to validate the name is well formed. + +Arguments: + + DeviceName - Supplies a UNICODE_STRING to initialize with the device name. + + Buffer - Supplies the buffer containing the device name. + + BufferLength - Supplies the length of the buffer containing the device name, + in bytes. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + size_t Length; + NTSTATUS Status; + + Status = RtlStringCchLengthW(Buffer, BufferLength / sizeof(WCHAR), &Length); + if (!NT_SUCCESS(Status)) { + goto GetDeviceNameEnd; + } + + if (Length > NTSTRSAFE_UNICODE_STRING_MAX_CCH) { + Status = STATUS_INVALID_PARAMETER; + goto GetDeviceNameEnd; + } + + DeviceName->Buffer = Buffer; + DeviceName->Length = (USHORT)(Length * sizeof(WCHAR)); + DeviceName->MaximumLength = (USHORT)(Length * sizeof(WCHAR)); + Status = STATUS_SUCCESS; + +GetDeviceNameEnd: + return Status; +}