Mapping kernel space from application

Hello,

In my device driver I’m allocating a continuous buffer with:

DmaAdapter = IoGetDmaAdapter(FdoData,&DeviceDescription,&NumberOfMapRegisters);
AllocateCommonBuffer = DmaAdapter->DmaOperations->AllocateCommonBuffer;
DevExt->KernelCommonBuffer[Channel] = AllocateCommonBuffer(DmaAdapter,
COMMON_BUFFER_SIZE,
&LogicalAddress,
FALSE);
RtlFillMemory (DevExt->KernelCommonBuffer[Channel], COMMON_BUFFER_SIZE, 0xAA);

DevExt->CommonBufferMdl[Channel] = IoAllocateMdl (DevExt->KernelCommonBuffer[Channel],COMMON_BUFFER_SIZE,FALSE, FALSE, NULL);
MmBuildMdlForNonPagedPool (DevExt->CommonBufferMdl[Channel]);

From the application I want to “see” this buffer. I’m getting its address using IOCTL command replied in the calling process context.
According to advice I got here:

In “DeviceAdd” I’m calling:

// Register the EvtIoInCallerContext to deal with IOCTLs that need to stay in original context
WdfDeviceInitSetIoInCallerContextCallback (DeviceInit, DeviceEvtIoInCallerContext);

VOID DeviceEvtIoInCallerContext (__in WDFDEVICE Device,
__in WDFREQUEST Request)
{
if (requestParameters.Type == WdfRequestTypeDeviceControl)
{
if (!processed)
{
processed = RequestDispatchToSequentialQueue(Device, Request, requestParameters);
}
}
}

BOOLEAN
RequestDispatchToSequentialQueue(
__in WDFDEVICE Device,
__in WDFREQUEST Request,
__in WDF_REQUEST_PARAMETERS RequestParameters
)
{
ULONG IoControlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;

KdPrint ((“PsGetCurrentProcessId=%x\n”,PsGetCurrentProcessId()));

if (IoControlCode == MAP_FROM_APP)
{
MmMapLockedPagesSpecifyCache(devExt->CommonBufferMdl[Channel],
UserMode,
MmCached ,
NULL,
FALSE,

NormalPagePriority);
WdfRequestCompleteWithInformation (Request,STATUS_SUCCESS,bytesReturned);
}
}

The problem:

Upon kernel buffer allocation, all the buffer is set to 0xAA.
But in the application I see 0x0 which means I do not see the correct address.

Under Server 2008\2012 it works Ok.
But under Win7-64 not.

How can I debug this problem ?
There is no “blue dump”.

Thank you,
Zvika

There is no guarantee that an IOCTL in KMDF will be processed in the calling
application. Consider using WdfDeviceInitSetIoInCallerContextCallback to
get a callback that you can process correctly, or get the IRP and use
IoGetRequestorProcess to find the process.

Don Burn
Windows Driver Consulting
Website: http://www.windrvr.com

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@gmail.com
Sent: Sunday, June 18, 2017 3:08 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] Mapping kernel space from application

Hello,

In my device driver I’m allocating a continuous buffer with:

DmaAdapter =
IoGetDmaAdapter(FdoData,&DeviceDescription,&NumberOfMapRegisters);
AllocateCommonBuffer = DmaAdapter->DmaOperations->AllocateCommonBuffer;
DevExt->KernelCommonBuffer[Channel] = AllocateCommonBuffer(DmaAdapter,

COMMON_BUFFER_SIZE,

&LogicalAddress,

FALSE);
RtlFillMemory (DevExt->KernelCommonBuffer[Channel], COMMON_BUFFER_SIZE,
0xAA);

DevExt->CommonBufferMdl[Channel] = IoAllocateMdl
DevExt->(DevExt->KernelCommonBuffer[Channel],COMMON_BUFFER_SIZE,FALSE,
DevExt->FALSE, NULL);
MmBuildMdlForNonPagedPool (DevExt->CommonBufferMdl[Channel]);

From the application I want to “see” this buffer. I’m getting its address
using IOCTL command replied in the calling process context.
According to advice I got here:

In “DeviceAdd” I’m calling:

// Register the EvtIoInCallerContext to deal with IOCTLs that need to stay
in original context WdfDeviceInitSetIoInCallerContextCallback (DeviceInit,
DeviceEvtIoInCallerContext);

VOID DeviceEvtIoInCallerContext ( in WDFDEVICE Device,
in
WDFREQUEST Request)
{
if (requestParameters.Type == WdfRequestTypeDeviceControl)
{
if (!processed)
{
processed = RequestDispatchToSequentialQueue(Device,
Request, requestParameters);
}
}
}

BOOLEAN
RequestDispatchToSequentialQueue(
in WDFDEVICE Device,
in WDFREQUEST Request,
__in WDF_REQUEST_PARAMETERS RequestParameters
)
{
ULONG IoControlCode =
RequestParameters.Parameters.DeviceIoControl.IoControlCode;

KdPrint ((“PsGetCurrentProcessId=%x\n”,PsGetCurrentProcessId()));

if (IoControlCode == MAP_FROM_APP)
{
MmMapLockedPagesSpecifyCache(devExt->CommonBufferMdl[Channel],

UserMode,

MmCached ,

NULL,

FALSE,

NormalPagePriority);
WdfRequestCompleteWithInformation
(Request,STATUS_SUCCESS,bytesReturned);
}
}

The problem:
----------------
Upon kernel buffer allocation, all the buffer is set to 0xAA.
But in the application I see 0x0 which means I do not see the correct
address.

Under Server 2008\2012 it works Ok.
But under Win7-64 not.

How can I debug this problem ?
There is no “blue dump”.

Thank you,
Zvika


NTDEV is sponsored by OSR

Visit the list online at:
http:

MONTHLY seminars on crash dump analysis, WDF, Windows internals and software
drivers!
Details at http:

To unsubscribe, visit the List Server section of OSR Online at
http:</http:></http:></http:>

On Jun 18, 2017, at 12:07 PM, xxxxx@gmail.com wrote:

In my device driver I’m allocating a continuous buffer with:

From the application I want to “see” this buffer. I’m getting its address using IOCTL command replied in the calling process context.
According to advice I got here:

BOOLEAN
RequestDispatchToSequentialQueue(
__in WDFDEVICE Device,
__in WDFREQUEST Request,
__in WDF_REQUEST_PARAMETERS RequestParameters
)
{
ULONG IoControlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;

KdPrint ((“PsGetCurrentProcessId=%x\n”,PsGetCurrentProcessId()));

if (IoControlCode == MAP_FROM_APP)
{
MmMapLockedPagesSpecifyCache(devExt->CommonBufferMdl[Channel],
UserMode,
MmCached ,
NULL,
FALSE,

NormalPagePriority);
WdfRequestCompleteWithInformation (Request,STATUS_SUCCESS,bytesReturned);
}
}

The problem:

Upon kernel buffer allocation, all the buffer is set to 0xAA.
But in the application I see 0x0 which means I do not see the correct address.

Where are you looking? You map the buffer into the calling process, but nowhere here do you ever return the newly mapped buffer’s address to user mode, so I don’t know where you are expecting to find the 0xAA values.

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

Hi Don, Tim,

Don: As you suggested, I’m using WdfDeviceInitSetIoInCallerContextCallback. Upon getting the IOCTL request I printed PsGetCurrentProcessId(). It’s the same as GetCurrentProcessId() I got just before sending this IOCTL from the appliucation.

Tim: The full code is:
PVOID UserSpaceCommonBuffer = MmMapLockedPagesSpecifyCache (…)

The variable UserSpaceCommonBuffer is returned as UINT64 in the reply of the IOCTL.

Thank you,
Zvika

On Jun 18, 2017, at 9:33 PM, xxxxx@gmail.com wrote:

Tim: The full code is:
PVOID UserSpaceCommonBuffer = MmMapLockedPagesSpecifyCache (…)

The variable UserSpaceCommonBuffer is returned as UINT64 in the reply of the IOCTL.

That is certainly not what you showed us. If you’re only going to show us fake code, then you’re going to get a slew of fake answers.

Show us the REAL code. Don’t assume “this part must be right”.

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

Hi Tim, All,

The code used to allocate the buffer is:


NTSTATUS AllocateContinuousPhysicalMemory (IN PDEVICE_EXTENSION DevExt,int Channel)
{
ULONG NumberOfMapRegisters;
DEVICE_DESCRIPTION DeviceDescription;
PHYSICAL_ADDRESS LogicalAddress;
PDMA_ADAPTER DmaAdapter;
PDEVICE_OBJECT FdoData;
PALLOCATE_COMMON_BUFFER AllocateCommonBuffer;
PVOID UserSpaceAddress;

PAGED_CODE();

RtlZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION));

#if defined(DMA_VER2)
DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION2;
#else
DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
#endif

DeviceDescription.Master = TRUE;
DeviceDescription.ScatterGather = FALSE;
DeviceDescription.Dma32BitAddresses = TRUE;
DeviceDescription.Dma64BitAddresses = FALSE;
DeviceDescription.InterfaceType = PCIBus;
DeviceDescription.MaximumLength = COMMON_BUFFER_SIZE;

FdoData = WdfDeviceWdmGetPhysicalDevice(DevExt->Device);

DmaAdapter = IoGetDmaAdapter(FdoData,&DeviceDescription,&NumberOfMapRegisters);

if (!DmaAdapter)
{
KdPrint ((“IoGetDmaAdapter failed.\n”));
return STATUS_INSUFFICIENT_RESOURCES;
}

AllocateCommonBuffer = DmaAdapter->DmaOperations->AllocateCommonBuffer;

if (!AllocateCommonBuffer)
{
KdPrint ((“devExt->AllocateCommonBuffer=0x0\n”));
return STATUS_INSUFFICIENT_RESOURCES;
}

//Allocate common buffer and return its kernel virtual address
DevExt->KernelCommonBuffer[Channel] = AllocateCommonBuffer(DmaAdapter,
COMMON_BUFFER_SIZE,
&LogicalAddress,
FALSE);

if (!DevExt->KernelCommonBuffer)
{
KdPrint ((“AllocateCommonBuffer failed.\n”));
return STATUS_INSUFFICIENT_RESOURCES;
}

//RtlZeroMemory(DevExt->KernelCommonBuffer[Channel], COMMON_BUFFER_SIZE);

RtlFillMemory (DevExt->KernelCommonBuffer[Channel], COMMON_BUFFER_SIZE, 0xAA);

DevExt->CommonBufferMdl[Channel] = IoAllocateMdl (DevExt->KernelCommonBuffer[Channel],COMMON_BUFFER_SIZE,FALSE, FALSE, NULL);

if (!DevExt->CommonBufferMdl)
{
KdPrint ((“IoAllocateMdl failed.\n”));
return STATUS_INSUFFICIENT_RESOURCES;
}

MmBuildMdlForNonPagedPool (DevExt->CommonBufferMdl[Channel]);

/*DevExt->PhysicalKernelCommonBuffer[Channel] = MmGetPhysicalAddress (DevExt->KernelCommonBuffer[Channel]);*/

DevExt->PhysicalKernelCommonBuffer[Channel] = LogicalAddress;

return STATUS_SUCCESS;
}

The code that handles the IOCTL request to map this buffer is:


BOOLEAN
RequestDispatchToSequentialQueue(
__in WDFDEVICE Device,
__in WDFREQUEST Request,
__in WDF_REQUEST_PARAMETERS RequestParameters
)
/*++
Routine Description:

These requests can be processed in a non-serialized manner, most of them don’t need to access device.

Arguments:

Device - handle to a WDF Device object

Request - handle to the incoming WDF Request object

RequestParameters - request parameters

Return Value:

BOOLEAN - TRUE (request processed); FALSE (request is not processed in this function).

–*/
{
NTSTATUS status = STATUS_SUCCESS;
size_t dataLength = 0;
PDEVICE_EXTENSION devExt = NULL;

MODULE_ALLOCATE_COMMON_BUFFER *pAllocateRequest;
MODULE_ALLOCATE_COMMON_BUFFER_REPLY *pAllocateReply;
MODULE_DEALLOCATE_COMMON_BUFFER *pDeAllocateRequest;

WDFREQUEST InternalRequest;
WDFREQUEST DpcRequest;

PMDL CommonBufferMdl;
PVOID CommonBufferVirtualAddress;
ULONG CommonBufferLength;
PUCHAR pDst;
ULONG PciAddress;
ULONG Offset;
ULONG PhysicalDataAddress;
size_t bytesReturned = 0;

void *pInBuffer;
void *pOutBuffer;
int Channel;
size_t Length;

ULONG IoControlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
devExt = PLxGetDeviceContext(Device);

switch (IoControlCode)
{
case MODULE_ALLOCATE_COMMON_BUFFER_CODE:
KdPrint ((“PsGetCurrentProcessId=%x\n”,PsGetCurrentProcessId()));

status = WdfRequestRetrieveInputBuffer (Request,sizeof(MODULE_ALLOCATE_COMMON_BUFFER),
&pInBuffer, &Length);
pAllocateRequest = (MODULE_ALLOCATE_COMMON_BUFFER *)pInBuffer;
Channel = pAllocateRequest->Channel;

status = WdfRequestRetrieveOutputBuffer (Request,sizeof(MODULE_READ_LONG_REPLY),
&pOutBuffer, &Length);

pAllocateReply = (MODULE_ALLOCATE_COMMON_BUFFER_REPLY *)pOutBuffer;

bytesReturned = sizeof(MODULE_ALLOCATE_COMMON_BUFFER_REPLY);

//Map common buffer to user space. This can be done only upon IOCTL request
__try
{
devExt->UserSpaceCommonBuffer[Channel] = MmMapLockedPagesSpecifyCache(devExt->CommonBufferMdl[Channel],
UserMode,
MmCached ,
NULL,
FALSE,
NormalPagePriority);

if (!devExt->UserSpaceCommonBuffer)
{
KdPrint ((“MmMapLockedPagesSpecifyCache failed.\n”));
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
devExt->UserSpaceCommonBuffer[Channel] = NULL;
KdPrint ((“MmMapLockedPagesSpecifyCache caused exception: %x\n”,GetExceptionCode()));
}

pAllocateReply->Address = (UINT64)devExt->UserSpaceCommonBuffer[Channel];

WdfRequestCompleteWithInformation (Request,STATUS_SUCCESS,bytesReturned);
break;

case MODULE_DEALLOCATE_COMMON_BUFFER_CODE:
status = WdfRequestRetrieveInputBuffer (Request,sizeof(MODULE_DEALLOCATE_COMMON_BUFFER),
&pInBuffer, &Length);
pDeAllocateRequest = (MODULE_DEALLOCATE_COMMON_BUFFER *)pInBuffer;
Channel = pDeAllocateRequest->Channel;

__try
{
MmUnmapLockedPages (devExt->UserSpaceCommonBuffer[Channel], devExt->CommonBufferMdl[Channel]);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KdPrint ((“MmUnmapLockedPages caused exception: %x\n”,GetExceptionCode()));
}
bytesReturned = 0;

WdfRequestCompleteWithInformation (Request,STATUS_SUCCESS,bytesReturned);

break;
default:
return FALSE; //The IOCTL code will be handled in the default IoControl handler
}

return TRUE; //This will mark the request as processed
}


VOID DeviceEvtIoInCallerContext (__in WDFDEVICE Device,
__in WDFREQUEST Request)
/*++
Routine Description:

Responds to EvtIoInCallerContext events from KMDF
It calls different functions to process different type of IOCTLs.

Arguments:

Device - handle to a WDF Device object

Request - handle to the incoming WDF Request object

Return Value:

VOID.

–*/
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION devExt = NULL;
WDF_REQUEST_PARAMETERS requestParameters;
BOOLEAN processed = FALSE;

WdfRequestGetParameters(Request, &requestParameters);

//KdPrint ((“–>DeviceEvtIoInCallerContext\n”));

devExt = PLxGetDeviceContext(Device);

// get the request parameters
WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
WdfRequestGetParameters(Request, &requestParameters);

if (requestParameters.Type == WdfRequestTypeDeviceControl)
{
//KdPrint ((“IoControlCode=0x%08x\n”,requestParameters.Parameters.DeviceIoControl.IoControlCode));
// 1. Requests that should be processed only in the context of the app’s process
if (!processed)
{
processed = RequestDispatchToSequentialQueue(Device, Request, requestParameters);
}
if (!processed)
{
//KdPrint ((“Forwarding to default IOCTL\n”));
status = WdfDeviceEnqueueRequest(Device, Request);
if (!NT_SUCCESS(status))
{
KdPrint ((“WdfDeviceEnqueueRequest failed\n”));
}
}
}

//KdPrint ((“<–DeviceEvtIoInCallerContext\n”));
}


I did not attach the code that handles regular IOCTL request or the BARs allocation.

Thank you,
Zvika

Good heavens! Why are you intermixing WDF and WDM like that? Why not just allocate the common buffer using WDF?

Peter
OSR
@OSRDrivers

Hi Pavel,

Where do you see a mix ?

Thank you,
Zvika

xxxxx@gmail.com wrote:

Hi Pavel,

That was Peter, not Pavel.

Where do you see a mix ?

Essentially all of your AllocateContinuousPhysicalMemory function could
be replaced by a single call to WdfCommonBufferCreate.

Will you actually be using this buffer for hardware DMA? If not, then
you don’t need continuous memory at all. You can use ExAllocatePool (or
WdfMemoryCreate).

Have you dumped the addresses you see in the driver and in the user-mode
app, to make sure the numbers actually match?

DeviceDescription.Master = TRUE;
DeviceDescription.ScatterGather = FALSE;
DeviceDescription.Dma32BitAddresses = TRUE;
DeviceDescription.Dma64BitAddresses = FALSE;
DeviceDescription.InterfaceType = PCIBus;
DeviceDescription.MaximumLength = COMMON_BUFFER_SIZE;

Why did you set Dma64BitAddresses to FALSE?


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

Hi Tim, All,

The buffer is used for hardware DMA. It has to be continuous.
The HW does not support descriptors at all.

The address returned by MmMapLockedPagesSpecifyCache is identical to the one used in the application.

Dma64BitAddresses is set to FALSE because the HW supports up to 32bits address.

Same code compiled for Server 2008-64 and Server 2012-64 works Ok (but on another PC).

Thank you,
Zvika

Hello,

I installed the same sys and used the same application in 2 PCs.

  1. Win7-64, 4GB
  2. Win7-64, 8GB

In works Ok only in the 4GB PC.

The physical address set to the HW is “LogicalAddress” found with:
AllocateCommonBuffer(DmaAdapter,COMMON_BUFFER_SIZE,&LogicalAddress,FALSE);

As mentioned, under Server2008-64, 16GB, it works fine also.

Is it possible that the LogicalAddress is wrong ?

BTW, the virtual address of the buffer found with IOCTL is right.
From the application I can see the pattern filled in the buffer.

But the DMA is done to another unknown space.

Thank you,
Zvika

On Jun 20, 2017, at 8:15 AM, xxxxx@gmail.com wrote:

I installed the same sys and used the same application in 2 PCs.

  1. Win7-64, 4GB
  2. Win7-64, 8GB

In works Ok only in the 4GB PC.

The physical address set to the HW is “LogicalAddress” found with:
AllocateCommonBuffer(DmaAdapter,COMMON_BUFFER_SIZE,&LogicalAddress,FALSE);

As mentioned, under Server2008-64, 16GB, it works fine also.

Is it possible that the LogicalAddress is wrong ?

You can check some of this in the debugger, by dumping the addresses and checking the mappings.

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

>Is it possible that the LogicalAddress is wrong ?

Well… Windows isn’t broken, that’s for sure.

If I were you, I’d use WDF to do what you’re doing… but hey, that’s just me.

Sooo… to make us all certain that the returned KVA and Logical Address are correct:

Using the debugger, write some data to the start of the buffer using the returned KVA. Then !dd the base of your buffer using the returned Logical Address… See the data you wrote? If so, the Logical Address is correct.

OH! Make sure Driver Verifier’s DMA verifier is OFF when you do this…

Peter
OSR
@OSRDrivers

Hi Peter,

What do you mean by “I’d use WDF” ?
Do you mean that “AllocateCommonBuffer” is WDM ?

I Also tried:
PHYSICAL_ADDRESS HighestAcceptableAddress = {0x0,0xFFFFFFFF};
PHYSICAL_ADDRESS LowestAcceptableAddress = {0x0,0x0};
PHYSICAL_ADDRESS BoundaryAddressMultiple = {0x0,0x0};

DevExt->KernelCommonBuffer[Channel] = MmAllocateContiguousMemorySpecifyCache (COMMON_BUFFER_SIZE,
LowestAcceptableAddress,
HighestAcceptableAddress,
BoundaryAddressMultiple,
MmCached);

DevExt->PhysicalKernelCommonBuffer[Channel] = MmGetPhysicalAddress (DevExt->KernelCommonBuffer[Channel]);

The results were the same under the 8GB PC.

Thank you,
Zvika

As Mr. Roberts already answered, when you asked about this earlier:

You’ve got a bug. You need to debug it. Did you try any of the ideas we suggested?

Peter
OSR
@OSRDrivers

Hi Peter,

Can I use WdfCommonBufferCreate to create a physically continuous kernel buffer ?

My HW does not support DMA descriptors.

The HW contains 2 registers: Start address , buffer size.

I set those registers only once.

HW is responsible to initiate DMA to the buffer upon getting data from external source.

Using “dd” I tried to display contents of the buffer address in “LogicalAddress”.

The value of “LogicalAddress” was 0x70080000.

But “dd” to this address gave me:
? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ?

Can I use “dd” to display contents of physical address ?

I got the “? ? ?” in a 4GB PC in which the driver is working Ok.

Thank you,
Zvika

> Can I use “dd” to display contents of physical address ?

Use !dd or !dd -m
It is extension (!) command, not same as dd.
(there’s also /p option for dd command but I prefer !dd)

You look desperate… find a consultant ASAP?

Regards,
P.

On Jun 21, 2017, at 9:58 AM, xxxxx@gmail.com wrote:

Can I use WdfCommonBufferCreate to create a physically continuous kernel buffer ?

Have you read any of the documentation? That is the exact definition of a “common buffer”.

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

Dude… slow down and READ carefully before trying things.

I wrote:

That’s EXCLAMATION-POINT followed by DD… not just DD. As I’m sure you know, DD takes a virtual address, so that is not likely to work, right?

You look desperate

Mr. Vered frequently looks this way in his posts, but he eventually manages to get his stuff working. I have yet to figure out what his ongoing interest in Windows drivers is (though, I readily admit, he owes us no explanation). I initially guessed him to be an SE for a silicon manufacturer, but he’s here an awful lot for that. Cardiologist? Kibutznik? Former fighter pilot? Hard to know… Perhaps he’s just an international man of mystery :slight_smile:

Peter
OSR
@OSRDrivers