X86_64 架构下的 IOMMU
DMA
DMA 是一种允许设备绕过 CPU 直接与内存交换数据的技术。在传统的数据传输方案中,设备需要通过中断请求 CPU 介入,从而完成数据的搬运。这种方式在处理大规模数据时会持续占用 CPU 资源,使得系统整体效率比较低下。DMA 技术通过引入专用的 DMA 控制器,使设备可以直接访问物理内存,从而显著提升传输效率。DMA 虚拟化通常用于设备直通的情形。当一个支持 DMA 的物理设备被分配给 guest OS 时,guest OS 提供给设备的内存地址均为 gPA。只有将 gPA 转化为 hPA 后,设备才能进行正确的 DMA 访问。负责进行 gPA 到 hPA 地址转换的硬件被称为输入输出内存管理单元(input–output memory management unit,IOMMU)。在启用 IOMMU 后,guest OS 可以继续使用 gPA 向设备传递地址信息,而 IOMMU 会在设备发起 DMA 操作时自动完成地址转换,确保 DMA 访问的正确性。在 Intel x86 架构中,IOMMU 的功能由 VT-d(Virtualization Technology for Direct I/O)硬件单元提供支持。
Hvisor 的 IOMMU 实现
Hvisor 使用 VT-d 实现了基于硬件的 DMA 虚拟化,从而支持 PCI 设备的直接内存访问。当宿主机启动时,BIOS 会检测 VT-d 硬件单元的存在并为其分配对应的地址空间,同时在 ACPI 表的 DMAR
子表(DMA remapping reporting table)中提供 VT-d 的硬件信息,例如寄存器基地址以及 VT-d 支持设备的 BDF 号。
DMA 虚拟化的核心在于将设备用于访存的 gPA 转换为 hPA。这一过程使用的页表结构与 EPT 完全一致,因此可以直接复用。然而,设备所属的 zone 不同,使用的 EPT 也不同,需要另一个类似页表的结构,将设备的 BDF 号映射到对应 EPT 的 hPA。VT-d 从硬件层面提供了对于此结构的支持:Hvisor 需要预先构建一个 4KB 大小的根表(root table),其中包含 256 个表项。每个表项对应一个 bus 号,并记录了该 bus 所对应的 4KB 上下文表(context table)的 hPA。而每个上下文表同样包含 256 个表项,每个表项对应一个 BDF 号,表项内记录了该 BDF 号对应 PCI 设备所使用 EPT 的 hPA。PCI 设备发起的 DMA 请求会包含 BDF 号作为索引,VT-d 使用根表和上下文表找到对应的 EPT 后,自动完成 gPA 到 hPA 的转换。
在 QEMU 中启用 VT-d 可以参见:QEMU Features/VT-d