linuxriscvmmu

MMU initialization on linux


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?


Solution

  • 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.