Checklist for 64-bit Microsoft Windows Drivers

Updated: July 15, 2008

This checklist provides a brief list of the coding and installation concerns for developers who are creating drivers that will run on 64-bit editions of Windows operating systems.

This information applies for the following operating systems:
Microsoft Windows Vista
Microsoft Windows Server 2008
64-bit editions of Microsoft Windows Server 2003
Microsoft Windows XP

*
On This Page
Getting Started with Drivers for 64-bit WindowsGetting Started with Drivers for 64-bit Windows
64-Bit Windows Programming Model64-Bit Windows Programming Model
Coding Guidelines and Porting IssuesCoding Guidelines and Porting Issues
Driver Installation for 64-bit WindowsDriver Installation for 64-bit Windows
Resources for Developing 64-bit DriversResources for Developing 64-bit Drivers

Getting Started with Drivers for 64-bit Windows

Adoption of 64-bit systems is expected to advance rapidly, especially in the enterprise space. This adoption will increase even more quickly as extensive device coverage and high-quality drivers for 64-bit systems become available.

To get started supporting 64-bit systems with your devices:

Make sure that your devices address 64 bits of physical memory.

Code Windows® drivers by using 64-bit safe programming practices, and use the 64-bit compiler to find problem areas:

Remember that Windows Driver Foundation (WDF), Windows Driver Model (WDM) and tools remain the same for 32-bit and 64-bit Windows.

Follow coding guidelines for driver-specific issues that are related to I/O controls (IOCTLs), direct memory access (DMA), and legacy device driver interfaces (DDIs).

Ensure that drivers support Plug and Play and power management.

Note that there is no support for 32-bit drivers:

Kernel-mode drivers run in the same address space as 64-bit Windows.

Drivers that scale to large systems can use larger address spaces for paged/nonpaged pool and system cache.

Top of pageTop of page

64-Bit Windows Programming Model

Windows 64-Bit Edition is enabled by an evolution of the Windows programming model and the Microsoft Win32® API. Windows provides a single-source code base for 32-bit and 64-bit platforms that:

Enables existing applications to scale to enterprise capacities.

Enables new designs that use huge address spaces and memory.

Supports existing 32-bit applications.

In addition:

The 64-bit Windows programming model uses the same Win32 DDIs and APIs.

In the LLP64 (LongLong and pointer) data model, only pointers expand to 64 bits. All other basic data types (integer and long) remain 32 bits in length. Although 64-bit pointers are required to accommodate systems with as much as 16 TB of virtual memory, most data still fits into 32-bit integers. For most applications, changing the default integer size to 64 bits only wastes space.

The 64-bit Windows programming model adds new, explicitly sized types, plus new integral types that match the precision of a pointer. For a list of types, see the "Data Types" table.

Pointers are 64 bits in length, but integer and long data types remain 32 bits.

Most memory allocations are 64 bits.

The CPU mask expands to 64 bits.

The length for I/O requests and Unicode strings remains 32 bits.

Data Types

Type nameWhat it is

Fixed-width data types

 

LONG32, INT32

32-bit signed

LONG64, INT64

64-bit signed

ULONG32,UINT32,DWORD32

32-bit unsigned

ULONG64,UINT64,DWORD64

64-bit unsigned

Pointer-precision data types

 

INT_PTR, LONG_PTR

Signed Int,
Pointer precision

UINT_PTR, DWORD_PTR

Unsigned Int,
Pointer precision

SIZE_T

Unsigned count, Pointer precision

SSIZE_T

Signed count,
Pointer precision

Memory Layout

Memoryx64-basedIntel Itanium-based

User address range

0x10000 – 0x000007FFFFFEFFFF

0x10000 – 0x000006FBFFFEFFFF

System cache

1 TB

1 TB

HyperSpace

512 GB

16 GB

System address space (for kernel threads and so on)

128 GB

128 GB

Page size

4K

8K

Paged pool

128 GB

128 GB

Nonpaged pool

40% of physical memory on Windows Vista and earlier up to a maximum of 128 GB;
75% of physical memory on Windows Server 2008, Windows Vista SP1, and later up to a maximum of 128 GB

40% of physical memory on Windows Vista and earlier up to a maximum of 128 GB;
75% of physical memory on Windows Server 2008, Windows Vista SP1, and later up to a maximum of 128 GB

Physical memory addresses

64 bits

64 bits

Top of pageTop of page

Coding Guidelines and Porting Issues

Coding Guidelines

Use Windows 64-bit and 32-bit safe data types.

Examine all pointer usage, especially pointer arithmetic.

Remove inline assembly code (use intrinsics or native assembly code).

For x64-specific code, use:
#if defined (__AMD64__)
No __X64__ definition is available.

For Itanium-specific code, use:
#if defined(__IA64__)

Porting Issues for Drivers

Drivers should support 32-bit and 64-bit versions of IOCTL commands.
See the "IOCTL Support" section.

DMA support should be implemented for hardware that can address only 32 bits of physical address.
See the "DMA Support" section.

Pointers, polymorphic usage, and alignment issues apply to all drivers.
See the "Pointers, Polymorphic Usage, and Alignment Issues" section.

All drivers should correctly support Plug and Play and power management by using WDF if possible.

In a WDM driver, move from a driver-centric to a device-centric model; that is, handle Plug and Play and power management I/O request packets (IRPs), or follow miniport guidelines.

User-mode drivers must be 64 bit for print, scan, and camera.

Legacy APIs (that are specific to Windows NT® 4.0) are not allowed.

For 64-bit miniports:

Follow the miniport-specific guidelines in the Windows Driver Kit (WDK).

Remember the difference in PAGE_SIZE (8 K on Itanium-based systems and 4 K on x64 systems).

Pointers, polymorphic usage, and alignment issues apply.

IOCTL, Plug and Play, and DMA issues do not apply for most miniports.

Build Environment and Tools

The build environment for x64 is AMD64.

64-bit tools and development process are similar to 32 bit:

Debugging Tools for Windows

The 64-bit versions of Debugging Tools for Windows let you debug both 32-bit and 64-bit user-mode applications that run on 64-bit processors. Use this package to debug both the application and the Windows-32-bit-on-Windows-64-bit (WOW64) emulator.

To debug an application on an Intel Itanium or x64 processor, use the package that is designed for the specific processor, which you can download at http://www.microsoft.com/whdc/devtools/debugging/install64bit.mspx.

Cross-compilers in the WDK

The 64-bit build environment in the WDK includes a 64-bit compiler that you can use to build and test 64-bit driver code that runs on x64-based processors. See issues and limitations in the WDK at http://msdn.microsoft.com/en-us/library/aa489554.aspx.

Checked builds

Driver developers use the checked build of Windows to identify and diagnose operating-system-level problems. Most checks in the checked build determine whether values in parameters or data structures are within expected typical ranges. For details, go to http://www.microsoft.com/whdc/DevTools/tools/chkblds.mspx.

Driver Verifier

This tool tests and traps many conditions that might otherwise go unnoticed in typical operation. Driver Verifier verifies that drivers are not making illegal function calls or causing system corruption. It can identify conditions such as memory corruption, mishandled IRPs, invalid DMA buffer usage, and possible deadlocks. For details, go to http://msdn.microsoft.com/en-us/library/ms792872.aspx.

BIOS, ACPI, and Patching Notes

Itanium-based systems must support ACPI 2.0 64-bit tables.

GUID partition table (GPT) disks are supported by 64-bit Windows.

BIOS callbacks are not allowed on x64 systems.

For compatibility with Windows for x64-based systems, drivers must avoid the following practices:

Modifying system service tables, for example, by hooking KeServiceDescriptorTable.

Modifying the interrupt dispatch table (IDT).

Modifying the global descriptor table (GDT).

Using kernel stacks that are not allocated by the kernel.

Patching any part of the kernel (detected only on AMD64-based systems).

Windows enforces these rules on x64 platforms. Any such modifications result in a bugcheck.

IOCTL Support

IOCTL structures that contain pointer-dependent types have different sizes in 64-bit and 32-bit applications. The driver must differentiate between IOCTLs that are issued from 32-bit threads and those that are issued from 64-bit threads. The driver validates the input and output buffer lengths based on the issuer's bit width.

Drivers can use any of three possible solutions to support both 32-bit and 64-bit callers:

Avoid using pointer types in IOCTL structures.

Use the IoIs32bitProcess() API.

Define a bit in the Function code field of the IOCTL code for 64-bit callers.

To use IoIs32BitProcess(), define a 32-bit IOCTL structure in your code, and then determine whether the issuing process is 32 bits or 64 bits.

IOCTL structure in header file:

typedef struct _IOCTL_PARAMETERS {
    PVOID   Addr;
    SIZE_T  Length;
    HANDLE  Handle;
} IOCTL_PARAMETERS, *PIOCTL_PARAMETERS;

			


32-bit IOCTL structure:

//
// This structure is defined 
// inside the driver source code
//
typedef struct _IOCTL_PARAMETERS_32 {
    VOID*POINTER_32  Addr;
    INT32            Length;
    VOID*POINTER_32  Handle;
} IOCTL_PARAMETERS_32, *PIOCTL_PARAMETERS_32;

			


32-bit and 64-bit IOCTL example that uses IoIs32BitProcess:

#ifdef _WIN64
case IOCTL_REGISTER:
if (IoIs32bitProcess(Irp)) {
  /* If this is a 32 bit process */             
  params32 =
 
   (PIOCTL_PARAMETERS_32)(Irp>AssociatedIrp.SystemBuffer);
  if(irpSp->Parameters.DeviceIoControl.InputBufferLength <

                    sizeof(IOCTL_PARAMETERS_32)) {
         status = STATUS_INVALID_PARAMETER;
       } else {
       LocalParam.Addr = params32->Addr;
        LocalParam.Handle = params32->Handle; 
           LocalParam.Length = params32->Length;
     /* Handle the ioctl here */
        status = STATUS_SUCCESS;
        Irp->IoStatus.Information = sizeof(IOCTL_PARAMETERS);
    }
} else { /* 64bit process IOCTL */
  params = (PIOCTL_PARAMETERS)
           (Irp->AssociatedIrp.SystemBuffer);
  if (irpSp->Parameters.DeviceIoControl.InputBufferLength
                            < sizeof(IOCTL_PARAMETERS)) {
         status = STATUS_INVALID_PARAMETER;
                 
  } else {                    
        RtlCopyMemory(&LocalParam, params,

sizeof(IOCTL_PARAMETERS));
     /*  Handle the ioctl here */
     status = STATUS_SUCCESS;
    }
    Irp->IoStatus.Information = sizeof(IOCTL_PARAMETERS);
}
break;

If you choose to define a bit for 64-bit callers, use the high-order bit in the Function field.

The current IOCTL codes have five fireld, including an 11-bit Function field:

Device Type (16)

Access (2)

Custom (1)

Function (11)               

Method (2)

The new IOCTL code uses the high-order bit in the Function field to indicate a 64-bit caller:

Device Type (16)

Access (2)

Custom (1)

64Bit (1)

Function (10)

Method (2)

DMA Support

Use the PHYSICAL_ADDRESS typedef to access physical addresses. PHYSICAL_ADDRESS is 64 bits long.

Treat all 64 bits as a valid physical address. Do not ignore the high-end 32 bits.

Use the kernel-mode driver framework (KMDF) or the Windows DMA DDIs to ensure correct operation on all platforms. Windows handles the size issues when you use the KMDF or Windows DMA routines.

Use scatter/gather DMA when appropriate.

Significantly improve performance by using devices with 64-bit addressing capability.

Pointers, Polymorphic Usage, and Alignment Issues

Pointer size is common across applications and drivers. The guidelines for porting applications also apply to drivers in the following situations:

Polymorphic data usage (related to pointer size).

Alignment issues.

Constant and macro arithmetic.

The first two items are discussed in this section. The third item is discussed in the MSDN® article "Beyond Windows XP: Get Ready Now for the Upcoming 64-Bit Version of Windows."

Polymorphic Data Usage

Do not cast a pointer to int, long, ULONG, or DWORD.

Use UINT_PTR, INT_PTR, ULONG_PTR, and so forth.

For example:

ImageBase = (PVOID)
            ((ULONG)ImageBase | 1);

Should be rewritten as:

ImageBase = (PVOID)
          ((ULONG_PTR)ImageBase | 1);

Use PtrToUlong() and PtrToLong() to truncate pointers:

Never cast an int or a ULONG that stores a truncated pointer back into a pointer.

Be careful when computing buffer sizes; sizes may exceed the capacity of ULONG.

Be careful to use the correct size when you call functions that have pointer OUT parameters:

void GetBufferAddress(OUT PULONG *ptr);
{
	*ptr=0x1000100010001000;
}
void foo()
{
  ULONG bufAddress;
  //
  // this call causes memory corruption
  //
  GetBufferAddress((PULONG*)&bufAddress);
}


Alignment Issues

Itanium-based systems require natural alignment for memory references (that is, 32-bit accesses at a 4-byte boundary). Misaligned memory references raise an exception on an Itanium-based system and bug check the system.

Alignment is required for x64 support to obtain better performance, although x64 is more forgiving.

Be aware of alignment issues when you use structure-packing directives.

Be especially careful if you use different PACK levels in a single header file.

For best results, put 64-bit values and pointers at the start of structures.

RtlCopyMemory() and memcpy() do not fault.

Use the UNALIGNED macro to fix alignment problems:

#pragma pack (1) /* also set by /Zp switch */
struct AlignSample {
	ULONG size;
	void *ptr;  
};
struct AlignSample s;
void foo(void *p) {
		*p = p;	// causes alignment fault
	...
}
foo((PVOID)&s.ptr);
void foo(void *p) {
	struct AlignSample s; 
    *(UNALIGNED void *)&s.ptr = p; 
}

For more information about alignment, see "Memory Management: What Every Driver Writer Needs to Know."

More Coding Issues

Carefully examine:

Explicit and implicit unions with pointers.

Data structures that are stored on disk or exchanged with 32-bit processes.

Security descriptors.

Code that deals with memory region sizes:

len = ptr2 - ptr1

len could be greater than 2^32

Truncate executive handles to 32 bits.

Use piecemeal-size allocations:

struct foo {
	DWORD NumberOfPointers;
	PVOID Pointers[1];
} xx;
Wrong:
malloc(sizeof(DWORD)+100*sizeof(PVOID)); 
Correct:
malloc(FIELD_OFFSET(struct foo,Pointers)
    +100*sizeof(PVOID));

Use care with hexadecimal constants and unsigned values.

Be careful when you use unsigned numbers as subscripts:

DWORD index = 0;

CHAR *p;

The expression if (p[index - 1] == '0') causes an access violation on Itanium-based systems:

On 32-bit machines:

On 64-bit machines:

p[index-1] == p[0xffffffff] == p[-1]

p[index-1] == p[0x00000000ffffffff] != p[-1]  

Miscellaneous Cleanup

-1 != 0xFFFFFFFF

0xFFFFFFFF != invalid handle

DWORD is always 32 bits. Do not use DWORD variables to store pointers or handles.

Cast pointers to PCHAR for pointer arithmetic:

ptr = (PVOID)((PCHAR)ptr + pageSize);

Use %I to print pointers in debug statements.

Addresses >= 0x80000000 are not necessarily kernel addresses.

Top of pageTop of page

Driver Installation for 64-bit Windows

64-Bit Driver INF Requirements

Windows Server 2003 SP1 and later versions of Windows do not install driver packages with undecorated INF sections on x64-based systems.

For compatibility with Intel Itanium systems, Windows Server 2003 SP1 will install driver packages with undecorated INF sections. However, INF decorations are required by the Windows-Logo Program for Hardware, so a driver package with undecorated INF sections cannot qualify for the logo.

[Manufacturer] section entries and [Models] section names of INFs must be decorated for driver packages to install on x64-based systems, and they should be decorated for Intel Itanium-based systems. These decorations are supported on all versions of Windows later than the original release of Windows XP; therefore, an INF decorated in this way works on all released NT-based Windows operating systems for the respective non-x86-based platforms.

The goal is to prevent the installation of incorrect driver binaries because customers do not understand the difference in platforms.

Use TargetOsVersion, which was introduced in Windows XP.

On Windows Server 2003 SP1 and later, the [Manufacturer] and [Models] INF sections must be decorated for 64-bit platforms. For example:

[Manufacturer] %mycompany% = MyCompanyModels, NTamd64
[MyCompanyModels.NTamd64] %MyDev% = mydevInstall, mydevHwid


Driver Installation for 32-bit and 64-bit Platforms

To simplify installation of 64-bit drivers from a 32-bit installer, Microsoft provides 64-bit versions of the Driver Install Frameworks (DIFx) tools in the WDK. The DIFx tools include:

The Driver Package Installer (DPInst).

The Driver Installation Frameworks for Applications (DIFxApp).

Instead of writing a separate 64-bit installer for 64-bit platforms, you can start the correct version of DPInst or DIFxApp from a single 32-bit installer.

You cannot use 32-bit driver installation packages to directly install 64-bit drivers, but you can use them to determine which architecture is being run and then start the appropriate installer. On 64-bit platforms, the 32-bit installer runs under WOW64, the x64 emulator that lets Win32-based applications run on 64-bit Windows. The installer must be able to detect the platform on which it is running so that it can install the correct driver package for that platform.

Top of pageTop of page

Resources for Developing 64-bit Drivers

"64-bit Issues" in the WDK

IA-64 Systems and ACPI 2.0 64-bit Tables

Advances in Memory Management for Windows

Beyond Windows XP: Get Ready Now for the Upcoming 64-Bit Version of Windows

DMA Support in KMDF Drivers

Hardware-related information on the WHDC Web site

INF Requirements for 64-bit Systems

Patching Policy for x64-Based Systems

Programming Guide for 64-bit Windows

Memory Management: What Every Driver Writer Needs to Know


Top of pageTop of page