3. RISC-V特权架构与内存管理

RISC-V 虚拟化的根基并不在 Hypervisor 扩展本身,而在其特权架构与内存管理机制。理解 M/S/U 三种特权模式、页表结构、Sv39/Sv48 虚拟内存布局,以及 TLB 与地址翻译流程,是后续理解 H 扩展、VS/VU 模式和二级页表(G-stage)的前提。本节从特权级出发,逐层剖析 RISC-V 的地址翻译链路。

一、M/S/U 三种特权模式与职责边界

RISC-V 定义了三个基础特权级:机器模式(Machine, M)、监管模式(Supervisor, S)和用户模式(User, U)。特权级数值越大权限越高,M 模式是唯一必须实现的模式,S 与 U 为可选。在虚拟化场景中,Hypervisor 通常运行在 HS 模式(S 的虚拟化扩展),Guest OS 运行在 VS 模式,Guest 应用运行在 VU 模式。

  • M 模式:最高权限,负责固件、平台初始化、异常委托配置(medeleg/mideleg)、物理内存保护(PMP)。M 模式不参与常规地址翻译,通常直接使用物理地址。
  • S 模式:操作系统内核运行级,管理页表(satp)、处理缺页异常、执行 sfence.vma 刷新 TLB。S 模式下的访存需经过页表翻译。
  • U 模式:应用程序运行级,权限最低,无法访问特权 CSR,所有访存必须经 S 模式配置的页表翻译。

特权级切换通过异常、中断和 ecall 指令触发,切换时硬件自动保存 sepc、scause、stval 等 CSR,并跳转到 stvec 指定的陷阱入口。这一机制是 Guest 陷入 Hypervisor 的物理基础。

二、页表机制与 Sv39/Sv48 虚拟内存

RISC-V 采用多级页表,Sv39 为三级、Sv48 为四级,页大小均为 4 KiB。虚拟地址被划分为多个 9 位 VPN 字段加 12 位页内偏移。Sv39 的虚拟地址为 39 位,需符号扩展至 64 位;Sv48 为 48 位。页表项(PTE)为 8 字节,包含 PPN、权限位(R/W/X/U/G/A/D)和有效位 V。

特性Sv39Sv48
虚拟地址位宽39 位48 位
页表级数3 级4 级
VPN 字段VPN[2:0],各 9 位VPN[3:0],各 9 位
页内偏移12 位12 位
PTE 大小8 字节8 字节
satp.MODE89
典型应用嵌入式、轻量虚拟化服务器、大内存 Guest

页表基址由 satp CSR 提供,其中 MODE 字段选择翻译模式,ASID 字段标识地址空间,PPN 字段指向根页表物理页号。当 satp.MODE 为 0 时关闭分页,直接使用物理地址。切换进程或 Guest 时,通过写入 satp 并执行 sfence.vma 完成地址空间切换。

关键点:Sv39 与 Sv48 的页表遍历是逐级进行的,每一级 PTE 的 PPN 指向下一级页表物理页。若某级 PTE 的 R/W/X 非零,则为叶节点,翻译终止;否则继续下一级。这种设计使页表可以稀疏分布,节省物理内存。

三、TLB 与地址翻译流程

为加速地址翻译,RISC-V 处理器使用 TLB 缓存近期翻译结果。TLB 表项通常包含 VPN、ASID、权限位和 PPN。当 satp 改变或页表更新后,必须执行 sfence.vma 使相关 TLB 表项失效,否则会出现陈旧翻译导致的安全问题。

完整的地址翻译流程如下:CPU 发出虚拟地址,先查 TLB;命中则直接得到物理地址并检查权限;未命中则触发硬件页表遍历(Page Table Walk),从 satp.PPN 指向的根页表开始,逐级读取 PTE,直到叶节点。若遍历过程中发现 V=0、权限不匹配或 A/D 位需要更新,则触发缺页异常,交由 S 模式处理。

RISC-V Sv39 地址翻译与 TLB 流程 CPU 发出虚拟地址 VA[38:0] 查询 TLB VPN + ASID 匹配 命中 得到物理地址 检查 R/W/X 权限 未命中 硬件页表遍历 (Page Walk) 从 satp.PPN 根页表开始 Level 2 VPN[2] 9 位索引 Level 1 VPN[1] 9 位索引 Level 0 VPN[0] 9 位索引 叶节点 PTE PPN + 权限 R/W/X 非零 物理地址 = PPN + 偏移 回填 TLB 异常路径 V=0 / 权限不匹配 / A/D 更新 → 缺页异常 → S 模式处理 stval 保存出错地址 satp CSR MODE=8 (Sv39) ASID + 根页表 PPN 提供基址 sfence.vma 刷新 TLB 切换地址空间

四、案例场景:Guest 缺页与二级翻译

在虚拟化环境中,Guest OS 配置的是 VS 模式的 vsatp,其页表翻译产生 Guest 物理地址(GPA)。GPA 还需经过 Hypervisor 的 G-stage 页表(由 hgatp 指向)翻译为真实物理地址(HPA)。因此一次 Guest 访存可能触发两级页表遍历,任一阶段缺页都会陷入 Hypervisor。

# 典型 Guest 缺页处理流程(伪代码)
1. Guest 访存 VA → VS 页表遍历 → 缺页
2. 陷入 HS 模式,读取 vsatp / hgatp
3. Hypervisor 检查 GPA 是否合法
4. 若合法,分配物理页,更新 G-stage 页表
5. 执行 hfence.gvma 刷新相关 TLB
6. 返回 Guest,重试访存指令
调试提示:若 Guest 频繁缺页但物理内存充足,应检查 hgatp 的 G-stage 页表是否覆盖了 Guest 的全部 GPA 范围,以及 A/D 位是否被正确更新。A/D 位未置位会导致反复缺页。

五、常见误区与复盘

  • 误区一:认为 M 模式也走页表翻译。实际上 M 模式默认使用物理地址,除非显式配置 mstatus.MPRV 进行特权访存模拟。
  • 误区二:修改页表后忘记 sfence.vma。TLB 可能保留旧翻译,导致写入新页表后仍访问旧物理页,引发数据损坏或安全漏洞。
  • 误区三:混淆 Sv39 与 Sv48 的 satp.MODE 值。MODE=8 对应 Sv39,MODE=9 对应 Sv48,写错会导致翻译模式错误甚至异常。
  • 误区四:忽略 ASID 的作用。ASID 使不同地址空间的 TLB 表项共存,减少切换时的刷新开销;但 ASID 回绕时仍需全局刷新。

复盘要点:地址翻译是 RISC-V 虚拟化的核心链路,M/S/U 模式决定权限边界,Sv39/Sv48 决定页表深度与地址空间大小,TLB 决定性能,而 sfence.vma 与 A/D 位决定正确性。掌握这条链路后,再理解 H 扩展的 VS/VU 模式和 G-stage 二级翻译就会水到渠成。