IOMMU配置过程

Linux 5.4.233 内核中 iommu_map 函数实现深度分析

iommu_map 是 Linux 内核中用于建立 IOVA(I/O 虚拟地址)到物理地址映射 的核心函数,其实现涉及 IOMMU 硬件操作、页表管理和内存保护机制。以下基于 5.4.233 内核源码进行详细解析:


1. 函数定义与参数

函数位于 drivers/iommu/iommu.c

int iommu_map(struct iommu_domain *domain, unsigned long iova,
              phys_addr_t paddr, size_t size, int prot)
  • domain:IOMMU 域,包含硬件特定的页表信息。
  • iova:设备使用的虚拟地址起始位置。
  • paddr:物理地址起始位置。
  • size:映射的内存区域大小。
  • prot:保护标志(如 IOMMU_READIOMMU_WRITE)。

2. 实现流程分析

(1) 参数合法性校验
if (!domain || !domain->ops->map)
    return -EINVAL;

if (size & ~PAGE_MASK)
    return -EINVAL; // 检查 size 是否页对齐
  • 确保 domain 有效且支持映射操作。
  • 映射大小必须是页对齐的(4KB/2MB/1GB)。
(2) 计算页表粒度和映射次数
size_t mapped = 0;
while (mapped < size) {
    // 尝试以最大支持的页大小(如 1GB)进行映射
    size_t pgsize = iommu_pgsize(domain, paddr, iova, size - mapped);
    // 调用具体 IOMMU 驱动的 map 方法
    ret = domain->ops->map(domain, iova + mapped, paddr + mapped,
                           pgsize, prot);
    if (ret)
        break;
    mapped += pgsize;
}
  • 页粒度选择:通过 iommu_pgsize() 选择当前硬件支持的最大页大小(优化页表项数量)。
  • 循环映射:若映射失败(如部分区域已被占用),则尝试更小的页粒度。
(3) 底层驱动映射操作

Intel IOMMU (VT-d) 为例(drivers/iommu/intel-iommu.c):

static int intel_iommu_map(...) {
    // 获取页表地址
    struct dmar_domain *dmar_domain = to_dmar_domain(domain);
    // 操作页表项
    return __domain_mapping(dmar_domain, iova, paddr, size, prot);
}
  • 页表遍历:通过多级页表(如 4 级页表)查找或创建对应的页表项。
  • 更新页表项:将 iova 映射到 paddr,并设置权限位(读/写/缓存等)。
(4) 错误处理与回滚
if (ret && mapped) {
    // 部分映射失败时,回滚已建立的映射
    iommu_unmap(domain, iova, mapped);
}
return ret ? ret : mapped == size ? 0 : -EINVAL;
  • 原子性保证:若部分映射失败,需解除已完成的映射,避免内存泄漏。

3. 关键机制详解

(1) 页粒度优化
  • iommu_pgsize 逻辑
    选择同时满足以下条件的最大页大小:
    • 硬件支持(通过 domain->pgsize_bitmap)。
    • iovapaddr 对齐到该页大小。
    • 剩余映射大小不小于该页大小。
(2) 硬件页表更新
  • Intel VT-d 示例
    // 更新页表项并刷新 IOTLB
    domain_flush_cache(dmar_domain, pgd, sizeof(*pgd));
    iommu_flush_iotlb_psi(domain, iova, pgsize, 0);
    
    • 缓存一致性:通过 clflush 或内存屏障保证页表写入对 IOMMU 可见。
    • IOTLB 刷新:使设备 DMA 请求使用新的映射。
(3) 锁机制
static int __iommu_map(...) {
    mutex_lock(&domain->lock);
    // 映射操作
    mutex_unlock(&domain->lock);
}
  • 锁类型:通常使用互斥锁 (mutex) 保护域的操作,防止并发修改页表。

4. 性能与安全设计

(1) 大页映射优化
  • 优先使用 2MB 或 1GB 大页,减少页表层级,降低 TLB 未命中率。
(2) DMA 保护
if (!(prot & IOMMU_WRITE))
    // 设置页表项为只读,阻止设备非法写入
  • 通过页表权限位限制设备访问,防止恶意 DMA 覆盖内存。

5. 典型调用链示例

iommu_map()
└── intel_iommu_map()
    └── __domain_mapping()
        ├── pgd_lookup()          // 查找页全局目录
        ├── alloc_pte()           // 分配页表项
        └── domain_flush_cache()  // 刷新缓存

6. 相关内核代码片段

  • 页表项操作(Intel):
    // 填充页表项物理地址和权限
    *pte = paddr | DMA_PTE_READ | DMA_PTE_WRITE;
    
  • IOTLB 刷新
    iommu_flush_iotlb(domain, iova, size);
    

7. Linux 5.4.233 内核中 AMD IOMMU 映射操作实现分析

AMD IOMMU(通常称为 AMD-Vi)的底层映射操作与 Intel VT-d 在架构设计上存在差异,但其核心目标一致:将设备 IOVA(I/O 虚拟地址)映射到物理内存,并管理页表权限。以下基于 5.4.233 内核源码,以 AMD IOMMU 为例解析底层驱动映射操作。

7.1. 关键数据结构与驱动入口
(1) AMD IOMMU 驱动入口
  • 驱动代码位于 drivers/iommu/amd_iommu.c
  • 映射操作入口函数为 amd_iommu_map
    static int amd_iommu_map(struct iommu_domain *domain, unsigned long iova,
                            phys_addr_t paddr, size_t size, int prot)
    
(2) 核心数据结构
  • struct protection_domain
    AMD 对 iommu_domain 的扩展,包含页表根地址、设备列表、页表锁等:
    struct protection_domain {
        struct iommu_domain  domain;    // 继承 iommu_domain
        spinlock_t      lock;           // 自旋锁保护并发操作
        unsigned mode;                  // 页表模式(如 4 级页表)
        u64 *pt_root;                   // 页表根指针(PML4)
        // ... 其他字段(设备链表、Domain ID 等)
    };
    

7.2. 映射操作流程(以 amd_iommu_map 为例)
(1) 参数转换与校验
struct protection_domain *pdom = to_pdomain(domain);
// 检查映射大小是否页对齐
if (!IS_ALIGNED(iova | paddr | size, PAGE_SIZE))
    return -EINVAL;
  • 将通用 iommu_domain 转换为 AMD 特有的 protection_domain
  • 验证 iovapaddrsize 的页对齐(AMD IOMMU 通常要求 4KB 对齐)。
7.3. 典型代码调用链
iommu_map()
└── amd_iommu_map()
    ├── iommu_map_page()          // 逐级填充页表
    │   ├── PML4 索引计算
    │   ├── PDPE/PDE 分配
    │   └── PTE 写入
    └── domain_flush_np_cache()     // 刷新 IOTLB

7.4. 安全与性能优化
(1) 地址空间隔离
  • 每个 protection_domain 对应一组设备,确保设备只能访问所属域的 IOVA 范围。
(2) 硬件加速
  • 利用 AMD IOMMU 的 ATS(Address Translation Services) 支持,允许设备缓存地址转换,减少页表查询开销。
(3) 中断映射
  • AMD IOMMU 同时管理设备中断的虚拟化(通过 IRTE 表),但此部分与 DMA 映射解耦。

7.5. iommu_map_page关键代码解析
7.5.1. 参数校验阶段
BUG_ON(!IS_ALIGNED(bus_addr, page_size));  // 确保总线地址页对齐
BUG_ON(!IS_ALIGNED(phys_addr, page_size)); // 确保物理地址页对齐

使用内核调试宏确保地址对齐,避免非法映射

7.5.2. 页表项操作
count = PAGE_SIZE_PTE_COUNT(page_size); // 计算所需PTE数量
pte = alloc_pte(dom, bus_addr, page_size, NULL, gfp, &updated); // 分配页表项
  • page_size可能为4K/2M/1G等不同层级
  • alloc_pte可能涉及多级页表遍历
7.5.3. 旧映射清除
for (i = 0; i < count; ++i)
    freelist = free_clear_pte(&pte[i], pte[i], freelist);

通过链表freelist回收旧页表页,采用延迟释放策略

7.5.4. 新PTE构造逻辑
if (count > 1) { // 处理大页映射
    __pte = PAGE_SIZE_PTE(__sme_set(phys_addr), page_size);
    __pte |= PM_LEVEL_ENC(7) | IOMMU_PTE_PR | IOMMU_PTE_FC; 
} else { // 普通页映射
    __pte = __sme_set(phys_addr) | IOMMU_PTE_PR | IOMMU_PTE_FC;
}
  • PM_LEVEL_ENC(7)设置页表层级
  • __sme_set()处理安全内存加密(如AMD SME)
  • IOMMU_PTE_FC表示完全缓存一致性
7.5.5. 权限设置
if (prot & IOMMU_PROT_IR) __pte |= IOMMU_PTE_IR; // 读权限
if (prot & IOMMU_PROT_IW) __pte |= IOMMU_PTE_IW; // 写权限

权限位对应设备访问能力控制

7.5.6. 缓存刷新
spin_lock_irqsave(&dom->lock, flags);
update_domain(dom);  // 更新IOMMU域
spin_unlock_irqrestore(&dom->lock, flags);

通过自旋锁保护关键操作,update_domain()会触发IOMMU TLB刷新

典型应用场景
当设备需要通过DMA访问内存时,驱动调用此函数建立地址映射。例如:

  1. 网卡接收数据包缓冲区映射
  2. GPU显存与系统内存共享
  3. PCIe设备直接内存访问

性能优化点

  1. 使用大页(count >1)减少TLB Miss
  2. 延迟释放页表页(free_page_list最后统一处理)
  3. 批量化PTE更新(循环写入多个PTE)

该函数体现了Linux内核中IOMMU子系统的核心映射机制,通过精细的页表管理实现设备安全访问内存的控制。

8. update_domain分析

函数功能

该函数用于更新保护域(protection_domain) 的状态,通常在域配置(如页表、设备映射等)发生变化后调用。其核心操作分为四步,确保硬件与内存管理单元(如IOMMU)的状态同步。


代码逐层解析
1. update_device_table(domain)
  • 作用:更新与保护域关联的设备表(如IOMMU设备上下文)。
  • 细节
    当域的页表或设备映射发生变更时,需将新配置写入硬件设备表。例如,在IOMMU中,设备表存储了地址转换规则,此函数可能更新这些条目。
2. domain_flush_devices(domain)
  • 作用:刷新关联设备的缓存。
  • 细节
    通知所有绑定到该域的硬件设备(如DMA设备)刷新其本地TLB或缓存。确保设备使用最新的地址映射,避免因缓存陈旧数据导致非法内存访问。
3. domain_flush_tlb_pde(domain)
  • 作用:刷新保护域的TLB(转换后备缓冲区)中与页目录条目(PDE)相关的条目。
  • 细节
    IOMMU或CPU的TLB可能缓存了旧的地址转换结果。此函数显式无效化这些缓存条目,强制后续访问重新加载最新页表数据。
4. domain_flush_complete(domain)
  • 作用:确保所有刷新操作完成。
  • 细节
    由于某些刷新操作可能是异步的(如硬件队列处理),此函数通过等待完成信号或检查状态寄存器,保证后续代码在刷新完全结束后执行。

典型应用场景
  1. 修改页表:调整内存映射后,需同步硬件状态。
  2. 动态绑定/解绑设备:当设备加入或离开保护域时,更新设备表并刷新缓存。
  3. 安全隔离:在虚拟化或安全敏感场景中,确保设备无法访问无效内存区域。

潜在关联机制
  • IOMMU/SMMU:该函数可能直接操作IOMMU硬件寄存器。
  • 缓存一致性:通过内存屏障或特定指令保证操作的顺序性。
  • 并发控制:可能在调用前已持有保护域的锁,防止竞态条件。

小结

此函数通过更新配置→刷新缓存→同步完成的流程,确保保护域的变更在硬件层面生效,是内存虚拟化与设备隔离的关键底层操作。

9.struct protection_domain 结构解析

struct protection_domain 是 AMD IOMMU 驱动中用于描述 内存保护域(Memory Protection Domain)的核心数据结构。每个保护域代表一组设备共享的地址转换规则和内存访问权限。以下是其成员的详细说明:


1. 核心成员说明
成员名称类型作用描述
liststruct list_head用于将域链接到全局保护域链表中(如系统所有域的列表)。
dev_liststruct list_head记录属于该域的所有设备的链表头(设备通过 struct device 关联)。
domainstruct iommu_domain通用 IOMMU 域结构,提供与 Linux 内核 IOMMU 框架交互的接口(见下文详细展开)。
lockspinlock_t自旋锁,保护域内页表操作(如修改页表、刷新 TLB)的并发访问。
api_lockstruct mutex互斥锁,保护用户态 API 路径(如 iommu_map/iommu_unmap)的页表操作。
idu16域的唯一标识符,写入设备表(Device Table)以关联设备与域。
modeint分页模式级别(0-6),表示页表的层级(如 4 级页表对应 mode=4)。
pt_rootu64*页表根指针,指向该域的页表根目录(物理地址)。
glxintGuest CR3 表的层级数(用于虚拟化场景)。
gcr3_tblu64*Guest CR3 表指针,用于虚拟机监控程序(如嵌套分页)。
flagsunsigned long标志位,标识域的类型(如是否为直通域、是否启用虚拟化功能等)。
dev_cntunsigned绑定到该域的设备数量。
dev_iommuunsigned[MAX_IOMMUS]记录每个 IOMMU 硬件单元上该域的引用计数(用于多 IOMMU 系统)。

9.2. struct iommu_domain 详解

struct iommu_domain 是 Linux 内核 IOMMU 框架定义的 通用域描述符,用于抽象不同厂商(如 Intel、AMD)的 IOMMU 实现。其核心字段如下:

9.2.1 结构定义(简化版)
struct iommu_domain {
    enum iommu_domain_type type;  // 域类型(如 DMA 域、直通域)
    const struct iommu_ops *ops;  // 操作函数集(厂商实现)
    unsigned long pgsize_bitmap;  // 支持的页大小位图(如 4K、2M、1G)
    struct iommu_domain_geometry geometry;  // 地址空间几何信息(起始/结束地址)
    void *iova_cookie;            // 私有数据(如 IOMMU DMA 映射器句柄)
    // ... 其他内核内部字段(如设备链表、TLB 刷新状态等)
};
9.2.2 关键成员说明
成员名称作用
type定义域的类型:
- IOMMU_DOMAIN_DMA:标准 DMA 映射域(使用 IOMMU 页表)
- IOMMU_DOMAIN_IDENTITY:直通模式(物理地址直接映射)
- IOMMU_DOMAIN_UNMANAGED:非托管域(用户手动管理页表)
ops函数指针表,指向厂商实现的 IOMMU 操作函数(如 mapunmapattach_dev 等)。
示例:
- map():将虚拟地址映射到物理地址
- unmap():取消映射
- flush_iotlb_all():刷新整个 TLB
pgsize_bitmap表示该域支持的页大小(如 `4K
geometry定义域的地址空间范围(如限制设备可访问的 DMA 地址范围)。
iova_cookie厂商私有数据指针,通常用于关联 DMA 映射器(如管理 IOVA 分配器)。

9.3. protection_domainiommu_domain 的关系
  • 继承与扩展
    struct protection_domain 内嵌struct iommu_domain,表明它是 AMD 对通用 IOMMU 域的具体实现。通过 iommu_domainops 字段,AMD 驱动注册了自定义的映射、刷新等函数。

  • 分工协作

    • 通用逻辑:由内核 IOMMU 框架通过 iommu_domain 统一处理(如设备绑定、地址分配)。
    • 硬件相关操作:由 protection_domain 的私有字段(如 pt_rootgcr3_tbl)和 ops 中的 AMD 实现函数完成。

9.4. 典型应用场景
  1. 设备绑定
    当设备通过 iommu_attach_device() 绑定到域时,内核会调用 protection_domainops->attach_dev() 函数,将设备添加到 dev_list 并更新设备表。

  2. 地址映射
    用户调用 iommu_map() 时,最终通过 ops->map() 操作 AMD 页表(修改 pt_root 指向的页表结构)。

  3. TLB 刷新
    映射更新后,调用 ops->flush_iotlb() 触发 domain_flush_pages(),利用 pt_rootlock 完成缓存刷新。


附:AMD IOMMU 页表模式示例
  • Mode=0:禁用分页(直通模式)
  • Mode=4:4 级页表(支持 48 位地址)
  • Mode=5:5 级页表(支持 57 位地址)
  • Mode=6:6 级页表(保留)
### Intel J1900 处理器与 IOMMU 支持的相关配置 Intel J1900 是基于 Bay Trail 平台的一款低功耗处理器,广泛应用于嵌入式系统和小型服务器环境。关于该平台的 IOMMU 配置和支持情况,可以从以下几个方面进行分析: #### 1. **硬件层面的支持** J1900 属于 Atom 系列处理器的一部分,在设计上支持 VT-d 技术(即 Intel 的 IOMMU 实现)。VT-d 提供了 DMA 和中断重映射功能,用于增强系统的安全性并实现高效的设备直通能力。然而,具体的功能启用依赖于 BIOS 设置以及主板的设计[^1]。 #### 2. **BIOS 启用 VT-d 功能** 为了使操作系统能够利用 VT-d/IOMMU 特性,必须在 BIOS 中明确开启 Virtualization Technology for Directed I/O (VT-d) 或类似的选项。如果此选项未被激活,则即使硬件本身具备 VT-d 能力也无法正常工作[^3]。 #### 3. **Linux 内核参数调整** 对于运行 Linux 操作系统的场景,可以通过修改 GRUB 配置文件来确保内核加载时正确初始化 IOMMU 子系统。以下是典型的内核命令行参数设置示例: ```bash GRUB_CMDLINE_LINUX="intel_iommu=on iommu=pt" ``` 上述参数的作用如下: - `intel_iommu=on`:强制启用了 Intel VT-d 功能。 - `iommu=pt`:将所有 PCI 设备分配到一个 pass-through 域中,从而允许这些设备直接访问主机内存而不经过额外的翻译层。 需要注意的是,某些情况下可能还需要增加其他特定标志位,比如 `iommupt=relaxed` 来放宽一些严格的检查条件以适应更复杂的虚拟化需求[^2]。 #### 4. **验证 IOMMU 是否成功启动** 完成以上步骤之后,可以执行以下命令确认 IOMMU 已经被正确加载并处于活动状态: ```bash dmesg | grep -e DMAR -e IOMMU ls /sys/kernel/iommu_groups/ ``` 第一条指令会显示有关 DRAM Remapping Table 初始化过程的日志信息;而第二条则列举出了当前系统中存在的各个 IOMMU 组成员列表。 #### 5. **常见问题排查** 当尝试通过 SR-IOV 创建 KVM 虚拟机时报错 “host doesn't support passthrough of host PCI devices”,通常是因为缺少必要的驱动程序或者错误地设置了相关联的资源归属关系所致。此时除了核查前面提到的各项基础准备工作外,还需特别留意目标设备是否满足单一根域隔离(Single Root IO Virtualization and Sharing Standard, SR-IOV)的要求,并且保证它们归属于同一个 NUMA node 下面以便减少潜在冲突风险。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值