AOSP Foundations
6 min read

Linux Kernel Initialization

Follow the execution path as the Linux kernel decompresses itself, probes the hardware, and mounts the root file system.

The bootloader has finished its job and shut down permanently. A compressed kernel image now sits in raw memory. But how does a single static binary wake up an entire mobile device?

The First Millisecond: Why the Kernel is Zipped

Bootloaders operate under strict constraints. Reading raw data from flash storage takes time, and system memory is limited. Android solves this by compressing the kernel image inside the boot partition. This creates a chicken-and-egg problem. If the kernel is a compressed archive, how can the processor execute it?

The kernel includes a decompression stub program glued to its front. This stub acts like a self-extracting archive on a desktop computer. When the bootloader jumps to the kernel address, it actually executes this stub first. The stub allocates RAM and unpacks the real Linux kernel binary into that space.

To inspect the raw kernel executable, you must extract it from the boot image. The following commands unpack the boot partition and decompress the LZ4 payload. This produces an uncompressed kernel_decompressed binary file on your host machine.

unpack_bootimg --boot_img boot.img
lz4 -d kernel kernel_decompressed

Engineers often incorrectly assume the bootloader unzips the kernel before execution. Linux actually unzips itself using the stub.

Did You Know: The kernel decompression stub is completely architecture-specific. On modern Android devices, this stub is written entirely in ARM64 assembly rather than C.

Once the stub fully unpacks the kernel into a clean stretch of RAM, Linux can start running its main C code.

Awakening the Core Subsystems

The kernel must establish its foundational environment before it can interact with the outside world. How does it set up memory management and scheduling before drivers even exist? The answer lies in an orchestrator function called start_kernel().

Tip: You can find the source code for this orchestrator at init/main.c in the AOSP kernel repository. Android does not modify this specific entry point.

This function builds the core of the operating system. First, it configures CPU interrupt controllers. Next, it initializes virtual memory page tables. Finally, it starts the task scheduler so the system can run multiple threads concurrently. Think of this phase like building the foundation and walls of a house before installing appliances.

The following flowchart illustrates how execution flows from the bootloader through decompression and into the core subsystem initializations. This visualizes the exact sequence of events that awaken the kernel.

The bootloader hands control to the stub, which extracts the kernel. Then, start_kernel() initializes memory, scheduling, and interrupts in a strict sequence.

Interview Note: Many engineers mistakenly think start_kernel() is an Android-specific addition. That function comes entirely from pure upstream Linux code.

With the brain of the kernel awake, it now needs to figure out what body it is attached to.

Reading the Hardware Map (Device Tree)

Historically, Android manufacturers hardcoded hardware details directly into the kernel source. This meant engineers had to compile a completely different kernel binary for every phone model. The Linux community solved this fragmentation by decoupling hardware definitions from executable code.

The Device Tree Blob (DTB) provides this separation. A DTB acts as a static data map that describes every physical component on the motherboard. Your bootloader passes this map to the kernel in memory. Linux reads it like a GPS instruction set. The system learns exactly where components reside, such as a touchscreen controller at a specific memory address.

touchscreen@38 {
    compatible = "synaptics,s3202";
}

Common Mistake: Beginners often confuse the Device Tree with driver code. The DTB contains pure data and zero executable logic.

Armed with the DTB map, the kernel can now match software drivers to physical chips.

Probing the Hardware

The kernel knows what hardware exists, but it must actively connect its code to those components. What happens when a driver loads? The kernel matches entries from the DTB against its list of compiled driver modules.

When it finds a match, the kernel calls a specific initialization function on the driver. Engineers call this process driver probing. The probe function wakes up the hardware chip, runs basic tests, and registers the device with the operating system. If a driver fails its probe, the OS will act as if the physical component does not exist.

This sequence diagram demonstrates the relationship between the kernel, the hardware map, and the drivers. It clarifies how dynamic discovery triggers driver initialization.

The kernel reads the hardware description from the DTB. It then triggers the matching driver to probe and register the physical device.

You can verify hardware discovery by reading the kernel ring buffer. The following command filters the logs for probe events. This outputs a chronological list of successfully initialized devices directly to your terminal.

dmesg | grep -i "probe"

Common Mistake: A frequent mistake is running this command without root privileges on Android, which results in empty or permission-denied output.

The kernel now has memory, scheduling, and working hardware, but it lacks files.

The File System Catch-22 and the Ramdisk

A Linux environment requires a file system to run user programs. You need the flash storage driver to mount the root file system. However, the flash driver file itself is often stored on that exact file system. This creates a bootstrapping dilemma.

Android resolves this by using an initial ramdisk (initramfs). The bootloader places this mini-filesystem into memory right next to the kernel. Linux mounts this temporary RAM disk as the root directory. This ramdisk contains the minimum binaries and kernel modules required to initialize the real storage partitions.

Common Mistake: Do not confuse the ramdisk with the final Android root filesystem. The ramdisk serves strictly as a temporary bridge.

After mounting a temporary file system in RAM, the kernel can finally execute its first user-space program.

Handing Over the Keys to PID 1

The kernel has finished setting up the hardware and its internal structures. When does it stop initializing and start running Android? The system achieves this by explicitly looking for an executable file named /init on the ramdisk.

It executes this binary as Process ID 1 (PID 1). This exact moment marks the handoff from kernel-space execution to user-space execution. The construction crew has finished the building and handed the keys to the property manager. In modern AOSP, this first execution does basic setup and then transitions to the real Android initialization sequence.

Common Mistake: The kernel does not start the boot animation or the Zygote. It only starts the init process.

The kernel's solo act is over, but the Android boot process has only just begun. Everything from this point forward relies on that single /init binary.