(This page is under construction)
All the operations required for DMA transfer must be implemented via WDF DMA API and not via legacy routines for physical memory manipulation (otherwise the device driver is not compatible with IOMMU and VIRTIO_F_ACCESS_PLATFORM feature). These operations include:
Virtio-WDF library includes necessary procedures to simplify implementation of DMA transfer between guest driver and virtio PCI device.
No special initialization for DMA operations. When the driver calls VirtIOWdfInitialize (typically from EvtDevicePrepareHardware procedure) the library initializes also WDFDMAENABLER object for the device. The library uses it during device lifecycle for DMA operations.
No special clean-up for DMA operations. When the driver calls VirtIOWdfShutdown (typically from EvtDeviceReleaseHardware) the library destroys also WDFDMAENABLER object
Use this method of allocation when you need memory blocks of one or more 4K pages
void *VirtIOWdfDeviceAllocDmaMemory(VirtIODevice *vdev, size_t size, ULONG groupTag)
size - block size to allocate
groupTag - optional value that the driver can use for memory allocation. All the DMA buffers allocated with the same non-zero tag can be freed together, see VirtIOWdfDeviceFreeDmaMemoryByTag below
Return value:
in case of error: NULL
in case of success: virtual address of the beginning of the allocated memory block
IRQL: PASSIVE
PHYSICAL_ADDRESS VirtIOWdfDeviceGetPhysicalAddress(VirtIODevice *vdev, void *va)
va - virtual address inside previously allocated memory block
Return value:
in case of error: NULL address
in case of success: physical address of the provided virtual address
IRQL: <= DISPATCH
void VirtIOWdfDeviceFreeDmaMemory(VirtIODevice *vdev, void *va)
void VirtIOWdfDeviceFreeDmaMemoryByTag(VirtIODevice *vdev, ULONG groupTag)
va - virtual address previously returned by VirtIOWdfDeviceAllocDmaMemory
groupTag - non-zero tag provided previously to VirtIOWdfDeviceAllocDmaMemory
IRQL: PASSIVE
Use this method of allocation when you need many small chunks of physical memory of the same size
PVIRTIO_DMA_MEMORY_SLICED VirtIOWdfDeviceAllocDmaMemorySliced(VirtIODevice *vdev, size_t blockSize, ULONG sliceSize)
This procedure allocates block of one or more 4K pages from which you can allocate and free small memory slices.
It returns a pointer to a library-allocated structure VIRTIO_DMA_MEMORY_SLICED
IRQL: PASSIVE
structure VIRTIO_DMA_MEMORY_SLICED includes 3 methods:
The procedure allocates a slice from the block, fills the physical address of the slice and returns its virtual address
IRQL: >= DISPATCH
This procedure returns the previosly allocated slice back to the block
IRQL: >= DISPATCH
The procedure frees the entire allocated block.
IRQL: PASSIVE
The library allows to convert any driver-owned memory buffer to scatter-gather table of physical fragments to be used for guest-to-device DMA operation.
The API for that is asynchronous because the WDF DMA API is also asynchronous, mapping of the buffer is executed by HAL on DISPATCH and requires the callback that receives the final mapping of the buffer. In order to avoid any misunderstanding with the lifetime of the buffer, the library reallocates the buffer and copies the content of the original buffer to the reallocated one.
To initiate the DMA operation the driver uses following sequence of operations
BOOLEAN (*VirtIOWdfDmaTransactionCallback)(PVIRTIO_DMA_TRANSACTION_PARAMS Parameters)
Call the library procedure
BOOLEAN VirtIOWdfDeviceDmaTxAsync(VirtIODevice *vdev, PVIRTIO_DMA_TRANSACTION_PARAMS params, VirtIOWdfDmaTransactionCallback callback)
VirtIOWdfDeviceDmaTxComplete(virtio_device, Parameters->transaction)
VirtIOWdfDeviceDmaTxComplete(virtio_device, transaction)
The sequence of the operations is similar to the previous one (when the driver-owned buffer is used):
BOOLEAN (*VirtIOWdfDmaTransactionCallback)(PVIRTIO_DMA_TRANSACTION_PARAMS Parameters)
BOOLEAN VirtIOWdfDeviceDmaTxAsync(VirtIODevice *vdev, PVIRTIO_DMA_TRANSACTION_PARAMS params, VirtIOWdfDmaTransactionCallback callback)
VirtIOWdfDeviceDmaTxComplete(virtio_device, Parameters->transaction)
VirtIOWdfDeviceDmaTxComplete(virtio_device, Parameters->transaction)
Important note:
If the WDF request is cancellable, the driver has a problematic flow in case of request’s cancellation. There is no possibility to cancel the virtqueue entry after it is pushed to the virtqueue. The driver should call VirtIOWdfDeviceDmaTxComplete(virtio_device, transaction)
some time. It is possible that the device will consume data from physical address that is not valid anymore.