Files
yttrium-virtio-gpu/viocrypt/sys/viocrypt.c
T
Viktor Prutyanov 56a7df8594 viocrypt: make driver routines (non-)paged properly (CA)
DeviceReleaseHardware and DeviceD0Exit have already used PAGED_CODE()
but weren't placed in pageable section, so move them.

DeviceD0Entry have used PAGED_CODE() also, but should not be placed
in pageable section despite of IRQL = PASSIVE_LEVEL because the
framework calls it when device returns to D0 state, so remove
PAGED_CODE().

Signed-off-by: Viktor Prutyanov <[email protected]>
2021-09-05 17:00:26 +03:00

388 lines
12 KiB
C

/*
* Copyright (C) 2018 Red Hat, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of their contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "driver.h"
#include "viocrypt-public.h"
#ifndef NO_WPP
#include "viocrypt.tmh"
#endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, VioCryptDeviceAdd)
#pragma alloc_text(PAGE, VioCryptDriverContextCleanup)
#pragma alloc_text(PAGE, VioCryptDevicePrepareHardware)
#pragma alloc_text(PAGE, VioCryptDeviceReleaseHardware)
#pragma alloc_text(PAGE, VioCryptDeviceD0Exit)
#endif
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
WDF_DRIVER_CONFIG config;
WDF_OBJECT_ATTRIBUTES attributes;
// Initialize the WPP tracing.
WPP_INIT_TRACING(DriverObject, RegistryPath);
Trace(TRACE_LEVEL_VERBOSE, "[%s] -->", __FUNCTION__);
// Register a cleanup callback so that we can call WPP_CLEANUP when
// the framework driver object is deleted during driver unload.
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.EvtCleanupCallback = VioCryptDriverContextCleanup;
WDF_DRIVER_CONFIG_INIT(&config, VioCryptDeviceAdd);
status = WdfDriverCreate(DriverObject, RegistryPath, &attributes,
&config, WDF_NO_HANDLE);
if (!NT_SUCCESS(status))
{
Trace(TRACE_LEVEL_ERROR, "[%s] WdfDriverCreate failed: status %X", __FUNCTION__, status);
WPP_CLEANUP(DriverObject);
}
Trace(TRACE_LEVEL_VERBOSE, "[%s] -->", __FUNCTION__);
return status;
}
NTSTATUS VioCryptDeviceAdd(IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit)
{
NTSTATUS status;
WDFDEVICE device;
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
WDF_OBJECT_ATTRIBUTES attributes;
WDFQUEUE queue;
WDF_IO_QUEUE_CONFIG queueConfig;
WDF_INTERRUPT_CONFIG interruptConfig;
PDEVICE_CONTEXT context;
UNREFERENCED_PARAMETER(Driver);
Trace(TRACE_LEVEL_VERBOSE, "[%s] -->", __FUNCTION__);
PAGED_CODE();
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
pnpPowerCallbacks.EvtDevicePrepareHardware = VioCryptDevicePrepareHardware;
pnpPowerCallbacks.EvtDeviceReleaseHardware = VioCryptDeviceReleaseHardware;
pnpPowerCallbacks.EvtDeviceD0Entry = VioCryptDeviceD0Entry;
pnpPowerCallbacks.EvtDeviceD0Exit = VioCryptDeviceD0Exit;
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_CONTEXT);
attributes.EvtCleanupCallback = VioCryptDeviceContextCleanup;
status = WdfDeviceCreate(&DeviceInit, &attributes, &device);
if (!NT_SUCCESS(status))
{
Trace(TRACE_LEVEL_ERROR, "[%s] WdfDeviceCreate failed: status %X", __FUNCTION__, status);
return status;
}
context = GetDeviceContext(device);
RtlZeroMemory(context, sizeof(*context));
InitializeListHead(&context->PendingBuffers);
WDF_INTERRUPT_CONFIG_INIT(&interruptConfig, VioCryptInterruptIsr, VioCryptInterruptDpc);
interruptConfig.EvtInterruptEnable = VioCryptInterruptEnable;
interruptConfig.EvtInterruptDisable = VioCryptInterruptDisable;
status = WdfInterruptCreate(device, &interruptConfig,
WDF_NO_OBJECT_ATTRIBUTES, &context->WdfInterrupt);
if (!NT_SUCCESS(status))
{
Trace(TRACE_LEVEL_ERROR, "[%s] WdfInterruptCreate failed: status %X", __FUNCTION__, status);
return status;
}
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.ParentObject = device;
status = WdfSpinLockCreate(&attributes,
&context->VirtQueueLock);
if (!NT_SUCCESS(status))
{
Trace(TRACE_LEVEL_ERROR, "[%s] WdfSpinLockCreate failed: status %X", __FUNCTION__, status);
return status;
}
status = WdfDeviceCreateDeviceInterface(device,
&GUID_DEVINTERFACE_VIOCRYPT, NULL);
if (!NT_SUCCESS(status))
{
Trace(TRACE_LEVEL_ERROR, "[%s] WdfDeviceCreateDeviceInterface failed: status %X", __FUNCTION__, status);
return status;
}
WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, WdfIoQueueDispatchSequential);
queueConfig.EvtIoDeviceControl = VioCryptIoControl;
queueConfig.EvtIoStop = VioCryptIoStop;
queueConfig.AllowZeroLengthRequests = FALSE;
status = WdfIoQueueCreate(device, &queueConfig,
WDF_NO_OBJECT_ATTRIBUTES, &queue);
if (!NT_SUCCESS(status))
{
Trace(TRACE_LEVEL_ERROR, "[%s] WdfIoQueueCreate failed: status %X", __FUNCTION__, status);
return status;
}
status = WdfDeviceConfigureRequestDispatching(device, queue, WdfRequestTypeDeviceControl);
if (!NT_SUCCESS(status))
{
Trace(TRACE_LEVEL_ERROR, "[%s] WdfDeviceConfigureRequestDispatching failed: status %X", __FUNCTION__, status);
return status;
}
Trace(TRACE_LEVEL_VERBOSE, "[%s] <--", __FUNCTION__);
return status;
}
VOID VioCryptDriverContextCleanup(IN WDFOBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
Trace(TRACE_LEVEL_VERBOSE, "[%s]", __FUNCTION__);
PAGED_CODE();
// Stop the WPP tracing.
WPP_CLEANUP(WdfDriverWdmGetDriverObject(DriverObject));
}
NTSTATUS VioCryptInterruptEnable(IN WDFINTERRUPT Interrupt, IN WDFDEVICE wdfDevice)
{
PDEVICE_CONTEXT context = GetDeviceContext(wdfDevice);
UNREFERENCED_PARAMETER(Interrupt);
virtqueue_enable_cb(context->ControlQueue);
virtqueue_kick(context->ControlQueue);
Trace(TRACE_LEVEL_VERBOSE, "[%s]", __FUNCTION__);
return STATUS_SUCCESS;
}
NTSTATUS VioCryptInterruptDisable(IN WDFINTERRUPT Interrupt, IN WDFDEVICE wdfDevice)
{
PDEVICE_CONTEXT context = GetDeviceContext(wdfDevice);
UNREFERENCED_PARAMETER(Interrupt);
Trace(TRACE_LEVEL_VERBOSE, "[%s]", __FUNCTION__);
virtqueue_disable_cb(context->ControlQueue);
return STATUS_SUCCESS;
}
BOOLEAN VioCryptInterruptIsr(IN WDFINTERRUPT Interrupt, IN ULONG MessageId)
{
PDEVICE_CONTEXT context = GetDeviceContext(
WdfInterruptGetDevice(Interrupt));
WDF_INTERRUPT_INFO info;
BOOLEAN processed;
WDF_INTERRUPT_INFO_INIT(&info);
WdfInterruptGetInfo(context->WdfInterrupt, &info);
processed = ((info.MessageSignaled && (MessageId == 0)) ||
VirtIOWdfGetISRStatus(&context->VDevice));
if (processed)
{
WdfInterruptQueueDpcForIsr(Interrupt);
}
Trace(TRACE_LEVEL_VERBOSE, "[%s] %sprocessed", __FUNCTION__, processed ? "" : "not ");
return processed;
}
VOID VioCryptInterruptDpc(IN WDFINTERRUPT Interrupt,
IN WDFOBJECT AssociatedObject)
{
PDEVICE_CONTEXT context = GetDeviceContext(
WdfInterruptGetDevice(Interrupt));
UNREFERENCED_PARAMETER(AssociatedObject);
UNREFERENCED_PARAMETER(context);
}
NTSTATUS VioCryptDevicePrepareHardware(IN WDFDEVICE Device,
IN WDFCMRESLIST Resources,
IN WDFCMRESLIST ResourcesTranslated)
{
PDEVICE_CONTEXT context = GetDeviceContext(Device);
NTSTATUS status = STATUS_SUCCESS;
UNREFERENCED_PARAMETER(Resources);
Trace(TRACE_LEVEL_VERBOSE, "[%s]", __FUNCTION__);
PAGED_CODE();
status = VirtIOWdfInitialize(
&context->VDevice,
Device,
ResourcesTranslated,
NULL,
VIO_CRYPT_MEMORY_TAG);
if (!NT_SUCCESS(status))
{
Trace(TRACE_LEVEL_ERROR, "VirtIOWdfInitialize failed with %x\n", status);
}
return status;
}
NTSTATUS VioCryptDeviceReleaseHardware(IN WDFDEVICE Device,
IN WDFCMRESLIST ResourcesTranslated)
{
PDEVICE_CONTEXT context = GetDeviceContext(Device);
UNREFERENCED_PARAMETER(ResourcesTranslated);
PAGED_CODE();
VirtIOWdfShutdown(&context->VDevice);
Trace(TRACE_LEVEL_VERBOSE, "[%s]", __FUNCTION__);
return STATUS_SUCCESS;
}
NTSTATUS VioCryptDeviceD0Entry(IN WDFDEVICE Device, IN WDF_POWER_DEVICE_STATE PreviousState)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_CONTEXT context = GetDeviceContext(Device);
VIRTIO_WDF_QUEUE_PARAM param;
struct virtqueue *queues[1];
Trace(TRACE_LEVEL_VERBOSE, "[%s] from D%d", __FUNCTION__, PreviousState - WdfPowerDeviceD0);
if (NT_SUCCESS(status))
{
param.Interrupt = context->WdfInterrupt;
status = VirtIOWdfInitQueues(&context->VDevice, 1, queues, &param);
}
if (NT_SUCCESS(status))
{
context->ControlQueue = queues[0];
VirtIOWdfSetDriverOK(&context->VDevice);
}
else
{
VirtIOWdfSetDriverFailed(&context->VDevice);
Trace(TRACE_LEVEL_ERROR, "[%s] VirtIOWdfInitQueues failed with %x\n", __FUNCTION__, status);
}
return status;
}
NTSTATUS VioCryptDeviceD0Exit(IN WDFDEVICE Device, IN WDF_POWER_DEVICE_STATE TargetState)
{
PDEVICE_CONTEXT context = GetDeviceContext(Device);
Trace(TRACE_LEVEL_VERBOSE, "[%s] to D%d", __FUNCTION__, TargetState - WdfPowerDeviceD0);
PAGED_CODE();
VirtIOWdfDestroyQueues(&context->VDevice);
return STATUS_SUCCESS;
}
VOID VioCryptDeviceContextCleanup(IN WDFOBJECT DeviceObject)
{
PDEVICE_CONTEXT context = GetDeviceContext(DeviceObject);
BOOLEAN bHasBuffers = !IsListEmpty(&context->PendingBuffers);
Trace(TRACE_LEVEL_VERBOSE, "[%s] %s", __FUNCTION__, bHasBuffers ? "Has unfreed buffers" : "");
// TODO: free buffers allocated by the driver
}
VOID VioCryptIoControl
(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t OutputBufferLength,
IN size_t InputBufferLength,
IN ULONG IoControlCode)
{
NTSTATUS status = STATUS_NOT_SUPPORTED;
UNREFERENCED_PARAMETER(Queue);
Trace(TRACE_LEVEL_INFORMATION, "[%s] code %X, in %lld, out %lld",
__FUNCTION__, IoControlCode, InputBufferLength, OutputBufferLength);
if (status != STATUS_PENDING)
{
WdfRequestComplete(Request, STATUS_NOT_SUPPORTED);
}
}
VOID VioCryptIoStop(IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN ULONG ActionFlags)
{
BOOLEAN bCancellable = (ActionFlags & WdfRequestStopRequestCancelable) != 0;
UNREFERENCED_PARAMETER(Queue);
Trace(TRACE_LEVEL_INFORMATION, "[%s] Req %p, action %X, the request is %scancellable",
__FUNCTION__, Request, ActionFlags, bCancellable ? "" : "not ");
if (ActionFlags & WdfRequestStopActionSuspend)
{
// the driver owns the request and it will not be able to process it
Trace(TRACE_LEVEL_INFORMATION, "[%s] Req %p can't be suspended", __FUNCTION__, Request);
if (!bCancellable || WdfRequestUnmarkCancelable(Request) != STATUS_CANCELLED)
{
WdfRequestComplete(Request, STATUS_CANCELLED);
}
}
else if (ActionFlags & WdfRequestStopActionPurge)
{
Trace(TRACE_LEVEL_INFORMATION, "[%s] Req %p purged", __FUNCTION__, Request);
if (!bCancellable || WdfRequestUnmarkCancelable(Request) != STATUS_CANCELLED)
{
WdfRequestComplete(Request, STATUS_CANCELLED);
}
}
}