Porting Steps for Graphics Drivers

This section provides step-by-step porting details that pertain to both display and print drivers.

Most functions that were originally exported by winsrv.dll are now exported by win32k.sys, so a simple relink of the driver resolves references to these functions. However, all other functions imported by your driver (from kernel32.dll, advapi32.dll, or any other DLL) must be replaced or eliminated because they are no longer available. If your driver directly references functions from any image other than win32k.sys, the system will not load your image. Therefore, the basic requirement for enabling a graphics driver to run under Windows NT 4.0 is to remove any calls to Win32 functions, and replace them with the calls exported by win32k.sys.

An easy way to determine exactly which functions your driver calls is to dump the list of imports, or external references, in your driver. One way to obtain this list is to run the following command:

link -dump -imports your_driver.dll

 

Here are some common changes to make to your graphics drivers to port them from Windows NT 3.51 to 4.0.

Step 1. Clean out the files used to compile your driver.

·Delete the .def file that described the exports from your graphics driver in 3.51; it is no longer necessary.

·Modify the sources files, which contain the list of files compiled to generate your driver. Update the file list from your 3.51 sources file, if necessary, and then use this list to define SOURCES in one of the sources files shipped with the sample Windows NT 4.0 drivers. This ensures that you pick up the new TARGETTYPE and other fields, enables warnings as errors, and so forth.

·Change your main header file so that it does not include windows.h. This will help you track down the now obsolete Win32 function calls in your 3.51 source code. Your main header file should look something like the following:

#include <stddef.h>

#include <stdarg.h>

#include <limits.h>

#include <windef.h>

#include <wingdi.h>

#include <winddi.h>

//These include files are for display drivers only.

#include <devioctl.h>

#include <ntddvdeo.h>

#include <ioaccess.h>

//These include files are for print drivers only.

#include <gdispool.h>

//Private defines

#include <my_headers>

 

Note Due to problems with the spooler API header file, you might encounter compiler warnings in the pre-beta Windows NT 4.0 release. These problems will be fixed by the official Windows 4.0 release.

Step 2. Replace Win32 calls with new kernel-mode counterparts.

·Critical sections have been replaced with semaphores:

CRITICAL_SECTION HSEMAPHORE
InitializeCriticalSection EngCreateSemaphore
EnterCriticalSection EngAcquireSemaphore
LeaveCriticalSection EngReleaseSemaphore
DeleteCriticalSection EngDeleteSemaphore

Note that where storage for a critical section was provided by the caller (the functions took a pointer to the critical section), the semaphore routines always use a handle (storage is allocated and destroyed within the semaphore routines).

·Calls to Win32 debugging functions must be replaced with their new GDI equivalents:

Win32 Debug Function GDI Debug Function
DebugBreak EngDebugBreak
OutputDebugString EngDebugPrint

The debug output routine can be copied from the sample drivers. This routine supports debug levels, and automatically appends the driver name to the debug message. The following is the code sample for the debug output routine:

VOID DebugPrint(LONG DebugPrintLevel, PCHAR DebugMessage, ...)

{

    #define STANDARD_DEBUG_PREFIX "MyDisplay:"

    va_list ap;

    

    va_start(ap, Debugmessage);

    if (DebugPrintLevel <= DebugLevel)

    {

        EngDebugPrint(STANDARD_DEBUG_PREFIX, DebugMessage, ap);

        // The following line appends the newline

        // automatically.

        // EngDebugPrint("", "\n", ap);

    }

    va_end(ap);

}

 

·Memory allocators must be replaced with the newly provided kernel-mode GDI equivalents. The following are mappings for the most common memory functions:

Win32 Memory Function KM GDI Memory Function
LocalAlloc, GlobalAlloc, VirtualAlloc EngAllocMem
VirtualFree, GlobalFree EngFreeMem
LocalLock, GlobalLock, LocalUnlock, GlobalUnlock none (not used)

There are some enhancements to EngAllocMem. It supports only the zero memory option (FL_ZERO_MEMORY) because the other options are not useful. Also, a third parameter, tag, was added to EngAllocMem. This tag is used by the system to provide information about resources. Both the kernel debugger !pool command and the user-mode poolmon utility can be used to track the amount of memory associated with each tag. The tag should be specified in reverse order, with single quotes. It is 4 bytes in size, and is stored in the pool header as 1 DWORD. The following is a sample call:

ppdef = (PPDEF) EngAllocMem(FL_ZERO_MEMORY,

              sizeof(PDEV),'agxD');

 

Note Memory is often referred to as pool in the kernel. The kernel memory functions are actually called ExAllocatePool and ExFreePool.

Note Heaps are not supported in kernel mode, so you cannot destroy the heap to clean up memory.

·There are no process/thread creation routines available to your driver. Your driver must be redesigned to work without them.

·There is no support for registry functions that pass data to kernel-mode drivers. Your driver must be redesigned to work without them.

Any remaining Win32 functions in the original Windows NT 3.51 driver must be removed. If your driver absolutely requires a function that is not currently exposed, then Microsoft should be made aware of this requirement so the function can be exposed or an appropriate alternative can be found.

Step 3. Clean out calls to C-runtime routines where necessary.

String routines, such as strcmp, are not exported from win32k.sys. Calls to these routines should be removed if possible. Limited C-runtime functionality is available in libcntpr.lib, which statically links to the driver.

Step 4. Replace floating point operations with FLOATOBJs.

Don’t use native floating point operations. Windows NT does not support kernel-mode floating point operations on some systems. Specifically, the Windows NT kernel does not preserve floating point registers on kernel-mode stack switches, so using floating point registers in the driver corrupts an application’s floating point registers.

GDI has introduced a new set of services that allows graphics drivers to emulate floating point arithmetic. See the reference pages for the FLOATOBJ_Xxx services for more information.

Step 5. Be frugal with the stack.

Each thread is allocated a limited amount of stack space for all function calls and interrupts. The Windows NT kernel allocates an initial 12K on x86 and MIPS platforms, and 16K on ALPHA and PowerPC platforms. The stack can grow to accommodate callbacks from the kernel to user mode and back; the maximum stack size is 60K/64K in the kernel. Only 12K/16K of the stack is visible at any point in time. The only exception to these policies is OpenGL, which is allocated the entire 60K/64K stack at once.

In Microsoft’s driver design, a guideline of 1K of stack space per function is used. This heuristic was arrived at by studying the stack needs of a typical graphics driver. The first few kilobytes on the stack contain the functions called to get from the application to the driver, such as the system service functions. The last 2-3 kilobytes are reserved for page faults and interrupts. The fact that some driver calls spawn several nested function calls is also accounted for. For example, the S3’s implementation of DrvStretchBlt calls EngStretchBlt, which calls DrvBitBlt, which calls another internal S3 fill function. Using the 1K per function guideline means that only up to 4K of the stack is used for these four functions.

You should also look at the size of your functions’ parameters. Only small structures and very small arrays of data should be stored on the stack. Arrays of 1024 or 512 elements and arrays the size of the screen’s width should not be passed. Consider the following: if each array element is a DWORD, 1024 elements uses 4K of stack space.

Memory beyond the stack pointer should not be touched in assembly routines because interrupts can occur at any time.

A routine is guaranteed to be using too much stack space if you get an undefined external for something such as _chkstk.

Step 6. Update your driver with other Windows NT 4.0 driver changes.

Because all Windows NT 3.51 drivers are incompatible in Windows NT 4.0, a few things have been cleaned up in the drivers:

·In DrvEnablePDEV, the second PWSTR parameter was converted to an HDEV.

·The DrvAssertMode function now returns a BOOL. The status of the operation should be reported.

Step 7. Examine the “Special Features” section of this appendix.

The section entitled Special Features of a User-Mode Process, described earlier in this document, should be examined. Drivers depending on any of these features must be changed to avoid these problems.

Step 8. Read the porting section of this appendix specific to your graphics driver type.

The sections that follow this one are Porting Steps for Display Drivers and Porting Steps for Printer Drivers. Read the section that pertains to your driver type for additional type-specific porting hints.

Step 9. Install your new driver.

Install your new driver, and test. Use the kernel-mode kd debugger or windbg to debug your driver. NTSD can no longer be used for debugging graphics drivers.