test_page
测试 2-stage translation 页表的 PTE的集合
vs 为 vs-stage translation 的pte
h 为 g-stage translation 的pte
1 2 3 4 5 6 7
| struct { uint64_t vs; uint64_t h; } test_page_perm_table [] = { # index ----------- vs ---------------------- h ------# [VSRWX_GRWX] = {PTE_V | PTE_RWX, PTE_V | PTE_RWX}, }
|
页表属性按照PTE的 7:0 位设置相关的bit位

hspt_init
测试框架执行 hspt_init 对2-stage 页表进行初始化
如 tinst_tests 测试项, 该测试集对指令进行测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| -+ tinst_tests \ -+ hspt_init \ - addr = 0x00000000; | -+ for(int i = 0; i < 4; i++) \ - hspt[0][i] = PTE_V | PTE_AD | PTE_RWX | (addr >> 2); "设置第一级页表 pgd" | - addr += PGSIZE << (2 * 9) "设置完后, hspt[0][0] -> hspt[0][3] 为 0 0x4000000 0x80000000 0xc0000000 base 地址" | - hspt[0][4] = PTE_V | (((uintptr_t)&hspt[1][0]) >> 2); "设置PPN 二级页表" | - hspt[1][0] = PTE_V | (((uintptr_t)&hspt[2][0]) >> 2); "设置 PPN 三级页表" | - addr = TEST_PPAGE_BASE; "0x88000000" | -+ for(int i = 0; i < TEST_PAGE_MAX; i++) "TEST_PAGE_MAX = 512, 一共设置512个PTE" \ -+ hspt[2][i] = (addr >> 2) | PTE_AD | test_page_perm_table[i].vs; "设置PTE 物理地址为 0x88000000 - 0x881ff000 / 间隔 1page" "每个PTE 对应的测试集的权限不同, 取自 test_page_perm_table.vs 权限集" | - addr += PAGE_SIZE; | - CSRW(satp, satp); "M-mode 或 Hs-mode 下设置satp 为 hspt 基地址即 hspt[0][0]的基址" | - goto_priv(PRIV_HS); "进入hs-mode" | - uintptr_t vaddr_f = hs_page_base(VSI_GI); "VSI_GI=100, vs_page_base 0x100000000 + 100* PGSIZE = 0x100064000" "访问不了, 应该报错" | - TEST_SETUP_EXCEPT(); "初始化 except 数据 为 0" | - value = lb(vaddr_f); "lb load vaddr_f 的数据" | - TEST_ASSERT(excpt.cause == CAUSE_LPF) "测试应该陷入 M-mode handler, 且mcause 应为 13 load page fault" " 如果未触发异常, 或mcause 不对, case 报错"
|
大概回顾下页表结构
可见这个是针对sv39 的页表
回顾下 sv39 页表的查表方法
sv39 in RV64:
当在 satp 寄存器中启用了分页时,S 模式和 U 模式
中的虚拟地址
会以从根部遍历页表的方式转换为物理地址。
- satp.PPN 给出了一级页表的基址, VA[38:30]给出了一级页号, 因此处理器会读取位于地址(satp.PPN × 4096 + VA[38:30] × 8)的页目录项
- 该 PTE 包含二级页表的基址, VA[29:21]给出了二级页号, 因此处理器读取位于地址(PTE.PPN × 4096 + VA[29:21] × 8)的页目录项
- 该 PTE 包含了三级页表的基址, VA[20:12] 给出了三级页号, 处理器读取位于地址(PTE.PPN × 4096 + VA[20:12] × 8)的页目录项
- 该页表项的 PTE. PPN 就是物理地址对应的 PPN * 4096 + offset 得到物理地址
PTE 的 [0-9] 位为权限位, [10-53] 的 44 位为 PPN.
sv39 上. 虚拟地址范围 39 位, 0-38 位 , 物理地址范围 56位, 0-55 位
#页表

hpt_init
此项为设置G-stage translation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| void hpt_init(){
for(int i = 0; i < 2048; i++){ hpt_root[i] = 0; }
uintptr_t addr = 0x0; for(int i = 0; i < 4; i++){ hpt_root[i] = PTE_V | PTE_U | PTE_AD | PTE_RWX | (addr >> 2); addr += SUPERPAGE_SIZE(0); } hpt_root[MEM_BASE/SUPERPAGE_SIZE(0)] = PTE_V | (((uintptr_t)&hpt[0][0]) >> 2);
addr = MEM_BASE; for(int i = 0; i < 512; i++) hpt[0][i] = 0; for(int i = 0; i < MEM_SIZE/SUPERPAGE_SIZE(1)/2; i++){ hpt[0][i] = PTE_V | PTE_U | PTE_AD | PTE_RWX | (addr >> 2); addr += SUPERPAGE_SIZE(1); } hpt_root[4] = PTE_V | (((uintptr_t)&hpt[1][0]) >> 2);
hpt_root[2047] = PTE_V | (((uintptr_t)&hpt[1][0]) >> 2); hpt[1][0] = PTE_V | (((uintptr_t)&hpt[2][0]) >> 2);
hpt[1][511] = PTE_V | (((uintptr_t)&hpt[2][0]) >> 2);
addr = TEST_PPAGE_BASE; for(int i = 0; i < TEST_PAGE_MAX; i++){ hpt[2][i] = (addr >> 2) | PTE_AD | test_page_perm_table[i].h; addr += PAGE_SIZE; } hpt[1][1] = PTE_V | (((uintptr_t)&hpt[3][0]) >> 2); addr = TEST_PPAGE_BASE; for(int i = 0; i < 512; i++){ hpt[3][i] = (addr >> 2) | PTE_V | PTE_U | PTE_AD | PTE_RWX; addr += PAGE_SIZE; }
hpt_root[5] = PTE_V | (((uintptr_t)&hpt[4][0]) >> 2); addr = TEST_PPAGE_BASE; for(int i = 0; i < 512; i++){ hpt[4][i] = (addr >> 2) | PTE_V | PTE_U | PTE_AD | PTE_RWX; addr += SUPERPAGE_SIZE(1); }
if(curr_priv == PRIV_HS || curr_priv == PRIV_M){ uintptr_t hsatp = (((uintptr_t)hpt_root) >> 12) | (0x8ULL << 60); CSRW(CSR_HGATP, hsatp); } else { ERROR("trying to set hs hgatp from lower privilege"); } }
|
再来看下 vs-stage的页表创建过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| void vspt_init(){
uintptr_t addr;
addr = 0x00000000; for(int i = 0; i < 4; i++){ vspt[0][i] = PTE_V | PTE_AD | PTE_RWX | (addr >> 2); addr += SUPERPAGE_SIZE(0); }
vspt[0][MEM_BASE/SUPERPAGE_SIZE(0)] = PTE_V | (((uintptr_t)&vspt[1][0]) >> 2);
addr = MEM_BASE; for(int i = 0; i < 512; i++) vspt[1][i] = 0; for(int i = 0; i < MEM_SIZE/SUPERPAGE_SIZE(1)/2; i++){ vspt[1][i] = PTE_V | PTE_AD | PTE_RWX | (addr >> 2); addr += SUPERPAGE_SIZE(1); }
vspt[0][4] = PTE_V | (((uintptr_t)&vspt[2][0]) >> 2);
vspt[2][0] = PTE_V | (((uintptr_t)&vspt[3][0]) >> 2);
addr = TEST_VPAGE_BASE; for(int i = 0; i < TEST_PAGE_MAX; i++){ vspt[3][i] = (addr >> 2) | PTE_AD | test_page_perm_table[i].vs; addr += PAGE_SIZE; } vspt[2][1] = PTE_V | (((uintptr_t)&vspt[4][0]) >> 2);
addr = 4 * SUPERPAGE_SIZE(0) + SUPERPAGE_SIZE(1); for(int i = 0; i < 512; i++){ vspt[4][i] = (addr >> 2) | PTE_V | PTE_AD | PTE_RWX; addr += PAGE_SIZE; }
vspt[0][5] = PTE_V | (((uintptr_t)&vspt[5][0]) >> 2);
addr = 5 * SUPERPAGE_SIZE(0); for(int i = 0; i < 512; i++){ vspt[5][i] = (addr >> 2) | PTE_V | PTE_AD | PTE_RWX; addr += SUPERPAGE_SIZE(1); }
uintptr_t satp = (((uintptr_t)vspt) >> 12) | (0x8ULL << 60); if(curr_priv == PRIV_VS){ CSRW(satp, satp); } else if(curr_priv == PRIV_HS || curr_priv == PRIV_M){ CSRW(CSR_VSATP, satp); } else { ERROR(""); } }
|
1 2 3 4
| uintptr_t addr1 = phys_page_base(SWITCH1); uintptr_t addr2 = phys_page_base(SWITCH2); uintptr_t vaddr1 = vs_page_base(SWITCH1); uintptr_t vaddr2 = vs_page_base(SWITCH2);
|
先看 vs-stage 的翻译, 0x10000_0000 + 108 * 4k
首先 satp = vsatp. PPN = vspt >> 12, 对应sv39模式,
va[38:30] (9位) 给出了一级页号偏移为 4, 在一级页表的物理页中找到二级页表的物理页号 vspt[0][4]
va[29:21] (9位) 给出了二级页号偏移为 0, 在二级页表的物理页中找到三级页表的物理页号 vspt[2][0]
va[20:12] (9位) 给出了三级页号偏移为 108, 在三级页表的物理页中找到要访问位置的物理页号 vspt[3][108]
物理页号对应的物理页基址(即物理页号左移12位)加上 offset 就是虚拟地址对应的物理地址
vspt[3][108].ppn << 12 + 0 (offset) = 0x10000_0000 + 108*4k + 0
所以 vs-stage 0x10000_0000 - 0x10000_0000 + 4M 的范围是直接映射的.
再来看G-stage的翻译过程, GPA为 0x10000_0000 + 108 * 4k
对应于 sv39x4, hgatp.PPN 给出了一级页表首地址
GPA[40:30] (11位) 给出了一级页号偏移为 4, 在一级页表的物理页中找到二级页表的物理页号 hpt_root[4]
GPA[29:21] (9位) 给出了二级页号偏移为 0, 在二级页表的物理页中找到三级页表的物理页号 hpt[1][0]
GPA[20:12] (9位) 给出了三级页号偏移为 108, 在三级页表的物理页中找到要访问位置的物理页号 hpt[2][108]
物理页号对应的物理页基址(即物理页号左移12位)加上 offset 就是虚拟地址对应的物理地址 , 对应的权限是 test_page_perm_table[108].h=PTE_U | PTE_RWX
hpt[2][108].ppn << 12 + 0 (offset) = 0x8800_0000 + 108*4k + 0
翻译后, addr1 与 vaddr1 正好是对应的 HPA 与 GVA的映射关系
再来看下 second_stage_only_translation 的测试项:
1
| vs_page_base_limit(TOP) = 0x1fffffff000 "即 0x20000_0000 前的最后一个 2M "
|
由于vstap 写了 0, guest os 未启用分页, 所以guest os 直接访问的就是GPA
对应于 sv39x4, hgatp.PPN 给出了一级页表首地址
GPA[40:30] (11位) 给出了一级页号偏移为 2047 (11个1), 在一级页表的物理页中找到二级页表的物理页号 hpt_root[2047]
GPA[29:21] (9位) 给出了二级页号偏移为 511 (9个1), 在二级页表的物理页中找到三级页表的物理页号 hpt[1][511]
GPA[20:12] (9位) 给出了三级页号偏移为 511 (9个1), 在三级页表的物理页中找到要访问位置的物理页号 hpt[2][511]
物理页号对应的物理页基址(即物理页号左移12位)加上 offset 就是虚拟地址对应的物理地址 , 对应的权限是 test_page_perm_table[511].h=PTE_U | PTE_RWX
hpt[2][511].ppn << 12 + 0 (offset) = 0x8800_0000 + 511*4k + 0
最后看下VS-mode下可以切换页表, 而页表都是落在原始地址范围 hpt 0x80020000 hspt 0x80032000 vspt 0x8002c000
先看下 vs-stage的转换 0x80020000
首先 satp = vsatp. PPN = vspt >> 12, 对应sv39模式,
va[38:30] (9位) 给出了一级页号偏移为 2, 在一级页表的物理页中找到二级页表的物理页号 vspt[0][2]
va[29:21] (9位) 给出了二级页号偏移为 0, 在二级页表的物理页中找到三级页表的物理页号 vspt[1][0]
, 此处为大页, 所以页目录walk 终止.
va[20:12] (9位) 和 va[11:10] 最终给出了页内偏移为 0x20000, 最终的GPA 地址为
vspt[1][0].ppn << 12 << 9 + 0x20000 = 0x8000_0000 + 0x20000
同理GVA-GPA的映射关系为 0x80032000 - 0x80032000 0x8002c000 - 0x8002c000
所以 0x8000_0000 - 0x8000_0000+64*0x200000 (0x8800_0000)
范围是线性映射的, GVA等同于GPA
再来看G-stage的转换 0x80020000
对应于 sv39x4, hgatp.PPN 给出了一级页表首地址
GPA[40:30] (11位) 给出了一级页号偏移为 2, 在一级页表的物理页中找到二级页表的物理页号 hpt_root[2]
GPA[29:21] (9位) 给出了二级页号偏移为 0, 在二级页表的物理页中找到三级页表的物理页号 hpt[0][0]
, 此处为大页, 所以页目录walk 终止.
GPA[20:12] (9位) 和 GPA[11:10] 最终给出了页内偏移为 0x20000, 最终的HPA 地址为
hpt[0][0].ppn << 12 << 9 + 0x20000 = 0x8000_0000 + 0x20000
同理GPA-HPA的映射关系为 0x80032000 - 0x80032000 0x8002c000 - 0x8002c000
所以 0x8000_0000 - 0x8000_0000+64*0x200000 (0x8800_0000)
范围是线性映射的, GPA等同于HPA
最终 VS-mode下可以切换 hs vs g-stage 的页表项, 页表项所处的地址范围 GVA 和 HPA 是线性映射的, GVA等同于 HPA.
调研 qemu 和 spike 的代码, G-stage 的页表项必须带有 PTE_U 的 flag, G-stage 才能不导致 load/write exception.