kvm-guest-drivers-windows
326 строк · 9.8 Кб
1/*
2* Copyright (C) 2019 Red Hat, Inc.
3*
4* Written By: Gal Hammer <ghammer@redhat.com>
5*
6* Redistribution and use in source and binary forms, with or without
7* modification, are permitted provided that the following conditions
8* are met :
9* 1. Redistributions of source code must retain the above copyright
10* notice, this list of conditions and the following disclaimer.
11* 2. Redistributions in binary form must reproduce the above copyright
12* notice, this list of conditions and the following disclaimer in the
13* documentation and / or other materials provided with the distribution.
14* 3. Neither the names of the copyright holders nor the names of their contributors
15* may be used to endorse or promote products derived from this software
16* without specific prior written permission.
17* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
18* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20* ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
21* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27* SUCH DAMAGE.
28*/
29
30#include "viofs.h"31#include "isrdpc.tmh"32
33static NTSTATUS MessageToQueueIdxs(BOOLEAN Signaled, ULONG Number,34ULONG NumQueues,35PULONG vq_idx_begin, PULONG vq_idx_end)36{
37if (Signaled)38{39if (Number == VQ_TYPE_HIPRIO)40{41*vq_idx_begin = 0;42*vq_idx_end = 1;43}44else if (Number == VQ_TYPE_REQUEST)45{46*vq_idx_begin = 1;47*vq_idx_end = NumQueues;48}49else50{51TraceEvents(TRACE_LEVEL_ERROR, DBG_INTERRUPT,52"Can't find VQ for MessageNumber: %lu", Number);53
54return STATUS_UNSUCCESSFUL;55}56}57else58{59*vq_idx_begin = 0;60*vq_idx_end = NumQueues;61}62
63return STATUS_SUCCESS;64}
65
66NTSTATUS VirtFsEvtInterruptEnable(IN WDFINTERRUPT Interrupt,67IN WDFDEVICE AssociatedDevice)68{
69PDEVICE_CONTEXT context;70WDF_INTERRUPT_INFO info;71ULONG vq_idx_begin = 0, vq_idx_end = 0;72NTSTATUS status;73
74TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT,75"--> %!FUNC! Interrupt: %p Device: %p",76Interrupt, AssociatedDevice);77
78context = GetDeviceContext(WdfInterruptGetDevice(Interrupt));79
80WDF_INTERRUPT_INFO_INIT(&info);81WdfInterruptGetInfo(Interrupt, &info);82
83status = MessageToQueueIdxs(info.MessageSignaled, info.MessageNumber,84context->NumQueues, &vq_idx_begin, &vq_idx_end);85
86for (ULONG i = vq_idx_begin; i < vq_idx_end; i++)87{88struct virtqueue *vq = context->VirtQueues[i];89
90virtqueue_enable_cb(vq);91virtqueue_kick(vq);92}93
94TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT, "<-- %!FUNC!");95
96return status;97}
98
99NTSTATUS VirtFsEvtInterruptDisable(IN WDFINTERRUPT Interrupt,100IN WDFDEVICE AssociatedDevice)101{
102PDEVICE_CONTEXT context;103WDF_INTERRUPT_INFO info;104ULONG vq_idx_begin = 0, vq_idx_end = 0;105NTSTATUS status;106
107TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT,108"--> %!FUNC! Interrupt: %p Device: %p",109Interrupt, AssociatedDevice);110
111context = GetDeviceContext(WdfInterruptGetDevice(Interrupt));112
113WDF_INTERRUPT_INFO_INIT(&info);114WdfInterruptGetInfo(Interrupt, &info);115
116status = MessageToQueueIdxs(info.MessageSignaled, info.MessageNumber,117context->NumQueues, &vq_idx_begin, &vq_idx_end);118
119for (ULONG i = vq_idx_begin; i < vq_idx_end; i++)120{121struct virtqueue *vq = context->VirtQueues[i];122
123virtqueue_disable_cb(vq);124}125
126TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT, "<-- %!FUNC!");127
128return status;129}
130
131BOOLEAN VirtFsEvtInterruptIsr(IN WDFINTERRUPT Interrupt, IN ULONG MessageId)132{
133PDEVICE_CONTEXT context;134WDF_INTERRUPT_INFO info;135BOOLEAN serviced;136
137TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT,138"--> %!FUNC! Interrupt: %p MessageId: %u", Interrupt, MessageId);139
140context = GetDeviceContext(WdfInterruptGetDevice(Interrupt));141
142WDF_INTERRUPT_INFO_INIT(&info);143WdfInterruptGetInfo(Interrupt, &info);144
145if ((info.MessageSignaled && (MessageId < VQ_TYPE_MAX)) ||146VirtIOWdfGetISRStatus(&context->VDevice))147{148WdfInterruptQueueDpcForIsr(Interrupt);149serviced = TRUE;150}151else152{153serviced = FALSE;154}155
156TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT, "<-- %!FUNC!");157
158return serviced;159}
160
161BOOLEAN VirtFsDequeueRequest(PDEVICE_CONTEXT Context, PVIRTIO_FS_REQUEST Req)162{
163PSINGLE_LIST_ENTRY iter;164BOOLEAN found = FALSE;165
166WdfSpinLockAcquire(Context->RequestsLock);167iter = &Context->RequestsList;168while (iter->Next != NULL)169{170PVIRTIO_FS_REQUEST removed = CONTAINING_RECORD(iter->Next,171VIRTIO_FS_REQUEST, ListEntry);172
173if (Req == removed)174{175TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC,176"Delete %p Request: %p", removed, removed->Request);177iter->Next = removed->ListEntry.Next;178found = TRUE;179break;180}181else182{183iter = iter->Next;184}185};186WdfSpinLockRelease(Context->RequestsLock);187return found;188}
189
190static VOID VirtFsReadFromQueue(PDEVICE_CONTEXT context,191struct virtqueue *vq,192WDFSPINLOCK vq_lock)193{
194PVIRTIO_FS_REQUEST fs_req;195NTSTATUS status = STATUS_SUCCESS;196unsigned int length;197
198for (;;)199{200WdfSpinLockAcquire(vq_lock);201
202fs_req = virtqueue_get_buf(vq, &length);203if (fs_req == NULL)204{205WdfSpinLockRelease(vq_lock);206break;207}208
209WdfSpinLockRelease(vq_lock);210
211TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC,212"Got %p Request: %p", fs_req, fs_req->Request);213
214VirtFsDequeueRequest(context, fs_req);215
216if (fs_req->Request != NULL)217{218// TODO: why are we sure the wdfReq is valid and not destroyed yet?219NTSTATUS status2 = WdfRequestUnmarkCancelable(fs_req->Request);220TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC,221"request %p -> uncancellable = %X", fs_req->Request, status2);222if (status2 == STATUS_CANCELLED)223{224fs_req->Request = NULL;225}226}227
228if (fs_req->Request != NULL)229{230#if !VIRT_FS_DMAR231PUCHAR out_buf;232size_t out_len;233PVOID out_buf_va;234
235status = WdfRequestRetrieveOutputBuffer(fs_req->Request, length,236&out_buf, &out_len);237
238if (NT_SUCCESS(status))239{240length = min(length, (unsigned)out_len);241
242out_buf_va = MmMapLockedPagesSpecifyCache(243fs_req->OutputBuffer, KernelMode, MmNonCached, NULL,244FALSE, NormalPagePriority);245
246if (out_buf_va != NULL)247{248RtlCopyMemory(out_buf, out_buf_va, length);249MmUnmapLockedPages(out_buf_va, fs_req->OutputBuffer);250}251else252{253TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL,254"MmMapLockedPages failed");255status = STATUS_INSUFFICIENT_RESOURCES;256length = 0;257}258}259else260{261TraceEvents(TRACE_LEVEL_ERROR, DBG_DPC,262"WdfRequestRetrieveOutputBuffer failed");263}264#else265VirtIOWdfDeviceDmaTxComplete(&context->VDevice.VIODevice,266fs_req->H2D_Params.transaction);267VirtIOWdfDeviceDmaRxComplete(&context->VDevice.VIODevice,268fs_req->D2H_Params.transaction,269length);270#endif271
272TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC,273"Complete Request: %p Status: %!STATUS! Length: %d",274fs_req->Request, status, length);275
276WdfRequestCompleteWithInformation(fs_req->Request, status,277(ULONG_PTR)length);278}279
280FreeVirtFsRequest(fs_req);281}282}
283
284VOID VirtFsEvtInterruptDpc(IN WDFINTERRUPT Interrupt,285IN WDFOBJECT AssociatedObject)286{
287PDEVICE_CONTEXT context;288WDF_INTERRUPT_INFO info;289struct virtqueue *vq = NULL;290WDFSPINLOCK vq_lock = NULL;291ULONG i;292
293UNREFERENCED_PARAMETER(AssociatedObject);294
295TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC,296"--> %!FUNC! Interrupt: %p", Interrupt);297
298context = GetDeviceContext(WdfInterruptGetDevice(Interrupt));299
300WDF_INTERRUPT_INFO_INIT(&info);301WdfInterruptGetInfo(Interrupt, &info);302
303if ((info.MessageSignaled == TRUE) &&304(info.MessageNumber < VQ_TYPE_MAX))305{306vq = context->VirtQueues[info.MessageNumber];307vq_lock = context->VirtQueueLocks[info.MessageNumber];308}309
310if (vq != NULL)311{312VirtFsReadFromQueue(context, vq, vq_lock);313}314else315{316for (i = 0; i < context->NumQueues; i++)317{318vq = context->VirtQueues[i];319vq_lock = context->VirtQueueLocks[i];320
321VirtFsReadFromQueue(context, vq, vq_lock);322}323}324
325TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "<-- %!FUNC!");326}
327