Thursday, February 14, 2008

CE6 Drivers: What you need to know

One of the biggest concerns people have about the new CE6 release is backward compatibility.  Every release we try very hard to make existing applications, drivers and OALs as compatible as possible.  With CE6 we expect very high compatibility for applications and even OAL code, but unfortunately I can’t say the same about drivers.  Many, in fact most, drivers will need modifications in order to run on CE6.  While binary compatibility (being able to run the exact same driver without a rebuild) is not likely, we do expect it to be easy to port almost all drivers.  That was our goal once we realized many drivers would have to change.

The primary reasons that drivers will need change are:

  • Deprecated APIs
  • Memory passing
  • Asynchronous buffer access
  • User interface handling

The biggest difference in CE6 is how drivers access embedded pointers and other data, as I described in detail in my memory marshalling post.  There are two main things you need to do to fix memory accesses.  First, look through your existing code for calls to mapping APIs like MapCallerPtr or MapPtrToProcess, and convert them to calls to marshalling APIs like CeOpenCallerBuffer / CeCloseCallerBuffer.  Second, look for calls to SetKMode and SetProcPermissions.  They most likely correspond to asynchronous memory access, for which you’ll now need CeAllocAsynchronousBuffer / CeFreeAsynchronousBuffer.

That will take care of most of the porting work.  The other thing to look for is UI functionality.  If your driver has any UI, you won’t be able to run it in the kernel.  And most CE6 drivers will run in the kernel.  Even if your driver will run in user mode, we recommend using the kernel UI handling to maximize portability between user and kernel mode.  In CE6, drivers that require UI should break that UI functionality out into a companion user-mode DLL.  Move all the resources, shell calls, etc. into the new DLL.  Then use the new CeCallUserProc API to call into the user-mode helper.

BOOL CeCallUserProc(
LPCWSTR pszDllName,
LPCWSTR pszFuncName,
LPVOID lpInBuffer, DWORD nInBufferSize,
LPVOID lpOutBuffer, DWORD nOutBufferSize,
LPDWORD lpBytesReturned);
 

This is something like a combination of LoadLibrary / GetProcAddress with an IOCTL call.  When a kernel-mode driver calls this API, we’ll load the DLL inside an instance of udevice.exe.  When a user-mode driver calls this API, the DLL will load in-proc inside the same instance of udevice.exe that the user-mode driver is running in.  So drivers that use this API can run in kernel or user mode without change.

The one big difference between CeCallUserProc and an IOCTL is that CeCallUserProc does NOT allow embedded pointers.  All arguments must be stored inside the single “in” buffer passed to CeCallUserProc, and return data must be stored in the single “out” buffer.  The problem is, if kernel code calls user code, user code cannot use CeOpenCallerBuffer or any other method to get the contents of kernel memory.  We never allow user-mode code to access kernel-mode memory.

And, while you are modifying your drivers to use the new marshalling helpers and CeCallUserProc, you might as well check to see if it needs to do any secure-copy or exception handling it never did before…  As I outlined in the marshalling post.  Remember, now that drivers run in the kernel, you must be more careful than ever to preserve the security and stability of the system.

User-Mode Drivers

As we’ve already mentioned, CE6 now supports running drivers inside a user-mode driver host, udevice.exe.  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?

As I explained in the marshalling post, 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.  This is exactly the same as saying that CeCallUserProc cannot support embedded pointers.  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, like CeCallUserProc, all the data is stored directly in the IN and OUT buffers instead of referenced via embedded pointers.

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

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 to the 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.

OEMs can choose to expose additional OAL IOCTLs and APIs to user mode by building a kernel-mode driver that provides these services – by essentially writing their own version of a reflector.  There is a kernel-mode driver, the oalioctl driver, that OEMs can extend to this end.  Anyone who’s not an OEM would have to write their own kernel-mode driver to do it.  But be warned!  Using oalioctl or writing new kernel-mode drivers to expose this functionality is essentially opening up a security gap that we (Microsoft) sought to close.  Personally I advise against it.

Writing CE5 drivers to be compatible with CE6

I would like to mention that Steve Maillet, one of our eMVPs, had a good suggestion: you can set up abstractions which combine the CE5 and CE6 driver needs, so that all you have to do is reimplement the abstraction layer in order to port from CE5 to CE6.  He even presented his abstraction layer at this year’s MEDC (Mobile & Embedded DevCon, 2006).  I don’t know if he’s interested in giving it out widely, but you could contact him at EmbeddedFusion (http://www.embeddedfusion.com/), or steal his idea and implement your own layer.

Juggs Ravalia did a Channel 9 interview on the topic of drivers in CE6 – if you don’t like my explanation, maybe you’ll like his better.  He knows much more about our user mode driver framework than I do. http://channel9.msdn.com/Showpost.aspx?postid=233119

Tuesday, February 12, 2008

Aricent Creates HD Video/Audio Encoders, Decoders for TI OMAP3430

Aricent, a full-service, full-spectrum communications software company, announced that it has developed high-definition video and audio encoders and decoders (codecs) for Texas Instruments' OMAP3430 processor-based mobile phones. To be demonstrated for the first time at Mobile World Congress, Barcelona, Aricent's high-definition software enables a new class of applications for the mobile consumer. The consumer will be able to use their mobile phone as a HD quality player and download high resolution HD content (720p) on their phone for playback on an HDTV screen or use the mobile phone as a DVD-quality camcorder recording in high resolution (720p). By decoding 720p MPEG-4 Simple Profile video in real-time, Aricent has become one of the first companies to offer DVD-quality record and playback capabilities for a range of mobile devices such as mobile handsets, portable media players and automotive entertainment systems.

Aricent's multimedia codecs were designed specifically for TI's OMAP3430 processor, the latest in a line of mobile platforms that combine PC- and consumer entertainment system-comparable performance with low power consumption. The Aricent MPEG4-SP 720p video and AAC audio codecs leverage the OMAP3430 processor capabilities (IVA(tm) 2.2 hardware accelerator and ARM® Cortex(TM)-A8 superscalar microprocessor core) to achieve encoding and decoding of video streams at unprecedented resolutions. Aricent's multimedia software on the OMAP3430 processor is designed for multiple mobile platforms like Linux, Symbian and Windows Mobile.

"The next generation of mobile phones will support multimedia features previously available only with high-end consumer electronics," said Markus Tremmel, worldwide wireless ecosystem manager Texas Instruments. "Using the processing power of the OMAP3430 processor, for the first time companies like Aricent are delivering better-than DVD-quality record and playback capabilities for mobile handsets."

"The Aricent high-definition codecs for the OMAP3430 processor will drive a completely new class of mobile multimedia experience," said Sajal Gupta, head of products, Aricent. "This software will allow consumers to record and play high-definition 720p video from their mobile phone, automotive entertainment system or personal media player."

The Aricent high-definition multimedia software will be on public demonstration at the Texas Instruments stand (Hall 8, A84) at Mobile World Congress from 13-14 February 2008, and by appointment at the Aricent stand (Hall 2, B25) for the remainder of the show.

Aricent is a member of TI's OMAP(tm) Developer Network, a group of leading software developers porting advanced applications to TI's high performance, power-efficient OMAP processors. Handset manufacturers adopting OMAP devices enjoy the rapid deployment of compelling wireless applications – including streaming audio and video, multimedia messaging, gaming, security, speech recognition, location based services and mobile commerce – across all leading operating systems. Systems level integration services are also provided worldwide by independent OMAP Technology Centers. The OMAP platform has been selected by leading manufacturers, such as Nokia, Palm, NEC, Fujitsu, LG Electronics, Hewlett-Packard, HTC and many more, for their 2.5 and 3G wireless devices.

About Aricent
With corporate offices in Palo Alto, California, Aricent is a full-service, full-spectrum communications software company. Aricent offers software services and products that enable the world's leading communications equipment manufacturers, device manufacturers and service providers to improve time-to-revenue and maximize efficiency. Owned by Kohlberg Kravis Roberts & Co. (KKR), Sequoia Capital and Flextronics International Ltd., Aricent has more than 350 clients worldwide.

Aricent is a trademark of Aricent, Inc. in the United States and/or other jurisdictions. OMAP is a trademark of Texas Instruments.