I'm writing my linux risc v emulator in rust.
I stumble on mmu initialization.
OpenSBI works fine, prints info and transfers control to the linux kernel. But the emulator crashes when it tries to read the next instruction after satp setup ffffffe00000008c in arch/riscv/kernel/head.S:119. (Transfer between SATP_MODE_BARE and SATP_MODE_SV39)
Emulator memory layout:
0x80000000 (2M) - opensbi image
0x80200000 (64M) - linux image
0x100000000 (1k) - device tree binary
early_pg_dir:
Root page: 0000000000080a04
MMU Mappings:
Virtual Physical
0000000040000000 -> 0000000082200000 (2M) // probably dtb
ffffffe000000000 -> 0000000080200000 (2M)
ffffffe000200000 -> 0000000080400000 (2M)
ffffffe000400000 -> 0000000080600000 (2M)
ffffffe000600000 -> 0000000080800000 (2M)
ffffffe000800000 -> 0000000080a00000 (2M)
ffffffe000a00000 -> 0000000080c00000 (2M)
ffffffe000c00000 -> 0000000080e00000 (2M)
ffffffe000e00000 -> 0000000081000000 (2M)
trampoline_pg_dir:
Root page: 00000000000810be
MMU Mappings:
Virtual Physical
ffffffe000000000 -> 0000000080200000 (2M)
I tried debugging this issue by single stepping in gdb with qemu, but it crashes and burns on address mode change too. If I understand correctly: system can't switch addressing mode without identity mapping or special page fault handler.
So trampoline_pg_dir should be identity mapping?
u/stepinfusion on reddit
If we are reading the same version of head.S, it looks to me that the instruction at ...008c is head.S:97, the first time satp is modified. head.S:119 is the ret instruction at ...00ac.
I don't know this code very well but it looks like it's supposed to trap at head.S:97 and stvec is pointing to the next instruction's virtual address. It's an interesting way to switch from running in physical space to virtual space without an identity mapping.