Writting User Mode Drivers
Summary: Windows ""CE6"" now supports running drivers inside a user-mode driver host, udevice.exe. Our goal has been to support kernel drivers to work “just-as-they-are” in user mode; though, some security restrictions apply as discussed below. The good news is that we managed to keep the overall user mode driver development experience and the driver interfaces exactly the same as the one for kernel mode drivers.
User-mode drivers work pretty much the same as kernel-mode drivers: an application calls ""ActivateDevice""(Ex) and ""DeactivateDevice"" on the driver. The device manager will check registry settings to see if the driver is supposed to be loaded in user mode. You can also use registry settings to specify an instance "ID" of udevice.exe to use, if you want multiple user-mode drivers to load into the same process.
For example, there is one user-mode driver group with ID 3. Multiple drivers load into this group. If you look inside the ""CE6"" %_WINCEROOT%\public\common\oak\files\common.reg (an unprocessed version of what you get in your release directory), you'll see how this group is created and a few drivers that belong to it.
[HKEY_LOCAL_MACHINE\Drivers\ProcGroup_0003]
"ProcName"="udevice.exe"
"ProcVolPrefix"="$udevice"
; Flags==0x10 is DEVFLAGS_LOAD_AS_USERPROC
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Ethman]
"Flags"=dword:12
"UserProcGroup"=dword:3
[HKEY_LOCAL_MACHINE\Drivers\Console]
"Flags"=dword:10
"UserProcGroup"=dword:3
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\SIP]
"Flags"=dword:10
"UserProcGroup"=dword:3
If you don't specify a process group, your driver will be launched inside a unique instance of udevice.exe.
The device manager creates a reflector service object to help the user-mode driver do its job. The reflector service launches udevice.exe, mounts the specified volume and registers the file system volume ""APIs"" for communicating with the driver. Communication between applications and the user mode driver pass through the reflector, which helps with buffer marshalling. The reflector also assists the user-mode driver with operations that user-mode code is not normally allowed to make, like mapping physical memory; more on this later.
It is our goal that drivers should be as close to 100% portable between kernel and user mode as possible. However, kernel code will always be more privileged than user code will be. Taking advantage of the increased kernel capabilities will make your kernel-mode driver impossible to port to user mode.
What are some of the incompatibilities you need to know about? What are the security restrictions that apply to user mode drivers?
Firstly, user-mode drivers cannot write back pointer parameters asynchronously. I take it a step further and say that user-mode drivers cannot operate on caller memory asynchronously. That you're better off keeping such drivers in kernel mode for now, or restructuring their communication with the caller so that nothing is asynchronous.
Another detail you should know about is that user-mode drivers cannot receive embedded pointers from the kernel. If you're writing a driver that talks to kernel-mode drivers, and those kernel-mode drivers pass you embedded pointers, then your driver may have no choice but to run in kernel mode. If you can reorganize the communication between drivers, you may be able to "flatten" the structure so that all the data is stored directly in the IN and OUT buffers instead of referenced via embedded pointers. That said, if you are sure that only user mode processes will call your user mode driver, then you do not need to worry about flattening embedded buffers – though, you better be absolutely sure that no kernel mode driver will call into your user mode driver ever.
Thirdly, there are some ""APIs"" which used to require trust that now are (mostly) blocked against use in user mode. One notable example is ""VirtualCopy"", and its wrapper function ""MmMapIoSpace"". Most user-mode code cannot call "VirtualCopy". User-mode drivers can, with a little help from the reflector. The reflector can call ""VirtualCopy"" on behalf of a user-mode driver, but it will not do so unless it knows the driver is allowed to use the addresses it's copying. Under each driver setup entry in the registry, there are ""IOBase"" and ""IOLen"" keys that we use to mark physical memory. When your driver calls ""VirtualCopy"", the reflector will check these values to make sure your driver is allowed to access the physical address. For example, the serial driver might specify a physical address like this:
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Serial]
"IoBase"=dword:02F8
"IoLen"=dword:8
If you have just one buffer to copy, use DWORD values. Use multi-strings to specify multiple base addresses and sizes.
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Serial]
"IoBase"=multi_sz:"2f8","3f6"
"IoLen"=multi_sz:"8","2"
Since only privileged applications can write to this part of the registry, the registry keys should protect against unprivileged code trying to gain access to these addresses.
Notable ""APIs"" that user-mode code cannot call:
* VM
APIs: ""VirtualCopy
Ex, LockPages
Ex, CreateStaticMapping""
* Interrupt
APIs: ""InterruptInitialize,
InterruptDone, LoadIntChainHandler""
* You cannot install IISR directly, though you can install GIISR via the reflector. (GIISR exposes well known interfaces and the reflector can do the required checks on these calls.)
* OAL ""IOCTLs"" that are not explicitly permitted by the kernel
And lastly, Call-backs from a user-mode driver to any process are also prohibited. The most important repercussion of this is, if you move a bus driver to user mode, you'd have to move the client drivers to user mode too. You can't have the client driver in the kernel since you cannot call back into the kernel mode client driver from the user mode bus driver. You may want to put the bus driver and all of its client drivers in the same udevice.exe instance, so that the callbacks are all within a single process. Again that said, you can easily move the client driver to user mode and keep the bus driver in kernel mode without any problems since the kernel mode bus driver can call back to the user mode client driver via the use of ""CeDriverCallback"" API.
In effect, if your driver is simple (no embedded pointers, no asynchronous usage) and does not deal with the restrictions discussed above, you can easily move your driver back and forth between user and kernel space by simply tweaking a Registry Key Setting.
Go up to
Features of a BSPGo up to
Big Book of BSP
Thank you for contributing to this BSP Wiki. To ensure your comments and concerns receive proper exposure, include bspwiki""@""microsoft"".""com when providing feedback or topical suggestions.