• 正文
  • 相关推荐
申请入驻 产业图谱

RISC-V IOMMU|第03天:寄存器窗口与启动顺序

13小时前
96
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

今日目标

今天学习软件如何把 IOMMU 带到可工作状态。重点不是背寄存器偏移,而是理解 capability、feature control、DDT pointer、队列 base/head/tail/CSR 之间的软硬件契约。

读完这篇文章,你应该能把本主题放回完整 IOMMU 数据流里:设备请求从 IO bridge 进入,经过设备身份识别、上下文定位、地址翻译、权限检查、缓存命中或 page walk,最后返回可访问的系统物理地址,或者通过 fault queue 把错误精确报告给软件。本文不假设读者已经打开规范或代码仓库,所有必要术语会在正文里展开。

为什么它对硬件设计重要

IOMMU 不是单纯的地址加法器,也不是只负责页表 walk 的外设。它处在设备和内存系统之间,一边面对 PCIe/CXL/片上 DMA master 等并发请求,另一边自己还要作为 bus master 读取 DDT、PDT、页表、命令队列、故障队列、MSI 表和 MRIF。硬件设计如果只看“输入 IOVA、输出 SPA”的理想路径,会漏掉三个关键问题:第一,配置结构可能不存在、无效或被软件并发修改;第二,翻译本身会产生隐式内存访问,这些访问也可能 fault;第三,虚拟化和 ATS/PRI 会把设备、进程、虚拟机三个维度交织在同一条流水线里。

核心概念

- `capabilities` 是只读能力地图,软件必须按它决定可用页表模式、ATS/PRI、MSI、PDT 级数、DBG/HPM/QoS 等。

- `fctl` 控制端序、中断方式和 GXL;规范要求在 IOMMU Off 时配置,运行中修改会进入未定义行为。

- `ddtp` 持有 DDT 根 PPN、iommu_mode 和 busy;它是从 Bare/Off 切到正式翻译的闸门。

- CQ/FQ/PQ 三组队列寄存器共同定义内存队列的 base、head、tail 和运行状态。

这些概念之间不是并列关系,而是有严格的先后依赖。通常先由 `ddtp` 决定 IOMMU 是否开启以及 DDT 有几级;再由 `device_id` walk 到 Device Context;如果请求携带 `process_id` 且 DC 使用 PDT,则继续 walk 到 Process Context;之后才进入第一阶段和第二阶段地址翻译。任何一步失败,都不能靠后续阶段“补救”,必须在对应位置产生精确 fault 或拒绝事务。

关键字段和结构图示

- capabilities.version:规范版本;1.0 通常编码为 0x10。

- capabilities.Sv32/Sv39/Sv48/Sv57:第一阶段页表支持。

- capabilities.Sv32x4/Sv39x4/Sv48x4/Sv57x4:G-stage 支持。

- ddtp.iommu_mode:Off、Bare、1LVL、2LVL、3LVL。

- cqcsr/fqcsr/pqcsr:enable、interrupt enable、memory fault、overflow/illegal、on、busy 等状态位。

可以把这些字段理解为硬件状态机的输入条件,而不是软件文档里的静态表格。比如 `EN_ATS` 不只是一个功能开关,它决定 Translated Request、ATS Translation Request 和 ATS invalidation completion 是否属于合法入站事务;`T2GPA` 不只是返回值格式,它会改变后续 Translated Request 是否还必须通过 G-stage;`PSCID/GSCID` 不只是标识符,它们决定 IOATC 项能否被复用以及失效命令能否做到精确。

硬件行为主流程

1. 复位后读 capabilities,形成驱动可用能力集合。

2. 保持 ddtp 为 Off 或 Bare,配置 fctl。

3. 分配并清零命令队列、故障队列和可选页请求队列。

4. 写 cqb/fqb/pqb,再设置对应 CSR enable。

5. 构造 DDT/DC/PDT/页表内存结构。

6. 写 ddtp.PPN 和 iommu_mode,等待 busy 清零。

7. 软件修改内存结构后,通过命令队列执行必要失效。

实现时建议把流程拆成“快速命中路径”和“慢速 walk 路径”。快速路径处理已缓存的 DC、PC 和 IOTLB 项;慢速路径负责读内存结构、处理 access fault、更新 A/D 位、生成 fault record。两条路径必须共享同一套权限和 fault 判定规则,否则缓存命中与缓存未命中的行为会不一致。

设计取舍与微架构建议

- 寄存器文件应把软件写入值和硬件运行状态分离,例如 cqen 与 cqon。

- base/size 寄存器在队列 active 时不应被重新采样,否则会出现跨队列 fetch。

- ddtp.busy 期间需要阻止后续 ddtp 写入,并给软件明确收敛点。

- 队列 CSR 的 RW1C 错误位要严格实现,否则软件无法恢复队列。

微架构上最容易被低估的是队列、walker 和缓存之间的反压关系。命令队列可能要求失效 IOATC;翻译流水线可能正持有旧缓存项;fault writer 又可能因为 fault queue 满而无法记录错误。一个稳妥的设计会把“接收设备请求”和“提交最终响应”分开,用内部 request context 保存 DID、PID、IOVA、请求类型、权限位和 fault 候选信息,这样即使中途经历多个内存访问,也能在失败时写出完整 fault record。

常见误区

- 只配置 ddtp 而不配置 fault queue,会导致错误不可观测。

- 在 cqon=1 时修改 cqb 是未定义行为,不应作为热切换方案。

- 忽略 fctl.BE/SBE 会让大端配置下的内存结构解释错误。

自测与练习

写出一个最小启动序列,并标注哪些步骤必须在 ddtp=Off 时完成,哪些步骤可以在 IOMMU 运行后通过 command queue 同步。

建议你在纸上画两张图。第一张画“成功路径”:Untranslated DMA write 从 device_id 到 DC,再到 PC、页表、IOATC fill,最后返回 SPA。第二张画“失败路径”:PDT 非叶项 reserved 位非零时,哪些字段进入 fault record,设备侧收到什么 completion 状态,软件如何从 fault queue 定位问题。能画出这两张图,就说明你已经不只是记住了字段名,而是在按硬件动作理解规范。

掌握要点

- 能说清 capability、fctl、ddtp 和队列寄存器的职责。

- 能设计一个安全启动顺序。

- 能理解 enable/on/busy/error 位为什么要分开。

场景推演

假设一个支持 PASID 的设备发出一次写请求。若该请求没有携带 process_id,硬件首先要看 DC 是否允许默认 process_id;若请求携带 process_id,硬件要检查 PDT 模式是否覆盖该 PID 的位宽。随后,第一阶段页表会决定 IOVA 是否属于进程允许的虚拟页;第二阶段页表会决定对应 GPA 是否属于该 VM 被 hypervisor 分配的物理内存。只有两个阶段都成功,且权限、A/D 位、内存属性都满足要求时,IO bridge 才能接收最终 SPA。

如果中途失败,不同失败点必须给出不同软件可观测结果:设备上下文不存在是 DDT 类 fault,进程上下文不存在是 PDT 类 fault,PTE 权限不满足是 page fault,G-stage 不满足是 guest page fault,MSI 表项错误是 MSI PTE fault。把这些错误混成一个“translation failed”会让操作系统无法判断是驱动配置、guest 页表、hypervisor 映射还是硬件集成问题。

 

【来源:www.hdlcode.com

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录