IOMMUFD

作者:

Jason Gunthorpe

作者:

Kevin Tian

概述

IOMMUFD 是用户 API,用于控制 IOMMU 子系统,因为它涉及使用文件描述符从用户空间管理 IO 页表。 它旨在通用并可被任何想要向用户空间公开 DMA 的驱动程序使用。 预计这些驱动程序最终将弃用它们可能已经/历史上实现的任何内部 IOMMU 逻辑(例如,vfio_iommu_type1.c)。

IOMMUFD 至少提供对所有 IOMMU 的 I/O 地址空间和 I/O 页表的通用管理支持,并在设计中留有空间以添加非通用功能以满足特定硬件功能。

在这种上下文中,大写字母 (IOMMUFD) 指的是子系统,而小写字母 (iommufd) 指的是通过 /dev/iommu 创建的供用户空间使用的文件描述符。

关键概念

用户可见对象

以下 IOMMUFD 对象暴露给用户空间

  • IOMMUFD_OBJ_IOAS,表示 I/O 地址空间 (IOAS),允许将用户空间内存映射/取消映射到 I/O 虚拟地址 (IOVA) 范围中。

    IOAS 是 VFIO 容器的功能替代品,与 VFIO 容器一样,它将 IOVA 映射复制到其中包含的 iommu_domains 列表中。

  • IOMMUFD_OBJ_DEVICE,表示由外部驱动程序绑定到 iommufd 的设备。

  • IOMMUFD_OBJ_HWPT_PAGING,表示由 iommu 驱动程序管理的实际硬件 I/O 页表(即,单个 struct iommu_domain)。“PAGING”主要指示此类型的 HWPT 应链接到 IOAS。它还表明它由具有 __IOMMU_DOMAIN_PAGING 特性标志的 iommu_domain 支持。这可以是运行在用户空间中的设备的 UNMANAGED 阶段 1 域,也可以是从客户机级别物理地址到主机级别物理地址的映射的嵌套父级阶段 2 域。

    IOAS 具有共享相同 IOVA 映射的 HWPT_PAGING 列表,它将使其映射与每个成员 HWPT_PAGING 同步。

  • IOMMUFD_OBJ_HWPT_NESTED,表示由用户空间(例如,客户机操作系统)管理的实际硬件 I/O 页表(即,单个 struct iommu_domain)。“NESTED”表示此类型的 HWPT 应链接到 HWPT_PAGING。它还表明它由类型为 IOMMU_DOMAIN_NESTED 的 iommu_domain 支持。这必须是运行在用户空间中的设备的阶段 1 域(例如,在启用 IOMMU 嵌套转换特性的客户机 VM 中。)因此,必须使用给定的嵌套父级阶段 2 域来创建它才能关联。此由用户空间管理的嵌套阶段 1 页表通常具有从客户机级别 I/O 虚拟地址到客户机级别物理地址的映射。

  • IOMMUFD_FAULT,表示一个软件队列,用于 HWPT 使用 IOMMU HW 的 PRI(页面请求接口)报告 IO 页面错误。此队列对象为用户空间提供一个 FD 来轮询页面错误事件并响应这些事件。必须首先创建一个 FAULT 对象以获得一个 fault_id,然后可以通过在 IOMMU_HWPT_ALLOC 命令中设置其 flags 字段中的 IOMMU_HWPT_FAULT_ID_VALID 位来分配一个启用错误的 HWPT。

  • IOMMUFD_OBJ_VIOMMU,表示物理 IOMMU 实例的一个切片,传递给 VM 或与 VM 共享。它可能是某些 HW 加速的虚拟化特性和 VM 使用的一些 SW 资源。例如

    • 客户机拥有的 ID 的安全命名空间,例如,客户机控制的缓存标记

    • 非设备相关的事件报告,例如,无效队列错误

    • 跨物理 IOMMU 访问可共享的嵌套父级页表

    • 各种平台 ID 的虚拟化,例如,RID 和其他 ID

    • 提供准虚拟化无效

    • 直接分配的无效队列

    • 直接分配的中断

    这样的 vIOMMU 对象通常可以访问嵌套父级页表以支持某些 HW 加速的虚拟化特性。因此,必须给定一个嵌套父级 HWPT_PAGING 对象来创建 vIOMMU 对象,然后它将封装该 HWPT_PAGING 对象。因此,可以使用 vIOMMU 对象来分配封装的 HWPT_PAGING 对象,而不是分配 HWPT_NESTED 对象。

    注意

    名称“vIOMMU”不一定与 VM 中的虚拟化 IOMMU 相同。VM 可以在具有多个物理 IOMMU 的机器上运行一个巨大的虚拟化 IOMMU,在这种情况下,VMM 会将来自此单个虚拟化 IOMMU 实例的请求或配置分派给为不同物理 IOMMU 的各个切片创建的多个 vIOMMU 对象。换句话说,vIOMMU 对象始终是一个物理 IOMMU 的表示,不一定是虚拟化 IOMMU 的表示。对于想要来自物理 IOMMU 的完整虚拟化特性的 VMM,建议构建与物理 IOMMU 数量相同的虚拟化 IOMMU,以便传递的设备连接到由相应 vIOMMU 对象支持的它们自己的虚拟化 IOMMU,在这种情况下,客户机操作系统会自然地进行“分派”,而不是 VMM 陷阱。

  • IOMMUFD_OBJ_VDEVICE,表示针对 IOMMUFD_OBJ_VIOMMU 的 IOMMUFD_OBJ_DEVICE 的虚拟设备。此虚拟设备保存设备的虚拟信息或属性(与 vIOMMU 相关)在 VM 中。一个直接的 vDATA 示例可以是设备在 vIOMMU 上的虚拟 ID,这是一个 VMM 分配给设备的唯一 ID,用于 vIOMMU 的转换通道/端口,例如 ARM SMMUv3 的 vSID、AMD IOMMU 的 vDeviceID 和 Intel VT-d 的 vRID 到上下文表。一些高级安全信息的潜在用例也可以通过此对象转发,例如,机密计算架构中的安全级别或领域信息。当 VMM 将设备连接到 vIOMMU 时,它应该创建一个 vDEVICE 对象以转发 VM 中的所有设备信息,这是与将同一设备附加到 vIOMMU 保持的 HWPT_PAGING 分开的 ioctl 调用。

  • IOMMUFD_OBJ_VEVENTQ,表示 vIOMMU 用于报告其事件(例如,发生到嵌套阶段 1 的转换错误(不包括应通过 IOMMUFD_OBJ_FAULT 发生的 I/O 页面错误)和特定于 HW 的事件)的软件队列。此队列对象为用户空间提供一个 FD 来轮询/读取 vIOMMU 事件。必须首先创建一个 vIOMMU 对象以获得其 viommu_id,然后可以使用该 viommu_id 来分配 vEVENTQ。每个 vIOMMU 可以支持多种类型的 vEVENT,但每个 vEVENTQ 类型限制为一个 vEVENTQ。

所有用户可见的对象都通过 IOMMU_DESTROY uAPI 销毁。

下图显示了用户可见对象和内核数据结构(iommufd 外部)之间的关系,数字是指创建对象和链接的操作

 _______________________________________________________________________
|                      iommufd (HWPT_PAGING only)                       |
|                                                                       |
|        [1]                  [3]                                [2]    |
|  ________________      _____________                        ________  |
| |                |    |             |                      |        | |
| |      IOAS      |<---| HWPT_PAGING |<---------------------| DEVICE | |
| |________________|    |_____________|                      |________| |
|         |                    |                                  |     |
|_________|____________________|__________________________________|_____|
          |                    |                                  |
          |              ______v_____                          ___v__
          | PFN storage |  (paging)  |                        |struct|
          |------------>|iommu_domain|<-----------------------|device|
                        |____________|                        |______|

 _______________________________________________________________________
|                      iommufd (with HWPT_NESTED)                       |
|                                                                       |
|        [1]                  [3]                [4]             [2]    |
|  ________________      _____________      _____________     ________  |
| |                |    |             |    |             |   |        | |
| |      IOAS      |<---| HWPT_PAGING |<---| HWPT_NESTED |<--| DEVICE | |
| |________________|    |_____________|    |_____________|   |________| |
|         |                    |                  |               |     |
|_________|____________________|__________________|_______________|_____|
          |                    |                  |               |
          |              ______v_____       ______v_____       ___v__
          | PFN storage |  (paging)  |     |  (nested)  |     |struct|
          |------------>|iommu_domain|<----|iommu_domain|<----|device|
                        |____________|     |____________|     |______|

 _______________________________________________________________________
|                      iommufd (with vIOMMU/vDEVICE)                    |
|                                                                       |
|                             [5]                [6]                    |
|                        _____________      _____________               |
|                       |             |    |             |              |
|      |----------------|    vIOMMU   |<---|   vDEVICE   |<----|        |
|      |                |             |    |_____________|     |        |
|      |                |             |                        |        |
|      |      [1]       |             |          [4]           | [2]    |
|      |     ______     |             |     _____________     _|______  |
|      |    |      |    |     [3]     |    |             |   |        | |
|      |    | IOAS |<---|(HWPT_PAGING)|<---| HWPT_NESTED |<--| DEVICE | |
|      |    |______|    |_____________|    |_____________|   |________| |
|      |        |              |                  |               |     |
|______|________|______________|__________________|_______________|_____|
       |        |              |                  |               |
 ______v_____   |        ______v_____       ______v_____       ___v__
|   struct   |  |  PFN  |  (paging)  |     |  (nested)  |     |struct|
|iommu_device|  |------>|iommu_domain|<----|iommu_domain|<----|device|
|____________|   storage|____________|     |____________|     |______|
  1. IOMMUFD_OBJ_IOAS 是通过 IOMMU_IOAS_ALLOC uAPI 创建的。一个 iommufd 可以保存多个 IOAS 对象。IOAS 是最通用的对象,并且不公开特定于单个 IOMMU 驱动程序的接口。对 IOAS 的所有操作都必须在其中的每个 iommu_domain 上平等地操作。

  2. 当外部驱动程序调用 IOMMUFD kAPI 以将设备绑定到 iommufd 时,会创建 IOMMUFD_OBJ_DEVICE。预期该驱动程序实现一组 ioctl 以允许用户空间启动绑定操作。成功完成此操作将建立对设备的所需 DMA 所有权。驱动程序还必须设置 driver_managed_dma 标志,并且在此操作成功之前不得触摸该设备。

  3. IOMMUFD_OBJ_HWPT_PAGING 可以通过两种方式创建

    • 当外部驱动程序调用 IOMMUFD kAPI 以将绑定的设备附加到 IOAS 时,会自动创建 IOMMUFD_OBJ_HWPT_PAGING。同样,外部驱动程序 uAPI 允许用户空间启动附加操作。如果 IOAS 的 HWPT_PAGING 列表中存在兼容的成员 HWPT_PAGING 对象,则将重复使用它。否则,将创建一个表示用户空间的 iommu_domain 的新 HWPT_PAGING,然后将其添加到列表中。成功完成此操作将设置 IOAS、设备和 iommu_domain 之间的链接。一旦完成,设备就可以进行 DMA。

    • 可以通过 IOMMU_HWPT_ALLOC uAPI 手动创建 IOMMUFD_OBJ_HWPT_PAGING,前提是提供通过 @pt_id 的 ioas_id 以将新的 HWPT_PAGING 关联到相应的 IOAS 对象。此手动分配的好处是允许分配标志(在 enum iommufd_hwpt_alloc_flags 中定义),例如,如果设置了 IOMMU_HWPT_ALLOC_NEST_PARENT 标志,则它会分配一个嵌套父级 HWPT_PAGING。

  4. 只能通过 IOMMU_HWPT_ALLOC uAPI 手动创建 IOMMUFD_OBJ_HWPT_NESTED,前提是提供通过 @pt_id 的 HWPT_PAGING 或封装嵌套父级 HWPT_PAGING 的 vIOMMU 对象的 hwpt_id 或 viommu_id 以将新的 HWPT_NESTED 对象关联到相应的 HWPT_PAGING 对象。关联的 HWPT_PAGING 对象必须是先前通过同一 uAPI 使用 IOMMU_HWPT_ALLOC_NEST_PARENT 标志手动分配的嵌套父级,否则分配将失败。IOMMU 驱动程序将进一步验证该分配以确保嵌套父级域和要分配的嵌套域是兼容的。成功完成此操作将设置 IOAS、设备和 iommu_domains 之间的链接。一旦完成,设备就可以通过两阶段转换(也称为嵌套转换)进行 DMA。请注意,可以通过(然后关联到)同一嵌套父级分配多个 HWPT_NESTED 对象。

    注意

    手动 IOMMUFD_OBJ_HWPT_PAGING 或 IOMMUFD_OBJ_HWPT_NESTED 都是通过同一 IOMMU_HWPT_ALLOC uAPI 创建的。区别在于通过 iommufd_hwpt_alloc 结构的 @pt_id 字段传递的对象类型。

  5. 只能通过 IOMMU_VIOMMU_ALLOC uAPI 手动创建 IOMMUFD_OBJ_VIOMMU,前提是提供 dev_id(用于支持 vIOMMU 的设备的物理 IOMMU)和 hwpt_id(用于将 vIOMMU 关联到嵌套父级 HWPT_PAGING)。iommufd 核心会将 vIOMMU 对象链接到 struct device 后面的 struct iommu_device。并且 IOMMU 驱动程序可以实现一个 viommu_alloc 操作以分配其自己的 vIOMMU 数据结构,该数据结构嵌入了核心级结构 iommufd_viommu 和一些驱动程序特定的数据。如有必要,该驱动程序还可以为该 vIOMMU(以及因此为 VM)配置其 HW 虚拟化特性。成功完成此操作将设置 vIOMMU 对象和 HWPT_PAGING 之间的链接,然后可以使用此 vIOMMU 对象作为嵌套父级对象来分配上面描述的 HWPT_NESTED 对象。

  6. 只能通过 IOMMU_VDEVICE_ALLOC uAPI 手动创建 IOMMUFD_OBJ_VDEVICE,前提是提供 iommufd_viommu 对象的 viommu_id 和 iommufd_device 对象的 dev_id。vDEVICE 对象将是这两个父对象之间的绑定。另一个 @virt_id 也将通过 uAPI 设置,从而为 iommufd 核心提供一个索引,用于将 vDEVICE 对象存储到每个 vIOMMU 的 vDEVICE 数组中。如有必要,IOMMU 驱动程序可以选择实现一个 vdevce_alloc 操作以初始化其 HW 以实现与 vDEVICE 相关的虚拟化特性。成功完成此操作将设置 vIOMMU 和设备之间的链接。

由于 DMA 所有权声明,设备只能绑定到 iommufd,并且最多只能附加到一个 IOAS 对象(尚不支持 PASID)。

内核数据结构

用户可见的对象由以下数据结构支持

  • IOMMUFD_OBJ_IOAS 的 iommufd_ioas。

  • IOMMUFD_OBJ_DEVICE 的 iommufd_device。

  • IOMMUFD_OBJ_HWPT_PAGING 的 iommufd_hwpt_paging。

  • IOMMUFD_OBJ_HWPT_NESTED 的 iommufd_hwpt_nested。

  • IOMMUFD_OBJ_FAULT 的 iommufd_fault。

  • IOMMUFD_OBJ_VIOMMU 的 iommufd_viommu。

  • IOMMUFD_OBJ_VDEVICE 的 iommufd_vdevice。

  • IOMMUFD_OBJ_VEVENTQ 的 iommufd_veventq。

查看这些数据结构时,有几个术语

  • 自动域 - 指将设备附加到 IOAS 对象时自动创建的 iommu 域。这与 VFIO type1 的语义兼容。

  • 手动域 - 指用户指定为要由设备附加的目标页表的 iommu 域。虽然当前没有直接创建此类域的 uAPI,但数据结构和算法已准备好处理该用例。

  • 内核用户 - 指像 VFIO mdev 这样的东西,它使用 IOMMUFD 访问接口来访问 IOAS。这首先创建一个 iommufd_access 对象,该对象类似于绑定物理设备的域。然后,访问对象将允许将 IOVA 范围转换为 struct page * 列表,或对 IOVA 进行直接读/写。

iommufd_ioas 用作元数据数据结构来管理 IOVA 范围如何映射到内存页面,由以下组成

  • 保存 IOVA 映射的 struct io_pagetable

  • 表示已填充 IOVA 部分的 struct iopt_area

  • 表示 PFN 存储的 struct iopt_pages

  • 表示 IOMMU 中的 IO 页表的 struct iommu_domain

  • 表示 PFN 的内核用户的 struct iopt_pages_access

  • struct xarray pinned_pfns,保存由内核用户固定的页面列表

每个 iopt_pages 表示一个完整的 PFN 的逻辑线性数组。PFN 最终通过 mm_struct 从用户空间 VA 派生。一旦固定,PFN 将存储在 iommu_domain 的 IOPTE 中,或者如果它们已通过 iommufd_access 固定,则存储在 pinned_pfns xarray 中。

PFN 必须在所有存储位置的组合之间复制,具体取决于存在哪些域以及存在哪些类型的内核“软件访问”用户。该机制确保一个页面仅固定一次。

io_pagetable 由指向 iopt_pages 的 iopt_areas 组成,以及镜像 IOVA 到 PFN 映射的 iommu_domains 列表。

多个 io_pagetable-s 可以通过它们的 iopt_area-s 共享一个 iopt_pages,这避免了多重固定和页面消耗的双重计算。

iommufd_ioas 可在子系统(例如,VFIO 和 VDPA)之间共享,只要由不同子系统管理的设备绑定到同一个 iommufd。

IOMMUFD 用户 API

通用 ioctl 格式

ioctl 接口遵循通用格式以允许扩展。每个 ioctl 都将一个结构指针作为参数传递,该参数在第一个 u32 中提供结构的大小。内核检查它不理解的任何结构空间是否为 0。这允许用户空间使用向后兼容的部分,同时始终使用更新、更大的结构。

ioctls 对常见错误使用标准含义

  • ENOTTY:根本不支持 IOCTL 编号本身

  • E2BIG:支持 IOCTL 编号,但提供的结构在内核不理解的部分中具有非零值。

  • EOPNOTSUPP:支持 IOCTL 编号,并且理解该结构,但是已知字段具有内核不理解或不支持的值。

  • EINVAL:理解了关于 IOCTL 的所有内容,但字段不正确。

  • ENOENT:提供的 ID 或 IOVA 不存在。

  • ENOMEM:内存不足。

  • EOVERFLOW:数学溢出。

以及特定 ioctl 中的其他错误。

struct iommu_destroy

ioctl(IOMMU_DESTROY)

定义:

struct iommu_destroy {
    __u32 size;
    __u32 id;
};

成员

size

sizeof(struct iommu_destroy)

id

要销毁的 iommufd 对象 ID。可以是任何可销毁的对象类型。

描述

销毁 iommufd 中保存的任何对象。

struct iommu_ioas_alloc

ioctl(IOMMU_IOAS_ALLOC)

定义:

struct iommu_ioas_alloc {
    __u32 size;
    __u32 flags;
    __u32 out_ioas_id;
};

成员

size

sizeof(struct iommu_ioas_alloc)

flags

必须为 0

out_ioas_id

分配的对象的输出 IOAS ID

描述

分配一个 IO 地址空间 (IOAS),该空间保存一个 IO 虚拟地址 (IOVA) 到内存的映射。

struct iommu_iova_range

ioctl(IOMMU_IOVA_RANGE)

定义:

struct iommu_iova_range {
    __aligned_u64 start;
    __aligned_u64 last;
};

成员

start

第一个 IOVA

last

包含性的最后一个 IOVA

描述

IOVA 空间中的一个间隔。

struct iommu_ioas_iova_ranges

ioctl(IOMMU_IOAS_IOVA_RANGES)

定义:

struct iommu_ioas_iova_ranges {
    __u32 size;
    __u32 ioas_id;
    __u32 num_iovas;
    __u32 __reserved;
    __aligned_u64 allowed_iovas;
    __aligned_u64 out_iova_alignment;
};

成员

size

sizeof(struct iommu_ioas_iova_ranges)

ioas_id

要从中读取范围的 IOAS ID

num_iovas

输入/输出 IOAS 中范围的总数

__reserved

必须为 0

allowed_iovas

指向 struct iommu_iova_range 的输出数组的指针

out_iova_alignment

映射 IOVA 所需的最小对齐

描述

查询 IOAS 中允许的 IOVA 范围。不允许在这些范围之外映射 IOVA。num_iovas 将设置为 iova 的总数,并且 allowed_iovas[] 将在空间允许的情况下填充。

允许的范围取决于 DMA 操作采用的 HW 路径,并且可以在 IOAS 的生命周期内更改。一个新的空 IOAS 将具有一个完整范围,并且每个附加的设备将根据该设备的 HW 限制缩小范围。分离设备可以扩大范围。用户空间应在每次附加/分离后查询范围,以了解哪些 IOVA 可用于映射。

在输入时,num_iovas 是 allowed_iovas 数组的长度。在输出时,它是填充的 iova 的总数。如果 num_iovas 太小,则 ioctl 将返回 -EMSGSIZE 并将 num_iovas 设置为所需的值。在这种情况下,调用者应分配一个更大的输出数组并重新发出 ioctl。

out_iova_alignment 返回可以提供给 IOMMU_IOAS_MAP/COPY 的最小 IOVA 对齐方式。IOVA 必须满足

starting_iova % out_iova_alignment == 0
(starting_iova + length) % out_iova_alignment == 0

out_iova_alignment 可以为 1,指示允许任何 IOVA。它不能高于系统 PAGE_SIZE。

struct iommu_ioas_allow_iovas

ioctl(IOMMU_IOAS_ALLOW_IOVAS)

定义:

struct iommu_ioas_allow_iovas {
    __u32 size;
    __u32 ioas_id;
    __u32 num_iovas;
    __u32 __reserved;
    __aligned_u64 allowed_iovas;
};

成员

size

sizeof(struct iommu_ioas_allow_iovas)

ioas_id

要从中允许 IOVA 的 IOAS ID

num_iovas

输入/输出 IOAS 中范围的总数

__reserved

必须为 0

allowed_iovas

指向 struct iommu_iova_range 数组的指针

描述

确保始终可以分配一系列 IOVA。如果此调用成功,则 IOMMU_IOAS_IOVA_RANGES 将永远不会返回比此处提供的范围更窄的 IOVA 范围列表。如果 IOMMU_IOAS_IOVA_RANGES 当前比给定的范围更窄,则此调用将失败。

首次创建 IOAS 时,IOVA_RANGES 将最大程度地调整大小,并且随着设备的附加,IOVA 将根据设备限制缩小。指定允许的范围后,将拒绝任何缩小,即,如果设备需要在允许的范围内进行限制,则设备附加可能会失败。

自动 IOVA 分配也会受到此调用的影响。如果存在允许的 IOVA,则 MAP 将仅在允许的 IOVA 中分配。

此调用将整个允许列表替换为给定的列表。

enum iommufd_ioas_map_flags

映射和复制的标志

常量

IOMMU_IOAS_MAP_FIXED_IOVA

如果清除,内核将计算一个适当的 IOVA 来放置映射

IOMMU_IOAS_MAP_WRITEABLE

允许 DMA 写入此映射

IOMMU_IOAS_MAP_READABLE

允许 DMA 从此映射读取

struct iommu_ioas_map

ioctl(IOMMU_IOAS_MAP)

定义:

struct iommu_ioas_map {
    __u32 size;
    __u32 flags;
    __u32 ioas_id;
    __u32 __reserved;
    __aligned_u64 user_va;
    __aligned_u64 length;
    __aligned_u64 iova;
};

成员

size

sizeof(struct iommu_ioas_map)

flags

enum iommufd_ioas_map_flags 的组合

ioas_id

IOAS ID 用于更改映射

__reserved

必须为 0

user_va

用户空间指针,用于从开始映射

length

要映射的字节数

iova

映射放置的 IOVA。如果设置了 IOMMU_IOAS_MAP_FIXED_IOVA,则必须将其作为输入提供。

描述

从用户指针设置 IOVA 映射。如果指定了 FIXED_IOVA,则映射将在 iova 处建立;否则,将自动选择一个基于保留列表和允许列表的合适位置,并在 iova 中返回。

如果指定了 IOMMU_IOAS_MAP_FIXED_IOVA,则 iova 范围当前必须未使用,不能替换现有的 IOVA。

struct iommu_ioas_map_file

ioctl(IOMMU_IOAS_MAP_FILE)

定义:

struct iommu_ioas_map_file {
    __u32 size;
    __u32 flags;
    __u32 ioas_id;
    __s32 fd;
    __aligned_u64 start;
    __aligned_u64 length;
    __aligned_u64 iova;
};

成员

size

sizeof(struct iommu_ioas_map_file)

flags

与 iommu_ioas_map 相同

ioas_id

与 iommu_ioas_map 相同

fd

要映射的 memfd

start

从文件开头开始映射的字节偏移量

length

与 iommu_ioas_map 相同

iova

与 iommu_ioas_map 相同

描述

从 memfd 文件设置 IOVA 映射。所有其他参数和语义都与 IOMMU_IOAS_MAP 的参数和语义匹配。

struct iommu_ioas_copy

ioctl(IOMMU_IOAS_COPY)

定义:

struct iommu_ioas_copy {
    __u32 size;
    __u32 flags;
    __u32 dst_ioas_id;
    __u32 src_ioas_id;
    __aligned_u64 length;
    __aligned_u64 dst_iova;
    __aligned_u64 src_iova;
};

成员

size

sizeof(struct iommu_ioas_copy)

flags

enum iommufd_ioas_map_flags 的组合

dst_ioas_id

IOAS ID 用于更改映射

src_ioas_id

要从中复制的 IOAS ID

length

要复制和映射的字节数

dst_iova

映射放置的 IOVA。如果设置了 IOMMU_IOAS_MAP_FIXED_IOVA,则必须将其作为输入提供。

src_iova

启动复制的 IOVA

描述

从 src_ioas_id 复制一个已存在的映射,并在 dst_ioas_id 中建立它。src iova/length 必须与 IOMMU_IOAS_MAP 使用的范围完全匹配。

这可以用于有效地将 IOAS 的一个子集克隆到另一个 IOAS,或者作为一种“缓存”来加速映射。与建立等效的新映射相比,复制具有效率优势,因为内部资源是共享的,并且内核只会固定用户内存一次。

struct iommu_ioas_unmap

ioctl(IOMMU_IOAS_UNMAP)

定义:

struct iommu_ioas_unmap {
    __u32 size;
    __u32 ioas_id;
    __aligned_u64 iova;
    __aligned_u64 length;
};

成员

size

sizeof(struct iommu_ioas_unmap)

ioas_id

IOAS ID 用于更改映射

iova

开始取消映射的 IOVA

length

要取消映射的字节数,并返回已取消映射的字节

描述

取消映射一个 IOVA 范围。iova/length 必须是先前使用 IOMMU_IOAS_MAP 或 IOMMU_IOAS_COPY 映射的范围的超集。不允许拆分或截断范围。值 0 到 U64_MAX 将取消映射所有内容。

enum iommufd_option

ioctl(IOMMU_OPTION_RLIMIT_MODE) 和 ioctl(IOMMU_OPTION_HUGE_PAGES)

常量

IOMMU_OPTION_RLIMIT_MODE

更改 RLIMIT_MEMLOCK 记帐的工作方式。调用者必须具有调用此函数的权限。值 0(默认)是基于用户的记帐,1 使用基于进程的记帐。全局选项,object_id 必须为 0

IOMMU_OPTION_HUGE_PAGES

值 1(默认)允许在生成 iommu 映射时组合连续的页面。值 0 禁用组合,所有内容都映射到 PAGE_SIZE。这对于基准测试很有用。这是一个按 IOAS 选项,object_id 必须是 IOAS ID。

enum iommufd_option_ops

ioctl(IOMMU_OPTION_OP_SET) 和 ioctl(IOMMU_OPTION_OP_GET)

常量

IOMMU_OPTION_OP_SET

设置选项的值

IOMMU_OPTION_OP_GET

获取选项的值

struct iommu_option

iommu 选项复用器

定义:

struct iommu_option {
    __u32 size;
    __u32 option_id;
    __u16 op;
    __u16 __reserved;
    __u32 object_id;
    __aligned_u64 val64;
};

成员

size

sizeof(struct iommu_option)

option_id

enum iommufd_option 之一

op

enum iommufd_option_ops 之一

__reserved

必须为 0

object_id

如果需要,对象的 ID

val64

要设置的选项值或在 get 上返回的值

描述

更改一个简单的选项值。此多路复用器允许控制对象的选项。IOMMU_OPTION_OP_SET 将加载一个选项,IOMMU_OPTION_OP_GET 将返回当前值。

enum iommufd_vfio_ioas_op

IOMMU_VFIO_IOAS_* ioctl

常量

IOMMU_VFIO_IOAS_GET

获取当前的兼容性 IOAS

IOMMU_VFIO_IOAS_SET

更改当前的兼容性 IOAS

IOMMU_VFIO_IOAS_CLEAR

禁用 VFIO 兼容性

struct iommu_vfio_ioas

ioctl(IOMMU_VFIO_IOAS)

定义:

struct iommu_vfio_ioas {
    __u32 size;
    __u32 ioas_id;
    __u16 op;
    __u16 __reserved;
};

成员

size

sizeof(struct iommu_vfio_ioas)

ioas_id

对于 IOMMU_VFIO_IOAS_SET,输入要设置的 IOAS ID。对于 IOMMU_VFIO_IOAS_GET,将输出 IOAS ID

op

enum iommufd_vfio_ioas_op 之一

__reserved

必须为 0

描述

VFIO 兼容性支持使用单个 ioas,因为 VFIO API 不支持 ID 字段。设置或获取 VFIO 兼容性将使用的 IOAS。当在 iommufd 上使用 VFIO_GROUP_SET_CONTAINER 时,它将获取兼容性 ioas,无论是通过获取已设置的内容还是自动创建一个。从那时起,VFIO 将继续使用该 ioas,并且不受此 ioctl 的影响。SET 或 CLEAR 不会销毁任何自动创建的 IOAS。

enum iommufd_hwpt_alloc_flags

HWPT 分配的标志

常量

IOMMU_HWPT_ALLOC_NEST_PARENT

如果设置,则分配一个 HWPT,该 HWPT 可以在嵌套配置中用作父 HWPT。

IOMMU_HWPT_ALLOC_DIRTY_TRACKING

在设备连接时强制执行设备 IOMMU 的脏跟踪支持

IOMMU_HWPT_FAULT_ID_VALID

hwpt 分配数据的 fault_id 字段有效。

IOMMU_HWPT_ALLOC_PASID

请求一个可以与 PASID 一起使用的域。该域可以连接到设备上的任何 PASID。连接到设备的非 PASID 部分的任何域也必须标记,否则连接 PASID 将被阻止。对于想要连接 PASID 的用户,不建议为设备的非 PASID 部分和 PASID 部分使用 ioas。如果 IOMMU 不支持 PASID,它将返回错误 (-EOPNOTSUPP)。

enum iommu_hwpt_vtd_s1_flags

Intel VT-d stage-1 页表条目属性

常量

IOMMU_VTD_S1_SRE

Supervisor 请求

IOMMU_VTD_S1_EAFE

扩展访问启用

IOMMU_VTD_S1_WPE

写保护启用

struct iommu_hwpt_vtd_s1

Intel VT-d stage-1 页表信息 (IOMMU_HWPT_DATA_VTD_S1)

定义:

struct iommu_hwpt_vtd_s1 {
    __aligned_u64 flags;
    __aligned_u64 pgtbl_addr;
    __u32 addr_width;
    __u32 __reserved;
};

成员

flags

enum iommu_hwpt_vtd_s1_flags 的组合

pgtbl_addr

stage-1 页表的基地址。

addr_width

stage-1 页表的地址宽度

__reserved

必须为 0

struct iommu_hwpt_arm_smmuv3

ARM SMMUv3 嵌套 STE (IOMMU_HWPT_DATA_ARM_SMMUV3)

定义:

struct iommu_hwpt_arm_smmuv3 {
    __aligned_le64 ste[2];
};

成员

ste

用于转换的用户空间 Stream Table Entry 的前两个双字。必须是小端序。允许的字段:(请参阅 SMMUv3 HW Spec 中的“5.2 Stream Table Entry”) - word-0: V, Cfg, S1Fmt, S1ContextPtr, S1CDMax - word-1: EATS, S1DSS, S1CIR, S1COR, S1CSH, S1STALLD

描述

如果 ste 不合法或包含任何不允许的字段,将返回 -EIO。Cfg 可用于选择 S1、Bypass 或 Abort 配置。Bypass 嵌套域将与嵌套父域以相同的方式进行转换。S1 将安装一个指向用户空间内存的 Context Descriptor Table,该内存由嵌套父域进行转换。

enum iommu_hwpt_data_type

IOMMU HWPT 数据类型

常量

IOMMU_HWPT_DATA_NONE

无数据

IOMMU_HWPT_DATA_VTD_S1

Intel VT-d stage-1 页表

IOMMU_HWPT_DATA_ARM_SMMUV3

ARM SMMUv3 Context Descriptor Table

struct iommu_hwpt_alloc

ioctl(IOMMU_HWPT_ALLOC)

定义:

struct iommu_hwpt_alloc {
    __u32 size;
    __u32 flags;
    __u32 dev_id;
    __u32 pt_id;
    __u32 out_hwpt_id;
    __u32 __reserved;
    __u32 data_type;
    __u32 data_len;
    __aligned_u64 data_uptr;
    __u32 fault_id;
    __u32 __reserved2;
};

成员

size

sizeof(struct iommu_hwpt_alloc)

flags

enum iommufd_hwpt_alloc_flags 的组合

dev_id

要为其分配此 HWPT 的设备

pt_id

要将此 HWPT 连接到的 IOAS 或 HWPT 或 vIOMMU

out_hwpt_id

新 HWPT 的 ID

__reserved

必须为 0

data_type

enum iommu_hwpt_data_type 之一

data_len

类型特定数据的长度

data_uptr

指向类型特定数据的用户指针

fault_id

IOMMUFD_FAULT 对象的 ID。仅当设置了 IOMMU_HWPT_FAULT_ID_VALID 的标志字段时有效。

__reserved2

填充到 64 位对齐。必须为 0。

描述

显式分配一个硬件页表对象。这与 iommufd_device_attach() 返回的对象类型相同,并且表示底层 iommu 驱动程序的 iommu_domain 内核对象。

将创建一个内核管理的 HWPT,其中包含来自给定 IOAS 的映射,通过 pt_id。此分配的 data_type 必须设置为 IOMMU_HWPT_DATA_NONE。通过 flags 传递 IOMMU_HWPT_ALLOC_NEST_PARENT,可以将 HWPT 分配为嵌套配置的父 HWPT。

将通过给定的 vIOMMU(包装父 HWPT)或父 HWPT(通过 pt_id)创建一个用户管理的嵌套 HWPT,其中父 HWPT 必须先前通过相同的 ioctl 从给定的 IOAS (pt_id) 分配。在这种情况下,data_type 必须设置为与底层 IOMMU 硬件支持的 I/O 页表类型相对应的预定义类型。通过 dev_id 的设备和通过 pt_id 的 vIOMMU 必须与同一个 IOMMU 实例关联。

如果 data_type 设置为 IOMMU_HWPT_DATA_NONE,则 data_lendata_uptr 应为零。否则,必须同时给出 data_lendata_uptr

enum iommu_hw_info_vtd_flags

VT-d hw_info 的标志

常量

IOMMU_HW_INFO_VTD_ERRATA_772415_SPR17

如果设置,则不允许在 nested_parent 域上进行只读映射。https://www.intel.com/content/www/us/en/content-details/772415/content-details.html

struct iommu_hw_info_vtd

Intel VT-d 硬件信息

定义:

struct iommu_hw_info_vtd {
    __u32 flags;
    __u32 __reserved;
    __aligned_u64 cap_reg;
    __aligned_u64 ecap_reg;
};

成员

flags

enum iommu_hw_info_vtd_flags 的组合

__reserved

必须为 0

cap_reg

在 VT-d 规范第 11.4.2 节 Capability Register 中定义的 Intel VT-d capability 寄存器的值。

ecap_reg

在 VT-d 规范第 11.4.3 节 Extended Capability Register 中定义的 Intel VT-d capability 寄存器的值。

描述

用户需要了解 Intel VT-d 规范来解码寄存器值。

struct iommu_hw_info_arm_smmuv3

ARM SMMUv3 硬件信息 (IOMMU_HW_INFO_TYPE_ARM_SMMUV3)

定义:

struct iommu_hw_info_arm_smmuv3 {
    __u32 flags;
    __u32 __reserved;
    __u32 idr[6];
    __u32 iidr;
    __u32 aidr;
};

成员

flags

必须设置为 0

__reserved

必须为 0

idr

ARM SMMU 非安全编程接口的已实现功能

iidr

有关 ARM SMMU 的实现和实现者以及支持的架构版本的信息

aidr

ARM SMMU 架构版本

描述

有关 idriidraidr 的详细信息,请参阅 SMMUv3 Spec 中的 6.3.1 到 6.3.6 章。

这报告了原始的 HW 功能,并非所有位对于 userspace 来说都是有意义的。只应使用以下字段

idr[0]: ST_LEVEL, TERM_MODEL, STALL_MODEL, TTENDIAN , CD2L, ASID16, TTF idr[1]: SIDSIZE, SSIDSIZE idr[3]: BBML, RIL idr[5]: VAX, GRAN64K, GRAN16K, GRAN4K

  • 如果可以创建 NESTED HWPT,则应假定 S1P 为 true

  • VFIO/iommufd 仅支持具有 COHACC 的平台,应假定为 true。

  • ATS 是一个按设备属性。如果 VMM 在 ACPI/DT 中将任何设备描述为具有 ATS 功能,则应设置相应的 idr。

此列表可能会在将来扩展(例如 E0PD、AIE、PBHA、D128、DS 等)。重要的是,VMM 不要读取列表之外的位,以允许与未来内核兼容。SMMUv3 架构中的几个功能当前内核不支持嵌套:HTTU、BTM、MPAM 等。

enum iommu_hw_info_type

IOMMU 硬件信息类型

常量

IOMMU_HW_INFO_TYPE_NONE

由不报告硬件信息的驱动程序使用

IOMMU_HW_INFO_TYPE_INTEL_VTD

Intel VT-d iommu 信息类型

IOMMU_HW_INFO_TYPE_ARM_SMMUV3

ARM SMMUv3 iommu 信息类型

enum iommufd_hw_capabilities

常量

IOMMU_HW_CAP_DIRTY_TRACKING

IOMMU 硬件支持脏跟踪。如果可用,则表示支持以下 API

IOMMU_HWPT_GET_DIRTY_BITMAP IOMMU_HWPT_SET_DIRTY_TRACKING

IOMMU_HW_CAP_PCI_PASID_EXEC

支持执行权限,当 struct iommu_hw_info::out_max_pasid_log2 为零时,用户忽略它。

IOMMU_HW_CAP_PCI_PASID_PRIV

支持特权模式,当 struct iommu_hw_info::out_max_pasid_log2 为零时,用户忽略它。

struct iommu_hw_info

ioctl(IOMMU_GET_HW_INFO)

定义:

struct iommu_hw_info {
    __u32 size;
    __u32 flags;
    __u32 dev_id;
    __u32 data_len;
    __aligned_u64 data_uptr;
    __u32 out_data_type;
    __u8 out_max_pasid_log2;
    __u8 __reserved[3];
    __aligned_u64 out_capabilities;
};

成员

size

sizeof(struct iommu_hw_info)

flags

必须为 0

dev_id

绑定到 iommufd 的设备

data_len

输入用户缓冲区长度(以字节为单位)。输出内核支持的数据长度

data_uptr

指向用户空间缓冲区的用户指针,内核使用该缓冲区填充 iommu 类型特定的硬件信息数据

out_data_type

输出 iommu 硬件信息类型,如 enum iommu_hw_info_type 中所定义。

out_max_pasid_log2

输出 PASID 的宽度。0 表示不支持 PASID。PCI 设备转向 out_capabilities 以检查是否支持特定功能。

__reserved

必须为 0

out_capabilities

输出通用 iommu 功能信息类型,如 enum iommu_hw_capabilities 中所定义。

描述

从绑定到 iommufd 的给定设备后面的 iommu 查询 iommu 类型特定的硬件信息数据。此硬件信息数据将用于同步虚拟 iommu 和物理 iommu 之间的功能,例如,嵌套转换设置需要检查硬件信息,以便访客 stage-1 页表可以与物理 iommu 兼容。

要捕获 iommu 类型特定的硬件信息数据,必须提供 data_uptr 及其长度 data_len。如果用户缓冲区大于内核拥有的数据,则尾随字节将被置零。否则,内核只使用 data_len 中给定的长度填充缓冲区。如果 ioctl 成功,data_len 将更新为内核实际支持的长度,out_data_type 将被填充以解码 data_uptr 指向的缓冲区中填充的数据。允许输入 data_len == 零。

struct iommu_hwpt_set_dirty_tracking

ioctl(IOMMU_HWPT_SET_DIRTY_TRACKING)

定义:

struct iommu_hwpt_set_dirty_tracking {
    __u32 size;
    __u32 flags;
    __u32 hwpt_id;
    __u32 __reserved;
};

成员

size

sizeof(struct iommu_hwpt_set_dirty_tracking)

flags

enum iommufd_hwpt_set_dirty_tracking_flags 的组合

hwpt_id

表示 IOMMU 域的 HW 页表 ID

__reserved

必须为 0

描述

切换 HW 页表上的脏页追踪。

enum iommufd_hwpt_get_dirty_bitmap_flags

获取脏位的标志

常量

IOMMU_HWPT_GET_DIRTY_BITMAP_NO_CLEAR

仅读取 PTE,而不清除任何脏位元数据。可以在期望下一次操作是对相同 IOVA 范围进行取消映射的情况下传递此标志。

struct iommu_hwpt_get_dirty_bitmap

ioctl(IOMMU_HWPT_GET_DIRTY_BITMAP)

定义:

struct iommu_hwpt_get_dirty_bitmap {
    __u32 size;
    __u32 hwpt_id;
    __u32 flags;
    __u32 __reserved;
    __aligned_u64 iova;
    __aligned_u64 length;
    __aligned_u64 page_size;
    __aligned_u64 data;
};

成员

size

sizeof(struct iommu_hwpt_get_dirty_bitmap)

hwpt_id

表示 IOMMU 域的 HW 页表 ID

flags

enum iommufd_hwpt_get_dirty_bitmap_flags 的组合

__reserved

必须为 0

iova

位图第一位的基本 IOVA

length

IOVA 范围大小

page_size

位图中每个位的页面大小粒度

data

用于设置脏位的位图。位图中的每个位代表一个 page_size,它与任意 iova 偏差。

描述

检查给定的 IOVA 是否为脏页

data[(iova / page_size) / 64] & (1ULL << ((iova / page_size) % 64))

遍历给定 IOVA 范围的 IOMMU 页表,以返回包含脏 IOVA 的位图。这样做的同时,默认情况下还会清除 IOPTE 中设置的所有脏位元数据。

enum iommu_hwpt_invalidate_data_type

IOMMU HWPT 缓存无效化数据类型

常量

IOMMU_HWPT_INVALIDATE_DATA_VTD_S1

VTD_S1 的无效化数据

IOMMU_VIOMMU_INVALIDATE_DATA_ARM_SMMUV3

ARM SMMUv3 的无效化数据

enum iommu_hwpt_vtd_s1_invalidate_flags

Intel VT-d 阶段 1 缓存无效化的标志

常量

IOMMU_VTD_INV_FLAGS_LEAF

指示无效化是应用于所有级别的页面结构缓存,还是仅应用于叶 PTE 缓存。

struct iommu_hwpt_vtd_s1_invalidate

Intel VT-d 缓存无效化 (IOMMU_HWPT_INVALIDATE_DATA_VTD_S1)

定义:

struct iommu_hwpt_vtd_s1_invalidate {
    __aligned_u64 addr;
    __aligned_u64 npages;
    __u32 flags;
    __u32 __reserved;
};

成员

addr

要无效化的范围的起始地址。 它需要 4KB 对齐。

npages

要无效化的连续 4K 页面的数量。

flags

enum iommu_hwpt_vtd_s1_invalidate_flags 的组合

__reserved

必须为 0

描述

用于嵌套转换中用户管理的阶段 1 缓存无效化的 Intel VT-d 特定无效化数据。 用户空间使用此结构在修改阶段 1 页表后告诉受影响的缓存范围。

通过将 addr 设置为 0 并将 npages 设置为 U64_MAX 来使所有与页表相关的缓存无效。

如果启用了 ATS,则设备 TLB 将自动无效。

struct iommu_viommu_arm_smmuv3_invalidate

ARM SMMUv3 缓存无效化 (IOMMU_VIOMMU_INVALIDATE_DATA_ARM_SMMUV3)

定义:

struct iommu_viommu_arm_smmuv3_invalidate {
    __aligned_le64 cmd[2];
};

成员

cmd

在 SMMU CMDQ 中运行的 128 位缓存无效化命令。 必须是小端字节序。

描述

仅在通过 hwpt_id 传入 vIOMMU 时支持的命令列表

CMDQ_OP_TLBI_NSNH_ALL CMDQ_OP_TLBI_NH_VA CMDQ_OP_TLBI_NH_VAA CMDQ_OP_TLBI_NH_ALL CMDQ_OP_TLBI_NH_ASID CMDQ_OP_ATC_INV CMDQ_OP_CFGI_CD CMDQ_OP_CFGI_CD_ALL

如果不支持该命令,将返回 -EIO。

struct iommu_hwpt_invalidate

ioctl(IOMMU_HWPT_INVALIDATE)

定义:

struct iommu_hwpt_invalidate {
    __u32 size;
    __u32 hwpt_id;
    __aligned_u64 data_uptr;
    __u32 data_type;
    __u32 entry_len;
    __u32 entry_num;
    __u32 __reserved;
};

成员

size

sizeof(struct iommu_hwpt_invalidate)

hwpt_id

用于缓存无效化的嵌套 HWPT 或 vIOMMU 的 ID

data_uptr

指向驱动程序特定缓存无效化数据数组的用户指针。

data_type

enum iommu_hwpt_invalidate_data_type 之一,定义无效化请求数组中所有条目的数据类型。 它应该是 hwpt_id 指向的 hwpt 支持的类型。

entry_len

请求数组中请求条目的长度(以字节为单位)

entry_num

输入数组中缓存无效化请求的数量。 输出内核成功处理的请求数量。

__reserved

必须为 0。

描述

使 iommu 缓存对用户管理的页表或 vIOMMU 无效。 如果通过 hwpt_id 传入 HWPT,则在修改用户管理的页表后,应执行此操作。 如果通过 hwpt_id 字段传入 vIOMMU,则可以刷新其他缓存,例如设备缓存或描述符缓存。

每个 ioctl 可以支持数组中的一个或多个缓存无效化请求,数组的总大小为 entry_len * entry_num

允许通过设置 entry_num**==0 来使用空的无效化请求数组,在这种情况下,将忽略 **entry_lendata_uptr。 这可用于检查内核是否支持给定的 data_type

enum iommu_hwpt_pgfault_flags

struct iommu_hwpt_pgfault 的标志

常量

IOMMU_PGFAULT_FLAGS_PASID_VALID

故障数据的 pasid 字段有效。

IOMMU_PGFAULT_FLAGS_LAST_PAGE

它是故障组的最后一个故障。

enum iommu_hwpt_pgfault_perm

struct iommu_hwpt_pgfault 的权限位

常量

IOMMU_PGFAULT_PERM_READ

请求读取权限

IOMMU_PGFAULT_PERM_WRITE

请求写入权限

IOMMU_PGFAULT_PERM_EXEC

(PCIE 10.4.1) 使用 PASID TLP 前缀中设置了 Execute Requested 位的 PASID 发出的请求。

IOMMU_PGFAULT_PERM_PRIV

(PCIE 10.4.1) 使用 PASID TLP 前缀中设置了 Privileged Mode Requested 位的 PASID 发出的请求。

struct iommu_hwpt_pgfault

iommu 页错误数据

定义:

struct iommu_hwpt_pgfault {
    __u32 flags;
    __u32 dev_id;
    __u32 pasid;
    __u32 grpid;
    __u32 perm;
    __u32 __reserved;
    __aligned_u64 addr;
    __u32 length;
    __u32 cookie;
};

成员

flags

enum iommu_hwpt_pgfault_flags 的组合

dev_id

发起设备的 ID

pasid

进程地址空间 ID

grpid

页面请求组索引

perm

enum iommu_hwpt_pgfault_perm 的组合

__reserved

必须为 0。

addr

错误地址

length

请求者希望提取多少数据的提示。 例如,如果 PRI 发起者知道它将进行 10MB 的传输,则它可以填写 10MB,并且 OS 可以预先错误地读取 10MB 的 IOVA。 如果没有这样的提示,则默认为 0。

cookie

内核管理的 cookie,用于标识一组故障消息。 应在响应消息中回显该组的最后一个页面错误中编码的 cookie 编号。

enum iommufd_page_response_code

故障处理程序的返回状态

常量

IOMMUFD_PAGE_RESP_SUCCESS

已处理故障并填充了页表,请重试访问。 这是 PCI 10.4.2.1 中定义的“成功”。

IOMMUFD_PAGE_RESP_INVALID

无法处理此故障,请勿重试访问。 这是 PCI 10.4.2.1 中的“无效请求”。

struct iommu_hwpt_page_response

IOMMU 页错误响应

定义:

struct iommu_hwpt_page_response {
    __u32 cookie;
    __u32 code;
};

成员

cookie

在故障消息中报告的内核管理的 cookie。

code

enum iommufd_page_response_code 中的响应代码之一。

struct iommu_fault_alloc

ioctl(IOMMU_FAULT_QUEUE_ALLOC)

定义:

struct iommu_fault_alloc {
    __u32 size;
    __u32 flags;
    __u32 out_fault_id;
    __u32 out_fault_fd;
};

成员

size

sizeof(struct iommu_fault_alloc)

flags

必须为 0

out_fault_id

新 FAULT 的 ID

out_fault_fd

新 FAULT 的 fd

描述

显式分配故障处理对象。

enum iommu_viommu_type

虚拟 IOMMU 类型

常量

IOMMU_VIOMMU_TYPE_DEFAULT

保留供将来使用

IOMMU_VIOMMU_TYPE_ARM_SMMUV3

ARM SMMUv3 驱动程序特定类型

struct iommu_viommu_alloc

ioctl(IOMMU_VIOMMU_ALLOC)

定义:

struct iommu_viommu_alloc {
    __u32 size;
    __u32 flags;
    __u32 type;
    __u32 dev_id;
    __u32 hwpt_id;
    __u32 out_viommu_id;
};

成员

size

sizeof(struct iommu_viommu_alloc)

flags

必须为 0

type

虚拟 IOMMU 的类型。 必须在 enum iommu_viommu_type 中定义

dev_id

设备的物理 IOMMU 将用于支持虚拟 IOMMU

hwpt_id

要关联的嵌套父 HWPT 的 ID

out_viommu_id

已分配对象的输出虚拟 IOMMU ID

描述

分配一个虚拟 IOMMU 对象,表示底层物理 IOMMU 的虚拟化支持,该支持是真实 IOMMU HW 的安全隔离切片,对于特定的 VM 是唯一的。 全局到 IOMMU 的操作连接到 vIOMMU,例如: - 访客拥有的 ID 的安全命名空间,例如访客控制的缓存标记 - 非设备关联的事件报告,例如无效化队列错误 - 跨物理 IOMMU 访问可共享的嵌套父页表 - 各种平台 ID 的虚拟化,例如 RID 等 - 提供准虚拟化无效 - 直接分配的无效化队列 - 直接分配的中断

struct iommu_vdevice_alloc

ioctl(IOMMU_VDEVICE_ALLOC)

定义:

struct iommu_vdevice_alloc {
    __u32 size;
    __u32 viommu_id;
    __u32 dev_id;
    __u32 out_vdevice_id;
    __aligned_u64 virt_id;
};

成员

size

sizeof(struct iommu_vdevice_alloc)

viommu_id

要与虚拟设备关联的 vIOMMU ID

dev_id

要在 vIOMMU 上分配虚拟实例的物理设备

out_vdevice_id

vDevice 的对象句柄。 传递给 IOMMU_DESTORY

virt_id

每个 vIOMMU 的虚拟设备 ID,例如 ARM SMMUv3 的 vSID、AMD IOMMU 的 vDeviceID 以及嵌套的 Intel VT-d 到上下文表的 vRID

描述

针对 vIOMMU 分配一个虚拟设备实例(对于物理设备)。 此实例在 VM 中保存设备的信息(与其 vIOMMU 相关)。

struct iommu_ioas_change_process

ioctl(VFIO_IOAS_CHANGE_PROCESS)

定义:

struct iommu_ioas_change_process {
    __u32 size;
    __u32 __reserved;
};

成员

size

sizeof(struct iommu_ioas_change_process)

__reserved

必须为 0

描述

这将上下文中每个 IOAS 中每个内存映射的固定内存计数转移到当前进程。 这仅支持使用 IOMMU_IOAS_MAP_FILE 创建的映射,如果存在其他映射,则返回 EINVAL。 如果 ioctl 返回失败状态,则不会进行任何更改。

此 API 对于将设备的操作从一个进程转移到另一个进程很有用,例如在用户空间实时更新期间。

enum iommu_veventq_flag

struct iommufd_vevent_header 的标志

常量

IOMMU_VEVENTQ_FLAG_LOST_EVENTS

vEVENTQ 已丢失 vEVENT

struct iommufd_vevent_header

vEVENTQ 状态的虚拟事件标头

定义:

struct iommufd_vevent_header {
    __u32 flags;
    __u32 sequence;
};

成员

flags

enum iommu_veventq_flag 的组合

sequence

vEVENTQ 中 vEVENT 的序列索引,范围为 [0, INT_MAX],其中 INT_MAX 的下一个索引为 0

描述

每个 iommufd_vevent_header 报告以下 vEVENT 的序列索引

header0 {sequence=0}

data0

header1 {sequence=1}

data1

...

dataN

并且此序列索引预计对于前一个 vEVENT 的序列索引是单调的。 如果两个相邻的序列索引的增量大于 1,则表示已丢失增量 - 1 个 vEVENT,例如,丢失了两个 vEVENT

...

header3 {sequence=3}

data3

header6 {sequence=6}

data6

...

如果 vEVENT 在 vEVENTQ 的尾部丢失,并且没有后续 vEVENT 提供下一个序列索引,则会将 IOMMU_VEVENTQ_FLAG_LOST_EVENTS 标头添加到尾部,并且此标头后不会跟随任何数据

header3 {sequence=3}

data3

header4 {flags=LOST_EVENTS, sequence=4}

enum iommu_veventq_type

虚拟事件队列类型

常量

IOMMU_VEVENTQ_TYPE_DEFAULT

保留供将来使用

IOMMU_VEVENTQ_TYPE_ARM_SMMUV3

ARM SMMUv3 虚拟事件队列

struct iommu_vevent_arm_smmuv3

ARM SMMUv3 虚拟事件 (IOMMU_VEVENTQ_TYPE_ARM_SMMUV3)

定义:

struct iommu_vevent_arm_smmuv3 {
    __aligned_le64 evt[4];
};

成员

evt

256 位 ARM SMMUv3 事件记录,小端字节序。 报告的事件记录:(请参阅 SMMUv3 HW Spec 中的“7.3 Event records”) - 0x04 C_BAD_STE - 0x06 F_STREAM_DISABLED - 0x08 C_BAD_SUBSTREAMID - 0x0a C_BAD_CD - 0x10 F_TRANSLATION - 0x11 F_ADDR_SIZE - 0x12 F_ACCESS - 0x13 F_PERMISSION

描述

StreamID 字段报告虚拟设备 ID。 要接收设备的虚拟事件,必须通过 IOMMU_VDEVICE_ALLOC 分配 vDEVICE。

struct iommu_veventq_alloc

ioctl(IOMMU_VEVENTQ_ALLOC)

定义:

struct iommu_veventq_alloc {
    __u32 size;
    __u32 flags;
    __u32 viommu_id;
    __u32 type;
    __u32 veventq_depth;
    __u32 out_veventq_id;
    __u32 out_veventq_fd;
    __u32 __reserved;
};

成员

size

sizeof(struct iommu_veventq_alloc)

flags

必须为 0

viommu_id

要与 vEVENTQ 关联的虚拟 IOMMU ID

type

vEVENTQ 的类型。 必须在 enum iommu_veventq_type 中定义

veventq_depth

vEVENTQ 中的最大事件数

out_veventq_id

新 vEVENTQ 的 ID

out_veventq_fd

新 vEVENTQ 的 fd。 用户空间在使用后必须关闭成功返回的 fd

__reserved

必须为 0

描述

显式分配 vIOMMU 的虚拟事件队列接口。 vIOMMU 可以具有不同类型的多个 FD,但每个 type 仅限于一个。 用户空间应打开 out_veventq_fd 以从 vEVENTQ 中读取 vEVENT,如果存在可用的 vEVENT。 如果 vEVENT 的数量达到 veventq_depth,则 vEVENTQ 将因溢出而丢失事件。

在 vEVENTQ 中的每个 vEVENT 包含一个 struct iommufd_vevent_header,后跟一个特定类型的数据结构,在正常情况下

header0

data0

header1

data1

...

headerN

dataN

除非记录了尾部的 IOMMU_VEVENTQ_FLAG_LOST_EVENTS 头部 (参考 struct iommufd_vevent_header)。

IOMMUFD 内核 API

IOMMUFD kAPI 以设备为中心,组相关技巧在幕后管理。这允许调用此 kAPI 的外部驱动程序实现一个简单的以设备为中心的 uAPI,用于将其设备连接到 iommufd,而不是像 VFIO 那样在其 uAPI 中显式强制组语义。

struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx, struct device *dev, u32 *id)

将物理设备绑定到 iommu fd

参数

struct iommufd_ctx *ictx

iommufd 文件描述符

struct device *dev

指向物理设备结构的指针

u32 *id

要返回给用户空间的此设备的输出 ID 号

描述

成功的绑定会建立设备的所有权并返回 struct iommufd_device 指针,否则返回错误指针。

使用此 API 的驱动程序必须设置 driver_managed_dma 并且在此例程成功并建立所有权之前不得触碰设备。

绑定 PCI 设备会将整个 RID 置于 iommufd 控制之下。

调用者必须使用 iommufd_device_unbind() 来撤消此操作。

bool iommufd_ctx_has_group(struct iommufd_ctx *ictx, struct iommu_group *group)

如果组中的任何设备绑定到 ictx,则为 True

参数

struct iommufd_ctx *ictx

iommufd 文件描述符

struct iommu_group *group

指向物理 iommu_group 结构的指针

描述

如果组中的任何设备已绑定到此 ictx,例如通过 iommufd_device_bind(),则表示 ictx 拥有该组。

void iommufd_device_unbind(struct iommufd_device *idev)

撤消 iommufd_device_bind()

参数

struct iommufd_device *idev

iommufd_device_bind() 返回的设备

描述

从 iommufd 控制中释放设备。DMA 所有权将返回到未拥有的状态,DMA 由 DMA API 控制。这会使 iommufd_device 指针失效,不得并发调用使用它的其他 API。

int iommufd_device_attach(struct iommufd_device *idev, ioasid_t pasid, u32 *pt_id)

将设备/pasid 连接到 iommu_domain

参数

struct iommufd_device *idev

要附加的设备

ioasid_t pasid

要附加的 pasid

u32 *pt_id

输入一个 IOMMUFD_OBJ_IOAS 或 IOMMUFD_OBJ_HWPT_PAGING 输出 IOMMUFD_OBJ_HWPT_PAGING ID

描述

这会将设备/pasid 连接到 iommu_domain,自动或手动选择。 完成此操作后,设备可以使用 **pasid** 执行 DMA。 如果此附件不用于 pasid,则 **pasid** 为 IOMMU_NO_PASID。

调用者应将结果 pt_id 返回给用户空间。 此函数通过调用 iommufd_device_detach() 来撤消。

int iommufd_device_replace(struct iommufd_device *idev, ioasid_t pasid, u32 *pt_id)

更改设备/pasid 的 iommu_domain

参数

struct iommufd_device *idev

要更改的设备

ioasid_t pasid

要更改的 pasid

u32 *pt_id

输入一个 IOMMUFD_OBJ_IOAS 或 IOMMUFD_OBJ_HWPT_PAGING 输出 IOMMUFD_OBJ_HWPT_PAGING ID

描述

这和

iommufd_device_detach();
iommufd_device_attach();

如果失败,则不会对附件进行任何更改。 iommu 驱动程序可以实现此功能,因此翻译中没有中断。 只有在 iommufd_device_attach() 已经成功后才能调用此函数。 对于没有 pasid 的情况,**pasid** 为 IOMMU_NO_PASID。

void iommufd_device_detach(struct iommufd_device *idev, ioasid_t pasid)

断开设备/设备与 iommu_domain 的连接

参数

struct iommufd_device *idev

要分离的设备

ioasid_t pasid

要分离的 pasid

描述

撤消 iommufd_device_attach()。 这会将 idev 从先前连接的 pt_id 断开连接。 该设备返回到阻止的 DMA 转换。 对于没有 pasid 的情况,**pasid** 为 IOMMU_NO_PASID。

struct iommufd_access *iommufd_access_create(struct iommufd_ctx *ictx, const struct iommufd_access_ops *ops, void *data, u32 *id)

创建一个 iommufd_access

参数

struct iommufd_ctx *ictx

iommufd 文件描述符

const struct iommufd_access_ops *ops

驱动程序的 ops 与访问相关联

void *data

要传递到 ops 函数中的不透明数据

u32 *id

要返回给用户空间的此访问的输出 ID 号

描述

iommufd_access 允许驱动程序在不使用 DMA 的情况下读取/写入 IOAS。 可以使用 iommufd_access_pin_pages()iommufd_access_rw() 函数访问底层 CPU 内存。

提供的 ops 需要使用 iommufd_access_pin_pages()

void iommufd_access_destroy(struct iommufd_access *access)

销毁 iommufd_access

参数

struct iommufd_access *access

要销毁的访问

描述

调用者必须先停止使用访问,然后再销毁它。

void iommufd_access_unpin_pages(struct iommufd_access *access, unsigned long iova, unsigned long length)

撤消 iommufd_access_pin_pages

参数

struct iommufd_access *access

要操作的 IOAS 访问

unsigned long iova

起始 IOVA

unsigned long length

要访问的字节数

描述

返回 struct page。 调用者必须在调用此函数之前停止访问它们。 iova/length 必须与提供给 access_pages 的完全匹配。

int iommufd_access_pin_pages(struct iommufd_access *access, unsigned long iova, unsigned long length, struct page **out_pages, unsigned int flags)

返回 iova 下的页面列表

参数

struct iommufd_access *access

要操作的 IOAS 访问

unsigned long iova

起始 IOVA

unsigned long length

要访问的字节数

struct page **out_pages

输出页面列表

unsigned int flags

IOPMMUFD_ACCESS_RW_* 标志

描述

读取从 iova 开始的 **length** 个字节,并返回 struct page * 指针。 这些可以由调用者进行 kmap 以进行 CPU 访问。

调用者必须在完成时执行 iommufd_access_unpin_pages() 以平衡此操作。

此 API 始终需要页面对齐的 iova。 如果 ioas 对齐 >= PAGE_SIZE 并且 iova 是 PAGE_SIZE 对齐的,则会自然发生这种情况。 但是,较小的对齐方式存在一些极端情况,否则此 API 可能会在对齐的 iova 上失败。

int iommufd_access_rw(struct iommufd_access *access, unsigned long iova, void *data, size_t length, unsigned int flags)

读取或写入 iova 下的数据

参数

struct iommufd_access *access

要操作的 IOAS 访问

unsigned long iova

起始 IOVA

void *data

要复制到/从复制的内核缓冲区

size_t length

要访问的字节数

unsigned int flags

IOMMUFD_ACCESS_RW_* 标志

描述

将内核数据复制到/从 IOVA/length 给定的范围内。 如果标志指示 IOMMUFD_ACCESS_RW_KTHREAD,则可以通过将其更改为 copy_to/from_user() 来优化大型副本。

void iommufd_ctx_get(struct iommufd_ctx *ictx)

获取上下文引用

参数

struct iommufd_ctx *ictx

要获取的上下文

描述

调用者必须已经拥有对 ictx 的有效引用。

struct iommufd_ctx *iommufd_ctx_from_file(struct file *file)

获取 iommufd 上下文的引用

参数

struct file *file

从中获取引用的文件

描述

返回指向 iommufd_ctx 的指针,否则返回 ERR_PTR。 struct file 仍归调用者所有,调用者仍必须执行 fput。 成功后,调用者有责任调用 iommufd_ctx_put()

struct iommufd_ctx *iommufd_ctx_from_fd(int fd)

获取 iommufd 上下文的引用

参数

int fd

从中获取引用的文件描述符

描述

返回指向 iommufd_ctx 的指针,否则返回 ERR_PTR。 成功后,调用者有责任调用 iommufd_ctx_put()

void iommufd_ctx_put(struct iommufd_ctx *ictx)

放回引用

参数

struct iommufd_ctx *ictx

要放回的上下文

VFIO 和 IOMMUFD

可以通过两种方式将 VFIO 设备连接到 iommufd。

第一种是 VFIO 兼容方式,通过直接实现 /dev/vfio/vfio 容器 IOCTL,将其映射到 io_pagetable 操作中。 这样做允许通过将 /dev/vfio/vfio 符号链接到 /dev/iommufd 或扩展 VFIO 来使用 iommufd 在传统的 VFIO 应用程序中,使用 iommufd 而不是容器 fd 来 SET_CONTAINER。

第二种方法直接扩展 VFIO 以支持一组新的基于设备的用户 API,这些 API 基于前面提到的 IOMMUFD 内核 API。 它需要用户空间更改,但更好地匹配了 IOMMUFD API 语义,并且与第一种方法相比,更容易支持新的 iommufd 功能。

目前,这两种方法仍在进行中。

正如 iommufd_vfio_check_extension() 中记录的那样,要赶上 VFIO type1,仍然有一些差距需要解决。

未来的待办事项

目前,IOMMUFD 仅支持内核管理的 I/O 页表,类似于 VFIO type1。 雷达上的新功能包括

  • 将 iommu_domain 绑定到 PASID/SSID

  • 用户空间页表,适用于 ARM、x86 和 S390

  • 绕过内核的用户页表失效

  • 在 IOMMU 中重用 KVM 页表

  • 在 IOMMU 中跟踪脏页

  • 运行时增加/减少 IOPTE 大小

  • 在用户空间解决错误的 PRI 支持