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.