MMU负责将虚拟地址转换为物理地址, 实现虚拟内存的管理。
在SUPERVISOR模式下,所有虚拟内存地址都经历单级地址转换过程,以将其转换为相应的物理地址;
当存在HYPERVISOR时,虚拟地址可以经历两级地址转换过程: 在第一阶段中,客户虚拟地址(GVA)被转换为客户物理地址(GPA), 在第二阶段中,GPA被转换为监管者物理地址。这两个阶段也被称为VS阶段和G阶段的转换。
五级页表MMU转换示意图
SATP寄存器
SATP 是 Supervisor Address Translation and Protection的缩写。 riscv架构的SATP寄存器是一个32位或者64位的读写寄存器,用于控制 supervisor 模式下的地址转换与保护。 类似于x86架构上的CR3寄存器。
satp寄存器各个字段解析如下:
- 页全局目录的物理页号(PPN),即物理地址除以 PAGESIZE ;
- 地址空间标识符(ASID),和进程绑定,转换隔离进程地址空间;
- MODE 字段,用于选择当前的地址转换方案。
地址空间标识符(ASID)和页表基地址存储在同一个 CSR(控制和状态寄存器)中,便于在上下文切换时可以原子地更改这一对值。
Mode 为0, MMU 关闭。 我自己的qemu环境默认采用的是Sv57模式,即mode值为 10。
写入satp并不意味着页面更新与随后的地址转换之间存在任何排序约束也不意味着使地址转换缓存失效。 如果新的地址空间的页面表已被修改,或者如果ASID被重用,可能需要在写入satp之后或在某些情况下在写入之前执行SFENCE.VMA指令。 不要求在写入satp时刷新地址转换缓存,这样可以降低上下文切换的成本,前提是ASID空间足够大。
页表项格式
Sv57页表格式如图
具体字段的解析:
PPN Physical Page Number 物理地址页帧号。 代表下一级页表(或者数据页)所在物理地址的页帧号
RSW Reserved for Software。用于预留给软件做自定义页表功能的位
D Dirty 该页是否被写过。 0代表当前页未被写; 1代表当前页已经被写过
A Accessed 该页是否被访问过
G Global 是否全局页面 0表示非全局页面,属于进程私有; 1表示进程页面,所有进程的内核地址空间页表项一致,标记内核地址空间页表项
U User 是否用户模式可访问
X executable 是否可执行
W Writable 是否可写
R Readable 是否可读
V Valid 是有效表项
转换过程
虚拟地址va转换为物理地址pa的过程如下:
1. 令 a 为 satp.ppn × PAGESIZE,并令 i = LEVELS − 1。
(对于Sv32,PAGESIZE = 2^12,LEVELS = 2。)satp寄存器必须处于活动状态,即有效特权模式必须为S模式或U模式。
2. 令 pte 为地址 a + va.vpn[i] × PTESIZE 处 的 PTE 的值。(对于Sv32,PTESIZE = 4。)
就是获取这一级页表中某个表项里存储的物理页帧号。 如果访问pte违反了PMA或PMP检查,则引发与原始访问类型相对应的访问故障异常。
3. 如果pte.v = 0 ,或者如果 pte.r = 0 且 pte.w = 1,或者如果pte中设置了任何保留供未来标准使用的位或编码,停止并引发与原始访问类型相对应的页故障异常。
4. 否则,PTE是有效的。 如果 pte.r = 1 或 pte.x = 1,则转到步骤5。 否则,此PTE是指向页表下一级的指针。令i = i − 1 。
如果i < 0 ,则停止并引发与原始访问类型相对应的页故障异常。否则,令a = pte.ppn × PAGESIZE ,并转到步骤2。
5. 找到了叶子PTE。 根据当前特权模式以及mstatus寄存器的SUM和MXR字段的值,确定所请求的内存访问是否允许pte.r、pte.w、pte.x和pte.u位。
如果不允许,则停止并引发与原始访问类型相对应的页故障异常。
6. 如果i > 0 且pte.ppn[i−1:0] ≠ 0 ,则这是一个不对齐的超页;停止并引发与原始访问类型相对应的页故障异常。
7. 如果pte.a = 0 ,或者如果原始内存访问是存储且pte.d = 0 ,则引发与原始访问类型相对应的页故障异常,或者:
如果对pte的存储会违反PMA或PMP检查,则引发与原始访问类型相对应的访问故障异常。
以原子方式执行以下步骤:
将pte与地址a + va.vpn[i]×PTESIZE 处的PTE的值进行比较。
如果值匹配,则将pte.a设置为1,并且如果原始内存访问是存储,则还将pte.d设置为1。
如果比较失败,则返回步骤2。
8. 转换成功。转换后的物理地址如下所示:
pa.pgoff = va.pgoff。
如果i > 0 ,则这是超页转换,且pa.ppn[i−1:0] = va.vpn[i−1:0] 。
pa.ppn[LEVELS−1:i] = pte.ppn[LEVELS−1:i] 。
上述算法适用于Sv39、Sv48和Sv57,具体变化如下:
对于Sv39,PTESIZE = 8,LEVELS = 3
对于Sv48,PTESIZE = 8,LEVELS = 4
对于Sv57,PTESIZE = 8,LEVELS = 5
PTE的任何级别都可以是叶子PTE,因此
除了4 KiB页,Sv32还支持4 MiB 超页。
除了4 KiB页,Sv39还支持2 MiB 超页和1 GiB 超页。
除了4 KiB页,Sv48还支持2 MiB 超页、1 GiB 超页和512 GiB 超页。
除了4 KiB页,Sv57还支持2 MiB 超页、1 GiB 超页、512 GiB 超页和256 TiB 超页。
参考
Volume 2, Privileged Specification version 20211203 https://chromite.readthedocs.io/en/latest/mmu.html