I'm trying to implement SV39 on a RISCV64 emulator that already is able to run a non-mmu linux kernel.
However, I run into a strange behavior where priv = 3 (machine mode) but satp.mode = 8 (SV39) shortly after boot. From what I understand, priv=3 should mean that the MMU is still disabled. But no matter if I interpret it as such, or not, memory lookups fail soon after.
I've been trying to find the linux kernel code that sets the priv mode and the satp.mode, but with no luck. The most likely candidate seems to be in arch/riscv/mm/init.c:paging_init, but a printk here never gets executed.
More importantly, the code that does get executed starts at address 0, not ffffffff80000000, which is where the kernel gets loaded.
So, this must be set up by the loader? But arch/riscv/boot/loader.S is nearly empty, no code there to set up page tables or enable the right satp mode. arch/riscv/kernel/head.S also seems suspicious, but how does it get linked in? So where is this code coming from?
This is how I build my kernel image:
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- loader riscv64-unknown-elf-objcopy -O binary arch/riscv/boot/loader loader_64.bin
For reference:
$ riscv64-unknown-elf-readelf -s arch/riscv/boot/loader
Symbol table '.symtab' contains 8 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: ffffffff80000000 0 SECTION LOCAL DEFAULT 1 .payload
2: ffffffff80a64b40 0 SECTION LOCAL DEFAULT 2 .text
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3 .riscv.attributes
4: 0000000000000000 0 FILE LOCAL DEFAULT ABS loader.o
5: ffffffff80a64b40 0 NOTYPE LOCAL DEFAULT 2 $xrv64i2p1_m2p0_[...]
6: ffffffff80000000 0 NOTYPE LOCAL DEFAULT 1 $d
7: ffffffff80000000 0 NOTYPE GLOBAL DEFAULT 1 _start
$ riscv64-unknown-elf-readelf -s arch/riscv/boot/loader.o
Symbol table '.symtab' contains 9 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 SECTION LOCAL DEFAULT 1 .text
2: 0000000000000000 0 SECTION LOCAL DEFAULT 3 .data
3: 0000000000000000 0 SECTION LOCAL DEFAULT 4 .bss
4: 0000000000000000 0 NOTYPE LOCAL DEFAULT 1 $xrv64i2p1_m2p0_[...]
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5 .payload
6: 0000000000000000 0 NOTYPE LOCAL DEFAULT 5 $d
7: 0000000000000000 0 SECTION LOCAL DEFAULT 6 .riscv.attributes
8: 0000000000000000 0 NOTYPE GLOBAL DEFAULT 5 _start
Where does the _start in loader.o come from?
priv = 3, satp.mode=8
Invalid Address, or no valid write pointer found, write not executed!: Addr: ffffffff80001084 Len: 8 Cycle: 63697 PC: ffffffff80001084
ffffffff80001084 is clearly a virtual address. While priv=3 (machine mode).