阅读:5225回复:2
64位的驱动程序开发技术
64 位 Microsoft Windows 驱动程序清单
Updated: July 15, 2008这个清单提供编码和安装注意事项的简要列表,主要针对正在创建将在 64 位版本 Windows 操作系统上运行的驱动程序开发人员。 此信息适用于以下操作系统: Microsoft Windows Vista Microsoft Windows Server 2008 64 位版本的 Microsoft Windows Server 2003 Microsoft Windows XP On This Page [table][tr][td][/td][td]64 位 Windows 驱动程序入门[/td][/tr][tr][td][/td][td]64 位 Windows 编程模型[/td][/tr][tr][td][/td][td]编码指南和移植问题[/td][/tr][tr][td][/td][td]64 位 Windows 的驱动程序安装[/td][/tr][tr][td][/td][td]开发 64 位驱动程序的参考资料[/td][/tr][/table]64 位 Windows 驱动程序入门 预期会有越来越多的领域采用 64 位系统,特别是在企业应用中。随着广泛的设备覆盖和针对 64 位系统的高质量驱动程序变得可用,这种应用将增长得更快。 设备支持 64 位系统的条件是: [table][tr][td]•[/td][td]确保设备寻址达到 64 位物理内存。 [/td][/tr][tr][td]•[/td][td]使用 64 位安全编程实践编写 Windows? 驱动程序,并使用 64 位编译器找出问题所属的领域: [table][tr][td]•[/td][td]记住, Windows Driver Foundation (WDF),Windows Driver Model (WDM) 和工具对于 32 位和 64 位 Windows 都一样。 [/td][/tr][tr][td]•[/td][td]遵循编码指南,解决与 I/O 控制(IOCTL)、直接内存访问(DMA)和传统设备驱动程序接口(DDI)相关的驱动程序特定的问题。 [/td][/tr][tr][td]•[/td][td]确保驱动程序支持即插即用和电源管理。 [/td][/tr][/table][/td][/tr][/table]注意,不支持 32 位驱动程序: [table][tr][td]•[/td][td]内核模式驱动程序运行在与 64 位 Windows 相同的地址空间中。 [/td][/tr][tr][td]•[/td][td]适应大型系统的驱动程序可以为分页/未分页池和系统缓存使用较大的地址空间。 [/td][/tr][/table]Top of page64 位 Windows 编程模型 Windows 64 位版本由 Windows 编程模型和 Microsoft Win32? API 发展而来。Windows 32 位和 64 位平台基于相同的源代码: [table][tr][td]•[/td][td]现有的应用程序可以扩展以满足企业需求。 [/td][/tr][tr][td]•[/td][td]新的设计可以使用巨大的地址空间和内存。 [/td][/tr][tr][td]•[/td][td]支持现有的 32 位应用程序。 [/td][/tr][/table]此外: [table][tr][td]•[/td][td]64 位 Windows 编程模型使用相同的 Win32 DDI 和 API。 [/td][/tr][tr][td]•[/td][td]在 LLP64(LongLong 和指针)数据模型中,只有指针扩展到 64 位。所有其他的基本数据类型(整型和长整型)的长度保持在 32 位。虽然 64 位指针需要适应具有高达 16 TB 虚拟内存的系统,但是大部分数据仍然在 32 位整数范围内。对于大部分应用程序,将默认整型大小改成 64 位只会浪费空间。 [/td][/tr][tr][td]•[/td][td]64 位 Windows 编程模型添加大小明确的新数据类型,以及匹配指针精度的新整数类型。类型列表请参见“数据类型”表。 [/td][/tr][tr][td]•[/td][td]指针长度为 64 位,但是整型和长整型数据类型仍然是 32 位。 [/td][/tr][tr][td]•[/td][td]大部分内存分配都是 64 位。 [/td][/tr][tr][td]•[/td][td]CPU 掩码扩展到 64 位。 [/td][/tr][tr][td]•[/td][td]I/O 请求和 Unicode 字符串的长度仍然是 32 位。 [/td][/tr][/table]数据类型 [table][tr][td]类型名称[/td][td]类型含义[/td][/tr][tr][td]固定宽度数据类型 [/td][td] [/td][/tr][tr][td]LONG32、INT32 [/td][td]32 位有符号 [/td][/tr][tr][td]LONG64、INT64 [/td][td]64 位有符号 [/td][/tr][tr][td]ULONG32、UINT32、DWORD32 [/td][td]32 位无符号 [/td][/tr][tr][td]ULONG64、UINT64、DWORD64 [/td][td]64 位无符号 [/td][/tr][tr][td]指针精度数据类型 [/td][td] [/td][/tr][tr][td]INT_PTR、LONG_PTR [/td][td]有符号整型、 指针精度 [/td][/tr][tr][td]UINT_PTR、 DWORD_PTR [/td][td]无符号整型、 指针精度 [/td][/tr][tr][td]SIZE_T [/td][td]无符号计数、指针精度 [/td][/tr][tr][td]SSIZE_T [/td][td]有符号计数、 指针精度 [/td][/tr][/table]内存布局 [table][tr][td]内存[/td][td]基于 x64[/td][td]基于 Intel Itanium[/td][/tr][tr][td]用户地址范围 [/td][td]0x10000 – 0x000007FFFFFEFFFF [/td][td]0x10000 – 0x000006FBFFFEFFFF [/td][/tr][tr][td]系统缓存 [/td][td]1 TB [/td][td]1 TB [/td][/tr][tr][td]超出空间 [/td][td]512 GB [/td][td]16 GB [/td][/tr][tr][td]系统地址空间(用于内核线程等) [/td][td]128 GB [/td][td]128 GB [/td][/tr][tr][td]页大小 [/td][td]4K [/td][td]8K [/td][/tr][tr][td]分页的池 [/td][td]128 GB [/td][td]128 GB [/td][/tr][tr][td]未分页的池 [/td][td]在 Windows Vista 和早期系统上占物理内存的 40%,最高达 128 GB; 在 Windows Server 2008、Windows Vista SP1 和以后系统上占物理内存的 75%,最高达 128 GB [/td][td]在 Windows Vista 和早期系统上占物理内存的 40%,最高达 128 GB; 在 Windows Server 2008、Windows Vista SP1 和以后系统上占物理内存的 75%,最高达 128 GB [/td][/tr][tr][td]物理内存地址 [/td][td]64 位 [/td][td]64 位 [/td][/tr][/table]Top of page编码指南和移植问题 编码指南 [table][tr][td]•[/td][td]使用 Windows 64 位和 32 位安全数据类型。 [/td][/tr][tr][td]•[/td][td]检查所有指针使用,特别是指针运算。 [/td][/tr][tr][td]•[/td][td]删除内联汇编代码(使用内部或本机汇编代码)。 [/td][/tr][tr][td]•[/td][td]对于特定于 x64 的代码,使用: #if defined (__AMD64__) 没有任何 __X64__ 定义可用。 [/td][/tr][tr][td]•[/td][td]对于特定于 Itanium 代码,使用: #if defined(__IA64__) [/td][/tr][/table]驱动程序的移植问题 [table][tr][td]•[/td][td]驱动程序应该支持 IOCTL 命令的 32 位和 64 位版本。 参见“IOCTL 支持”部分。 [/td][/tr][tr][td]•[/td][td]应该为只能寻址 32 位物理地址的硬件实现 DMA 支持。 参见“DMA 支持”部分。 [/td][/tr][tr][td]•[/td][td]指针、多态使用和对齐问题适用于所有驱动程序。 参见“指针、多态使用和对齐问题”部分。 [/td][/tr][tr][td]•[/td][td]如有可能,所有驱动程序都应该通过使用 WDF 正确地支持即插即用和电源管理。 [table][tr][td]•[/td][td]在 WDM 驱动程序中,从驱动中心模型过渡到设备中心模型;就是说,处理即插即用和电源管理 I/O 请求包(IRP)或者遵循微型端口指南。 [/td][/tr][tr][td]•[/td][td]对于打印、扫描和相机,用户模式驱动程序必须是 64 位。 [/td][/tr][tr][td]•[/td][td]不允许使用传统 API(针对 Windows NT? 4.0)。 [/td][/tr][/table][/td][/tr][tr][td]•[/td][td]对于 64 位微型端口: [table][tr][td]•[/td][td]遵循 Windows Driver Kit (WDK) 中的微型端口特定的指南。 [/td][/tr][tr][td]•[/td][td]记住 PAGE_SIZE 的不同(基于 Itanium 的系统上为 8 K,x64 系统上为 4 K)。 [/td][/tr][tr][td]•[/td][td]指针、多态使用和对齐问题适用于此处。 [/td][/tr][tr][td]•[/td][td]IOCTL、即插即用和 DMA 问题不适用于大部分微型端口。 [/td][/tr][/table][/td][/tr][/table]构建环境和工具 [table][tr][td]•[/td][td]用于 x64 的构建环境是 AMD64。 [/td][/tr][tr][td]•[/td][td]64 位工具和开发过程与 32 位类似: [table][tr][td]•[/td][td]Debugging Tools for Windows 使用 64 位版本的 Windows 调试工具能够调试在 64 位处理器上运行的 32 位和 64 位用户模式应用程序。使用这个工具包来调试应用程序和 Windows 64 位上的 Windows 32 位(Windows-32-bit-on-Windows-64-bit,WOW64)模拟器。 要调试 Intel Itanium 或 x64 处理器上的应用程序,请使用为特定处理器设计的工具包,可以在此处下载:http://www.microsoft.com/whdc/devtools/debugging/install64bit.mspx. [/td][/tr][tr][td]•[/td][td]WDK 中的交叉编译器 WDK 中的 64 位构建环境包含一个 64 位编译器,可以用来构建和测试运行在基于 x64 的处理器上的 64 位驱动程序代码。参见 WDK 中的问题和限制(http://msdn.microsoft.com/en-us/library/aa489554.aspx). [/td][/tr][tr][td]•[/td][td]已检验版本 驱动程序开发人员使用 Windows 的已检验版本来识别和诊断操作系统级别的问题。已检验版本中的大部分检查用来确定参数或数据结构中的值是否在期望的特定范围内。详细信息请参见http://www.microsoft.com/whdc/DevTools/tools/chkblds.mspx. [/td][/tr][tr][td]•[/td][td]Driver Verifier 这个工具测试和捕获正常操作时通常难以发现的许多情况。Driver Verifier 将验证驱动程序未进行非法的函数调用或引起系统崩溃。它可以识别内存破坏、错误处理的 IRP、无效的 DMA 缓存占用以及可能的死锁等情况。详细信息请参见http://msdn.microsoft.com/en-us/library/ms792872.aspx. [/td][/tr][/table][/td][/tr][/table]BIOS、ACPI 和补丁注意事项 [table][tr][td]•[/td][td]基于 Itanium 的系统必须支持 ACPI 2.0 64 位表。 [/td][/tr][tr][td]•[/td][td]64 位 Windows 支持 GUID 分区表(GPT)磁盘。 [/td][/tr][tr][td]•[/td][td]x64 系统上不允许 BIOS 回调。 [/td][/tr][tr][td]•[/td][td]要与基于 x64 的 Windows 操作系统兼容,驱动程序必须避免以下实践: [table][tr][td]•[/td][td]修改系统服务表,例如与KeServiceDescriptorTable挂钩。 [/td][/tr][tr][td]•[/td][td]修改中断调度表 (IDT)。 [/td][/tr][tr][td]•[/td][td]修改全局描述符表 (GDT)。 [/td][/tr][tr][td]•[/td][td]使用不是由内核分配的内核堆栈。 [/td][/tr][tr][td]•[/td][td]修补内核的任何部分(仅限基于 AMD64 的系统)。 [/td][/tr][/table]Windows 在 x64 平台上强制执行这些规则。任何这种修改都会导致错误检测。 [/td][/tr][/table]IOCTL 支持 包含依赖于指针的类型的 IOCTL 结构在 64 位和 32 位应用程序中具有不同的大小。驱动程序在从 32 位线程发出的 IOCTL 和从 64 位线程发出的 IOCTL 之间必须有所不同。驱动程序基于发出方的位宽验证输入和输出缓冲区的长度。 驱动程序可以使用三种可能的解决方案中的任意一种来同时支持 32 位和 64 位调用者: [table][tr][td]•[/td][td]避免在 IOCTL 结构中使用指针类型。 [/td][/tr][tr][td]•[/td][td]使用 IoIs32bitProcess() API。 [/td][/tr][tr][td]•[/td][td]在 IOCTL 代码的 Function 代码字段中为 64 位调用者定义一个位。 [/td][/tr][/table]要使用 IoIs32BitProcess(),在代码中定义一个 32 位 IOCTL 结构,然后确定发出进程是 32 位还是 64 位。 头文件中的 IOCTL 结构: typedef struct _IOCTL_PARAMETERS { PVOID Addr; SIZE_T Length; HANDLE Handle; } IOCTL_PARAMETERS, *PIOCTL_PARAMETERS; [/pre] 32 位 IOCTL 结构: // // 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; [/pre] 使用 IoIs32BitProcess 的 32 位和 64 位 IOCTL 例子: #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;[/pre]如果选择为 64 位调用者定义一个位,那么使用 Function 字段中的高位。 当前 IOCTL 码有 5 个字段,包括一个 11 位的 Function 字段: [table][tr][td]Device Type (16) [/td][td]Access (2) [/td][td]Custom (1) [/td][td]Function (11) [/td][td]Method (2) [/td][/tr][/table]新的 IOCTL 码使用 Function 字段中的高位来指示 64 位调用者: [table][tr][td]Device Type (16) [/td][td]Access (2) [/td][td]Custom (1) [/td][td]64Bit (1) [/td][td]Function (10) [/td][td]Method (2) [/td][/tr][/table]DMA 支持 [table][tr][td]•[/td][td]使用 PHYSICAL_ADDRESS 类型定义来访问物理地址。PHYSICAL_ADDRESS 为 64 位长。 [/td][/tr][tr][td]•[/td][td]将所有 64 位视为一个有效的物理地址。不要忽略高端的 32 位。 [/td][/tr][tr][td]•[/td][td]使用内核模式驱动程序框架(KMDF)或 Windows DMA DDI 来确保在所有平台上正确运行。当使用 KMDF 或 Windows DMA 例程时,Windows 处理大小问题。 [/td][/tr][tr][td]•[/td][td]在合适的时候使用分散/聚集 DMA。 [/td][/tr][tr][td]•[/td][td]通过使用具备 64 位寻址能力的设备极大地提高性能。 [/td][/tr][/table]指针、多态使用和对齐问题 指针大小在应用程序和驱动程序中一样。用于移植应用程序的指南在下列情况中也适用于驱动程序: [table][tr][td]•[/td][td]多态数据使用(与指针大小相关)。 [/td][/tr][tr][td]•[/td][td]对齐问题。 [/td][/tr][tr][td]•[/td][td]常量和宏运算。 [/td][/tr][/table]本节讨论前两种情况。第三种情况在 MSDN? 文章“超越 Windows XP:现在准备好迎接 64 位版本 Windows 的到来”中讨论。 多态数据使用 [table][tr][td]•[/td][td]不要将指针强制转换成 int、long、ULONG 或 DWORD。 使用 UINT_PTR、INT_PTR、ULONG_PTR 等。 例如: ImageBase = (PVOID) ((ULONG)ImageBase | 1);[/pre]应该改写成: ImageBase = (PVOID) ((ULONG_PTR)ImageBase | 1);[/pre][/td][/tr][tr][td]•[/td][td]使用 PtrToUlong() 和 PtrToLong() 截断指针: [table][tr][td]•[/td][td]决不要将存储已截断指针的 int 或 ULONG 强制转换回指针。 [/td][/tr][tr][td]•[/td][td]计算缓冲区大小时应该小心;大小可能超出 ULONG 的容量。 [/td][/tr][/table][/td][/tr][tr][td]•[/td][td]当调用具有指针 OUT 参数的函数时,应小心地使用正确的大小: void GetBufferAddress(OUT PULONG *ptr); { *ptr=0x1000100010001000; } void foo() { ULONG bufAddress; // // this call causes memory corruption // GetBufferAddress((PULONG*)&bufAddress); }[/pre][/td][/tr][/table] 对齐问题 基于 Itanium 的系统需要自然对齐内存引用(也就是说,以 4 字节为边界访问 32 位)。错误对齐的内存引用在基于 Itanium 的系统上引发一个异常并对系统进行错误检测。 虽然 x64 更宽容,但要获取更好的性能,也需要对齐。 [table][tr][td]•[/td][td]使用结构封装的指令时,应意识到对齐问题。 [/td][/tr][tr][td]•[/td][td]如果在单个头文件中使用不同的封装级别,应该格外小心。 [/td][/tr][tr][td]•[/td][td]为了获得最佳的结果,将 64 位值和指针放在结构的开始处。 [/td][/tr][tr][td]•[/td][td]RtlCopyMemory() 和 memcpy() 不会发生错误。 [/td][/tr][tr][td]•[/td][td]使用 UNALIGNED 宏修复对齐问题: #pragma pack (1) /* also set by /Zp switch */ struct AlignSample { ULONG size; void *ptr; }; struct AlignSample s; void foo(void *p) { *p = p; // 导致对齐错误 ...} foo((PVOID)&s.ptr); void foo(void *p) { struct AlignSample s; *(UNALIGNED void *)&s.ptr = p; }[/pre][/td][/tr][/table]有关对齐的更多信息,请参阅“ 内存管理:每个驱动程序作者都需要知道的技巧。” 更多编码问题 [table][tr][td]•[/td][td]仔细地检查: [table][tr][td]•[/td][td]含指针的显式和隐式联合体。 [/td][/tr][tr][td]•[/td][td]存储在磁盘上或与 32 位进程进行交换的数据结构。 [/td][/tr][tr][td]•[/td][td]安全描述符。 [/td][/tr][tr][td]•[/td][td]处理内存区域大小的代码: len = ptr2 - ptr1 len 可以大于 2^32 [/td][/tr][/table][/td][/tr][tr][td]•[/td][td]截断执行控制代码为 32 位。 [/td][/tr][tr][td]•[/td][td]使用 piecemeal-size 分配: 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));[/pre][/td][/tr][tr][td]•[/td][td]小心使用十六进制常量和无符号值。 [/td][/tr][tr][td]•[/td][td]当您使用无符号数作为下标时,应小心操作: [table][tr][td]•[/td][td]DWORD index = 0; [/td][/tr][tr][td]•[/td][td]CHAR *p; 表达式 if (p[index - 1] == '0') 将会在 Itanium 系统造成存取违规:
64 位驱动程序 INF 需求 Windows Server 2003 SP1 和更高的 Windows 版本不会在基于 x64 的系统上安装带有未修饰 INF 节的驱动程序包。 为了与 Intel Itanium 系统兼容,Windows Server 2003 SP1 将安装带有未修饰 INF 节的驱动程序包。但是,Windows 硬件徽标计划需要 INF 修饰,所以带有未修饰 INF 节的驱动程序包无法满足徽标的资格要求。 INF 的 [Manufacturer] 节条目和 [Models] 节名称必须针对将要安装在基于 x64 的系统上的驱动程序包添加修饰,而且应该针对基于 Intel Itanium 的系统添加修饰。原始发布版 Windows XP 之后的所有 Windows 版本都支持这些修饰;因此,通过这种方式修饰的非 x86 系统 INF 将适用于所有已发布的基于 NT 的 Windows 操作系统。 目标是防止安装错误的驱动程序二进制文件,因为客户不了解平台之间的差别。
32 位和 64 位平台的驱动程序安装 为了简化从 32 位安装程序安装 64 位驱动程序的过程,Microsoft 在 WDK 中提供 Driver Install Frameworks (DIFx) 工具的 64 位版本。DIFx 工具包括:
无法使用 32 位驱动程序安装包直接安装 64 位驱动程序,但是可以使用它们确定当前正在运行哪个体系结构,然后启动合适的安装程序。在 64 位平台上,32 位安装程序在 WOW64 下运行,WOW64 就是让基于 Win32 的应用程序在 64 位 Windows 上运行的 x64 模拟器。安装程序必须能够检测其运行的平台,从而能够为该平台安装正确的驱动程序包。 Top of page开发 64 位驱动程序的参考资料
|
|||||||||||||||||||||||||||||||||||||||||||
|
沙发#
发布于:2009-04-16 11:32
太丰富,研究研究,顶!
|
|
板凳#
发布于:2012-05-01 11:02
牛牛牛,整理得不错啊
|
|
|