DMA 验证

DMA验证用以监控直接内存访问(DMA)的使用情况。 由于随着 Windows 的发展,DMA 例程发生了变化,因此许多驱动程序使用 DMA 调用不正确。 此外,一些驱动程序编写器尝试完全绕过 HAL DMA 子系统。 这种做法可能会引入隐蔽的 bug 到驱动程序中。

驱动程序验证程序 DMA 验证选项尝试捕获常见的 DMA 错误。 除了 !dma 内核调试器扩展之外,它还可用于验证驱动程序是否以正确的方式使用 DMA。

此驱动程序验证程序选项也称为 HAL 验证。 驱动程序验证程序生成的一些错误消息可能使用此术语。

不同类型的直接内存访问 (DMA)

DMA 是一种机制,通过该机制,硬件设备可以在不使用处理器的情况下将数据传输到内存或从内存中传输数据。 需要处理器才能设置传输,设备将在完成传输后向处理器发出信号。 此系统的优点是处理器在执行 DMA 传输时可以执行其他任务。

Windows 2000 及更高版本中使用了多种类型的 DMA:

Common-buffer DMA
当系统可以分配一个可供硬件和软件访问的缓冲区时,将执行通用缓冲区 DMA。 驱动程序负责同步对缓冲区的访问。 内存未缓存,使此同步更易于驱动程序使用。 设置通用缓冲区后,驱动程序和硬件都可以直接写入缓冲区中的地址,而无需 HAL 进行任何干预。

数据包 DMA
当存在必须映射供硬件使用的单个现有缓冲区时,将执行数据包 DMA。 使用数据包 DMA 的示例是将文件从内存传输到磁盘。 在这种情况下,使用通用缓冲区 DMA 会浪费,因为必须先将文件传输到通用缓冲区,然后硬件才能将其传输到磁盘。 而是咨询 HAL;它为驱动程序提供帮助硬件在内存中查找实际缓冲区所需的信息。 由于涉及的程序需要在不同的体系结构上工作,此操作变得复杂。

分散/聚集 DMA
散点/收集 DMA 是一种快捷方式方法,可同时设置多个数据包 DMA 传输。 例如,如果要通过网络传输数据包,则网络堆栈的每个部分都会添加其自己的标头(TCP、IP、以太网等)。 这些标头都从内存中的不同位置分配。 在这种情况下,散点/收集 DMA 通过向 HAL 发出批处理请求来映射每个标头以及硬件访问的数据段,从而节省时间。 此方法不必在数据包的每个部分调用数据包 DMA 例程,而是调用每个例程一次,并允许 HAL 负责单独映射每个例程。

请注意,散点/收集功能 并不意味着设备可以使用散点/收集例程。 散点/收集功能是指设备说明中的标志,指示设备能够读取或写入内存中的任何区域,而不仅仅是特定范围。

系统 DMA
系统 DMA 通过编程主板上的系统 DMA 控制器来执行直接传输。 只有 ISA 卡可以使用系统 DMA。

DMA 验证的效果

当 DMA 验证处于活动状态时,驱动程序验证程序会检测 DMA 例程的滥用,包括:

  • 溢出或未满 DMA 内存缓冲区(这些错误可能由硬件或驱动程序导致)。

  • 二次释放通用缓冲区、适配器通道、映射寄存器或散布/收集列表。

  • 通过不释放常见缓冲区、适配器通道、映射寄存器、散点/收集列表或适配器来泄漏内存。

  • 一次为适配器提供多个适配器通道。

  • 尝试使用已释放且不再存在的适配器。

  • 不刷新适配器缓冲区。

  • 适配器的未完成引用计数过多。

  • 在可分页缓冲区上执行 DMA(应在 DMA 传输开始之前锁定所有缓冲区)。

  • 在带有损坏标志的 MDL 上执行 DMA。

  • 在第一个 MDL 之前或第一个 MDL 末尾之前引用无效的系统地址,或使用比 MDL 缓冲区更长的传输长度,并跨越 MDL 中的页面边界。

  • 一次分配过多的映射寄存器,或分配比允许的最大数目更多的映射寄存器。

  • 映射寄存器的双重映射。

  • 尝试释放映射寄存器时,某些寄存器仍被映射。

  • 尝试刷新尚未映射的映射寄存器。

  • 尝试清空映射寄存器文件末尾时,字节数过多。

  • 在不正确的 IRQL 中调用 DMA 例程。

  • 将 null 值 DMA_ADAPTER 传递给 HAL 例程。

  • 当该地址不包含在 MDL 中时,将地址和 MDL 传递到 HAL 例程。

  • 尝试映射已映射的地址范围。

  • 尝试刷新未映射的缓冲区。

  • 尝试映射零长度的缓冲区进行传输。

  • 调用过时的函数 HalGetAdapter (所有驱动程序都必须改用 IoGetDmaAdapter )。

驱动程序验证程序监视驱动程序的行为,并在发生任何违规时发出错误检查0xE6。 有关错误检查参数的列表,请参阅 错误检查 0xE6(DRIVER_VERIFIER_DMA_VIOLATION)。

DMA 验证何时有用?

所有直接使用 DMA 的驱动程序(通过调用 HAL DMA 例程)都应使用 DMA 验证进行测试。

此外,还应测试微型端口驱动程序,因为它们经常间接使用 DMA(通过调用使用 DMA 的端口驱动程序)。

DMA 验证也可以是检测内存损坏的有效方法,因为它可以在驱动程序或硬件设备溢出 DMA 缓冲区时发现。

监视 DMA 验证

内核调试器扩展 !dma 可用于显示大量 DMA 信息。 它可以显示有关每个 DMA 适配器行为的各种详细信息。 Windows 包的调试工具文档中提供了 !dma 扩展的详细示例,以及有关调试器扩展的一般信息。 有关详细信息,请参阅 Windows 调试

激活此选项

可以通过使用驱动程序验证管理器或 Verifier.exe 命令行来激活一个或多个驱动程序的 DMA 验证功能。 有关详细信息,请参阅 “选择驱动程序验证程序选项”。

  • 在命令行

    在命令行中,DMA 验证选项由 位 7 (0x80) 表示。 若要激活 DMA 验证,请使用标志值0x80或向标志值添加0x80。 例如:

    verifier /flags 0x80 /driver MyDriver.sys
    

    下一次启动后,该功能将处于活动状态。

    在 Windows Vista 和更高版本的 Windows 上,还可以通过在命令中添加 /volatile 参数来激活和停用 DMA 验证,而无需重新启动计算机。 例如:

    verifier /volatile /flags 0x80 /adddriver MyDriver.sys
    

    此设置立即生效,但在关闭或重新启动计算机时会丢失。 有关详细信息,请参阅 “使用易失性设置”。

    标准设置中还包括 DMA 验证功能。 例如:

    verifier /standard /driver MyDriver.sys
    
  • 使用驱动程序验证管理器

    1. 启动驱动程序验证程序管理器。 在命令提示符窗口中键入 验证程序
    2. 选择 “创建自定义设置”(面向代码开发人员), 然后单击“ 下一步”。
    3. 从完整列表中选择单个设置
    4. 选择(检查) DMA 验证

    标准设置中还包括 DMA 验证功能。 若要使用此功能,请在驱动程序验证程序管理器中,单击“ 创建标准设置”。