当前位置: 首页 > news >正文

ARM SMMUv3控制器注册过程分析(八)

1.概述

ARM SMMUv3控制器初始化及设备树分析(七)中描述了IOMMU控制器初始化过程。SMMU驱动最后调用iommu_device_register将其注册到内核中,下面分析一下SMMU控制器注册过程中都做了那些工作。

如下图所示,SMMU控制器注册过程中主要做了4部分工作:

  1. 遍历内核中所有总线,初始化使用SMMU的设备,主要是创建设备的Stream Table,给设备分配或者创建iommu_group
  2. 遍历所有使用SMMU的设备,创建iommu_domian,并和设备关联起来。主要是初始化设备使用的STE和CD表。
  3. 遍历所有使用SMMU的设备,初始化DMA映射的iommu_domian。主要是初始化iova_domain、TLB刷新队列,设置保留的IOVA。
  4. 释放相关锁,SMMU不需要,这里不多介绍。

iommu_device_register

如下代码所示,iommu_device_register函数会遍历iommu_buses定义的所有总线,初始化其中使用SMMU的设备。

[drivers/iommu/iommu.c]
static const struct bus_type * const iommu_buses[] = {&platform_bus_type,
#ifdef CONFIG_PCI&pci_bus_type,
#endif
#ifdef CONFIG_ARM_AMBA&amba_bustype,
#endif
#ifdef CONFIG_FSL_MC_BUS&fsl_mc_bus_type,
#endif
#ifdef CONFIG_TEGRA_HOST1X_CONTEXT_BUS&host1x_context_device_bus_type,
#endif
#ifdef CONFIG_CDX_BUS&cdx_bus_type,
#endif
};

2.初始化设备

初始化使用IOMMU的client设备,主要是为其创建Stream Table及分配或者创建iommu_group

  1. 调用arm_smmu_probe_device函数初始化设备。
    1. 由于client设备是smmu的master,因此smmu驱动会给每一个client设备分配一个arm_smmu_master,然后通过arm_smmu_master将client设备和smmu控制器关联起来。dev_iommu中的私有数据指针priv指向了arm_smmu_masterarm_smmu_master中保存了设备的Stream ID信息。smmu驱动使用红黑树管理Stream ID。
    2. 如果采用2级Stream table,则需要分配client设备使用的第二级STE表内存,一次性分配256个STE表。然后将STE暂时设置成ABORT。
    3. 处理PCIe设备ATS相关功能。
  2. iommu_device保存到设备的dev_iommu中。这样可以通过设备的device数据结构找到smmu控制器。
  3. 在sysfs中关联设备和iommu,这样在sys文件系统中device目录下会有iommu控制器的文件。
  4. 调用arm_smmu_device_group函数为设备创建iommu_group。PCI设备可能会共享iommu_group,因此驱动会依据PCI总线拓扑结构、isolation特性或者DMA别名quirks查找或者创建iommu_group。其他设备每个设备分配一个iommu_group
  5. 分配group_device,然后挂到iommu_groupdevices链表中。一个iommu_group内可能有多个设备,每个设备使用group_device表示。
  6. iommu_group挂到group_list链表中,稍后统一处理。

probe_iommu_group

3.创建iommu_domian

遍历group_list链表,为每个iommu_group设置默认的iommu_domain,主要的工作内容如下:

  1. 首先获取iommu_domain的类型,传入的参数为0,则iommu_domain类型由驱动和系统共同决定。对于PCI untrusted设备,类型为IOMMU_DOMAIN_DMA,其他设备返回0。
  2. 分配默认的iommu_domain。如果请求的iommu_domain类型非0,则使用请求的类型,否则使用iommu_def_domain_type表示的类型。iommu_def_domain_type是一个全局变量,由命令行参数和内核配置共同决定,通常情况下为IOMMU_DOMAIN_DMAIOMMU_DOMAIN_DMA_FQ。如果iommu_domain类型为DMA,则调用arm_smmu_domain_alloc_paging分配iommu_domain,smmu驱动底层会分配一个arm_smmu_domain,内部包含了iommu_domain,然后将arm_smmu_domainarm_smmu_devicearm_smmu_master)关联在一起,最后初始化domain内页表相关内容。
  3. 遍历iommu_group内的每一个设备,如果其保留内存区域类型为IOMMU_RESV_DIRECTIOMMU_RESV_DIRECT_RELAXABLE,则创建direct mappings。避免这些内存区域被误使用。
  4. 遍历group中的每一个device,调用arm_smmu_attach_dev关联对应的iommu_domain。最主要的是初始化arm_smmu_domain中的CD表。

分配iommu_domian
iommu_domain的类型不同,其分配策略也不同。iommu_domain__iommu_domain_alloc函数中分配,结合smmu驱动,可以总结iommu_domain的分配策略如下:

  1. 如果分配的iommu_domain类型是IOMMU_DOMAIN_IDENTITYIOMMU_DOMAIN_BLOCKED,则直接使用arm_smmu_ops驱动中静态定义的arm_smmu_identity_domainarm_smmu_blocked_domain。这样就存在多个iommu_group使用一个iommu_domain的情况。
  2. 如果分配的iommu_domain类型是IOMMU_DOMAIN_UNMANAGEDIOMMU_DOMAIN_DMAIOMMU_DOMAIN_DMA_FQ,则smmu驱动会动态分配arm_smmu_domain(内部包含了iommu_domain),这样一个iommu_group对应一个iommu_domain,使用default_domain_ops,smmu驱动需要管理IOVA页表。
  3. 如果分配的iommu_domain类型是IOMMU_DOMAIN_SVA,说明DMA共享进程进程地址空间,则smmu驱动会动态分配iommu_domain,并且使用arm_smmu_sva_domain_ops,由于和进程共享,smmu驱动只需要管理iommu_domain和PASID。

3.1.分配iommu_domian

arm_smmu_domain_alloc_paging函数主要分配支持iommu_map/unmap接口的iommu_domain,然后根据不同的地址转换阶段,做不同的初始化。主要的工作如下:

  1. 分配arm_smmu_domain,内部包含了iommu_domain
  2. 初始化arm_smmu_domainARM_SMMU_DOMAIN_S1ARM_SMMU_DOMAIN_S2初始化内容有所不同。
    1. 初始化IO页表配置。包括SMMU支持的页表大小掩码、是否支持Cache一致性、刷新TLB回调函数arm_smmu_flush_ops
    2. 如果是第一阶段地址转换。设置输入地址位宽为48位,如果支持虚拟地址扩展(VAX),则设置为52位,设置输出地址位宽等于IPA地址的位宽。设置IO页表格式为ARM_64_LPAE_S1
    3. 如果是第二阶段地址转换。设置输入地址位宽等于IPA地址的位宽,设置输出地址位宽等于PA地址的位宽。设置IO页表格式为ARM_64_LPAE_S2
    4. 根据不同的IO页表格式,创建管理IO页表的接口集合io_pgtable_ops
      1. 对于ARM_64_LPAE_S1页表格式,调用arm_64_lpae_alloc_pgtable_s1创建io_pgtable_ops。设置TCR(类似于MMU中的TCR_ELx)中的参数,如共享属性、页表大小、IPA地址宽度。设置MAIRs。分配保存第0级页表的内存(CD表中TTB0/1指向这块内存)。
      2. 对于ARM_64_LPAE_S2页表格式,调用arm_64_lpae_alloc_pgtable_s2创建io_pgtable_ops。设置VTCR(类似于MMU中的VTCR_EL2)中的参数,如共享属性、页表大小、PA地址宽度。分配保存第0级页表的内存(STE表中S2TTB指向这块内存)。
    5. 如果是ARM_SMMU_DOMAIN_S1,则需要分配ASID。如果是ARM_SMMU_DOMAIN_S2,则需要分配VMID。

分配iommu_domian

arm_smmu_domain_alloc_paging函数分配iommu_domian的过程中涉及到重要的代码定义如下所示:

[drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c]
// TLB刷新回调函数集合
static const struct iommu_flush_ops arm_smmu_flush_ops = {.tlb_flush_all      = arm_smmu_tlb_inv_context,.tlb_flush_walk     = arm_smmu_tlb_inv_walk,.tlb_add_page       = arm_smmu_tlb_inv_page_nosync,
};[drivers/iommu/io-pgtable.c]
// 根据不同的IO页表格式,对应不同的创建io_pgtable_ops的函数
static const struct io_pgtable_init_fns *
io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = {
#ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE[ARM_32_LPAE_S1] = &io_pgtable_arm_32_lpae_s1_init_fns,[ARM_32_LPAE_S2] = &io_pgtable_arm_32_lpae_s2_init_fns,[ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns,[ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns,[ARM_MALI_LPAE]  = &io_pgtable_arm_mali_lpae_init_fns,
#endif......
};// io_pgtable_ops函数集合
[drivers/iommu/io-pgtable-arm.c]
data->iop.ops = (struct io_pgtable_ops) {.map_pages            = arm_lpae_map_pages,.unmap_pages          = arm_lpae_unmap_pages,.iova_to_phys         = arm_lpae_iova_to_phys,.read_and_clear_dirty = arm_lpae_read_and_clear_dirty,

3.2.关联设备

arm_smmu_attach_dev函数的主要作用是将iommu_domain和设备关联起来。如果是第一阶段地址转换的iommu_domain,则需要初始化CD表,如果是第二阶段地址转换的iommu_domain,则需要初始化STE表中S2相关的参数。具体的内容如下:

  1. 如果是ARM_SMMU_DOMAIN_S1,则分配CD表。如果是线性CD表(STRTAB_STE_0_S1FMT_LINEAR),则直接分配全部内存。如果是2级CD表(STRTAB_STE_0_S1FMT_64K_L2),先分配第一级L1CD内存,接着分配SSID ==0的第二级CD表数组(可以保存1024个CD),随后将第二级CD表数组的DMA地址写到L1CD内存中(l2.l1tab[0]),最后invalid L1CD。
  2. 处理和ATS相关的功能。
  3. 如果是第一阶段地址转换(ARM_SMMU_DOMAIN_S1)。
    1. 初始化第0个CD表。
    2. 初始化设备需要的STE表(根据SID初始化),默认SID==0的STE(STRTAB_STE_1_S1DSS_SSID0)无法使用,如果使用则会ABORT。
  4. 如果是第二阶段地址转换(ARM_SMMU_DOMAIN_S2)。初始化设备需要的STE表(根据SID初始化),如果该STE表有对应的CD表,则将第一个CD表清零。
  5. 按照EATS设置完成STE/CD的配置之后,需要完成对PCIe设备ATC的同步操作。

关联设备

4.初始化DMA iommu_domian

iommu_setup_dma_ops函数初始化DMA映射的iommu_domian,主要的工作如下:

  1. 设置dev->dma_iommu=true。如果启用DMA-API和IOMMU-API的中间层,则设备在分配DMA内存或者进行流式映射内存时,底层将调用DMA-IOMMU接口。
  2. 初始化iommu_domain。初始化管理IO虚拟地址的iova_domain及地址缓存、TLB刷新队列和注册保留的IO虚拟地址。

初始化IOVA

参考资料

  1. linux 6.12.35 source code.
http://www.lryc.cn/news/600924.html

相关文章:

  • ISIS分片扩展实验案例
  • 【Android】内容提供器
  • Kubernetes 与 Docker的爱恨情仇
  • 1.安装anaconda详细步骤(含安装截图)
  • C++20 协程
  • ​机器学习从入门到实践:算法、特征工程与模型评估详解
  • 是德科技 | AI上车后,这条“高速公路”如何畅通?
  • 聚类-一种无监督分类算法
  • 聚类里面的一些相关概念介绍阐述
  • Digit Queries
  • OpenFeign-远程调用
  • 数据结构 二叉树(2)---二叉树的实现
  • excel删除重复项场景
  • HarmonyOS中的PX、 VP、 FP 、LPX、Percentage、Resource 详细区别是什么
  • 商汤InternLM发布最先进的开源多模态推理模型——Intern-S1
  • CUDA杂记--FP16与FP32用途
  • P2392 kkksc03考前临时抱佛脚
  • Linux——线程互斥
  • 【RHCSA 问答题】第 13 章 访问 Linux 文件系统
  • PYTHON从入门到实践-16数据视图化展示
  • 卫星通信终端天线对星之:参考星对星
  • DOM元素添加技巧全解析
  • 单片机CPU内部的定时器——滴答定时器
  • Linux DNS 服务器正反向解析
  • Mybatis学习之配置文件(三)
  • Linux随记(二十一)
  • 变频器实习DAY15
  • Linux内核设计与实现 - 第13章 虚拟文件系统(VFS)
  • Linux shuf命令随机打乱行顺序
  • 差模干扰 共模干扰