kvm-guest-drivers-windows
426 строк · 14.4 Кб
1/*
2* Implementation of virtio_system_ops VirtioLib callbacks
3*
4* Copyright (c) 2016-2017 Red Hat, Inc.
5*
6* Author(s):
7* Yuri Benditovich <ybendito@redhat.com>
8*
9* Redistribution and use in source and binary forms, with or without
10* modification, are permitted provided that the following conditions
11* are met :
12* 1. Redistributions of source code must retain the above copyright
13* notice, this list of conditions and the following disclaimer.
14* 2. Redistributions in binary form must reproduce the above copyright
15* notice, this list of conditions and the following disclaimer in the
16* documentation and / or other materials provided with the distribution.
17* 3. Neither the names of the copyright holders nor the names of their contributors
18* may be used to endorse or promote products derived from this software
19* without specific prior written permission.
20* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
21* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23* ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
24* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30* SUCH DAMAGE.
31*/
32#include "osdep.h"33#include "virtio_pci.h"34#include "VirtIOWdf.h"35#include "private.h"36
37static EVT_WDF_OBJECT_CONTEXT_DESTROY OnDmaTransactionDestroy;38static EVT_WDF_PROGRAM_DMA OnDmaTransactionProgramDma;39
40static void *AllocateCommonBuffer(PVIRTIO_WDF_DRIVER pWdfDriver, size_t size, ULONG groupTag)41{
42NTSTATUS status;43WDFCOMMONBUFFER commonBuffer;44PVIRTIO_WDF_MEMORY_BLOCK_CONTEXT context;45WDF_OBJECT_ATTRIBUTES attr;46WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, VIRTIO_WDF_MEMORY_BLOCK_CONTEXT);47
48if (KeGetCurrentIrql() > PASSIVE_LEVEL) {49DPrintf(0, "%s FAILED(irql)\n", __FUNCTION__);50return NULL;51}52status = WdfCommonBufferCreate(pWdfDriver->DmaEnabler, size, &attr, &commonBuffer);53if (!NT_SUCCESS(status)) {54return NULL;55}56WdfSpinLockAcquire(pWdfDriver->DmaSpinlock);57status = WdfCollectionAdd(pWdfDriver->MemoryBlockCollection, commonBuffer);58if (!NT_SUCCESS(status)) {59WdfObjectDelete(commonBuffer);60WdfSpinLockRelease(pWdfDriver->DmaSpinlock);61return NULL;62}63context = GetMemoryBlockContext(commonBuffer);64context->WdfBuffer = commonBuffer;65context->Length = size;66context->PhysicalAddress = WdfCommonBufferGetAlignedLogicalAddress(commonBuffer);67context->pVirtualAddress = WdfCommonBufferGetAlignedVirtualAddress(commonBuffer);68context->groupTag = groupTag;69context->bToBeDeleted = FALSE;70WdfSpinLockRelease(pWdfDriver->DmaSpinlock);71RtlZeroMemory(context->pVirtualAddress, size);72
73DPrintf(1, "%s done %p@%I64x(tag %08X), size 0x%x\n", __FUNCTION__,74context->pVirtualAddress,75context->PhysicalAddress.QuadPart,76context->groupTag,77(ULONG)size);78
79return context->pVirtualAddress;80}
81
82void *VirtIOWdfDeviceAllocDmaMemory(VirtIODevice *vdev, size_t size, ULONG groupTag)83{
84return AllocateCommonBuffer(vdev->DeviceContext, size, groupTag);85}
86
87static BOOLEAN FindCommonBuffer(88PVIRTIO_WDF_DRIVER pWdfDriver,89void *p,90PHYSICAL_ADDRESS *ppa,91size_t *pOffset,92BOOLEAN bRemoval)93{
94BOOLEAN b = FALSE;95ULONG_PTR va = (ULONG_PTR)p;96ULONG i, n;97WDFOBJECT obj = NULL;98WdfSpinLockAcquire(pWdfDriver->DmaSpinlock);99n = WdfCollectionGetCount(pWdfDriver->MemoryBlockCollection);100for (i = 0; i < n; ++i) {101obj = WdfCollectionGetItem(pWdfDriver->MemoryBlockCollection, i);102if (!obj) {103break;104}105PVIRTIO_WDF_MEMORY_BLOCK_CONTEXT context = GetMemoryBlockContext(obj);106if (context->bToBeDeleted && !bRemoval) {107continue;108}109ULONG_PTR currentVaStart = (ULONG_PTR)context->pVirtualAddress;110if (va >= currentVaStart && va < (currentVaStart + context->Length)) {111*ppa = context->PhysicalAddress;112*pOffset = va - currentVaStart;113b = TRUE;114if (bRemoval) {115b = *pOffset == 0;116if (b) {117context->bToBeDeleted = TRUE;118}119}120break;121}122}123WdfSpinLockRelease(pWdfDriver->DmaSpinlock);124if (!b) {125DPrintf(0, "%s(%s) FAILED!\n", __FUNCTION__, bRemoval ? "Remove" : "Locate");126}127else if (bRemoval) {128if (KeGetCurrentIrql() == PASSIVE_LEVEL) {129
130WdfSpinLockAcquire(pWdfDriver->DmaSpinlock);131WdfCollectionRemove(pWdfDriver->MemoryBlockCollection, obj);132WdfSpinLockRelease(pWdfDriver->DmaSpinlock);133
134WdfObjectDelete(obj);135DPrintf(1, "%s %p freed (%d common buffers)\n", __FUNCTION__, va, n - 1);136}137else {138DPrintf(0, "%s %p marked for deletion\n", __FUNCTION__, va);139}140}141return b;142}
143
144static PHYSICAL_ADDRESS GetPhysicalAddress(PVIRTIO_WDF_DRIVER pWdfDriver, PVOID va)145{
146PHYSICAL_ADDRESS pa;147size_t offset;148pa.QuadPart = 0;149if (FindCommonBuffer(pWdfDriver, va, &pa, &offset, FALSE)) {150pa.QuadPart += offset;151}152return pa;153}
154
155PHYSICAL_ADDRESS VirtIOWdfDeviceGetPhysicalAddress(VirtIODevice *vdev, void *va)156{
157return GetPhysicalAddress(vdev->DeviceContext, va);158}
159
160void VirtIOWdfDeviceFreeDmaMemory(VirtIODevice *vdev, void *va)161{
162PHYSICAL_ADDRESS pa;163size_t offset;164FindCommonBuffer(vdev->DeviceContext, va, &pa, &offset, TRUE);165}
166
167static BOOLEAN FindCommonBufferByTag(168PVIRTIO_WDF_DRIVER pWdfDriver,169ULONG tag
170)
171{
172BOOLEAN b = FALSE;173ULONG i, n;174WDFOBJECT obj = NULL;175PVIRTIO_WDF_MEMORY_BLOCK_CONTEXT context = NULL;176WdfSpinLockAcquire(pWdfDriver->DmaSpinlock);177n = WdfCollectionGetCount(pWdfDriver->MemoryBlockCollection);178for (i = 0; i < n; ++i) {179obj = WdfCollectionGetItem(pWdfDriver->MemoryBlockCollection, i);180if (!obj) {181break;182}183context = GetMemoryBlockContext(obj);184if (context->groupTag == tag) {185b = TRUE;186break;187}188}189WdfSpinLockRelease(pWdfDriver->DmaSpinlock);190if (b) {191DPrintf(1, "%s %p (tag %08X) freed (%d common buffers)\n", __FUNCTION__,192context->pVirtualAddress, tag, n - 1);193WdfSpinLockAcquire(pWdfDriver->DmaSpinlock);194WdfCollectionRemove(pWdfDriver->MemoryBlockCollection, obj);195WdfSpinLockRelease(pWdfDriver->DmaSpinlock);196WdfObjectDelete(obj);197}198return b;199}
200
201void VirtIOWdfDeviceFreeDmaMemoryByTag(VirtIODevice *vdev, ULONG groupTag)202{
203if (KeGetCurrentIrql() > PASSIVE_LEVEL) {204DPrintf(0, "%s FAILED(irql)\n", __FUNCTION__);205return;206}207if (!groupTag) {208DPrintf(0, "%s FAILED(default tag)\n", __FUNCTION__);209return;210}211if (!vdev->DeviceContext) {212DPrintf(0, "%s was not initialized\n", __FUNCTION__);213return;214}215while (FindCommonBufferByTag(vdev->DeviceContext, groupTag));216}
217
218static void FreeSlicedBlock(PVIRTIO_DMA_MEMORY_SLICED p)219{
220size_t offset;221FindCommonBuffer(p->drv, p->va, &p->pa, &offset, TRUE);222ExFreePoolWithTag(p, p->drv->MemoryTag);223}
224
225static PVOID AllocateSlice(PVIRTIO_DMA_MEMORY_SLICED p, PHYSICAL_ADDRESS *ppa)226{
227ULONG offset, index = RtlFindClearBitsAndSet(&p->bitmap, 1, 0);228if (index >= p->bitmap.SizeOfBitMap) {229return NULL;230}231offset = p->slice * index;232ppa->QuadPart = p->pa.QuadPart + offset;233return (PUCHAR)p->va + offset;234}
235
236static void FreeSlice(PVIRTIO_DMA_MEMORY_SLICED p, PVOID va)237{
238PHYSICAL_ADDRESS pa;239size_t offset;240if (!FindCommonBuffer(p->drv, va, &pa, &offset, FALSE)) {241DPrintf(0, "%s: block with va %p not found\n", __FUNCTION__, va);242return;243}244if (offset % p->slice) {245DPrintf(0, "%s: offset %d is wrong for slice %d\n", __FUNCTION__,246(ULONG)offset, p->slice);247return;248}249ULONG index = (ULONG)(offset / p->slice);250if (!RtlTestBit(&p->bitmap, index)) {251DPrintf(0, "%s: bit %d is NOT set\n", __FUNCTION__, index);252return;253}254RtlClearBit(&p->bitmap, index);255}
256
257PVIRTIO_DMA_MEMORY_SLICED VirtIOWdfDeviceAllocDmaMemorySliced(258VirtIODevice *vdev,259size_t blockSize,260ULONG sliceSize)261{
262PVIRTIO_WDF_DRIVER pWdfDriver = vdev->DeviceContext;263size_t allocSize = sizeof(VIRTIO_DMA_MEMORY_SLICED) + (blockSize / sliceSize) / 8 + sizeof(ULONG);264PVIRTIO_DMA_MEMORY_SLICED p = ExAllocatePoolUninitialized(NonPagedPool, allocSize, pWdfDriver->MemoryTag);265if (!p) {266return NULL;267}268__analysis_assume(allocSize > sizeof(*p));269RtlZeroMemory(p, sizeof(*p));270p->va = AllocateCommonBuffer(pWdfDriver, blockSize, 0);271p->pa = GetPhysicalAddress(pWdfDriver, p->va);272if (!p->va || !p->pa.QuadPart) {273ExFreePoolWithTag(p, pWdfDriver->MemoryTag);274return NULL;275}276p->slice = sliceSize;277p->drv = pWdfDriver;278RtlInitializeBitMap(&p->bitmap, p->bitmap_buffer, (ULONG)blockSize / sliceSize);279p->return_slice = FreeSlice;280p->get_slice = AllocateSlice;281p->destroy = FreeSlicedBlock;282return p;283}
284
285VOID OnDmaTransactionDestroy(WDFOBJECT Object)286{
287PVIRTIO_WDF_DMA_TRANSACTION_CONTEXT ctx = GetDmaTransactionContext(Object);288DPrintf(1, "%s %p\n", __FUNCTION__, Object);289if (ctx->mdl) {290IoFreeMdl(ctx->mdl);291}292if (ctx->buffer) {293ExFreePoolWithTag(ctx->buffer, ctx->parameters.allocationTag);294}295}
296
297static FORCEINLINE void RefTransaction(PVIRTIO_WDF_DMA_TRANSACTION_CONTEXT ctx)298{
299InterlockedIncrement(&ctx->refCount);300}
301
302static FORCEINLINE void DerefTransaction(PVIRTIO_WDF_DMA_TRANSACTION_CONTEXT ctx)303{
304if (!InterlockedDecrement(&ctx->refCount)) {305WdfObjectDelete(ctx->parameters.transaction);306}307}
308
309BOOLEAN OnDmaTransactionProgramDma(310WDFDMATRANSACTION Transaction,311WDFDEVICE Device,312WDFCONTEXT Context,313WDF_DMA_DIRECTION Direction,314PSCATTER_GATHER_LIST SgList
315)
316{
317PVIRTIO_WDF_DMA_TRANSACTION_CONTEXT ctx = GetDmaTransactionContext(Transaction);318RefTransaction(ctx);319ctx->parameters.transaction = Transaction;320ctx->parameters.sgList = SgList;321DPrintf(1, "-->%s %p %d frags\n", __FUNCTION__,322Transaction,323SgList->NumberOfElements);324BOOLEAN bFailed = !ctx->callback(&ctx->parameters);325DPrintf(1, "<--%s %s\n", __FUNCTION__, bFailed ? "Failed" : "OK");326DerefTransaction(ctx);327return TRUE;328}
329
330static BOOLEAN VirtIOWdfDeviceDmaAsync(VirtIODevice *vdev,331PVIRTIO_DMA_TRANSACTION_PARAMS params,332VirtIOWdfDmaTransactionCallback callback,333WDF_DMA_DIRECTION Direction)334{
335PVIRTIO_WDF_DRIVER pWdfDriver = vdev->DeviceContext;336WDFDMATRANSACTION tr;337WDF_OBJECT_ATTRIBUTES attr;338NTSTATUS status;339WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, VIRTIO_WDF_DMA_TRANSACTION_CONTEXT);340attr.EvtDestroyCallback = OnDmaTransactionDestroy;341status = WdfDmaTransactionCreate(pWdfDriver->DmaEnabler, &attr, &tr);342if (!NT_SUCCESS(status)) {343DPrintf(0, "%s FAILED(create) %X\n", __FUNCTION__, status);344return FALSE;345}346PVIRTIO_WDF_DMA_TRANSACTION_CONTEXT ctx = GetDmaTransactionContext(tr);347RtlZeroMemory(ctx, sizeof(*ctx));348ctx->parameters = *params;349ctx->callback = callback;350ctx->refCount = 1;351ctx->direction = Direction;352if (params->req) {353status = WdfDmaTransactionInitializeUsingRequest(354tr, params->req, OnDmaTransactionProgramDma, Direction);355} else {356ctx->buffer = ExAllocatePoolUninitialized(NonPagedPool, ctx->parameters.size, ctx->parameters.allocationTag);357if (ctx->buffer) {358if (Direction == WdfDmaDirectionWriteToDevice) {359RtlCopyMemory(ctx->buffer, params->buffer, params->size);360}361ctx->mdl = IoAllocateMdl(ctx->buffer, params->size, FALSE, FALSE, NULL);362if (ctx->mdl) {363MmBuildMdlForNonPagedPool(ctx->mdl);364status = WdfDmaTransactionInitialize(365tr, OnDmaTransactionProgramDma, Direction,366ctx->mdl, ctx->buffer, params->size);367} else {368status = STATUS_INSUFFICIENT_RESOURCES;369}370} else {371status = STATUS_INSUFFICIENT_RESOURCES;372}373}374
375if (!NT_SUCCESS(status)) {376DPrintf(0, "%s FAILED(init) %X\n", __FUNCTION__, status);377WdfObjectDelete(tr);378return FALSE;379}380
381status = WdfDmaTransactionExecute(tr, NULL);382if (!NT_SUCCESS(status)) {383DPrintf(0, "%s FAILED(execution) %X\n", __FUNCTION__, status);384WdfObjectDelete(tr);385return FALSE;386}387
388return TRUE;389}
390
391BOOLEAN VirtIOWdfDeviceDmaTxAsync(VirtIODevice* vdev,392PVIRTIO_DMA_TRANSACTION_PARAMS params,393VirtIOWdfDmaTransactionCallback callback)394{
395return VirtIOWdfDeviceDmaAsync(vdev, params, callback, WdfDmaDirectionWriteToDevice);396}
397
398BOOLEAN VirtIOWdfDeviceDmaRxAsync(VirtIODevice* vdev,399PVIRTIO_DMA_TRANSACTION_PARAMS params,400VirtIOWdfDmaTransactionCallback callback)401{
402return VirtIOWdfDeviceDmaAsync(vdev, params, callback, WdfDmaDirectionReadFromDevice);403}
404
405void VirtIOWdfDeviceDmaTxComplete(VirtIODevice *vdev, WDFDMATRANSACTION transaction)406{
407PVIRTIO_WDF_DRIVER pWdfDriver = vdev->DeviceContext;408PVIRTIO_WDF_DMA_TRANSACTION_CONTEXT ctx = GetDmaTransactionContext(transaction);409NTSTATUS status;410DPrintf(1, "%s %p\n", __FUNCTION__, transaction);411WdfDmaTransactionDmaCompletedFinal(transaction, 0, &status);412DerefTransaction(ctx);413}
414
415void VirtIOWdfDeviceDmaRxComplete(VirtIODevice* vdev, WDFDMATRANSACTION transaction, ULONG length)416{
417PVIRTIO_WDF_DRIVER pWdfDriver = vdev->DeviceContext;418PVIRTIO_WDF_DMA_TRANSACTION_CONTEXT ctx = GetDmaTransactionContext(transaction);419NTSTATUS status;420DPrintf(1, "%s %p, len %d\n", __FUNCTION__, transaction, length);421WdfDmaTransactionDmaCompletedFinal(transaction, length, &status);422if (length && ctx->buffer && ctx->parameters.buffer) {423RtlCopyMemory(ctx->parameters.buffer, ctx->buffer, length);424}425DerefTransaction(ctx);426}
427