PCIe根复合体模式下stream ID与iommu specifier如何确定?
iommu-map映射规则说明 你没读懂的那句描述本质是一段连续RID区间的线性映射规则,逻辑非常直白:
- 每个映射元组
(rid-base,iommu,iommu-base,length)首先划定一段连续的RID范围:左闭右开区间[rid-base, rid-base + length)内的所有16位请求ID,全部归属元组里指定的IOMMU管理 - 区间内任意一个RID值
r,对应的IOMMU侧输入标识(也就是iommu specifier)按固定偏移计算:先算r相对于当前段起始RID的偏移r - rid-base,再加上当前段对应的IOMMU标识基值iommu-base,结果就是最终传给IOMMU的specifier。
举个实际例子:如果某段映射写为iommu-map = <0x100 &smmu 0x800 0x100>,那么RID=0x100的设备对应的IOMMU specifier就是0x100 - 0x100 + 0x800 = 0x800,RID=0x1FF的设备对应的specifier就是0x1FF - 0x100 + 0x800 = 0x8FF,就是个连续地址段的固定偏移映射,没有特殊逻辑。你看到的官方示例是恒等映射场景:rid-base和iommu-base都为0,length=0x10000刚好覆盖16位RID的全部取值范围(0~0xFFFF),所以任意RID算出来的specifier和RID本身相等。
问题1:单数值形式的iommu specifier如何确定?是否由驱动设置决定?
这个值既不是PCIe设备动态生成的,也不是设备驱动可以配置修改的,完全是SoC硬件设计阶段就固定死的静态配置,只和硬件连线相关:
- PCIe根复合体转发PCIe事务给IOMMU时,会给事务附带侧带标识,这个标识的生成逻辑是硬件硬连线实现的:部分SoC会把PCIe的16位RID直接直连到SMMU的stream ID输入引脚,对应示例里的恒等映射;部分SoC会给PCIe总线预留固定的stream ID段基址,比如所有PCIe设备的stream ID从0x8000开始编号,就把
iommu-base设为0x8000;如果SoC把多个PCIe控制器的RID分别映射到SMMU的不同stream ID区间,就会在iommu-map里写多个映射元组。 - Linux PCI核心子系统做的工作只是在枚举阶段按照设备树写死的规则,提前给每个PCI设备计算好对应的IOMMU specifier,存在设备的私有数据结构里。整个过程设备驱动完全不参与,也没有权限修改这个映射关系,一旦
iommu-map配置和硬件实际连线不匹配,设备发起DMA时会直接被IOMMU拦截报总线错误,驱动层面无法修复。
问题2:arm64架构SMMUv3场景下,#iommu-cells = <1>时stream ID如何传递给SMMUv3?
整个流程分软件预计算和硬件自动执行两个阶段,运行时不存在软件动态传ID的过程:
- 系统启动阶段,PCI控制器驱动probe时会解析根复合体节点下的
iommu-map、iommu-map-mask属性,把映射规则保存到PCI主机桥的结构体中。 - PCI总线枚举下游设备时,先读取设备的16位RID(即BDF号,编码格式为
总线号[15:8] | 设备号[7:3] | 功能号[2:0]),如果配置了iommu-map-mask就先对RID做按位与屏蔽,再匹配对应的映射段,按照前面说的线性公式算出该设备对应的SMMU stream ID,同时完成设备和SMMU实例的绑定。 - 设备驱动调用DMA API映射DMA缓冲区时,SMMUv3驱动直接从设备对应的iommu数据结构里取出提前算好的stream ID,配置对应的流表项(STE),把IOMMU页表基地址写入对应表项。
- 硬件运行时,PCIe设备发送的TLP到达根复合体后,根复合体硬件会按照固定连线逻辑把TLP对应的RID转换成SMMU要求的stream ID,通过侧带信号随事务一起送给SMMU,SMMU硬件直接拿这个stream ID查流表完成地址翻译,整个数据路径不需要软件参与传值。
补充说明:你提到的PASID属于子流标识,是TLP里携带的事务前缀,和基础stream ID是两层独立的标识:基础stream ID用来定位设备对应的一级流表项,PASID只有在设备开启PASID功能时才会生效,用来索引流表项下挂载的子流上下文,PASID值是驱动在启用PASID功能时配置给设备的,和iommu-map定义的基础stream ID没有关系。
内容的提问来源于stack exchange,提问作者Chan Kim
相关产品推荐
相关产品推荐

