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_domain 列表。

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

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

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

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

  • IOMMUFD_OBJ_VIOMMU,表示传递给 VM 或与 VM 共享的物理 IOMMU 实例的一部分。它可能是一些硬件加速的虚拟化功能和 VM 使用的一些软件资源。 例如

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

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

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

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

    • 传递半虚拟化失效

    • 直接分配的失效队列

    • 直接分配的中断

    这样的 vIOMMU 对象通常可以访问嵌套的父页表,以支持一些硬件加速的虚拟化功能。因此,必须给定一个嵌套的父 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,以便传递的设备连接到其自己的虚拟化 IOMMU,这些 IOMMU 由相应的 vIOMMU 对象支持,在这种情况下,客户机操作系统将自然地执行“分派”,而不是 VMM 陷阱。

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

所有用户可见的对象都通过 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 对象,则将重用它。否则,将创建一个新的 HWPT_PAGING,该 HWPT_PAGING 表示用户空间的 iommu_domain,然后将其添加到列表中。此操作成功完成后,将建立 IOAS、设备和 iommu_domain 之间的链接。完成此操作后,设备可以执行 DMA。

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

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

    注意

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

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

  6. IOMMUFD_OBJ_VDEVICE 只能通过 IOMMU_VDEVICE_ALLOC uAPI 手动创建,前提是提供一个 iommufd_viommu 对象的 viommu_id 和一个 iommufd_device 对象的 dev_id。vDEVICE 对象将是这两个父对象之间的绑定。还将通过 uAPI 设置另一个 @virt_id,从而为 iommufd 核心提供一个索引,以便将 vDEVICE 对象存储到每个 vIOMMU 的 vDEVICE 数组中。如有必要,IOMMU 驱动程序可以选择实现 vdevce_alloc op,以初始化其与 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_VIOMMU 的 iommufd_viommu。

  • 用于 IOMMUFD_OBJ_VDEVICE 的 iommufd_vdevice。

查看这些数据结构时的一些术语

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

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

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

iommufd_ioas 作为元数据数据结构,用于管理 IOVA 范围如何映射到内存页,由以下部分组成:

  • struct io_pagetable,保存 IOVA 映射

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

  • struct iopt_pages,表示 PFN 的存储

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

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

  • 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_area 以及镜像 IOVA 到 PFN 映射的 iommu_domain 列表组成。

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

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

IOMMUFD 用户 API

通用 ioctl 格式

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

ioctl 对常见的错误码使用标准含义

  • 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 操作采取的硬件路径,并且可以在 IOAS 的生命周期内更改。一个新的空 IOAS 将具有完整的范围,并且每个附加的设备将根据该设备的硬件限制缩小范围。分离设备可以扩大范围。用户空间应在每次附加/分离后查询范围,以了解哪些 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/长度必须与 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/长度必须是先前使用 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

要设置的选项值或获取时返回的值

描述

更改一个简单的选项值。此多路复用器允许控制对象上的选项。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。

IOMMU_HWPT_ALLOC_DIRTY_TRACKING

设备 IOMMU 的脏跟踪支持在设备附加时强制执行

IOMMU_HWPT_FAULT_ID_VALID

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

IOMMU_HWPT_ALLOC_PASID

请求一个可以与 PASID 一起使用的域。该域可以附加到设备上的任何 PASID。附加到设备的非 PASID 部分的任何域也必须标记,否则附加 PASID 将被阻止。如果 IOMMU 不支持 PASID,它将返回错误 (-EOPNOTSUPP)。

enum iommu_hwpt_vtd_s1_flags

英特尔 VT-d stage-1 页表条目属性

常量

IOMMU_VTD_S1_SRE

监管者请求

IOMMU_VTD_S1_EAFE

扩展访问使能

IOMMU_VTD_S1_WPE

写保护使能

struct iommu_hwpt_vtd_s1

英特尔 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

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

描述

如果 ste 不合法或包含任何不允许的字段,将返回 -EIO。Cfg 可用于选择 S1,Bypass 或 Abort 配置。Bypass 嵌套域的转换将与嵌套父级相同。S1 将安装一个指向用户空间内存的上下文描述符表,该用户空间内存由嵌套父级转换。

enum iommu_hwpt_data_type

IOMMU HWPT 数据类型

常量

IOMMU_HWPT_DATA_NONE

无数据

IOMMU_HWPT_DATA_VTD_S1

英特尔 VT-d stage-1 页表

IOMMU_HWPT_DATA_ARM_SMMUV3

ARM SMMUv3 上下文描述符表

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 的 flags 字段时才有效。

__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 硬件信息的标志

常量

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

Intel VT-d 规范 11.4.2 节“能力寄存器”中定义的 Intel VT-d 能力寄存器的值。

ecap_reg

Intel VT-d 规范 11.4.3 节“扩展能力寄存器”中定义的 Intel VT-d 扩展能力寄存器的值。

描述

用户需要理解 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 规范中的 6.3.1 到 6.3.6 章。

这报告了原始硬件功能,并非所有位都对用户空间有意义。仅应使用以下字段

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

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;
    __u32 __reserved;
    __aligned_u64 out_capabilities;
};

成员

size

sizeof(struct iommu_hw_info)

flags

必须为 0

dev_id

绑定到 iommufd 的设备

data_len

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

data_uptr

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

out_data_type

输出在 enum iommu_hw_info_type 中定义的 iommu 硬件信息类型。

__reserved

必须为 0

out_capabilities

输出在 enum iommu_hw_capabilities 中定义的通用 iommu 功能信息类型。

描述

从绑定到 iommufd 的给定设备后面的 iommu 查询 iommu 类型特定的硬件信息数据。此硬件信息数据将用于同步虚拟 iommu 和物理 iommu 之间的功能,例如,嵌套转换设置需要检查硬件信息,以便来宾阶段 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。

描述

使⽤户管理⻚表或 vIOMMU 的 iommu 缓存失效。如果通过 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 在 PASID TLP 前缀中设置了 Execute Requested 位。

IOMMU_PGFAULT_PERM_PRIV

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

struct iommu_hwpt_pgfault

iommu ⻚⾯故障数据

定义:

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

成员

flags

enum iommu_hwpt_pgfault_flags 的组合

dev_id

始发设备的 ID

pasid

进程地址空间 ID

grpid

⻚⾯请求组索引

perm

enum iommu_hwpt_pgfault_perm 的组合

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 中定义的“Success”。

IOMMUFD_PAGE_RESP_INVALID

⽆法处理此故障,请⽆需重试访问。这是 PCI 10.4.2.1 中的“Invalid Request”。

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 ⽤于将设备的操作从⼀个进程转移到另⼀个进程,例如在⽤户态实时更新期间。

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 拥有该组,则为 True。

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, u32 *pt_id)

将设备连接到 iommu_domain

参数

struct iommufd_device *idev

要连接的设备

u32 *pt_id

输入 IOMMUFD_OBJ_IOAS 或 IOMMUFD_OBJ_HWPT_PAGING,输出 IOMMUFD_OBJ_HWPT_PAGING ID

描述

这会将设备连接到 iommu_domain,可以是自动选择的,也可以是手动选择的。一旦完成此操作,设备就可以进行 DMA。

调用者应将生成的 pt_id 返回给用户空间。此函数可通过调用 iommufd_device_detach() 来撤销。

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

更改设备的 iommu_domain

参数

struct iommufd_device *idev

要更改的设备

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() 成功后才能调用此函数。

void iommufd_device_detach(struct iommufd_device *idev)

断开设备与 iommu_domain 的连接

参数

struct iommufd_device *idev

要断开的设备

描述

撤销 iommufd_device_attach()。这将断开 idev 与先前连接的 pt_id 的连接。设备返回到阻止 DMA 转换的状态。

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

驱动程序要与访问关联的操作

void *data

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

u32 *id

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

描述

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

必须使用提供的操作 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 操作来实现。这样做允许在传统的 VFIO 应用程序中使用 iommufd,方法是将 /dev/vfio/vfio 符号链接到 /dev/iommufd,或者扩展 VFIO 以使用 iommufd 而不是容器 fd 来 SET_CONTAINER。

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

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

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

未来的 TODO

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

  • 将 iommu_domain 绑定到 PASID/SSID

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

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

  • 在 IOMMU 中重用 KVM 页表

  • 在 IOMMU 中跟踪脏页

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

  • 使用用户空间中解决的故障的 PRI 支持