An Intro to Kernel Development - MMU - Part 5
5 mins
Let's see how to jump from PA to VA...
In the previous blog we went through more table layout and the structure debugging, now that it is over, we can finally enable the MMU properly and jump into Virtul Address range.
the flow seemed straight forward when i initial thought about it
Kernel Gets loadded into 0x80000000 (my example)
|
we want our kernel to use 0xffff000080000000 VA
|
we add pages for 0xffff000080000000 -> 0x0000000080000000
|
enable MMU
|
we jump into VA adddress range and execute from there
that should be good, i tired this, but there is one problem, the moment we enable MMU, that next instruction that needs to executed will throw a translation fault
0x80000920 orr x0, x0, #0x1
0x80000924 orr x0, x0, #0x4
0x80000928 orr x0, x0, #0x1000
●→ 0x8000092c msr sctlr_el1, x0
0x80000930 dsb sy
0x80000934 isb
0x80000938 ldp x29, x30, [sp, #128]
0x8000093c add sp, sp, #0x90
0x80000940 ret
──────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, stopped 0x8000092c in mmu_init (), reason: BREAKPOINT
────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x8000092c → mmu_init()
[#1] 0x80000034 → _start()
─────────────────────────────────────────────────────────────────────────────────────────
(remote) gef➤
Look at this break point, since my kernel is loaded into 0x8000000, all the instructions will exist in that range
enabling mmu instruction is on 0x8000092c
the moment we execute this instruction, mmu will be enabled and the next address 0x80000930 will throw a translation fault
as you know, we only have page table entry for 0xffff000080000000
we dont have any entry for 0x80000000
how can we get over this, i was looking into how linux tackles this scenario.
Linux just creates an identity map for 0x80000000 -> 0x80000000
this way the next instruction will be still be reachable and then we can add the extra code to jump into the VA instruction
this old commit message confirms that identity map is created during MMU initialization
so now we will have two page table entries, one for the VA and one for the identiy map
how do we find the correct address to jump into?
bl mmu_init
ldr x0, =0xFFFF000080000000
ldr x1, =0x0000000080000000
sub x2, x0, x1
adrp x3, virt_entry
add x3, x3, :lo12:virt_entry
add x3, x3, x2
br x3
virt_entry:
bl kmain
b .
we can calculate that, we know the VA base address, we know the PA base address, subtract them to get the Base offset
for the last 12bits, which will be the acutal instruction with the offset, we can use labbel and get its last 12bits
joining base offset + index offset should give the correct address that we need to jump into.
all good, and it work perfectly.
Security Note:
since we just create the idenity map, if we dont clear that after we jumped into the VA, it would be as security issue, as we are leaving access to reach the physical address directly, we need to make sure we clear that entry right after we jump into the VA.