hypervisor 使用 IOMMU/SMMU 管理器 smmuman,确保任何pass-through DMA 设备都无法访问尚未明确授予其访问权限的主机物理内存。
smmuman 服务是 QNX 操作系统的一个组件。此处介绍它是为了方便hypervisor 用户。有关更完整的信息,请参阅 SMMUMAN 用户指南。
DMA devices and IOMMUs/SMMUs
非 CPU 启动的读取或写入是来自直接内存访问 (DMA) 设备(例如 GPU、网卡、声卡)的读取或写入请求。 IOMMU(ARM 架构上的 SMMU,x86 架构上的 VT-d)是一种硬件/固件组件,可为非 CPU 启动的读取和写入提供转换和访问控制,类似于第二阶段页表为 CPU 启动的读取和写入提供的转换和访问控制。
硬件限制
在某些主板上,IOMMU/SMMU 硬件不向 smmuman
服务提供映射单个设备并报告其尝试违规所需的信息。
例如,在某些 ARM 主板上,IOMMU 不会向 smmuman
服务提供识别单个 PCI 设备所需的信息。
同样,在 x86 板上,VT-d 硬件无法识别或控制执行 DMA 的单个 MMIO 设备。
此外,某些主板可能没有足够的会话标识符 (session identifiers: SID) 来为每个硬件设备分配唯一的 SID,因此多个硬件设备可能必须共享一个 SID。
这些限制意味着在这些板上,smmuman 服务可能无法映射单个设备。 在hypervisor 系统中,服务可能能够将传递到guest 的设备映射到分配给该guest 的 VM 的内存,并报告这些设备尝试访问 VM 内存外部的内存。 但它可能无法报告单个设备尝试访问其分配内存之外但 VM 分配内存内的内存。
责任
smmuman
服务与受支持硬件平台上的 IOMMU 或 SMMU 配合使用,以:
- 管理guest-physical内存到host-physical 内存的转换,以及非 CPU 启动的读取和写入(即 DMA 设备)的访问。
- 确保任何pass-through都无法访问其映射(允许)主机物理内存位置之外的主机物理内存。
在hypervisor 中使用smmuman
在 QNX hypervisor 系统中,smmuman 服务在hypervisor 主机域中运行,并在hypervisor 主机启动时启动。 启动时,该服务会了解板上的哪些 SMMU 控制哪些 DMA 设备。 稍后,qvm 进程实例将能够要求 smmuman 通知电路板 SMMU DMA 设备允许访问的内存位置。
利用在启动时获得的有关 DMA 设备的信息,在操作期间,IOMMU/SMMU 将知道拒绝 DMA 设备访问其允许位置之外的内存的任何尝试。
在操作过程中,smmuman 监控系统 IOMMU/SMMU。如果所有 DMA 设备都按预期运行,则 smmuman 除了继续监视外不执行任何操作。但是,如果 DMA 设备尝试访问其允许位置之外的内存,IOMMU/SMMU 硬件将拒绝该尝试,并且** smmuman 会在内部记录事件**。
客户端可以使用 libsmmu
中提供的 smmuman API 获取信息并将其输出到有用的位置,例如system logger(参见 QNX Neutrino 实用程序参考中的 slogger2)。
然后,您可以使用此信息了解有关非法内存访问尝试的来源的详细信息,并解决问题。
smmuman and qvm
QNX hypervisor系统中的 qvm 进程实例是 smmuman 客户端:
- 如果 smmuman 未运行,qvm 进程实例将拒绝在其 VM 中包含它们知道是 DMA 设备的任何pass-through设备,并拒绝启动 (请参阅Configuration章节中 loc 选项的“d”和“n”访问类型属性)。
- 如果 smmuman 正在运行并报告它不知道如何处理 DMA 直通设备,则 qvm 进程将报告问题并退出。
上述行为规则有一个例外:unity-mapped guests
。
如果在guest虚拟机中分配的所有guest-physical内存都直接映射到主机物理内存(例如,客户机中的0x80000000映射到主机中的0x80000000),则guest 是unity-mapped的。
如果guest 是unity-mapped的,如果 smmuman 未运行或不知道如何处理设备,则托管guest 的 qvm 进程实例将按预期报告问题,但它也会启动。
在某些有限的情况下,Unity-mapping 对于测试和实验可能很有用。但是,Unity-mapping绕过了虚拟化环境的基本内存抽象,不应在生产系统中使用。
有关 DMA 设备的 IOMMU/SMMU 控制的信息
有关哪个 IOMMU/SMMU 控制哪个 DMA 设备的信息可能在不同的位置可用,具体取决于主板体系结构和特定板本身(例如,在 ACPI 表中,在特定于板的代码中)。为了获取此信息,smmuman 查询固件,并接受用户输入的配置信息。
默认情况下,用户输入的信息将覆盖从开发板获得的信息,以便它可以替换固件上损坏或不可靠的信息。但是,用户可以要求smmuman保留板获得的信息并将其与用户输入的信息合并。
The smmuman service in QNX guests
如果要在具有 QNX guest的系统上的任何位置实施pass-through设备,则应在hypervisor 主机和 QNX guest中实施 smmuman 服务。 guest将使用 smmu vdev,它为guest实现 VM 中所需的 IOMMU/SMMU 功能。 (参考Virtual Device Reference章节的vdev smmu)。
下图说明了如何在hypervisor guest中实现该服务,以将pass-through DMA 设备限制到其分配的内存区域:
为简单起见,该图显示了具有单个 pass-through DMA 设备的单个guest :
- 在hypervisor 序主机中,将托管guest 的虚拟机(qvm 进程实例)使用 smmuman 服务对 IOMMU/SMMU 进行编程,其中包含它将呈现给guest 物理内存区域的内存范围和权限 (A)(
例如,0x20000000 到 0xB0000000
)。 如果 DMA 设备错误或恶意地尝试访问其内存,这将保护hypervisor 主机和其他guest 。但是,它不会阻止传递给guest 的 DMA 设备不正确地访问分配给该guest 的内存。 - 在guest 中, pass-through DMA 设备的驱动程序使用 SMMUMAN 客户端 API 将所需的内存分配和权限 (B) 编程到guest 中运行的 smmuman 服务中。
- guest 的 smmuman 服务对在其托管 VM 中运行的 smmu 虚拟设备进行编程,就像对硬件中的 IOMMU/SMMU 进行编程一样:它将 DMA 设备的内存分配和权限编程到 smmu 虚拟设备中。
- smmu 虚拟设备使用hypervisor 主机中运行的 smmuman 服务的客户端 API,要求它将客户机的 smmuman 服务请求的内存分配和权限 (B) 编程到主板的 IOMMU/SMMU 中。
- 主机的 smmuman 服务将 pass-through DMA 设备的内存分配和权限 (B) 编程到主板 IOMMU/SMMU 中。
DMA 设备对内存 (C) 的访问现在仅限于guest (B) 中 DMA 设备驱动程序请求的区域和权限,并且guest 操作系统和其他组件受到保护,不会受到此设备错误或恶意访问其内存的影响。
请注意,由于以下原因, pass-through DMA 设备的内存分配和权限 (B) 不能简单地在guest 启动时分配,必须在启动后分配:
- 托管guest 的 VM 不知道guest 将用于其 DMA 设备的内存映射。
- guest 中的驱动程序可能会更改其内存映射。
- guest的 DMA 设备驱动程序(例如,图形驱动程序)可能会在guest运行时动态创建和销毁内存区域。
您还应该使用hypervisor 主机中运行的 smmuman 服务对主板 IOMMU/SMMU 进行编程,其中包含系统中每个虚拟机将向其guest提供的整个物理内存区域的内存范围和权限。这样,这些guest拥有的任何 pass-through设备都无法访问其guest内存区域之外的内存。
有关如何在 QNX guest中使用 smmuman 服务的更多信息,参考SMMUMAN User’s Guide。
Linux guest不支持 smmuman 服务; 但是,您仍应使用hypervisor 主机中的服务对 IOMMU/SMMU 进行编程,其中包含hypervisor 主机将提供给每个 Linux guest的整个物理内存区域的内存范围和权限。
在 ARM 平台上的guest中运行 smmuman
要运行 smmuman 服务,在 ARM 平台上的 QNX hypervisor 虚拟机中运行的guest必须加载 libfdt.so
。请确保将此共享对象包含在guest的构建文件中。
libfdt 库经过认证,仅供内部使用。除非 QNX 指示且仅在该方向的上下文中,否则您不得使用此库。