An Intro to Kernel Development - MMU - Part 2
5 mins
Let's Setup and Enable MMU to jump into the kerenl...
In the previous blog we started of with why we need a MMU and how it works, we got the picture of the seperation between two processes achieved through having a table with virtual to physical address translation.
MMU requires Tables to convert virtual to physical address
we have two cases, user level table, kernel level table
MMU excepts these table's base address to be in a special registers
TTBR0_EL1 - User Table
TTBR1_EL1 - Kernel Table
we need to initialize these table base addresses.
ldr x0, =user_table_base
ldr x0, [x0]
msr TTBR0_EL1, x0
ldr x0, =kernel_table_base
ldr x0, [x0]
msr TTBR1_EL1, x0
FUN FACT: at this point, i had a stupid doubt, because, we just enabled MMU, will MMU read this address in these special register as virtual or physical, if it does take it as virtual, then we will have chicken or egg problem, where mmu has to translate an address that points to the table which has the translations, sad, however, MMU considers the address in this special registers to be pointing to physical address, so we are good.
From user processes POV, if two of them were running, when we switch between them, we also have to update the Table base address (TTBR0_EL1) accordingly.
Security Fact: if we can write to this register using some kernel expoit, we can effectivly get into another processes's Virtual space.
now we have to tell the MMU about our address layout, the seperation between User Space and Kernel Space, total physical space.
this is important, because user's process will use a Virtual address range that doesn't overlap with the kernel address
EG: all kernel address will start with 0xffff and all user address will start with 0x0000
so if user process try to reach 0xffff, MMU can directly throw a fault instead of looking into its table
this is set in the register TCR_EL1, this register is separated into multiple parts, we only care about three of them,
T0SZ [5:0] = hold the size of user space
T1SZ [21:16] = hold the size of kernel space
IPS [34:32] = total size of the physical space
Eg: Linux uses 42 bit physical space, ie 4TB, i have use 40 bit, 1TB
64 - 16 = 48 bits, virtual space, ie 128 TB for both user and kernel
Bit: [63........35] [34:32] [31:22] [21:16] [15:6] [5:0]
Value: 0 010 0 10000 0 10000
Meaning: — IPS=40b — T1SZ=16 — T0SZ=16
ldr x0, =((16) | (16 << 16) | (2 << 32))
msr TCR_EL1, x0
isb
let's finally enable the MMU, as we have set all need registers,
SCTLR_EL1 register's bit 0 is the MMU unit
MRS x0, SCTLR_EL1
ORR x0, x0, #(1 << 0) // Enable MMU only
MSR SCTLR_EL1, x0
ISB
now the MMU is ready, we can jump into kernel main.