函数Iommu_probe_device()是通过如下图所示建立起IO设备与SMMU的关联:
其中红色部分会调用SMMUV3驱动中定义的iommu_ops,后面在被调用时再对涉及的回调作介绍。这里分成以下几个部分进行讲解:
1. 为IO设备分配iommu_device
在上述函数中,通过ops->probe_device()为IO设备分配对应SMMU的iommu_device。对于SMMUV3驱动,ops->probe_device = arm_smmu_probe_device。
在SMMUV3驱动中先简单介绍涉及的几个结构体:
(1)结构体arm_smmu_device对应于SMMU设备;
(2)结构体arm_smmu_master对应于IO设备;
(3)结构体arm_smmu_stream对应于IO设备中的FUNCTION(若仅一个FUNCTION,也对应IO设备);
函数定义如下:
函数执行操作如下:
(1)根据IO设备找到所对应的SMMU设备(结构体arm_smmu_device);
(2)根据IO设备分配所对应在结构体arm_smmu_master;
(3)根据IORT表中定义的IO设备的stream情况分配arm_smmu_stream,若支持二级STE,对STE的第二级STE进行初始化(前面SMMU设备初始化已介绍),并将arm_smmu_stream插入到arm_smmu_device中;
(4)使能PASID;
2. 分配iommu_group并添加IO设备
函数iommu_group_get_for_dev()首先查找设备是否存在对应的iommu_group,第一次时设备不存在对应的iommu_group,调用ops->device_group()分配或查找iommu_group;调用函数iommu_group_add_device()将IO设备添加到iommu_group。
2.1 分配iommu_group
(1)判断当前设备是否为PCI设备,若为PCI设备,PCIE设备存在alias,尝试使用alias对应的iommu_group;若PCIE设备到root bus之间存在没有使能ACS特性的设备,那么此部分为同一个group,使用对应的group(后续再讲解ACS特性);若PCIE设备对应的function存在alias,尝试使用alias对应的iommu_group;最后调用iommu_group_alloc()分配iommu_group,此函数生成iommu_group,且生成SYSFS属性reserved_regions和type。
(2)若为其他设备如platform设备,同样调用iommu_group_alloc()。
2.2 将IO设备添加到iommu_group
通过函数iommu_group_add_device()将设备添加到iommu_group。这里通过函数sysfs_create_link()建立起IO设备的device与iommu_group的device相互Link,即从IO设备的device目录可以到达iommu_group对应在device目录,也可从iommu_group的device目录到达IO设备的目录。
其中对于group->domain且!iommu_is_attach_deferred()情况,将设备与group->domain关联,在第一次时group->domain为空,这里暂且不分析。
3. 为IO设备分配iommu_domain
对于iommu_group,存在两个iommu_domain: iommu_group->domain(作用?)和iommu_group->default_domain。函数iommu_alloc_default_domain()为iommu_group分配default_domain。
(1)设置DMA类型。若命令行中定义DMA类型,使用命令行中DMA类型;否则使用默认类型;IOMMU_DOMAIN_DMA表示进行DMA转换; IOMMU_DOMAIN_IDENTIFY表示PASSTHROUGH,不经过SMMU;
(2)分配iommu_group->default_domain,这是调用SMMUV3回调函数实现的;
(3)填充iommu_domain对应的成员域;
4. 将IO设备连接到iommu_domain
通过函数__iommu_attach_device()将IO设备连接到iommu_domain,最后调用SMMUV3驱动回调函数arm_smmu_attach_dev(),它的执行流程如下:
(1)调用arm_smmu_detach_dev(),作attach_dev相反的操作;
(2)调用arm_smmu_domain_finalise()作与页表相关的设置,后面单独分析;
(3)调用arm_smmu_install_ste_for_dev()设置STE,后面单独介绍;
5. 建立直接映射
所谓直接映射为IOVA=PA。在某些场景如驱动希望保留部分IOVA区域,或虚拟机中MSI的方案等,都会使用直接映射。调用如下:
(1)获取设备所对应的保留区域;
(2)对每个保留区域,使用iommu_map()建立起IOVA=PA的映射,后面对iommu_map()等 map/unmap API操作讲解;
(3)刷新domain中所有TLB项;
6. 小结
函数iommu_probe_device()实现IO设备与对应SMMU的关联。在此过程中为IO设备和SMMU设备分配IOMMU框架层和SMMUV3驱动对应的结构体。
IOMMU框架的结构体 | SMMUV3驱动对应的结构 | 含义 |
Iommu_device | arm_smmu_device | SMMU设备 |
group_device | arm_smmu_master | IO设备 |
Iommu_group | arm_smmu_group | IO设备所对应的group |
Iommu_domain | arm_smmu_domain | IO设备所对应的domain |