KMDF Can't Create DMA Enabler Object with WdfDmaEnablerCreate

Hi folks,
I want to create a DMA driver in KMDF and the driver is not based on any physical device, that is to say it wouldn’t configure any registers of a real DMA controller. However, I cannot create the DMA enabler object with WdfDmaEnablerCreate. WdfDmaEnablerCreate function always returns 0xC0000001 when configuring non-system profile in WDF_DMA_PROFILE. And using system DMA profiles(i.e. WdfDmaProfileSystem) leads to BSOD when doing WdfCommonBufferCreate.

I also want to ask, if a KMDF DMA driver can run without binding any physical device?

Thanks a lot.

Here is the code:

#include "driver.h"
#include "device.tmh"

#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, DmaTestCreateDevice)
#endif

NTSTATUS
DmaTestCreateDevice(
    _Inout_ PWDFDEVICE_INIT DeviceInit
    )
{
    WDF_OBJECT_ATTRIBUTES deviceAttributes;
    PDEVICE_CONTEXT deviceContext;
    WDFDEVICE device;
	WDF_DMA_ENABLER_CONFIG	dmaConfig;
	WDFDMAENABLER		DmaEnabler;

	WDFCOMMONBUFFER		CommonBuffer;
	PVOID				CommonBufferBase;
	PHYSICAL_ADDRESS	CommonBufferBaseLA;
	WDFDMATRANSACTION	DmaTransaction;
    
    NTSTATUS status;

    PAGED_CODE();
    
	WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);
    
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);
    
	deviceAttributes.SynchronizationScope = WdfSynchronizationScopeDevice;
    
    status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);

    if (NT_SUCCESS(status)) {

        deviceContext = DeviceGetContext(device);

        //
        // Initialize the context.
        //
        deviceContext->PrivateDeviceData = 0;

		//
		// Create DMAEnabler
		//
		WdfDeviceSetAlignmentRequirement(device, FILE_OCTA_ALIGNMENT);

		WDF_DMA_ENABLER_CONFIG_INIT(&dmaConfig, WdfDmaProfilePacket, MAXNLEN);

		status = WdfDmaEnablerCreate(device,
			&dmaConfig,
			WDF_NO_OBJECT_ATTRIBUTES,
			&DmaEnabler);

		if (!NT_SUCCESS(status)) {
			DbgPrint("WdfDmaEnablerCreate failed: status=0x%x\n", status);
			return status;
		}
		DbgPrint("WdfDmaEnablerCreate succeeded\n");

		status = WdfCommonBufferCreate(DmaEnabler,
			MAXNLEN,
			WDF_NO_OBJECT_ATTRIBUTES,
			&CommonBuffer);

		if (!NT_SUCCESS(status)) {
			DbgPrint("WdfCommonBufferCreate failed: status=0x%x\n", status);
			return status;
		}
		DbgPrint("WdfCommonBufferCreate succeeded\n");
		CommonBufferBase =
			WdfCommonBufferGetAlignedVirtualAddress(CommonBuffer);

		CommonBufferBaseLA =
			WdfCommonBufferGetAlignedLogicalAddress(CommonBuffer);

		RtlZeroMemory(CommonBufferBase, MAXNLEN);

		DbgPrint("CommonBuffer VA 0x%x ,LA 0x%x, Len 0x%x\n",
			CommonBufferBase,
			CommonBufferBaseLA.LowPart,
			WdfCommonBufferGetLength(CommonBuffer));

		status = WdfDmaTransactionCreate(DmaEnabler,
			WDF_NO_OBJECT_ATTRIBUTES,
			&DmaTransaction);

		if (!NT_SUCCESS(status)) {
			DbgPrint("WdfDmaTransactionCreate failed: status=0x%x\n", status);
		}
		DbgPrint("WdfDmaTransactionCreate status=%x\n", status);

        //
        // Create a device interface so that applications can find and talk
        // to us.
        //
        status = WdfDeviceCreateDeviceInterface(
            device,
            &GUID_DEVINTERFACE_DmaTest,
            NULL // ReferenceString
            );

        if (NT_SUCCESS(status)) {
            //
            // Initialize the I/O Package and any Queues
            //
            status = DmaTestQueueInitialize(device);
        }
    }

    return status;
}

Of course it can’t. How could it? If there’s no hardware, then there’s no bus, there are no physical addresses, and of course there’s no hardware to perform the DMA. The DMA enabler requires strong interaction with the PCI bus driver.

What do you think you would be achieving with “virtual” DMA?

1 Like

Tim,

Of course it can’t. How could it? If there’s no hardware, then there’s no bus, there are no physical addresses, and of course there’s no hardware to perform the DMA. The DMA enabler requires strong interaction with the PCI bus driver.

It’s a great help to me, since I don’t know about the internals of DMA enabler.

What do you think you would be achieving with “virtual” DMA?
I want to use this “virtual” DMA to configure the Intel CBDMA transferring data from memory to device. The target device has its own driver. I woulder if I can run the driver as a service(like NT driver) or non-pnp driver, which doesn’t rely on any physical device.

By the way, the docs of Microsoft say “The framework does not support system-mode DMA transfers on PC-based platforms”, is this the cause of BSOD?

Thanks.

How are you going to get access to the device’s memory, if you are not the driver? The resources for a piece of hardware are owned by the driver. You could possibly make a filter driver to get into the same stack, I suppose. You’d need to do that in order to get the physical addresses you need.

However, if the driver isn’t expecting anyone else to do transfers, it’s not going to like you going behind its back. You’ll be messing up its internal state.

I thought Windows had added support for the system DMA controllers that Intel unwisely reintroduced in the mid-2010s, but it looks like that support does not extend to KMDF. Relying on a system DMA controller in a PC system is always going to be a problem, because it’s a shared resources, and shared resources are not always available.

https://docs.microsoft.com/en-us/windows-hardware/drivers/wdf/supporting-system-mode-dma

1 Like

Tim,
you did me a big favor, thank you very much.

(moving to the right topic… this isn’t an Admin issue or an Annoucement)

1 Like

Unwisely reintroduced? I’m not sure.

I suppose I’m being snarky. System DMA will always be a scarce resource. That means you have to plan for it to be unavailable, and that means you might as well just skip it. If your device really needs DMA, make it a bus master.

System DMA was reintroduced primarily to support SoC systems.

It’s a scarce resource like Map Registers. The system DMA controller is shared. If it’s unavailable you queue for it, you don’t skip it.

Peter