Which Page table is appled when MMU is turned on in boot process of ARM64

Chan Kim ckim at etri.re.kr
Fri Mar 18 07:02:52 EDT 2022

Hi JeongHwan,

The boot loader (like u-boot) loads the linux Image (in arm64, Image is uncompressed format) on memory and make the pc jump to that address.

For example, see this code from u-boot.

msr spsr_el3, \tmp

    msr elr_el3, \ep


The u-boot program normally runs at el3 and making all the conditions right for starting the linux (there are some requirements),

Sets elr_el3 (exception link register el3) register with the ep (entry point) value, which physical address like 0x80080000. 

When the cpu executes ‘eret’(return from exception), the pc value becomes the value in elr_el3(for example 0x80080000) and it goes into el2.

In linux head.S, it either remains in el2 or goes down to el1 before jumping to start_kernel().

I found the -fno-PIE is needed for ASLR (address space location randomization from kernel 4.xx) and I don’t know exactly what ASLR is. (but I know what KASLR is)

Anyway, the codes are placed packed or sometimes spaced in kernel virtual addresses by the linker. As you said they are all express in kernel virtual address.

In my recent experiment , for example _head was in 0xffffffc010080000 but it’s 0x80080000 physically. And ffffffc0103a0320 is just 0x803a0320. 

There is this linear relationship for the kernel.(convenient). But the codes and data before the “primary_switched” are accessed using physical address.

In assembly b (branch, it’s goto. No return) or bl (branch and link, this is like function. It returns to current location using link register) is using relative address when the distance is less then some value.

(maybe 24 bits?) so there is no problem for using b or bl in the head.S file. But as you saw, when it call primary_switched, ( br x8 ), the address in x8 is pure virtual address for the first time.

Hope that helps.


Chan Kim



From: JeongHwan Kim <frog007.kernel.kr at gmail.com> 
Sent: Friday, March 18, 2022 4:41 PM
To: Chan Kim <ckim at etri.re.kr>; kernelnewbies at kernelnewbies.org
Subject: Re: Which Page table is appled when MMU is turned on in boot process of ARM64


Thank you for reply.


I have a question about the meaning of "Until MMU is turned on, the CPU has been 

using physical address and pc-relative jumps."

I understand the CPU runs on physical address in assembly code before MMU is enabled.
But how is it possible?
For example, when the head.S is compiled, the PIC(position independent code) option is disabled as like this using -fno-PIE option:
aarch64-linux-gnu-gcc -Wp,-MMD,arch/arm64/kernel/.head.o.d  -nostdinc -isystem /usr/lib/gcc-cross/aarch64-linux-gnu/9/include -I./arch/arm64/include -I./arch/arm64/include/generated  -I./include -I./arch/arm64/include/uapi -I./arch/arm6
4/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/compiler-version.h -include ./include/linux/kconfig.h -D__KERNEL__ -mlittle-endian -DKASAN_SHADOW_SCALE_SHIFT= -fmacro-prefix-map=./= -D__ASSEMB
LY__ -fno-PIE -mabi=lp64 -fno-asynchronous-unwind-tables -fno-unwind-tables -DKASAN_SHADOW_SCALE_SHIFT= -Wa,-gdwarf-2    -c -o arch/arm64/kernel/head.o arch/arm64/kernel/head.S
and system.map shows all the symbols of assembly code are virtual address. 
Please let me know the method how the assembly code runs in PIC way.
Only are the pc-relative jumps needed?
J.Hwan KIm 

On 22. 3. 18. 14:35, Chan Kim wrote:

Hi JeongHwan,
Recently I followed the kernel boot process so now I understand how it's
The kernel starts at HEAD and it jumps to primary_entry. (code below is
5.10.0.  but in 5.4.21 it jumped to stext, just name change)
Primary_entry looks like this.
            bl  preserve_boot_args
            bl  el2_setup           // Drop to EL1, w0=cpu_boot_mode
            adrp    x23, __PHYS_OFFSET
            and x23, x23, MIN_KIMG_ALIGN - 1    // KASLR offset, defaults to
            bl  set_cpu_boot_mode_flag
            bl  __create_page_tables
             * The following calls CPU setup code, see arch/arm64/mm/proc.S
             * details.
             * On return, the CPU will be ready for the MMU to be turned on
             * the TCR will have been set.
            bl  __cpu_setup         // initialise processor
            b   __primary_switch
The page tables for id mapping (for 0x80000000 ~ .. physical address) and
swapper_pgdir (for virtual address) are setup inside __create_page_tables.
MMU is turned on in __primary_switch. (code below for 5.10.0-rc5)
You see there is bl __enable_mmu. Until MMU is turned on, the CPU has been
using physical address and pc-relative jumps. 
At the instant MMU is turned on, the page table for the current physical
address range is already setup so there is no problem using physical
address.(just identical mapping) 
The first place where kernel virtual address is used is at the end of
__primary_switch. The value __primary_switched is a virtual address
(starting with 0xffffffc0....) and from that point on virtual->physical
address mapping is used.  Old version will be the same. (I checked
previously for 5.4.21)
            mov x19, x0             // preserve new SCTLR_EL1 value
            mrs x20, sctlr_el1          // preserve old SCTLR_EL1
            adrp    x1, init_pg_dir
            bl  __enable_mmu
        #ifdef CONFIG_RELR
            mov x24, #0             // no RELR displacement yet
            bl  __relocate_kernel
            ldr x8, =__primary_switched
            adrp    x0, __PHYS_OFFSET
            blr x8
             * If we return here, we have a KASLR displacement in
x23 which we need
             * to take into account by discarding the current kernel
mapping and
             * creating a new one.
            msr sctlr_el1, x20          // disable the MMU
            bl  __create_page_tables        // recreate kernel
            tlbi    vmalle1             // Remove any stale TLB
            dsb nsh
            msr sctlr_el1, x19          // re-enable the MMU
            ic  iallu               // flush instructions fetched
            dsb nsh             // via old mapping
            bl  __relocate_kernel
            ldr x8, =__primary_switched
            adrp    x0, __PHYS_OFFSET
            br  x8
Hope this helps.
Have a nice day!

-----Original Message-----
From: JeongHwan Kim  <mailto:frog007.kernel.kr at gmail.com> <frog007.kernel.kr at gmail.com>
Sent: Friday, March 18, 2022 10:41 AM
To: kernelnewbies at kernelnewbies.org <mailto:kernelnewbies at kernelnewbies.org> 
Subject: Which Page table is appled when MMU is turned on in boot process
of ARM64
Hi, everyone
When ARM64 boots, it setup 2 page tables ("idmap_pg_dir" and
On turning on MMU, which page table is applied?
I heard that "idmap_pg_dir" is applied as soon as MMU is turned on.
I cannot understand why "idmap_pg_dir" is applied, which is for "Lower"
As I understand, the virtual address of kernel code is in "Higher"


Can you explain why "idmap_pg_dir" is applied on MMU turning on?
Thanks in advance
J.Hwan Kim
Kernelnewbies mailing list
Kernelnewbies at kernelnewbies.org <mailto:Kernelnewbies at kernelnewbies.org> 


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.kernelnewbies.org/pipermail/kernelnewbies/attachments/20220318/dbaa20f1/attachment-0001.html>

More information about the Kernelnewbies mailing list