getting HANDLE from FILE_OBJECT

Inside Windows driver routine which handles a DeviceIoControl call from client, I’d like to call many other DeviceIoControl routines using the same HANDLE as the client application. The initial DeviceIoControl routine runs only at the passive level in the driver. The calls to the many other DeviceIoControl routines are not restricted to the passive level. I have a working driver for a PCIe device. The initial call allocates memory on the device. The routines called from within the driver would initiate DMA IN to initialize the memory. Today, the allocation routine works and the DMA IN routines work. I’ve seen ZwDeviceIoControl, but it needs a HANDLE. Unfortunately, inside the driver, I only have the FILE_OBJECT. Is there a way to get a HANDLE from the FILE_OBJECT? It would need to be the same HANDLE or a duplicate HANDLE since the driver and PCIe device enforce memory protection. Only the PCI device’s memory that is allocated to the FILE_OBJECT can be written by the DMA IN.

On Apr 3, 2019, at 8:24 PM, Patrick_Kling wrote:
>
> Inside Windows driver routine which handles a DeviceIoControl call from client, I’d like to call many other DeviceIoControl routines using the same HANDLE as the client application.

You need to explain your concept a bit more. Any ioctls you issue on that same handle are going to end up right back in your driver. Why don’t you just call the appropriate functions directly? What do you think you’re going to gain by going through the I/O process again?

> I have a working driver for a PCIe device. The initial call allocates memory on the device. The routines called from within the driver would initiate DMA IN to initialize the memory. Today, the allocation routine works and the DMA IN routines work. I’ve seen ZwDeviceIoControl, but it needs a HANDLE. Unfortunately, inside the driver, I only have the FILE_OBJECT.

Why not just call those routines? What’s the point of going through ZwDeviceIoControl to get right back to your own driver? Are you trying to send ioctls to the drivers below you? You can do that just by creating your own IRP and calling IoCallDriver.

> Is there a way to get a HANDLE from the FILE_OBJECT?

The FILE_OBJECT is the kernel instantiation of the user-mode HANDLE. It doesn’t make sense to go back the other way.

> It would need to be the same HANDLE or a duplicate HANDLE since the driver and PCIe device enforce memory protection. Only the PCI device’s memory that is allocated to the FILE_OBJECT can be written by the DMA IN.

That makes no sense. DMA doesn’t care about memory “ownership”. Please describe your situation in more detail.

Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

Inside Windows driver routine which handles a DeviceIoControl call from client, I’d >like to call many other DeviceIoControl routines using the same HANDLE as the client >application.

You need to explain your concept a bit more. Any ioctls you issue on that same handle >are going to end up right back in your driver. Why don’t you just call the appropriate >functions directly? What do you think you’re going to gain by going through the I/O >process again?

Today, the allocate memory ioctl is a single call which stays at the passive level. The DMA IN ioctl does not stay at the passive level. It does all the typical things that a PCIe driver does like, pin the MDL of the source memory, queue the transfer with the board (if board HW has room), setup interrupt for when DMA completes, then exits with status PENDING. Anything which doesn’t fit on board remains queued in the driver. The device itself protects regions of its own memory based on the client (effectively the FILE_OBJECT) to guarantee no cross contamination between clients (regulated industry - requirement). Anyway, the DMA is limited to a certain max transfer size per request. To clear all the memory, many such DMAs must be scheduled. Some will be given to the board, but some will have to be queued in driver until board has room.

I was hoping to avoid complexity by reusing existing functionality. If ZwDeviceIoControl is not the best way to do that, what would be the best way to handle the situation. The memory allocation ioctl shouldn’t complete until the memory is cleared.

I may be totally misreading this, but it sounds like you want to call ZwDeviceIoControl to do PASSIVE_LEVEL memory allocation, from higher IRQL code? If so, it doesn’t work that way. If I misunderstood, feel free to ignore me.

_Ron

Hi – I clearly have done a poor job explaining my question. Maybe I can try again. The current situation is that I have a working PCIe board WDM driver. Two of the working DeviceIoControl calls 1) allocate board memory 2) DMA from PC to board memory. The #1 allocate function is implemented completely at the passive level. It does allocates some of the boards memory, then writes a few PCIe registers to tell the board about the allocated range. It never blocks. The #2 DMA IN function is a typical WDM DMA. At the passive level, it checks to see if board DMA HW is available, if so, it submits the DMA and sets an interrupt mask to get alerted when the DMA completes. If the board DMA HW is not available, it queues the work inside the driver. The DPC level started by receiving the interrupt will submit the DMA to the board HW when the HW is available.

The DMA is limited to 256 MB. The amount of allocated memory could be much much larger than that.

Anyway, all that works well.

I’d like to know the simplest way to add the following functionality: The #1 allocate function should DMA IN zeros to the board memory before returning to the user – I hope I can reuse the existing functionality.

iThe #1 allocate function should DMA IN zeros to the board memory before returning to the user –
I hope I can reuse the existing functionality.

Well, it seems to be more of a question of your hardware, rather than of a driver, don’t you think. Unless your hardware supports zeroing memory upon the allocation/initialisation, the only option available to you seems to be extending IOCTL 1, i.e making it allocate memory + DMA zeroed memory to the device. Once, according to you, the amount of memory that you allocate may well exceed your DMA limitations, you would have to make multiple DMA transfers before completing the IOCTL. To be honest, I don’t see any other available options here

Anton Bassov

Allocate a single page of non-pages pool, zero it, and create a Scatter Gather List (SGL) that references this page as many times in a row as necessary to zero your device memory.

If you need to, you could presumably hand-build an MDL that was of whatever length you needed, using the same page over and over again.

Peter

create a Scatter Gather List (SGL) that references this page as many times in a row as necessary to zero your device memory.

According to the OP, due to the specifics of his board, the maximum length of such SGL is 256 * 256 entires (i.e. 256MB * 256 entries per MB), but the amount of memory on the target device may well exceed this limitation. Therefore, no matter how you look at it, he would have to do this operation multiple times before completing his IOCTL

Anton Bassov

due to the specifics of his board, the maximum length of such SGL is 256 * 256 entires

Then rinse and repeat as usual. Takes longer to talk about than to code.

I wonder what his question really is?

Peter

is there any way to call the existing DeviceIoControl for DMA IN from within the driver so that all the issues (like multiple calls) you all mentioned are handled easily. For example, I saw a function ZwDeviceIoControl that can call a driver, but it needs a HANDLE. Is there any way to create the HANDLE from within the driver from the FileObject I already have?

there might be DMAs from other clients running as well on the same HW so reuse of the existing DeviceIoControl would be very convenient since that call already manages the multiple clients according to an appropriate priority scheme.

To answer the question asked:
ObOpenObjectByPointer(…) will create a handle to an object.
A previous OSR thread on getting a handle from a file object is here (from a long time ago):
https://community.osr.com/discussion/81432

I have never worked with this before, so I could absolutely be wrong here; I will watch to see the outcome of this thread, but I still read this the same as I originally did. Your DMA IN code, queued or “immediate”, driven by the hardware ISR, is at DIRQL. The PASSIVE_LEVEL portion of #2 is just posting a request and waiting for it to complete. You want to reuse PASSIVE_LEVEL memory management code of #1. But you cannot call PASSIVE_LEVEL code from DIRQL to achieve PASSIVE_LEVEL execution. It is only PASSIVE_LEVEL because it is called from user mode. If called from higher IRQLs, it would execute at the higher IRQL, and would likely be a violation of the calling conventions of the function.

OTOH, if you want to call the IOCTL of #1 from the PASSIVE_LEVEL portion of #2, then you should either be able to call the allocation function directly, or roll an IRP to do the allocation, before handing off to the HW.

Thank you all (especially Jamey Kirby) for your help.

If you already have a File Object you can just build a new IRP, stuff the File Object pointer in it, and send it to yourself.

Scott_Noone_(OSR) wrote:

If you already have a File Object you can just build a new IRP, stuff the File Object pointer in it, and send it to yourself.

This is certainly true, but I have a hard time imagining a situation
where this is the Right Answer.    If you’re already processing within
your driver, why wouldn’t you just queue up whatever activity would be
done in response to that ioctl?  Why involve the kernel?  Although I
regularly talk to myself, I don’t call up my wife to have her relate my
inner conversation back and forth.  “Hey, tell Tim he’s a good and
valuable member of society.  Tell Tim I appreciate that.”

My wife is always reminding me that I don’t need to share my inner converstations with her, so a bad example for me :slight_smile:

But, I say it depends…Say I need to reset my device and already have an IOCTL_RESET_DEVICE handler that asychronously resets and completes the IRP when it’s done. I could plumb another path that lets me asynchronously reset and signal me when I’m done, or I can just send myself an IRP with a completion routine.

I’ve been doing this more frequently in my WDF drivers by opening an I/O Target to myself and sending requests. Not only does it avoid me creating more special case code but it also honors my Queue topology (dispatch type, IRQL constraint, etc.). It’s not always the right answer, but it’s an option.