The free OSR Learning Library has more than 50 articles on a wide variety of topics about writing and debugging device drivers and Minifilters. From introductory level to advanced. All the articles have been recently reviewed and updated, and are written using the clear and definitive style you've come to expect from OSR over the years.
Check out The OSR Learning Library at: https://www.osr.com/osr-learning-library/
I’m working on a 64bit KMDF driver for a card which is restricted to 32bit DMA transfers, so I’ve configured a DMA enabler using WdfDmaProfilePacket in order that KMDF will allocate suitable map registers for me. I’m using automatic synchronisation to reduce the need for locking throughout the driver.
When I send a read request to my device from usermode, in my EvtProgramReadDma function I note that the WDF read request’s output buffer has an address in 64bit space as expected, and has been initialised such that every byte is a 0xE0 filler byte. I then program the PLX9x5x chip on the card to perform the DMA card-to-RAM read operation, noting that the read buffer address provided to me by KMDF in the single-element SG list is a 32bit address, also as expected, and this buffer initially has seemingly arbitrary bytes in it.
EvtProgramReadDma successfully tells the PLX to start the transfer, and I then receive an interrupt from the chip’s DMA controller via my EvtInterruptIsr function confirming that the transfer is complete; I queue my EvtInterruptDpc function, which then executes.
If I set a WinDbg breakpoint at this point in execution, I can inspect the two buffers and see that the DMA-transferred data has arrived in the 32bit read buffer (map registers) as expected, and the final 64bit output buffer is still filled with the 0xE0 filler byte, also as expected. (For clarity, the read data in question will never be a series of 0xE0 bytes.)
I then call WdfDmaTransactionDmaCompletedFinal because, while I’m requesting theoretically large reads (of one 4K page), the device will only transfer the data it has already available before interrupting. The usermode application deals with inspecting the returned data (which sometimes might only amount to a few bytes) and with determining whether to issue a further read request - so I want my driver to indicate to the usermode application that no more bytes are expected to be transferred for this request. Thus KMDF will not try to start any further DMA reads for the request.
I will _almost_ always find after calling WdfDmaTransactionDmaCompletedFinal that the contents of the 32bit buffer have been transferred by KMDF to the final request output buffer (in 64bit address space).
The problem I’m having is that very occasionally, this doesn’t appear to happen. In this scenario, I log the call to WdfDmaTransactionCompletedDmaFinal with the correct non-zero byte count provided, it returns TRUE as expected, but the 64bit output buffer remains apparently unaltered, when usually it would have had data copied into it by KMDF by this point. When I continue to complete the IO request with WdfRequestSetInformation (providing the same non-zero byte count) and WdfRequestCompleteWithPriorityBoost (with STATUS_SUCCESS since I’m indicating a successful transfer, albeit of fewer bytes than requested), the usermode application finds that while the read request appears to have completed successfully with the noted byte count, the bytes in the 64bit buffer are still all the 0xE0 filler byte.
The particularly puzzling aspect is that in this scenario KMDF appears not to be copying _anything_ from the 32bit read buffer to the final 64bit output buffer – I’ve verified from my trace output that the length provided to WdfDmaTransactionDmaCompletedFinal is non-zero and the length provided to WdfRequestSetInformation is the same non-zero value. I’d expect still to get the specified number of bytes written to the 64bit buffer even if they were the wrong bytes (say if, owing to some bug in my code, the copy had somehow taken place just before the DMA controller placed the read data in the 32bit buffer etc) – but it appears that on occasion nothing at all is copied.
I did also try the slightly more convoluted approach of using WdfDmaTransactionDmaCompletedWithLength to indicate the success of the first transfer, resulting in the framework calling my EvtProgramReadDma function to program a further transfer for the remaining bytes, and in this case immediately calling WdfDmaTransactionCompletedDmaFinal in EvtProgramReadDma to indicate no more bytes will be transferred - but the same scenario would still arise on occasion.
Can anyone suggest why this might be happening?
Processor: x64 (Intel Core i5-4590)
Driver test PC: Dell Optiplex 3020
OS: Windows 10 20H2
KMDF: 1.9 (I still want to maintain Windows 7 compatibility)
|Upcoming OSR Seminars|
|OSR has suspended in-person seminars due to the Covid-19 outbreak. But, don't miss your training! Attend via the internet instead!|
|Internals & Software Drivers||7 February 2022||Live, Online|
|Kernel Debugging||21 March 2022||Live, Online|
|Developing Minifilters||23 May 2022||Live, Online|
|Writing WDF Drivers||12 September 2022||Live, Online|