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

[IOMMU]基于 AMD IOMMU(AMD‑Vi/IOMMUv2)的系统化总结与落地方案

基于 AMD IOMMU(AMD‑Vi/IOMMUv2)的系统化总结与落地方案

       摘要:基于 AMD IOMMU(AMD‑Vi/IOMMUv2)的系统化总结与落地方案,覆盖架构要点、SoC 验证方法(功能/异常/并发/性能/虚拟化/SVM)、覆盖指标,并给出一个可直接使用的 C 参考模型(AMD 风格:RID→DTE→x86‑64 I/O 页表,含 1G/2M/4K、RW/NX、可选 PASID、IOTLB、TLBI API)。模型可通过 DPI‑C 或 TLM 轻松接入 UVM 记分牌。

一、AMD IOMMU(AMD‑Vi/IOMMUv2)核心要点(验证所需视角)

  • 目标与能力

    • 隔离与保护:把设备 DMA 的 IOVA 翻译成物理地址(PA),并按 R/W/X 权限、内存属性执行访问控制。
    • 进阶特性:
      • IOMMUv2/SVM:设备与进程共享虚地址(PASID 标识进程);设备缺页通过 PPR(Page Request)上报,主机完成映射后重试。
      • ATS/PRI:设备侧地址转换缓存(ATC)与缺页请求能力(PRI),减小缺页/行走延迟。
      • 中断重映射:MSI/MSI‑X 重定向与隔离(SoC 上可映射到平台 GIC/PLIC 等)。
  • 关键数据结构与硬件模块

    • Device Table(设备表):内存中按 RID(B:D.F)索引的 DTE(Device Table Entry)。
      • 常用字段(验证必测):V(有效)、TV(翻译使能)、DomainID(保护域标识)、PT Root(I/O 页表根地址,PML4 风格)、NX 控制、(v2)PASID 表基址等。
    • PASID 表(IOMMUv2):按 PASID 查找每个进程上下文的 I/O 页表根或进程页表根(简化为根指针)。
    • I/O 页表:x86‑64 风格 4 级(PML4→PDPT→PD→PT),叶子大小 1G/2M/4K。
      • 位语义(精简):P(present)、RW(写使能)、PS(大页,在 PD/PT 上下一级条目)、NX(不可执行)。
    • IOTLB/ATC:IOMMU 侧转换缓存与设备侧 ATS 缓存;需要一致性/失效协同。
    • 命令/事件
      • Command Ring:失效类命令(按域/按地址/全局)、同步(Completion Wait/CMD_SYNC)等。
      • Event Log:翻译/权限/表项错误、队列错误等事件。
      • PPR Log(v2):设备页请求/响应日志。
  • 典型翻译路径(简化)

    • 输入:RID、可选 PASID、IOVA、访问类型(读/写/执行标志)。
    • 步骤:RID→DTE;若 v2 且 PASID!=0,用 PASID 表覆盖页表根;按 I/O 页表行走至叶子(1G/2M/4K);检查 RW/NX 权限;返回 PA 和“到页边界”的映射长度;缓存入 IOTLB。
  • 失效与同步
    • OS/驱动修改页表或域映射后,通过命令环发 IOTLB/ATC 失效命令;CMD_SYNC 之后才能观察到新映射(顺序性需在验证中断言)。
  • 错误与上报(需覆盖)

    • DTE invalid/TV=0、PTE not present、权限拒绝(RW/NX)、表项对齐/保留位错误、命令环溢出/门铃错误、事件/PPR 日志满与背压。

二、SoC 上的 IOMMU 验证方法(环境、要点、覆盖)

  • 环境与观测点

    • 事务源:PCIe/NOC/AXI 主设备 Agent,支持并发流(多 RID/SSID/PASID)、可选 ATS/PRI。
    • 内存模型:可编程 DRAM/缓存一致性内存,暴露 IOMMU 设备表/页表/队列所在区域。
    • IOMMU 寄存器/队列:MMIO Monitor(命令环、队列基址、门铃写)、内存写监控(Event/PPR 入队)。
    • Scoreboard:基于 C 参考模型做期望翻译与错误预测;比对 IOMMU 出侧 PA 总线事务和事件日志。
  • 验证要点(功能/异常/并发/性能)

    1. 翻译正确性
      • 页大小:4K/2M/1G;跨页/边界对齐/非对齐突发;长突发分页切分。
      • 权限:R/W/X 组合;NX 生效(执行请求被拒绝)。
      • 域/上下文:不同 RID→不同域;同 RID 不同 PASID(v2)→不同页表根。
    2. 异常/错误路径
      • DTE 未有效/TV=0、PASID miss、PTE not present、RW/NX 权限拒绝。
      • 表项异常:未对齐、保留位错误、越界索引。
      • 命令/事件:队列满、指针越界、门铃写丢、事件类型与字段编码正确。
    3. 失效与同步
      • 修改页表后发 IOTLB 失效(按域/按地址/全局),在 CMD_SYNC 前后 DUT 行为差异(允许窗口 vs. 强一致)。
      • 与设备 ATS/ATC 的协同失效(若实现):ATC invalidate 广播及时到达,IOMMU/IOTLB 一致。
    4. 并发/压力
      • 多 RID/多 PASID 并发;翻译/失效/事件入队并行;PRIQ(页请求)与命令/事件同时高压。
      • 复位与电源门控:IOMMU 局部复位、设备 FLR 后上下文恢复。
    5. 虚拟化/SVM(IOMMUv2)
      • PASID 绑定/解绑/切换;设备缺页→PPR→主机映射→TLBI→重试成功。
    6. 性能/QoS
      • IOTLB 命中率、页行走带宽、命令/事件队列利用率与背压;1G/2M 大页收益。
  • 覆盖建议

    • 结构覆盖:页表层级(PML4/PDPT/PD/PT)各层命中/穿透、PS 位大页覆盖、不同权限组合。
    • 错误覆盖:各类 fault 码、事件子类型、PPR 场景、队列满/几近满。
    • 并发覆盖:多流/多域/多 PASID 的交叉、失效与翻译并发交叠。
    • 时序覆盖:CMD_SYNC 前后可见性、ATS/ATC 失效窗口。
  • 记分牌工作流

    • 入侧 Monitor 抽取 {RID,PASID,IOVA,len,is_write,need_exec}。
    • 调用参考模型 translate 得到 {PA,len_mapped,fault};将长突发按 len_mapped 分段循环。
    • 无 fault:与出侧 PA 事务一一比对;有 fault:核对 Event Log/PPR 条目与阻断行为。
    • 捕获命令环失效:调用参考模型 TLBI API 保持一致。

三、参考 C 模型(AMD 风格,通用接口,可接 DPI/TLM)

3.1 说明

  • 适用于验证记分牌作为黄金参考:RID→DTE→(可选 PASID)→x86‑64 I/O 页表行走(1G/2M/4K),RW/NX 权限检查;返回“至页边界”的映射长度;内置简易 IOTLB 与 TLBI 接口。
  • 简化且贴近 AMD 语义:DTE 32B 条目(示例),包含 V/TV/PT Root/NX 与 PASID base;PTE 使用 x86‑64 常见位(P/RW/PS/NX);PASID 表条目 8B,提供 per‑PASID 根指针。
  • 通过回调读取内存(read64),从而与 DUT 共享“同一份”表与队列内存镜像。
  • 注意:具体位定义/对齐在不同版本 SoC/RTL 可能有所差异,务必按实际设计调整 read_dte()/read_pasid_root() 的位段。
// SPDX-License-Identifier: MIT
// AMD-like IOMMU reference C model for SoC verification (C99)
// Features:
//  - RID->DTE lookup (32B entry), optional PASID table (8B entry)
//  - x86-64 style 4-level I/O page tables: 1G/2M/4K
//  - RW/NX permissions; returns partial length to page boundary
//  - Tiny IOTLB + TLBI APIs
//  - Pluggable 64-bit little-endian memory read callback#include <stdint.h>
#include <stdbool.h>
#include <string.h>// ------------ Memory read callback ------------
typedef bool (*iommu_mem_rd64_fn)(uint64_t addr, uint64_t* data, void* user);// ------------ Fault codes ------------
typedef enum {AMD_IOMMU_FAULT_NONE           = 0,AMD_IOMMU_FAULT_CTX_INVALID    = 1, // DTE invalid / TV=0 / PASID missAMD_IOMMU_FAULT_PTE_NOTPRESENT = 2,AMD_IOMMU_FAULT_PERMISSION     = 3,AMD_IOMMU_FAULT_MEM_READ       = 4,AMD_IOMMU_FAULT_ADDR_SIZE      = 5
} amd_iommu_fault_e;// ------------ Inputs / Outputs ------------
typedef struct {uint16_t rid;       // Requestor ID (B:D.F)uint32_t pasid;     // 0 if noneuint64_t iova;      // IO virtual addressuint32_t len;       // request length (bytes)uint8_t  is_write;  // 1=write, 0=readuint8_t  need_exec; // 1 if execute (X) required
} amd_iommu_req_t;typedef struct {uint64_t pa;         // translated physical address (first chunk)uint32_t len_mapped; // bytes mapped until page boundary or req enduint32_t attrs;      // opaque attributes (not modeled here)amd_iommu_fault_e fault;uint64_t fault_addr; // iova that faulted (best effort)uint8_t  leaf_lvl;   // 0:4K, 1:2M, 2:1G
} amd_iommu_resp_t;// ------------ Config / State ------------
typedef struct {uint64_t devtbl_base;   // Device Table base (byte address)uint8_t  levels;        // 4 for 48-bit VAuint8_t  v2_enable;     // PASID table enableduint8_t  nx_enable;     // Enforce NX bit
} amd_iommu_cfg_t;typedef struct {amd_iommu_cfg_t cfg;iommu_mem_rd64_fn mem_rd;void* mem_user;
} amd_iommu_env_t;// ------------ Simplified DTE/PASID formats ------------
typedef struct {uint8_t  valid;       // Vuint8_t  tv;          // Translation validuint8_t  nxe;         // NX enforcement flag (optional)uint64_t pt_root;     // I/O page table root (PML4)uint64_t pasid_base;  // PASID table base (optional)
} amd_dte_t;static bool read_dte(const amd_iommu_env_t* env, uint16_t rid, amd_dte_t* dte) {const uint64_t addr = env->cfg.devtbl_base + ((uint64_t)rid << 5); // 32B/entryuint64_t lo=0, hi=0, ex0=0, ex1=0;if (!env->mem_rd(addr + 0, &lo, env->mem_user)) return false;if (!env->mem_rd(addr + 8, &hi, env->mem_user)) return false;if (!env->mem_rd(addr +16, &ex0, env->mem_user)) return false;if (!env->mem_rd(addr +24, &ex1, env->mem_user)) return false;dte->valid    = (uint8_t)(lo & 0x1);dte->tv       = (uint8_t)((lo >> 1) & 0x1);dte->pt_root  = (lo & 0x000FFFFFFFFFF000ULL); // [51:12]dte->nxe      = (uint8_t)((hi >> 31) & 0x1);  // example bit usagedte->pasid_base = env->cfg.v2_enable ? (ex0 & 0x000FFFFFFFFFF000ULL) : 0ULL;return true;
}static bool read_pasid_root(const amd_iommu_env_t* env, uint64_t pasid_base, uint32_t pasid, uint64_t* root_out) {const uint64_t addr = pasid_base + ((uint64_t)pasid << 3); // 8B/entryuint64_t e=0;if (!env->mem_rd(addr, &e, env->mem_user)) return false;if ((e & 0x1ULL) == 0) return false; // invalid entry*root_out = (e & 0x000FFFFFFFFFF000ULL);return true;
}// ------------ x86-64 style PTE bits ------------
#define PTE_P   (1ULL<<0)   // Present
#define PTE_RW  (1ULL<<1)   // Write enable
#define PTE_PS  (1ULL<<7)   // Large page
#define PTE_NX  (1ULL<<63)  // No-Execute
static inline uint64_t pte_addr(uint64_t e) { return e & 0x000FFFFFFFFFF000ULL; }// ------------ IOTLB (tiny) ------------
#define IOTLB_SZ 1024u
typedef struct {uint8_t  valid;uint16_t rid;uint32_t pasid;uint64_t va_tag;   // page-aligned IOVAuint64_t pa_base;  // page-aligned PAuint8_t  leaf_lvl; // 0/1/2uint8_t  perms;    // bit0 R, bit1 W, bit2 Xuint32_t attrs;
} tlb_ent_t;typedef struct {amd_iommu_env_t env;tlb_ent_t tlb[IOTLB_SZ];
} amd_iommu_model_t;static inline uint32_t hash_key(uint16_t rid, uint32_t pasid, uint64_t va_tag) {uint64_t h = ((uint64_t)rid << 32) ^ ((uint64_t)pasid << 16) ^ (va_tag >> 12);h ^= (h >> 33) * 0xff51afd7ed558ccdULL;return (uint32_t)(h ^ (h >> 32));
}
static inline uint64_t lvl_pg_sz(uint8_t lvl) { return (lvl==2)?(1ULL<<30): (lvl==1)?(1ULL<<21):(1ULL<<12); }// ------------ Public APIs ------------
static inline void amd_iommu_init(amd_iommu_model_t* m,const amd_iommu_cfg_t* cfg,iommu_mem_rd64_fn mem_rd,void* mem_user) {memset(m, 0, sizeof(*m));m->env.cfg = *cfg;m->env.mem_rd = mem_rd;m->env.mem_user = mem_user;
}static inline void amd_iommu_tlbi_all(amd_iommu_model_t* m) {memset(m->tlb, 0, sizeof(m->tlb));
}
static inline void amd_iommu_tlbi_rid(amd_iommu_model_t* m, uint16_t rid) {for (unsigned i = 0; i < IOTLB_SZ; ++i)if (m->tlb[i].valid && m->tlb[i].rid == rid) m->tlb[i].valid = 0;
}
static inline void amd_iommu_tlbi_addr(amd_iommu_model_t* m, uint16_t rid, uint32_t pasid, uint64_t iova) {for (unsigned i = 0; i < IOTLB_SZ; ++i) {if (!m->tlb[i].valid) continue;if (m->tlb[i].rid == rid && m->tlb[i].pasid == pasid) {uint64_t pg_sz = lvl_pg_sz(m->tlb[i].leaf_lvl);uint64_t mask = pg_sz - 1ULL;if ((m->tlb[i].va_tag & ~mask) == (iova & ~mask)) m->tlb[i].valid = 0;}}
}// Translate one request; returns true if call succeeded; resp->fault indicates OK/fault
static inline bool amd_iommu_translate(amd_iommu_model_t* m,const amd_iommu_req_t* req,amd_iommu_resp_t* resp) {memset(resp, 0, sizeof(*resp));if (req->len == 0) return true;// IOTLB lookupconst uint64_t va = req->iova;const uint64_t va4k = va & ~0xFFFULL;uint32_t h = hash_key(req->rid, req->pasid, va4k);for (unsigned probe = 0; probe < 4; ++probe) {tlb_ent_t* e = &m->tlb[(h + probe) & (IOTLB_SZ-1)];if (e->valid && e->rid == req->rid && e->pasid == req->pasid) {uint64_t pg_sz = lvl_pg_sz(e->leaf_lvl);uint64_t mask = pg_sz - 1ULL;if ((va & ~mask) == e->va_tag) {if ((req->is_write && !(e->perms & 0x2)) ||(req->need_exec && !(e->perms & 0x4))) {resp->fault = AMD_IOMMU_FAULT_PERMISSION;resp->fault_addr = va;return true;}uint64_t off = va & mask;resp->pa = e->pa_base + off;uint32_t remain = (uint32_t)(pg_sz - off);resp->len_mapped = (req->len < remain) ? req->len : remain;resp->attrs = e->attrs;resp->leaf_lvl = e->leaf_lvl;resp->fault = AMD_IOMMU_FAULT_NONE;return true;}}}// DTE lookupamd_dte_t dte;if (!read_dte(&m->env, req->rid, &dte)) {resp->fault = AMD_IOMMU_FAULT_MEM_READ; resp->fault_addr = m->env.cfg.devtbl_base; return false;}if (!dte.valid || !dte.tv) {resp->fault = AMD_IOMMU_FAULT_CTX_INVALID; resp->fault_addr = va; return true;}// Resolve root (PASID override if enabled)uint64_t root = dte.pt_root;if (m->env.cfg.v2_enable && req->pasid != 0) {uint64_t pr = 0;if (!read_pasid_root(&m->env, dte.pasid_base, req->pasid, &pr)) {resp->fault = AMD_IOMMU_FAULT_CTX_INVALID; resp->fault_addr = va; return true;}root = pr;}// Page walk (up to 4 levels): PML4(3)->PDPT(2)->PD(1)->PT(0)const int top_lvl = (m->env.cfg.levels >= 4) ? 3 : (m->env.cfg.levels - 1);uint64_t idx[4] = {(va >> 12) & 0x1FFULL, // PT(va >> 21) & 0x1FFULL, // PD(va >> 30) & 0x1FFULL, // PDPT(va >> 39) & 0x1FFULL  // PML4};uint64_t table = root;uint8_t  leaf_lvl = 0;uint64_t leaf_base = 0;uint8_t  perms = 0;uint32_t oattrs = 0;for (int lvl = top_lvl; lvl >= 0; --lvl) {uint64_t eaddr = table + (idx[lvl] << 3);uint64_t e = 0;if (!m->env.mem_rd(eaddr, &e, m->env.mem_user)) {resp->fault = AMD_IOMMU_FAULT_MEM_READ; resp->fault_addr = eaddr; return false;}if ((e & PTE_P) == 0) {resp->fault = AMD_IOMMU_FAULT_PTE_NOTPRESENT; resp->fault_addr = va; return true;}// Large page (PDPT/PD)if ((lvl == 2 || lvl == 1) && (e & PTE_PS)) {leaf_lvl  = (lvl == 2) ? 2 : 1;     // 2:1G, 1:2Mleaf_base = pte_addr(e);perms = 0x1;                         // Rif (e & PTE_RW) perms |= 0x2;        // Wif (m->env.cfg.nx_enable) {if (!(e & PTE_NX)) perms |= 0x4;   // X if NX==0} else perms |= 0x4;                 // X ignored -> allowbreak;}// Leaf at PTif (lvl == 0) {leaf_lvl  = 0;                       // 4Kleaf_base = pte_addr(e);perms = 0x1;if (e & PTE_RW) perms |= 0x2;if (m->env.cfg.nx_enable) {if (!(e & PTE_NX)) perms |= 0x4;} else perms |= 0x4;break;}// Next leveltable = pte_addr(e);if ((table & 0xFFFULL) != 0) {resp->fault = AMD_IOMMU_FAULT_ADDR_SIZE; resp->fault_addr = va; return true;}}// Permissionif ((req->is_write && !(perms & 0x2)) || (req->need_exec && !(perms & 0x4))) {resp->fault = AMD_IOMMU_FAULT_PERMISSION; resp->fault_addr = va; return true;}// Compose responseconst uint64_t pg_sz = lvl_pg_sz(leaf_lvl);const uint64_t mask  = pg_sz - 1ULL;const uint64_t off   = va & mask;resp->pa = leaf_base + off;const uint32_t remain = (uint32_t)(pg_sz - off);resp->len_mapped = (req->len < remain) ? req->len : remain;resp->attrs = oattrs;resp->leaf_lvl = leaf_lvl;resp->fault = AMD_IOMMU_FAULT_NONE;// Fill IOTLBfor (unsigned probe = 0; probe < 4; ++probe) {tlb_ent_t* te = &m->tlb[(h + probe) & (IOTLB_SZ-1)];if (!te->valid) {te->valid   = 1;te->rid     = req->rid;te->pasid   = req->pasid;te->va_tag  = va & ~mask;te->pa_base = leaf_base;te->leaf_lvl= leaf_lvl;te->perms   = perms;te->attrs   = oattrs;break;}}return true;
}// ------------ Example RAM-backed memory provider (optional for unit tests) ------------
typedef struct {const uint8_t* base;uint64_t size;uint64_t offset; // map memory window base address
} amd_mem_blob_t;static bool amd_blob_rd64(uint64_t addr, uint64_t* data, void* user) {const amd_mem_blob_t* b = (const amd_mem_blob_t*)user;if (addr < b->offset || addr + 8 > b->offset + b->size) return false;uint64_t off = addr - b->offset;// little-endianconst uint8_t* p = b->base + off;*data = (uint64_t)p[0] | ((uint64_t)p[1]<<8) | ((uint64_t)p[2]<<16) | ((uint64_t)p[3]<<24) |((uint64_t)p[4]<<32) | ((uin
http://www.lryc.cn/news/615378.html

相关文章:

  • 【33】C#实战篇——点击按钮弹出指定路径对话框,选择指定类型文件;;;文件过滤器显示指定的一种文件,几种类型文件 同时显示
  • 云渲染的未来已来:渲酷云如何重新定义数字内容生产效率
  • 卫星遥感与AI大模型
  • 疏老师-python训练营-Day40训练和测试的规范写法
  • ADB(Android Debug Bridge)—— Android调试桥
  • PAT 1052 Linked List Sorting
  • java之父-新特性
  • React中实现完整的登录鉴权与权限控制系统
  • 算法题(183):质量检测
  • 【递归、搜索和回溯】FloodFill 算法介绍及相关例题
  • 比亚迪第五代DM技术:AI能耗管理的深度解析与实测验证
  • ToB大型软件可靠性测试方案
  • Dell PowerEdge: Servers by generation (按代系划分的服务器)
  • imx6ull-驱动开发篇15——linux自旋锁
  • Orange的运维学习日记--36.NFS详解与服务部署
  • 回答“http协议 ,js组件化,工程化, seo优化策略 ,针对不同平台终端适配 web标注和兼容性”
  • Vue3的简单学习
  • Vuex 数据共享
  • JVM常用参数有哪些?
  • 06.【数据结构-C语言】队列(先进先出,队列实现:入队列、出队列、获取队头or队尾元素,队列实现代码,队列相关题目)
  • idea设置注释--带日期和作者和描述
  • 排序概念以及插入排序
  • Oracle字段操作
  • (nice!!!)(LeetCode 面试经典 150 题) 146. LRU 缓存 (哈希表+双向链表)
  • 在 Vue 中动态引入SVG图标的实现方案
  • STM32 外设驱动模块四:光敏电阻(LDR) 模块
  • 後端開發技術教學(四) 數據交互延伸
  • 2025年渗透测试面试题总结-09(题目+回答)
  • 力扣(轮转数组)
  • 欧拉公式的意义