You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RV64 Sv39内核MMU启用失败:写入satp后内存访问异常求助

RV64 Sv39虚拟内存启用失败问题排查

问题背景

基于QEMU和OpenSBI实现RV64 Sv39内核虚拟内存,执行MMU启用代码后触发内存访问错误。

启用核心代码:

map_pages(&base_pgdir, 0, KERNBASE, KERNBASE, 0x0000000002000000, PTE_R | PTE_W | PTE_X);
u_long satp = SATP_MODE_SV39 | (base_pgdir >> 12);
asm volatile("csrw satp, %0" : : "r"(satp));

其中KERNBASE为0x80000000L,SATP_MODE_SV39为0x8000000000000000。

执行csrw satp的下一条指令时,GDB报错:

Cannot access memory at address 0x802024ec

通过get_pa(pgdir, va)和debug_page(pgdir)测试显示,0x802024ec已做恒等映射到0x802024ec:

pa=00000000802024ec
--------------------page---------------------|0000000080000000->0000000080000000  RWX    |
|0000000080001000->0000000080001000  RWX    |
......
|0000000081e7f000->0000000081e7f000  RWX    |
---------------------------------------------

可能的问题原因

1. 页表地址转换宏定义错误

这是最可能的核心问题:RV64 Sv39的PTE格式中,PPN(物理页号)对应物理地址的位12-55,而你的转换宏逻辑完全错误:

  • PA2PTE(x)将物理地址右移2位后与PTE_PPN按位与,正确逻辑应为(x >> 12) << 10(把物理地址转为页号后放到PTE的PPN字段)
  • PTE2PA(x)将PTE的PPN部分左移2位,正确逻辑应为(x >> 10) << 12(把PTE中的页号转回物理地址)
    这种错误会导致PTE中存储的物理地址完全偏离实际值,MMU无法正确翻译地址。

2. 页表自身未被映射

启用MMU后,CPU会通过页表翻译所有地址,包括页表本身的物理内存:

  • 检查base_pgdir及各级页表所在的物理页是否被包含在KERNBASE到KERNBASE+0x2000000的映射范围内
  • 若页表物理地址不在该区间,map_page中的pa < KERNBASE检查会阻止映射,导致CPU访问页表时触发错误

3. SATP寄存器设置错误

  • 确认base_pgdir是物理地址而非虚拟地址:如果base_pgdir存储的是虚拟地址,右移12位后得到的PPN无效,CPU无法定位页表根目录
  • 检查SATP_MODE_SV39的位位置是否正确:Sv39模式对应SATP寄存器的第63-60位为0b100,你的定义0x8000000000000000是正确的,但需排查类型转换导致的符号扩展问题

4. 指令预取与TLB一致性问题

执行csrw satp后,CPU预取队列可能保留MMU启用前的翻译结果,导致下一条指令访问异常:

  • 需要在csrw satp后添加sfence.vma刷新TLB、fence.i刷新指令预取队列:
    asm volatile("csrw satp, %0" : : "r"(satp));
    asm volatile("sfence.vma");
    asm volatile("fence.i");
    

5. 中间页表项权限错误

RV64要求中间级页表项(PTE1、PTE2)必须设置R/W权限才能被CPU访问:

  • 你的代码中已设置PTE_R | PTE_W | PTE_V,但需确认是否存在权限位被意外覆盖或缺失的情况

调试方法

1. 直接校验页表物理地址

在GDB中设置断点在csrw satp前,打印页表相关地址:

p/x base_pgdir          # 确认页表根目录是物理地址
p/x *base_pgdir         # 查看根页表第一个PTE的内容
p/x (PTE2PA(*base_pgdir))  # 手动计算一级页表地址,验证是否正确

2. 使用QEMU监控查看物理内存

在QEMU监控中执行xp命令直接访问物理内存,对比软件生成的页表:

xp 8x base_pgdir        # 查看根页表的8个PTE
xp 8x 0xXXXXXXX         # 替换为一级页表物理地址,查看其内容

3. 启用QEMU MMU调试日志

启动QEMU时添加-d mmu参数,查看MMU操作的详细日志,包括页错误的具体原因:

qemu-system-riscv64 -d mmu ...其他启动参数

4. 验证页表自身的映射

手动检查页表根目录的物理地址是否被正确映射:

u_long pgdir_pa = (u_long)base_pgdir;
u_long mapped_pa = get_pa(base_pgdir, pgdir_pa);
printk("pgdir_pa: %lx, mapped_pa: %lx\n", pgdir_pa, mapped_pa);

若mapped_pa != pgdir_pa,说明页表自身未被正确映射。

5. 单步检查寄存器状态

在GDB中执行csrw satp后,立即查看SATP寄存器的值:

info registers satp

确认MODE字段为Sv39,PPN部分与页表根目录物理地址匹配。

内容的提问来源于stack exchange,提问作者nyan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 18:07:05