Inside Linux Kernel Boot on ARM: Decompression, MMU Setup & Page Tables
The article explains the Linux kernel boot process on ARM platforms, covering how the bootloader loads and decompresses the gzipped kernel image, the entry points of the compressed zImage, the early serial output, the initialization of the MMU and page tables, and the transition to the main C kernel code.
Linux Kernel Loading Process
The Linux kernel is typically stored as a gzipped image. The bootloader copies the compressed image into RAM, the kernel self‑decompresses, and execution continues from the decompressed code.
Location of Compiled Kernel Images
./vmlinux– uncompressed ELF kernel image. arch/arm/boot/compressed/vmlinux – compressed ELF kernel image. arch/arm/boot/zImage – final compressed kernel image used by the bootloader.
Entry Point of the Compressed Kernel (zImage)
The linker script /arch/arm/boot/compressed/vmlinux.lds defines the link order and places the entry symbol _start in the .start section.
Early execution begins in /arch/arm/boot/compressed/head.S. The sequence performed there is:
Detect available system memory.
Initialise the area that will hold the C code.
Branch to the C function decompress_kernel defined in arch/arm/boot/compressed/misc.c.
Early Serial Output Before Decompression
The function used for early serial output is puts, defined in include/asm-arm/arch-s3c2410/uncompress.h. After decompression the boot code jumps to register r5, which contains the kernel’s start address.
Transition to the Real Linux Kernel
The real kernel entry point is in arch/arm/kernel/head-armv.S. The steps are:
Identify the processor type via __lookup_processor_type and __lookup_architecture_type.
Create temporary page tables with __create_page_tables.
Initialise the C code region.
Branch to the C function start_kernel (defined in init/main.c).
ARM MMU Overview
The Memory Management Unit (MMU) translates virtual addresses to physical addresses, enforces access permissions, and controls cache behaviour.
Memory Access Through the MMU
The MMU first looks up the virtual address in the Translation Lookaside Buffer (TLB).
If the entry is missing, hardware walks the page tables in main memory to obtain the translation and associated permissions.
MMU Page Table Granules
The ARM MMU supports four granule sizes:
Section – 1 MiB
Large page – 64 KiB
Small page – 4 KiB
Tiny page – 1 KiB
Page Table Levels
Two levels of page tables reside in RAM:
Level‑1 table stores section descriptors and pointers to Level‑2 tables.
Level‑2 table stores descriptors for large, small, and tiny pages.
Level‑1 Descriptor Layout
Each Level‑1 entry describes how a 1 MiB virtual region is mapped. Important bits are:
Bits[1:0] – descriptor type (10b for a section).
Bits[3:2] – cache and buffer flags.
Bits[4] – implementation‑defined.
Bits[8:5] – domain.
Bits[11:10] – access permissions (AP).
Bits[31:20] – section base address (high 12 bits of the physical address).
Section Translation Example
The diagram below illustrates how a virtual address is translated using a section descriptor.
Creating Temporary Kernel Page Tables (__create_page_tables)
__create_page_tables: pgtbl r4 @ page table address (0x30008000‑0x4000) mov r0, r4 @ r0 = 0x30004000 (start of Level‑1 table) mov r3, #0 add r2, r0, #0x4000 @ r2 = end address of the table 1: str r3, [r0], #4 @ clear one entry (4 bytes) str r3, [r0], #4 str r3, [r0], #4 str r3, [r0], #4 teq r0, r2 bne 1bThis loop zero‑initialises the Level‑1 page‑table region from 0x30004000 to 0xa0080000.
krnladr r2, r4 @ r2 = start of the kernel imageWith r4 = 0xa0004000, r2 points to the 1 MiB‑aligned kernel base at 0x30000000.
add r3, r8, r2 @ r8 holds MMU page‑table flags (e.g., 0xc0e)The resulting value r3 = 0x30000c0e encodes the section base address and the required MMU flags.
str r3, [r4, r2, lsr #18] @ identity mapping entryWrites 0x30000c0e to address 0x300068000, establishing a 1 MiB identity mapping for the kernel.
add r0, r4, #(TEXTADDR & 0xff000000) >> 18 @ base of page‑table entries bic r2, r3, #0x00f00000 @ clear lower bits for PAGE_OFFSET str r2, [r0] @ PAGE_OFFSET + 0 MiB entry add r0, r0, #(TEXTADDR & 0x00f00000) >> 18 @ next entry str r3, [r0], #4 @ KERNEL + 0 MiB entryFurther entries are generated in the same manner to map the kernel’s virtual address space.
Mapping Table Visualisation
Resulting Memory Map
Transition to C Code (start_kernel)
The C entry point is the function start_kernel in init/main.c. Its early actions are:
Initialise the printk subsystem for kernel logging.
Re‑initialise the page tables with the final kernel mappings.
Set up interrupt handling via trap_init.
Initialise the system timer, console, and other core services.
Create the first kernel thread named init, which subsequently starts user‑space init processes.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
