KMDF memory mapping to userspace?

I have to write a windows driver (for vista and XP) that matches an already written linux driver for a PCI card with memory mapped registers. The linux driver maps the memory registers into the kernel driver, which I understand can be easily done in KMDF or even WDF. The linux driver also maps a portion of these same registers to it’s userspace driver using mmap. My windows driver will have to do the same thing. The goal was to simplify the kernel driver in linux and move some complexity to userspace, thus making the kernel-mode driver more portable.

Can this be done in KMDF (map card memory registers that have already been mapped into a kernel driver, out to userspace) and are there coding examples?

Hi,

xxxxx@sedsystems.ca wrote:

I have to write a windows driver (for vista and XP) that matches an already written linux driver for a PCI card with memory mapped registers. The linux driver maps the memory registers into the kernel driver, which I understand can be easily done in KMDF or even WDF. The linux driver also maps a portion of these same registers to it’s userspace driver using mmap. My windows driver will have to do the same thing. The goal was to simplify the kernel driver in linux and move some complexity to userspace, thus making the kernel-mode driver more portable.

Can this be done in KMDF (map card memory registers that have already been mapped into a kernel driver, out to userspace) and are there coding examples?

I’ve only been able to do such a thing with XP DDK a while ago. The
principle is explained at:

http://community.reverse-engineering.net/viewtopic.php?t=5511

Maybe some people at osr can elaborate more in this issue ;-). That
solution seems to be working fine with my application-driver duo ;-).

Cheers,
Darmawan

Hi again,

xxxxx@sedsystems.ca wrote:

I have to write a windows driver (for vista and XP) that matches an already written linux driver for a PCI card with memory mapped registers. The linux driver maps the memory registers into the kernel driver, which I understand can be easily done in KMDF or even WDF. The linux driver also maps a portion of these same registers to it's userspace driver using mmap. My windows driver will have to do the same thing. The goal was to simplify the kernel driver in linux and move some complexity to userspace, thus making the kernel-mode driver more portable.

Can this be done in KMDF (map card memory registers that have already been mapped into a kernel driver, out to userspace) and are there coding examples?


Questions? First check the Kernel Driver FAQ at http://www.osronline.com/article.cfm?id=256

To unsubscribe, visit the List Server section of OSR Online at ListServer/Forum

a sample code from my driver:

typedef struct _MMIO_RING_0_MAP{
PVOID sysAddrBase; // the starting system virtual address of
the mapped physical address range
ULONG size; // size of the mapped physical address range
PVOID usermodeAddrBase; // pointer to the usermode virtual address
where this range is mapped
PMDL pMdl; // Memory Descriptor List for the memory
mapped I/O range to be mapped
}MMIO_RING_0_MAP, *PMMIO_RING_0_MAP;

typedef struct _DEVICE_EXTENSION{
MMIO_RING_0_MAP mapZone[MAX_MAPPED_MMIO];
}DEVICE_EXTENSION, *PDEVICE_EXTENSION;

..................

NTSTATUS MapMmio(PDEVICE_OBJECT pDO, PIRP pIrp)
/*++
Routine Description:
Process the IRPs with IOCTL_MAP_MMIO code.
This routine maps a physical address range
to the usermode application address space.

Arguments:
pDO - pointer to the device object of this driver.
pIrp - pointer to an I/O Request Packet.

Return Value:
NT Status code

Notes:
This function can only map the area
below the 4-GB limit.
--*/
{
PDEVICE_EXTENSION pDevExt;
PHYSICAL_ADDRESS phyAddr;
PMMIO_MAP pUsermodeMem;
ULONG i, free_idx;

pDevExt = (PDEVICE_EXTENSION) pDO->DeviceExtension;

//
// Check for free mapZone in the device extension.
// If none is free, return an error code.
//
for(i = 0; i < MAX_MAPPED_MMIO; i++)
{
if( pDevExt->mapZone[i].sysAddrBase == NULL )
{
free_idx = i;
break;
}
}

if( i == MAX_MAPPED_MMIO )
{
return STATUS_INVALID_DEVICE_REQUEST;
}

//
// We have obtained a free mapZone, map the physical address range.
//
pUsermodeMem = (MMIO_MAP*) MmGetSystemAddressForMdlSafe(
pIrp->MdlAddress, NormalPagePriority );
if( NULL == pUsermodeMem) {
return STATUS_INVALID_USER_BUFFER;
}

phyAddr.HighPart = 0;
phyAddr.LowPart = pUsermodeMem->phyAddrStart;

pDevExt->mapZone[free_idx].sysAddrBase = MmMapIoSpace( phyAddr,
pUsermodeMem->size, MmNonCached);
if(NULL == pDevExt->mapZone[free_idx].sysAddrBase)
{
return STATUS_BUFFER_TOO_SMALL;
}

pDevExt->mapZone[free_idx].pMdl =
IoAllocateMdl(pDevExt->mapZone[free_idx].sysAddrBase,
pUsermodeMem->size, FALSE, FALSE, NULL);
if(NULL == pDevExt->mapZone[free_idx].pMdl)
{
MmUnmapIoSpace(pDevExt->mapZone[free_idx].sysAddrBase,
pUsermodeMem->size);
pDevExt->mapZone[free_idx].sysAddrBase = NULL;
return STATUS_BUFFER_TOO_SMALL;
}

pDevExt->mapZone[free_idx].size = pUsermodeMem->size;

//
// Map the system virtual address to usermode virtual address
//
MmBuildMdlForNonPagedPool(pDevExt->mapZone[free_idx].pMdl);
pDevExt->mapZone[free_idx].usermodeAddrBase =
MmMapLockedPagesSpecifyCache( pDevExt->mapZone[free_idx].pMdl,
UserMode, MmNonCached,
NULL, FALSE,
NormalPagePriority);
if(NULL == pDevExt->mapZone[free_idx].usermodeAddrBase)
{
IoFreeMdl(pDevExt->mapZone[free_idx].pMdl);
MmUnmapIoSpace(pDevExt->mapZone[free_idx].sysAddrBase,
pDevExt->mapZone[free_idx].size);
pDevExt->mapZone[free_idx].sysAddrBase = NULL;
pDevExt->mapZone[free_idx].size = 0;
return STATUS_BUFFER_TOO_SMALL;
}

// copy the resulting usermode virtual address to IRP "buffer"
pUsermodeMem->usermodeVirtAddr =
pDevExt->mapZone[free_idx].usermodeAddrBase;

return STATUS_SUCCESS;
}

NTSTATUS CleanupMmioMapping(PDEVICE_EXTENSION pDevExt, ULONG i)
/*++
Routine Description:
This routine cleanup the mapping of a MMIO range
and resources it consumes.

Arguments:
pDevExt - pointer to the device extension of the driver
i - index of the mapZone to cleanup

Return Value:
NT Status code
--*/
{
if( NULL != pDevExt->mapZone[i].usermodeAddrBase )
{
MmUnmapLockedPages( pDevExt->mapZone[i].usermodeAddrBase,
pDevExt->mapZone[i].pMdl);
pDevExt->mapZone[i].usermodeAddrBase = NULL;
}

if( NULL != pDevExt->mapZone[i].pMdl )
{
IoFreeMdl(pDevExt->mapZone[i].pMdl);
pDevExt->mapZone[i].pMdl = NULL;
}

if( NULL != pDevExt->mapZone[i].sysAddrBase )
{
MmUnmapIoSpace( pDevExt->mapZone[i].sysAddrBase,
pDevExt->mapZone[i].size);
pDevExt->mapZone[i].sysAddrBase = NULL;
pDevExt->mapZone[i].size = 0;
}

return STATUS_SUCCESS;
}

NTSTATUS UnmapMmio(PDEVICE_OBJECT pDO, PIRP pIrp)
/*++
Routine Description:
Process the IRPs with IOCTL_UNMAP_MMIO code.
This routine unmaps a previously mapped physical
address range.

Arguments:
pDO - pointer to the device object of this driver.
pIrp - pointer to an I/O Request Packet.

Return Value:
NT Status code

Notes:
This function can only unmap the area
below the 4-GB limit.
--*/
{
PDEVICE_EXTENSION pDevExt;
PMMIO_MAP pMmioMap;
ULONG i;

//
// Unmap the requested zone from the system address space
// and update the device extension data
//
pDevExt = (PDEVICE_EXTENSION) pDO->DeviceExtension;
pMmioMap = (PMMIO_MAP) MmGetSystemAddressForMdlSafe(
pIrp->MdlAddress, NormalPagePriority );

for(i = 0 ; i < MAX_MAPPED_MMIO; i++)
{
if(pDevExt->mapZone[i].usermodeAddrBase == pMmioMap->usermodeVirtAddr)
{
CleanupMmioMapping(pDevExt, i);
break;
}
}

return STATUS_SUCCESS;
}

NTSTATUS
DispatchIoControl(
IN PDEVICE_OBJECT pDO,
IN PIRP pIrp
)
/*++
Routine Description:
Io control code dispatch routine

Arguments:
DeviceObject - pointer to a device object.
Irp - pointer to current Irp

Return Value:
NT status code.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(pIrp);

switch(irpStack->Parameters.DeviceIoControl.IoControlCode)
{
......
case IOCTL_MAP_MMIO:
{
if(irpStack->Parameters.DeviceIoControl.InputBufferLength >=
sizeof(MMIO_MAP)) {
status = MapMmio(pDO, pIrp);

} else {
status = STATUS_BUFFER_TOO_SMALL;
}
}break;

case IOCTL_UNMAP_MMIO:
{
if(irpStack->Parameters.DeviceIoControl.InputBufferLength >=
sizeof(MMIO_MAP)) {
status = UnmapMmio(pDO, pIrp);

} else {
status = STATUS_BUFFER_TOO_SMALL;
}
}break;

default:
{
status = STATUS_INVALID_DEVICE_REQUEST;
}break;
}

//
// complete the I/O request and return appropriate values
//
pIrp->IoStatus.Status = status;

// Set number of bytes to copy back to user-mode
if(status == STATUS_SUCCESS)
{
pIrp->IoStatus.Information =
irpStack->Parameters.DeviceIoControl.OutputBufferLength;
}
else
{
pIrp->IoStatus.Information = 0;
}
IoCompleteRequest( pIrp, IO_NO_INCREMENT );

return status;
}

I hope it help in some way :wink:

Cheers,
Darmawan

The principle is explained in the current WDK documentation as well,
under MmMapLockedPagesSpecifyCache, and the hideous pitfalls of mapping
device register space into user mode have been discussed here
repeatedly. KMDF allows you to drop down to standard WDK interfaces,
consequently if it is supported in the WDK you also have support in
KMDF. However the OP should consider dropping this functionality, as the
goals of simplification and portability are not achieved, and in fact
you lose portability and you increase complexity.

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Darmawan Salihun
Sent: Monday, June 25, 2007 12:30 PM
To: Windows System Software Devs Interest List
Subject: Re: [ntdev] KMDF memory mapping to userspace?

Hi,

xxxxx@sedsystems.ca wrote:

I have to write a windows driver (for vista and XP) that matches an
already written linux driver for a PCI card with memory mapped
registers. The linux driver maps the memory registers into the kernel
driver, which I understand can be easily done in KMDF or even WDF. The
linux driver also maps a portion of these same registers to it’s
userspace driver using mmap. My windows driver will have to do the same
thing. The goal was to simplify the kernel driver in linux and move
some complexity to userspace, thus making the kernel-mode driver more
portable.

Can this be done in KMDF (map card memory registers that have already
been mapped into a kernel driver, out to userspace) and are there coding
examples?

I’ve only been able to do such a thing with XP DDK a while ago. The
principle is explained at:

http://community.reverse-engineering.net/viewtopic.php?t=5511

Maybe some people at osr can elaborate more in this issue ;-). That
solution seems to be working fine with my application-driver duo ;-).

Cheers,
Darmawan


Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

I’ve just completed and integrated successfully such an UIO replacement
driver using KMDF.

The driver includes support for powering down and up the device, and
template interrupt support.

If you’re interested, contact me offline for details

Guy

xxxxx@sedsystems.ca wrote:

I have to write a windows driver (for vista and XP) that matches an already written linux driver for a PCI card with memory mapped registers. The linux driver maps the memory registers into the kernel driver, which I understand can be easily done in KMDF or even WDF. The linux driver also maps a portion of these same registers to it’s userspace driver using mmap. My windows driver will have to do the same thing. The goal was to simplify the kernel driver in linux and move some complexity to userspace, thus making the kernel-mode driver more portable.

Can this be done in KMDF (map card memory registers that have already been mapped into a kernel driver, out to userspace) and are there coding examples?

As Mark Roddy has already pointed out, you have blown the concept of making
the kernel-mode driver more portable. Now your design is going to open
potentially serious security holes, since you have a user application
accessing a device. Since you are talking about multiple OS’es you are not
talking an embedded device, and even if your device is used onlly by the
single controlling program this is a poor design.

Forget this lousy Linux design, and create something reasonable.


Don Burn (MVP, Windows DDK)
Windows 2k/XP/2k3 Filesystem and Driver Consulting
Website: http://www.windrvr.com
Blog: http://msmvps.com/blogs/WinDrvr
Remove StopSpam to reply

wrote in message news:xxxxx@ntdev…
>I have to write a windows driver (for vista and XP) that matches an
>already written linux driver for a PCI card with memory mapped registers.
>The linux driver maps the memory registers into the kernel driver, which I
>understand can be easily done in KMDF or even WDF. The linux driver also
>maps a portion of these same registers to it’s userspace driver using
>mmap. My windows driver will have to do the same thing. The goal was to
>simplify the kernel driver in linux and move some complexity to userspace,
>thus making the kernel-mode driver more portable.
>
> Can this be done in KMDF (map card memory registers that have already
> been mapped into a kernel driver, out to userspace) and are there coding
> examples?
>