Path: news.mitre.org!blanket.mitre.org!philabs!newsjunkie.ans.net!newsfeeds.ans.net!news-was.dfn.de!news-spur1.maxwell.syr.edu!news.maxwell.syr.edu!xfer.kren.nm.kr!hammer.uoregon.edu!netnews.nwnet.net!news.microsoft.com!news From: "Paul Sanders" Newsgroups: comp.os.ms-windows.programmer.nt.kernel-mode Subject: Re: Modify large buffers in a device driver Date: 5 Nov 1997 19:30:44 GMT Organization: Microsoft Lines: 368 Message-ID: <01bcea20$da424340$e0b8369d@paulsan90> References: <345F74C7.2056@swipnet.se> NNTP-Posting-Host: 157.54.184.224 X-Newsreader: Microsoft Internet News 4.70.1157 Connie, I repost this every now and then to let people read it who have not seen it before. -Paul Here is an explanation of buffers and DeviceIoControl. First, here are the parameters, BOOL DeviceIoControl( HANDLE hDevice, // handle to device of interest DWORD dwIoControlCode, // control code of operation to perform LPVOID lpInBuffer, // pointer to buffer to supply input data DWORD nInBufferSize, // size of input buffer LPVOID lpOutBuffer, // pointer to buffer to receive output data DWORD nOutBufferSize, // size of output buffer LPDWORD lpBytesReturned, // pointer to variable to receive output byte count LPOVERLAPPED lpOverlapped // pointer to overlapped structure for asynchronous operation ); METHOD_BUFFERED user-mode perspective lpInBuffer - optional, contains data that is written to the driver lpOutBuffer - optional, contains data that is read from the driver after the call has completed lpInBuffer and lpOutBuffer can be two buffers or a single shared buffer. If a shared buffer, lpInBuffer is overwritten by lpOutBuffer. I/O Manager perspective examines nInBufferSize and nOutBufferSize. Allocates memory from non-paged pool and puts the address of this pool in Irp->AssociatedIrp.SystemBuffer. The size of this buffer is equal to the size of the larger of the two bufferes. This buffer is accessible at any IRQL. copies nInBufferSize to irpSp->Parameters.DeviceIoControl.InputBufferLength copies nOutBufferSize to irpSp->Parameters.DeviceIoControl.OutputBufferLength copies contents of lpInBuffer to SystemBuffer allocated above calls your driver Device Driver perspective you have one buffer, Irp->AssociatedIrp.SystemBuffer. You read input data from this buffer and you write output data to the same buffer, overwriting the input data. Before calling IoCompleteRequest, you must - set IoStatus.Status to an approriate NtStatus - if IoStatus.Status == STATUS_SUCCESS set IoStatus.Information to the number of bytes you want copied from the SystemBuffer back into lpOutBuffer. I/O Manager Completion Routine perspective looks at IoStatus block, if IoStatus.Status = STATUS_SUCCESS, copies the number of bytes specified by IoStatus.Information from Irp->AssociatedIrp.SystemBuffer into lpOutBuffer completes the request METHOD_IN_DIRECT user-mode perspective lpInBuffer - optional, contains data that is written to the driver. This buffer is used in the exact same fashion as METHOD_BUFFERED. To avoid confusion, mentally rename this buffer to lpControlBuffer. This is typically a small, optional buffer that might contain a control structure with useful information for the device driver. This buffer is smal and is double buffered. lpOutBuffer - NOT OPTIONAL, This LARGE buffer contains data that is read by the driver. To avoid confusion, mentally rename this buffer to lpDataTransferBuffer. This is physically the same buffer that the device driver will read from. There is no double buffering. Technically, this buffer is still optional, but since you are using this buffering method, what would be the point??? I/O Manager perspective If lpInBuffer exists, allocates memory from non-paged pool and puts the address of this pool in Irp->AssociatedIrp.SystemBuffer. This buffer is accessible at any IRQL. copies nInBufferSize to irpSp->Parameters.DeviceIoControl.InputBufferLength copies nOutBufferSize to irpSp->Parameters.DeviceIoControl.OutputBufferLength copies contents of lpInBuffer to SystemBuffer allocated above So far this is completely identical to METHOD_BUFFERED. Most likely lpInBuffer (mentally renamed to lpControlBuffer) is very small in size. For lpOutBuffer (mentally renamed to lpDataTransferBuffer), an MDL is allocated. lpOutBuffer is probed and locked into memory. Then, the user buffer virtual addresses are checked to be sure they are readable in the caller's access mode. The MDL is address is stored in Irp->MdlAddress. Your driver is called. Device Driver perspective The device driver can read the copy of lpOutBuffer via Irp->AssociatedIrp.SystemBuffer. Anything written by the device driver to this buffer is lost. The I/O Manager does not copy any data back to the user-mode buffers as it did in the completion routine for METHOD_BUFFERED. Art Baker's book is wrong in this respect (page 168, "data going from the driver back to the caller is passed through an intermediate system-space buffer" and page 177, "When the IOCTL IRP is completed, the contents of the system buffer will be copied back into the callers original output buffer". The device driver accesses the Win32 buffer directly via Irp->MdlAddress. The driver uses whatever Mdl API's to read the buffer. Usually, this buffer is to be written to some mass storage media or some similar operation. Since this is a large data transfer, assume a completion routine is required. mark the Irp pending queue it return status pending Device Driver Completion Routine perspective standard completion routine operations set IoStatus.Status to an approriate NtStatus IoStatus.Information is not needed completete the request I/O Manager Completion Routine perspective standard I/O Manager completion routine operations unmap the pages deallocate the Mdl complete the request METHOD_OUT_DIRECT user-mode perspective lpInBuffer - optional, contains data that is written to the driver. This buffer is used in the exact same fashion as METHOD_BUFFERED. To avoid confusion, mentally rename this buffer to lpControlBuffer. This is typically a small, optional buffer that might contain a control structure with useful information for the device driver. This buffer is smal and is double buffered. lpOutBuffer - NOT OPTIONAL, This LARGE buffer contains data that is written by the driver and read by the wer-mode application when the request is completed. To avoid confusion, mentally rename this buffer to lpDataTransferBuffer. This is physically the same buffer that the device driver will write to. There is no double buffering. Technically, this buffer is still optional, but since you are using this buffering method, what would be the point??? I/O Manager perspective If lpInBuffer exists, allocates memory from non-paged pool and puts the address of this pool in Irp->AssociatedIrp.SystemBuffer. This buffer is accessible at any IRQL. copies nInBufferSize to irpSp->Parameters.DeviceIoControl.InputBufferLength copies nOutBufferSize to irpSp->Parameters.DeviceIoControl.OutputBufferLength copies contents of lpInBuffer to SystemBuffer allocated above So far this is completely identical to METHOD_BUFFERED. Most likely lpInBuffer (mentally renamed to lpControlBuffer) is very small in size. For lpOutBuffer (mentally renamed to lpDataTransferBuffer), an MDL is allocated. lpOutBuffer is probed and locked into memory. Then the user buffer's addresses are checked to make sure the caller could write to them in the caller's access mode. The MDL is address is stored in Irp->MdlAddress. Your driver is called. Device Driver perspective The device driver can read the copy of lpOutBuffer via Irp->AssociatedIrp.SystemBuffer. Anything written by the device driver to this buffer is lost. The device driver accesses the Win32 buffer directly via Irp->MdlAddress. The driver uses whatever Mdl API's to write data to the buffer. Usually, this buffer is to be read from some mass storage media or some similar operation. Since this is a large data transfer, assume a completion routine is required. mark the Irp pending queue it return status pending Device Driver Completion Routine perspective standard completion routine operations set IoStatus.Status to an approriate NtStatus IoStatus.Information is not needed completete the request I/O Manager Completion Routine perspective standard I/O Manager completion routine operations unmap the pages deallocate the Mdl complete the request METHOD_NEITHER I/O Manager perspective Irp->UserBuffer = lpOutputBuffer; IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = lpInputBuffer; No comments here. Don't use METHOD_DIRECT unless you know what you are doing. Simple rule. If your IOCtl involves no data transfer buffers, then METHOD_NEITHER is the fastest path through the I/O Manager that involves an Irp. Final Comment Don't touch Irp->UserBuffer. This is a bookmark for the I/O Manager. Two major problems can occur. 1 - page fault at high IRQL, or 2 - you write something to Irp->UserBuffer and the I/O Manager overwrites you in its completion routine. File systems access Irp->UserBuffer, but FSD writers know all of the above and know when it is safe to touch Irp->UserBuffer. In article <339706A7.43BE@proaxis.com>, Chet Douglas writes: > Can someone tell me if I have the following correct?? Not even close, sorry. > This chart is trying to show how the input and output pointers in the > DeviceIOControl call from ring 3 are translated into a system supplied > IRP in ring 0. > > ADDR XLATED refers to whether the system supplied IRP has translated the > buffer into kernel mode address space. This NEVER happens. What does happen is that a system-space buffer may be allocated and the user buffer *copied* to or from the process buffer. > PROBED/LOCKED refers to whether the system has probed and locked the > pages into physical memory for the system supplied IRP. > > DATA DIR refers to the direction in which data can flow. IN means the > data will be going into the buffer. OUT means the data will be going > out of the buffer. BOTH means either direction will work. This is the backwards of DeviceIoControl's interpretation of "in" and "out", eg in the terms InBuffer and OutBuffer. Let's change it to "memory access", read vs. write - memory is read by driver and/or DMA device, or memory is written by driver and/or DMA device. Another key point is that for DeviceIoControl, the device object flags DO_BUFFERED_IO and DO_DIRECT_IO have no, repeat, no significance whatsoever. Those flags only apply to WriteFile and ReadFile calls. ----------------------------------------------------------------------- DEVIOCTL DRIVER DATA PROBED/ MEMORY RING 3 RING 0 COPIED LOCKED ACCESS =============== =============================== ======= ======= ====== METHOD_BUFFERED pInBuffer pIRP->AssociatedIrp.SystemBuffer Y N read pOutBuffer pIRP->AssociatedIrp.SystemBuffer Y N write METHOD_IN_DIRECT pInBuffer pIRP->AssociatedIrp.SystemBuffer Y N read pOutBuffer pIRP->MDLAddress N Y read METHOD_OUT_DIRECT pInBuffer pIRP->AssociatedIrp.SystemBuffer Y N read pOutBuffer pIRP->MDLAddress N Y write METHOD_NEITHER pInBuffer pIRPStack->Params.DevIoctl.Type3InputBuffer N N any (unsafe) pOutBuffer pIRP->UserBuffer N N any (unsafe) In this last case the "raw" user-space addresses and sizes are simply placed in the indicated fields in the IRP. I do not believe that NT has even probed them for safe access. While in requesting thread context the driver can reference these buffers in either "direction" but must do so in a try/except block, as the app could undefine these buffers at any time. It could also do IoAllocateMdl / MmProbeAndLockPages on them; this latter call would also have to be done in a try/except block. --- Jamie Hanrahan, Kernel Mode Systems, San Diego CA Internet: jeh@cmkrnl.com (JH645) CompuServe: 74140,2055 drivers, internals, networks, applications, and training for VMS and Windows NT NT driver FAQ, links, and other information: http://www.cmkrnl.com/ If you post a reply in news, please don't e-mail it too. Conny Ljungqvist wrote in article <345F74C7.2056@swipnet.se>... > Hi, > > I want to send a large data buffer to a device driver, modify it and > then return it to calling application as efficient as possible. > > If METHOD_IN_DIRECT is used, data to device driver is not copied from > app. and can be read from by device driver but can't I also write in > the incoming buffer? > If not, howcome? > > If METHOD_OUT_DIRECT is used, data from device driver is not copied to > app. and can be written to by device driver but can't I also read from > the outgoing buffer? > If not, howcome? > > I guess I can use METHOD_NEITHER but it seems one has to know an awful > lot about how NT works internally. Is there any sample around solving > this problem using METHOD_NEITHER? > > Thanks, Conny Ljungqvist >