370 likes | 572 Views
Drivers For 64-Bit Windows ® Nar Ganapathy Software Design Engineer Windows NT ® Base Systems Microsoft Corporation. Agenda . What is 64-bit Windows Programming model Porting guidelines Programming issues. 64-Bit Windows 2000 Goals. Porting from Win32 ® to Win64 should be simple
E N D
Drivers For 64-Bit Windows®Nar GanapathySoftware Design EngineerWindows NT® Base SystemsMicrosoft Corporation
Agenda • What is 64-bit Windows • Programming model • Porting guidelines • Programming issues
64-Bit Windows 2000 Goals • Porting from Win32® to Win64 should be simple • A single source code base for Win32 and Win64 • Enable existing applications to scale to enterprise capacities • Enable new designs that use huge address space and memory • Support existing 32-bit applications • 32-bit device drivers not supported
What Is 64-Bit Version Of Windows 2000? • 64-Bit Windows is an evolution of the Windows programming model and APIs • LLP64 model • Pointers are expanded to 64 bits • Memory allocation sizes are 64 bits • Longs and ints are 32 bits
Win64 Programming Model • Uses the same Win32 DDIs/APIs • Adds new explicitly sized types • Adds new integral types that match the precision of a pointer • Almost all Win32 32-bit data types remain 32-bits • E.g., size of ULONG is still 32 bits under Win64 • Pointers are 64-bits
Win64 Programming Model • Items expanded to 64 bits • Length for most memory allocations • CPU mask • Unchanged • Length for I/O operations • Unicode string lengths
Win64 Porting Guidelines • Use Win64 safe data types • Examine all pointer usage, especially pointer arithmetic • Two major porting topics • Driver specific issues (e.g., IOCTLs, DMA) • Programming issues
Memory Management (IA64) • User addresses range from 0x10000 – 0x000006FBFFFEFFFF • One terabyte system cache • Sixteen gigabyte HyperSpace • 128 gigabyte paged pool • 128 gigabyte System address space for kernel threads, etc. • 128 gigabyte non-paged pool • Physical memory addresses are 64 bits
WOW64 • Runs Windows NT x86 binaries • No support for mixing of x86 and IA-64 code within a process • User/kernel transitions are thunked to account for structure differences and transition between instruction sets • Only a few dlls are thunked: ntdll.dll user.dll, gdi.dll plus a set of LPC calls • The rest of the dlls are stock x86 binaries copied from the release shares
Driver Specific Issues • Drivers need to support Plug and Play and Power Management • I/O request length is limited to 32 bits • 32-bit devices will be double buffered for DMA transfers • Some drivers will need to support 32- and 64-bit versions of IOCTL command
Support 32-Bit IOCTLS • IOCTL structures that contain pointer dependent types have different sizes in 64-bit and 32-bit applications • Driver has to distinguish between Ioctls issued from 32-bit or 64-bit threads • Driver validates the input or output buffer lengths based on the issuer’s bit width
Supporting 32-Bit IOCTL • Three possible solutions • Avoid having pointer types in IOCTL structures • Use the API IoIs32bitProcess() • Use a new bit in the Function code field of the IOCTL code for 64-bit callers
32 And 64-Bit IOCTL Example • IOCTL structure in header file typedef struct _IOCTL_PARAMETERS { PVOID Addr; SIZE_T Length; HANDLE Handle; } IOCTL_PARAMETERS, *PIOCTL_PARAMETERS;
32 And 64-Bit IOCTL Example • 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 And 64-Bit IOCTL Example #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(PIOCTL_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); }
32 And 64-Bit IOCTL Example } 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;
Use New IOCTL Code • Use bit in function field to indicate 64-bit IOCTL • Current IOCTL codes have four fields • New IOCTL code has one extra bit for 64 bit
DMA APIs • Drivers should use PHYSICAL_ADDRESS typedef to access physical addresses (64 bits long) • Driver must treat all 64 bits as a Valid Physical Address, do not ignore top 32 bits • Drivers must use the Windows DMA APIs • IoAllocateAdapter, IoAllocateAdapterChannel,IoMapTransfer,etc. • Use new scatter/gather DMA APIs • Devices with 64-bit addressing capability will significantly improve performance
Programming Issues • Polymorphic Data usage • Miscellaneous cleanup • Alignment issues
Polymorphic Data Usage • Don’t cast pointer to int, long, ULONG, DWORD • Use UINT_PTR and INT_PTR ImageBase = (PVOID) ((ULONG)ImageBase | 1); Should be re-written as ImageBase = (PVOID) ((ULONG_PTR)ImageBase | 1);
…Polymorphic Data Usage… • Use PtrToUlong and PtrToLong to truncate pointers • Never cast a int or ULONG that stores a truncated pointer back to a pointer • Be careful when computing buffer sizes; Sizes may exceed capacity of ULONG
…Polymorhpic Data Usage • Be careful when calling 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); }
Miscellaneous Cleanup • -1 != 0xFFFFFFFF • 0xFFFFFFFF != invalid handle • DWORD is always 32 bits • Look for DWORD variables used to store pointers or handles • Cast pointers to PCHAR for Pointer arithmetic • ptr = (PVOID) ((PCHAR) ptr + pageSize);
Miscellaneous Cleanup • Use %I to print pointers in debug statements • Addresses >= 0x80000000 are not necessarily kernel addresses
Alignment • Misaligned memory references will raise an exception on Win64 #pragma pack (1) /* also set by /Zp switch */ struct AlignSample { ULONG size; void *ptr; }; void foo(void *p) { struct AlignSample s; s.ptr = p; // will cause alignment fault ... }
Alignment • Use UNALIGNED macro to fix • Better solution is to put 64-bit values and pointers at the beginning of the structure void foo(void *p) { struct AlignSample s; *(UNALIGNED void *)&s.ptr = p; }
Alignment • Be cautious of any structure packing directives • Be especially cautious of different PACK levels used in the same header • RtlCopyMemory and memcpy will not fault
More Programming Issues • Carefully examine • Explicit and implicit unions with pointers • Data structures stored on disk or exchanged with 32-bit processes • Security descriptors • Code which deals with memory region sizes • len = ptr2 – ptr1 • len could be greater than 2^32
Miscellaneous Programming Issues • Executive handles can be truncated to 32 bit • 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));
MiscellaneousProgramming Issues • Be careful when using hexadecimal constants and unsigned values • Following assertion not true on 64 bit systems ~((UINT64)(PAGE_SIZE-1)) == (UINT64)~(PAGE_SIZE-1) PAGE_SIZE = 0x1000UL // Unsigned Long - 32 bits PAGE_SIZE - 1 = 0x00000fff • LHS expression: // Unsigned expansion (UINT64)(PAGE_SIZE -1 ) = 0x0000000000000fff ~((UINT64)(PAGE_SIZE -1 ))= 0xfffffffffffff000 • RHS expression: ~(PAGE_SIZE-1) = 0xfffff000(UINT64)(~(PAGE_SIZE - 1)) = 0x00000000fffff000 • Hence ~((UINT64)(PAGE_SIZE-1))!= (UINT64)(~(PAGE_SIZE-1))
Miscellaneous Programming Issues • Be careful when using unsigned numbers as subscripts: DWORD index = 0; CHAR *p; If (p[index – 1] == ‘0’) causes access violation on Win64! • On 32-bit machines: p[index-1] == p[0xffffffff] == p[-1] • On 64-bit machines: p[index-1] == p[0x00000000ffffffff] != p[-1]
Call To Action • Develop Windows 2000 ready drivers by supporting Plug and Play and Power Management • Start coding using Win64 safe programming practices now! • Use the 64-bit compiler to find problem areas • Make your devices address 64 bits of physical memory
Additional Information • Read “Getting Ready for 64-bit Windows” porting guide on Windows 2000 SDK • Other information available at http://www.microsoft.com/windows2000/guide/platform/strategic/64bit.asp