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
| Getting Started with Drivers for 64-bit Windows | |
| 64-Bit Windows Programming Model | |
| Coding Guidelines and Porting Issues | |
| Driver Installation for 64-bit Windows | |
| Resources for Developing 64-bit Drivers |
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:
|
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. |
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 name | What 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, |
UINT_PTR, DWORD_PTR | Unsigned Int, |
SIZE_T | Unsigned count, Pointer precision |
SSIZE_T | Signed count, |
Memory Layout
| Memory | x64-based | Intel 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; | 40% of physical memory on Windows Vista and earlier up to a maximum of 128 GB; |
Physical memory addresses | 64 bits | 64 bits |
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: |
| • | For Itanium-specific code, use: |
Porting Issues for Drivers
| • | Drivers should support 32-bit and 64-bit versions of IOCTL commands. | ||||||||
| • | DMA support should be implemented for hardware that can address only 32 bits of physical address. | ||||||||
| • | Pointers, polymorphic usage, and alignment issues apply to all drivers. | ||||||||
| • | All drivers should correctly support Plug and Play and power management by using WDF if possible.
| ||||||||
| • | For 64-bit miniports:
|
Build Environment and Tools
| • | The build environment for x64 is AMD64. | ||||||||
| • | 64-bit tools and development process are similar to 32 bit:
|
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:
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:
| ||||
| • | 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:
| ||||||||
| • | 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:
|
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. |
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.
| • | |
| • | |
| • | |
| • | Beyond Windows XP: Get Ready Now for the Upcoming 64-Bit Version of Windows |
| • | |
| • | |
| • | |
| • | |
| • | |
| • |