这篇文章是对Intel VT-d入门研究的总结,主要是关于DMA重映射编程以及Windows上使用的内容,希望本文可以帮助你对它有一个基本的了解,并在感兴趣的基础上开始研究更多细节。

英特尔VT-d

英特尔VT-d(正式名称是定向I / O的英特尔VT)具有以下三个功能:

· DMA Remapping

· Interrupt Remapping

· Interrupt Posting

DMA重映射是其中最常讨论的功能,也是本文的重点。

DMA重映射

DMA重映射是一项重要功能,因为它允许软件通过为每个物理内存页面配置访问权限来实现针对恶意设备的直接内存访问(DMA)的安全检测。虽然可以通过分页结构配置普通的内存页面保护,并且当使用Intel VT-x时,可以通过扩展页面表(EPT)进行配置,但是在进行DMA访问的情况下,这些配置将被完全忽略。奇热因此,需要其他保护机制来完成对存储器的保护,DMA重映射可实现此目的。

规范中的以下图片显示了DMA是通过DMA重映射而不是CPU内存虚拟化(即EPT)进行映射的。

不需要VT-x

一个典型的技术误解是,VT-d(DMA重映射)与VT-x,虚拟机等相关联。事实是,没有VT-x,DMA重映射也是可用且有用的。例如,Windows可以启用基于DMA重映射的安全功能(称为 内核DMA保护),而无需启用基于VT-x的安全保护措施(VBS:基于虚拟化的安全保护措施)。

https://docs.microsoft.com/en-us/windows/security/information-protection/kernel-dma-protection-for-thunderbolt 下面示例项目也可以独立地进行DMA重映射。

IOMMU

DMA重映射也称为IOMMU,因为它的功能类似于用于IO内存访问的内存管理单元(MMU)。不仅概念相似,而且与MMU的编程接口也非常相似,即分页结构和EPT。

从高级的角度来看,主要区别在于DMA重映射在熟悉的PML5、4,PDPT,PD和PT的顶部使用了两个表进行转换。简而言之,使用MMU进行的转换如下:

· Hardware register => PML4 => PDPT => …

而IOMMU的是:

· Hardware register => Root table => Context table => PML4 => PDPT => …

该规范将上下文表中引用的表称为第二级页表,下图说明了转换流程。

注意:

· 根据请求DMA的设备的总线号选择root表的条目。

· 基于设备和设备的功能编号的组合来选择上下文表的条目。

作为bus:device:function(source-id)分配的示例,我测试的具有DMA功能的设备在一个系统上被列为Bus 6:Device 0:Function 0,如下所示。

示例代码

让我们看一下下面的代码,HelloIommuPkg是一个运行时DXE驱动程序,可以使DMA重映射并保护它的第一页(PE头)从DMA读取和写入。

https://github.com/tandasat/HelloIommuPkg 加载此命令将产生以下输出,并在成功的情况下保护页表。

然后,使用PCILeech对测试PCI设备执行DMA读取操作, 证明另一页可读。

https://github.com/ufrisk/pcileech

但受保护的页面不可读。

通过使用RWEverything报告的故障记录寄存器,可以确认DMA确实由于缺少读取权限而被阻塞。

http://rweverything.com/

· 第一列表示故障地址(0x6ff48000)

· 第三列表示请求设备的源ID(总线6:设备0:功能0)

· 第四列中的6表示缺少读取权限

IOMMU 编程

启用DMA重映射可以分为以下步骤:

  1. 找到DMA重映射报告(DMAR)ACPI表。
  2. 从(1)中的DMA重映射硬件单元定义(DRHD)结构中收集有关可用DMA重映射硬件单元的信息。
  3. 通过初始化上述表来配置转换。
  4. 编写硬件寄存器以使用(3)并激活DMA重映射。

HelloIummuDxe.c大致遵循此序列,并带有一些错误检查代码。(1)和(2)非常简单,可以通过RWEverything等经过验证的工具。

(3)的复杂度在很大程度上取决于对粒度和选择性转换以及内存保护的要求。HelloIummuPkg允许从任何设备到任何地方的任何访问,除了对单个页面的访问之外,这简化了此步骤。(4)大多只是遵循规范。

总体而言,步骤很简单,没有注释的HelloIummuPkg的行数少于700行。

在Windows上使用DMA重映射

Windows使用DMA重映射(如果可用),如果系统未启用内核DMA保护,则它将转换配置为大多数情况下将所有设备的所有请求传递给其他用户。

以下是从没有内核DMA保护的系统中截取的以下截图,显示了总线7上支持DMA的设备的转换:设备0:功能0。右下角的值9表示已考虑DMA请求(请参见“ TT:转换类型”)在规范中)。

请注意,大多数条目指向0x1ac000处的同一上下文表,该上下文表配置为直通,不提供任何保护。

附带说明,除非启用了VBS,否则第三方Windows驱动程序在技术上可能会修改这些转换并尝试针对DMA提供额外的安全保护。

DMA重映射与内核DMA保护

如果启用了内核DMA保护,则大多数转换都会配置为失败,这可以通过指向填充有零的第二级PML4来实现,这意味着不存在转换。

下面的截图显示了具有内核DMA保护的示例配置。请注意,位于0x1ac000的上下文表指向位于0x251000的第二级PML4,它们全为零。

请注意,如果启用了VBS,则这些内存位置将不可见,禁用以检查它们。

有趣的是,无法观察到描述的内核DMA保护行为,因为无论屏幕是否被锁定,针对设备执行DMA都会导致错误检查0xE6:DRIVER_VERIFIER_DMA_VIOLATION(类型0x26)。从我从Hal.dll读取的内容来看,进行错误检查是有意义的,但是我怀疑这是内核DMA保护应该如何保护系统的方式。

研究结论

DMA重映射是Intel VT-d体系结构的一部分,可提供针对恶意设备的DMA的安全保护,并且无需将Intel VT-x一起使用即可启用,示例项目HelloIommuPkg演示了用不到700行代码从UEFI重配置DMA的简单设置。

可以看到Windows启用了DMA重映射(如果可用),并且当启用内核DMA保护功能时,通过第二级PML4大部分会阻止DMA访问。

学习资源

· Intel® Virtualization Technology for Directed I/O Architecture Specification

· A Tour Beyond BIOS: Using IOMMU for DMA Protection in UEFI Firmware

· VTd module is IntelSiliconPkg