mirror of
https://github.com/arehnman/yttrium-virtio-gpu.git
synced 2026-11-07 02:06:32 +00:00
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]>
388 lines
12 KiB
C
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, ¶m);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|