This page looks best with JavaScript enabled

Intel-VTD与DMAR表结构解析

 ·  ☕ 7 min read · 👀... views

DMAR

DMAR(DMA Remapping Reporting)表,是ACPI表中的一张子表。在系统上电时,BIOS/UEFI负责检测并初始化重定向硬件,为这些设备分配相应的物理地址,并在DMAR表上注册对应的信息。其也是操作系统和VTD重定向硬件交互控制的桥梁,所以理解表上对应的结构含义对于使用VTD技术来说至关重要。

目前对于VTD技术的详细文档以及DMAR表结构与上面的子结构,主要依赖Intel发布的VTD白皮书:《Intel Virtualization Technology for Directed I/O》,本文为笔者学习VTD相关知识所记录的一些随笔

解析DMAR相关的结构可以用RWEverything工具
Rw.exe运行后会自动加载其驱动并读取相关的物理地址并解析出结构展示。比如我们要看DMAR表需要先打开 ACPI Table 然后在下面的标签中打开DMAR标签,rw工具会输出本机DMAR表的物理地址并给出hex dump以及解析出的结构信息
rw_ACPITable_DMAR

我们可以在 《Intel Virtualization Technology for Directed I/O》chapter8.1: DMA Remapping Reporting Suructure 查到DMAR表的结构
DMAR_Structure

我们也可以对照这个结构,自己在内存页面手动解析对应的结构
DMAR_Structure_rw

DRHD

在DMAR表上我们主要要关注的是 Remapping Structure[] 结构,这是一个数组结构(不是指针数组)。其中每一个结构都是联合体,第一个字段是Type,决定这个成员是什么类型:DRHD、RMRR、ATSR、RHSA等,并且不同的类型大小也不一样,所以第二个字段通常是length,决定这个成员的大小。在遍历时候就从下表0开始取type和length向下解析获得整个数组的结构。

在这些类型中需要重点关注的是 DRHD(DMA Remapping Hardware Unit Definition) 结构,这个结构唯一的标识平台中存在的重映射硬件单位。DRHD的结构如下
DRHD_Structure

重点需要关注的是 FlagsRegister Base Address 两个字段:

  1. Flags : 只有0或1两种情况,简单来说如果是0的话里面的设备只有部分设备,为1的话里面会有指定节区的全部设备。所以我们需要关注的是Flags=1的DRHD
  2. Register Base Address : VT-D重定向硬件寄存器基地址

Register Descriptions

Register Base Address 指向的就是重映射寄存器组,其管理重映射功能的相关属性,也是软件通过读写这些寄存器控制VTD重映射机制。虽然说是寄存器,但这里可以把它当作内存来理解(但又不是真的内存,在Intel上用直接内存访问是访问不到这块区域的,应该将其理解为寄存器在内存空间的映射)。值得注意的是开启VBS后这块区域会被HyperV接管保护,届时Guest将无法访问这块区域。

我们可以在 《Intel Virtualization Technology for Directed I/O》chapter11.4: Register Descriptions 查到重映射寄存器组的结构
RegisterDescriptions

主要用到的是前0x40的部分,其中我们重点要关注的是+0x20的 Root Table Address Register(RTADDR_REG),它里面存储了 Root-Table 的地址。然后我们要注意,并非 RTADDR_REG 直接就是 Root-Table 的地址,其低位被BIOS复用作为标识其他的一些信息,只有 63:12 位才是 Root Table Address

我们可以在 《Intel Virtualization Technology for Directed I/O》chapter11.4.5: Root Table Address Register 查到 Root Table Address Register 的结构
RootTableAddressRegister

其中我们要重点关注的是 11:10 位,TTM: Translation Table Mode。该位标识当前使用的DMA Remapping所用的转化模式。其有四种值:

  1. 00b: Legacy Mode : 标准传统模式,使用Root-Table和Context-Table两级结构
  2. 01b: Scalable Mode : 可拓展模式
  3. 10b: Reserved : 保留值
  4. 11b: Abort-DMA Mode : 中止所有的DMA操作

LegacyMode是目前主流的转化模式,后面的内容也均在此模式上介绍

Root-Table & Context-Table

在介绍 Root-Table 之前还需要介绍一个概念,就是 PCI Source Identifier, 相关内容在 《Intel Virtualization Technology for Directed I/O》chapter3.4.1: Source Identifier 中介绍。VTD要求每一个参与地址转换的硬件设备需要有一个唯一的 source-id 标识。在PCIE设备中使用 Bus:Device:Function 作为这个唯一标识,该标识的分配方式和系统对设备的遍历相关。

VTD使用Domains管理各个设备映射的内存,而作为顶层结构的根表(Root-Table)的作用就是将这些设备映射到各自的domains上。根表本质是一个4KB的内存页,其中包含256个根条目(root-entries),(即每个root-entry占用16Bytes=4*1024/256),覆盖PCI总线号空间(0-255),而每一个root-entry包含上下文表(Context-Table)的指针,

Context-Table的结构也是类似的,每个Context-Table表包含256个条目对应于 Bus:Device:FunctionDeviceFunction,即通过设备号和功能号组成index索引得到 Second-Stage Page Table(SSPT) 指针, 该指针指向顶级页表。不同的设备可以配置相同的指针,这样这些设备就会被分配到同一个域,使用相同的地址转化关系。

总结其实就是,通过Root-Table和Context-Table这二级表完成了每一个 Bus:Device:Function 到Domain的映射

rw_roottable_contexttable

PS:我们可以看到 3:0:0 的SSPT指针(其实是Context Entry)是 00000009,00000002,这是什么意思呢?这需要了解一下 Context Entry 结构

Context Entry

不仅是 00000009,00000002,我们看到一些SSPT指针的低位是有值的,但寻址时我们会忽略低位的值。比如 335AC001, 00000001 这样一个ContextEntry我们会找到其SSPT指针值为 0x1335AC000。这是因为BIOS复用了Entry的低位标识一些属性。具体参考 《Intel Virtualization Technology for Directed I/O》chapter9.3: Context Entry

ContextEntry
这里讲两个比较关键的位

  1. P: Present : 有效位,只有该位被设置才标识该Entry是有效的
  2. TT: Translation Type : 这里需要注意的是 10b,也就是PassThrough直通模式,因为untranslated请求直接通过不进行地址转化,此时SSPT会被忽略,相当于直接内存访问不走VTD的SSDT页表了,所以也就是我们所说的“全允许访问”。在上一节中我们看到 3:0:0 的ContextEntry是 00000009,00000002,即 1001b,对应P位为1、TT为 10b,也就是 3:0:0 这个设备当前为PassThrough直通模式,对所有内存全允许访问。
  3. SSPTPTR : 这个就很好理解了,12位到63位是真正的SSPT指针值,所以在寻址的时候要忽略低位的属性值

地址转换流程

通过Root-Table和Context-Table将每一个设备精确的指向一个 Second-Stage Page Table 结构,根据该结构的信息将I/O设备的DMA请求地址转化为物理地址(PA->PA, 类似于EPT的转化)。SSPT和内存页表一样,支持N级结构,其具体是几级取决于硬件支持的 Guest Address Width(GAW)

比如我的电脑在 2:0:0 的位置是NVM Express存储控制器,可以用RWEveryThing整理出整个寻址关系
rw_SSPT_translation

地址转化关系的详细图文介绍在 《Intel Virtualization Technology for Directed I/O》chapter3.5: Hierarchiacal Translation Structures,这边做个速记摘要

转换为4KB页:

  1. 使用输入地址的bits 47:39索引PML4表
  2. 使用bits 38:30索引PDPT
  3. 使用bits 29:21索引PDT
  4. 使用bits 20:12索引PTT
  5. 组合PTE中的物理页帧号和输入地址的bits 11:0得到最终物理地址
    Translation_4-KByte

转换为2MB大页

  1. 使用输入地址的bits 47:39索引PML4表
  2. 使用bits 38:30索引PDPT
  3. 使用bits 29:21索引PDT
  4. 当PD条目中PS=1时,直接组合PD条目中的21位页帧号和输入地址的bits 20:0
    Translation_2-MByte

转换为1GB大页

  1. 使用输入地址的bits 47:39索引PML4表
  2. 使用bits 38:30索引PDPT
  3. 当PDP条目中PS=1时,直接组合PDP条目中的30位页帧号和输入地址的bits 29:0
    Translation_1-GByte

Reference

  1. 《Intel Virtualization Technology for Directed I/O》 chapter3: DMA Remapping
  2. 《Intel Virtualization Technology for Directed I/O》 chapter8: BIOS Considerations
  3. 《Intel Virtualization Technology for Directed I/O》 chapter9: Translation Structure Formats
  4. Introductory Study of IOMMU (VT-d) and Kernel DMA Protection on Intel Processors
  5. https://zhuanlan.zhihu.com/p/51110851
  6. https://geekdaxue.co/read/u2321266@lb7ty6/vizldy
Share on

Qfrost
WRITTEN BY
Qfrost
CTFer, Anti-Cheater, LLVM Committer