summaryrefslogtreecommitdiff
path: root/powerlimit/plpolicy/wdf.c
diff options
context:
space:
mode:
Diffstat (limited to 'powerlimit/plpolicy/wdf.c')
-rw-r--r--powerlimit/plpolicy/wdf.c620
1 files changed, 620 insertions, 0 deletions
diff --git a/powerlimit/plpolicy/wdf.c b/powerlimit/plpolicy/wdf.c
new file mode 100644
index 00000000..14760ac7
--- /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;
+}