Linux boot phases
- title
- Linux boot phases
- type
- concept
- summary
- The six-phase x86_64 Linux boot model โ assembly trampoline, early C, setup_arch, start_kernel, rest_init, kernel_init โ each phase unlocking the capabilities the next one needs
- tags
- linux, kernel, boot
- parent
- linux-kernel-startup
- created
- 2026-05-14
- updated
- 2026-05-14
Linux boot on x86_64 is a strictly ordered construction sequence, not a script. Each phase has a different "what can we do" envelope, and the order matters because each step unlocks the capability the next one needs. Espino's framing in linux-kernel-startup groups it into six phases.
The phases
| Phase | Entry point | What becomes possible |
|---|---|---|
| 1. Assembly | startup_64 |
CPU in Long Mode, page tables fixed, ready to run C |
| 2. Early C | x86_64_start_kernel |
.bss cleared, fault handlers, boot params saved, microcode patched |
3. setup_arch |
setup_arch() |
CPU features known, memory map parsed, full RAM mapped, early printk |
4. start_kernel |
start_kernel() |
kmalloc works, scheduler/RCU/timers up, interrupts on, real console, self-patched for this CPU |
5. rest_init |
rest_init() |
PID 1 (kernel_init) and PID 2 (kthreadd) exist; boot thread becomes idle |
6. kernel_init |
kernel_init_freeable() |
Other CPUs awake, drivers loaded, root mounted, __init freed, userspace running |
Why the order matters
Two ordering rules carry most of the structure:
Interrupts off until everything that takes interrupts exists. Phase 4 runs almost entirely with interrupts disabled. The kernel can't accept an interrupt until the IRQ controller is wired, timers can fire, RCU is alive, the timekeeper has a clock, and the printk machinery can complain on failure. local_irq_enable() happens deep inside start_kernel(), not at its start.
Each allocator hands off to the next. Boot rides on memblock (a tracker, not really an allocator) until mm_core_init() brings the buddy โ SLUB โ vmalloc stack online. memblock_free_all() is the discrete moment ownership transfers; from then on every allocation flows through the page allocator and the slab cache.
The handoff: boot thread becomes idle
The most consequential boundary is between Phase 4 and Phase 5. Throughout Phase 4, a single thread (init_task) is doing all the work. sched_init() quietly relabels it as the boot CPU's idle task โ but it keeps running init code. The actual handover happens in rest_init(): schedule_preempt_disabled() yields the CPU, and when control comes back, the original thread enters do_idle() forever. PID 1 (kernel_init) carries the boot work from that point. Same execution gets split into three threads: PID 0 (idle), PID 1 (init โ userspace), PID 2 (kthreadd).
The "lock the doors last" rule
Several hardenings happen in Phase 6 after most code has run because the hardening would prevent the running code:
mark_readonly()flipsrodatato read-only at the page-table level โ can't do it earlier because the kernel was patching its own code (alternatives, jump labels).pti_finalize()finalizes Page Table Isolation โ same reason.free_initmem()reclaims__init-tagged functions โ has to be after every__initfunction has finished running.
What's reusable from the model
The six-phase shape is specific to Linux, but two general patterns survive in any boot path:
- Per-phase capability envelope. Each phase has a different "what can we call?" set. Documenting that envelope is the most useful thing a boot map can do.
- Allocator handoff as a discrete event. A primitive bootstrap allocator gives way to the real one at a specific moment. Phases before that moment can only use raw chunks; phases after can use objects.
Cross-references
- linux-kernel-startup โ the article this model came from
- kernel-self-patching โ the runtime-rewriting that happens in Phase 4
- linux-preemption-models โ what happens to scheduling after boot