L33: The Linux Boot Process on x86 — A Full Walkthrough

 

EmbeddedPathashala  ›  Free Linux Kernel Programming Course  ›  Chapter 3

The Linux Boot Process on x86 — A Full Walkthrough

From the moment you press the power button to the shell prompt — every stage, explained clearly for students and engineers.

📘 Lecture 33
⏰ 15 min read
🐧 Linux 6.x + UEFI

Topics Covered

POST / BIOS / UEFI
MBR vs GPT
GRUB2
vmlinuz
initramfs
switch_root
systemd PID 1
Secure Boot
U-Boot ARM
Device Tree

What You Will Learn

  • Every stage of the Linux x86 boot sequence — power-on to login prompt
  • The difference between legacy BIOS and modern UEFI boot paths
  • What GRUB2 does and why Linux needs a bootloader at all
  • How the kernel initialises hardware and transitions through initramfs
  • Why systemd replaced SysV init, and what it does that SysV could not
  • How the embedded Linux boot path differs on ARM boards

📷 Suggested Image for This Post

A vertical flowchart image with five labelled boxes: Firmware → GRUB2 → Linux Kernel → initramfs → systemd, each box a different shade of blue-green. Alt text: Linux x86 boot sequence diagram showing all stages from BIOS to systemd.

Two Paths — BIOS and UEFI

Before going through the boot stages, you need to know that modern x86 hardware can boot in two very different ways. Which one your machine uses affects the early stages of the boot sequence significantly.

BIOS vs UEFI — Key Differences at a Glance

Feature Legacy BIOS UEFI (Modern, 2012+)
Disk format MBR — max 2 TB, 4 partitions GPT — 9.4 ZB, 128 partitions
Boot entry point First 512 bytes of disk (MBR) EFI System Partition (FAT32)
Bootloader space 446 bytes for Stage 1 code Full .efi binary, no size limit
Security None Secure Boot (cryptographic signing)
GRUB on this path Stage 1 → Stage 2 → GRUB GRUB loaded directly as grubx64.efi

The kernel initialisation steps from Step 4 onward are identical regardless of which firmware path was taken. The difference is only in how you get to the point where the kernel starts running.

The Linux Boot Sequence — Step by Step

Step 1 — Power On and POST

The instant the power button is pressed, the CPU begins executing instructions from a fixed address in ROM. This is where the firmware (BIOS or UEFI chip) lives. The very first thing it runs is POST — Power-On Self Test.

POST checks that the CPU, RAM modules, and basic hardware are functioning. If something critical fails, the system halts — on older machines you hear beep codes that indicate which component failed.

After POST, the firmware initialises the keyboard controller, display, storage controllers, and PCI bus. It then scans the configured boot order (NVMe SSD, USB, network) looking for a bootable device.

Step 2 — Stage 1 Bootloader (MBR) or EFI Handoff

💾 BIOS Path

Reads first 512 bytes of disk — the MBR. First 446 bytes = Stage 1 bootloader code. Just enough space to locate and load Stage 2 (which can read filesystems). Stage 2 then loads GRUB2.

⛅ UEFI Path

No MBR needed. Firmware reads the EFI System Partition (a small FAT32 partition) and loads grubx64.efi directly as a proper application. Much simpler — no Stage 1/Stage 2 complexity.

Step 3 — GRUB2 Runs

GRUB2 (Grand Unified Bootloader version 2) is the main bootloader used on virtually all x86 Linux systems. It reads its configuration from /boot/grub/grub.cfg and, if multiple kernels are installed, shows a menu.

GRUB’s main job is to load two files into RAM and jump to the kernel entry point:

  • /boot/vmlinuz-6.x.y — the compressed kernel image
  • /boot/initrd.img-6.x.y — the compressed initramfs archive
# What a GRUB2 menu entry looks like in grub.cfg:
menuentry "Ubuntu 24.04, Linux 6.8.0-48-generic" {
    linux  /boot/vmlinuz-6.8.0-48-generic root=UUID=xxxx ro quiet splash
    initrd /boot/initrd.img-6.8.0-48-generic
}

After loading both images, GRUB jumps to the kernel’s entry point. GRUB’s job is now done.

Step 4 — Kernel Takes Control and Initialises Hardware

The Linux kernel is now running. Its very first task is to decompress itself — the vmlinuz file is a self-extracting compressed archive. Once decompressed into RAM, the real kernel code starts executing.

A crucial design principle kicks in here: the kernel makes no assumptions about what the bootloader did. It re-initialises all hardware from scratch — CPU state, MMU (memory management unit), interrupt controllers, timers, PCI enumeration. This is what makes the kernel portable: it can boot correctly whether loaded by GRUB, U-Boot, or any other bootloader.

CPU mode setup
MMU / paging
Interrupt tables
Timer / clock
Memory map
PCI / ACPI
Console / earlycon

Step 5 — Kernel Detects and Unpacks initramfs

Once hardware initialisation is mostly done, the kernel checks whether CONFIG_BLK_DEV_INITRD=y is set (it always is on distribution kernels). It locates the initramfs archive that GRUB placed in RAM, decompresses the cpio archive, and mounts it as a temporary root filesystem in RAM.

At this moment the kernel’s / (root) is a tiny in-memory filesystem — not your hard drive. The kernel can now read files from it.

# Verify initramfs support in your running kernel:
grep CONFIG_BLK_DEV_INITRD /boot/config-$(uname -r)
# Output: CONFIG_BLK_DEV_INITRD=y

Step 6 — initramfs /init Script Runs, Loads Modules

The kernel executes /init inside the initramfs. This is a shell script (using busybox utilities) that does the heavy lifting:

  • Loads the filesystem driver module — ext4.ko, btrfs.ko, etc. — from the initramfs itself
  • If the disk is encrypted: runs cryptsetup to prompt for a passphrase and unlock the LUKS volume
  • If LVM is used: activates volume groups with lvm vgchange -ay
  • Locates the real root partition using the UUID or device label passed as a kernel parameter by GRUB
  • Mounts the real root filesystem at /root inside the initramfs environment

Step 7 — switch_root Transitions to the Real Filesystem

Now that the real root filesystem is mounted and ready, the init script calls switch_root. This is the modern replacement for the older pivot_root system call.

switch_root atomically performs three actions:

  • Makes the real disk filesystem the new / for the running kernel
  • Unmounts and discards the initramfs tmpfs — freeing that RAM
  • Execs /sbin/init on the real filesystem (which is systemd on modern systems)

After this point, the kernel is running with your real disk as its root — the initramfs is completely gone from memory.

Step 8 — systemd Starts as PID 1

The first process the kernel launches in the real userspace is always PID 1. On virtually all modern Linux systems — including most embedded distributions — this is systemd, which replaced the older SysV /sbin/init.

systemd reads unit files from /etc/systemd/system/ and /lib/systemd/system/ and starts all required services in parallel, respecting dependency declarations between them.

# Confirm what is running as PID 1 on your system:
ps -p 1 -o comm=
# Output: systemd

ls -la /sbin/init
# /sbin/init -> /lib/systemd/systemd

Step 9 — System Services Come Up, Login Appears

systemd works through its targets — sysinit.target, basic.target, multi-user.target, and optionally graphical.target. Services like networking, udev, SSH, and the display manager start in parallel where dependencies allow. When all required services reach their target, the login prompt or desktop appears.

# See how long each stage of boot took:
systemd-analyze

# See which services took the most time:
systemd-analyze blame | head -20

The Complete Boot Sequence — Visual Overview

Linux x86 Boot Sequence — All Stages

FIRMWARE
Power ON → POST → hardware enumeration → select boot device
BIOS: reads MBR  |  UEFI: reads EFI System Partition

GRUB2
Stage 1 → Stage 2 → GRUB2 (BIOS)  |  grubx64.efi (UEFI)
Reads /boot/grub/grub.cfg, shows menu, loads into RAM:
vmlinuz-6.x.y (compressed kernel)    › initrd.img-6.x.y (initramfs)
Jumps to kernel entry point

KERNEL
Self-decompression → hardware re-init (CPU, MMU, interrupts, timers, PCI)
Makes zero assumptions about what GRUB set up. Portable design.
Detects initramfs (CONFIG_BLK_DEV_INITRD=y) → unpacks cpio → mounts mini root in RAM

INITRAMFS
/init script runs:
› Loads ext4.ko / btrfs.ko from RAM  › Unlocks LUKS (if encrypted)  › Activates LVM
› Finds real root by UUID  › Mounts at /root
switch_root → real FS becomes /, RAM freed, exec systemd

SYSTEMD
systemd PID 1 starts → reads unit files → parallel service startup
› udev  › networking  › dbus  › SSH  › display manager
Reaches multi-user.target / graphical.targetLogin prompt appears ✓

Why systemd Replaced SysV init

Older books (and older kernels) describe /sbin/init and SysV runlevels as PID 1. On modern Linux — including most embedded systems — that is now systemd. Understanding why helps you reason about boot behaviour and debug startup issues.

❌ Problems with SysV init

  • Services start one at a time, sequentially — slow boot
  • Each service is a shell script — hard to debug and maintain
  • No dependency tracking — manual ordering required
  • No socket activation — services must pre-start before clients connect
  • Logging scattered across /var/log/ files with no central index

✓ What systemd Does Better

  • Parallel start — independent services launch simultaneously
  • Socket activation — start a service only when first connection arrives
  • Declarative units — simple config files instead of shell scripts
  • Dependency graphRequires=, After=, Wants=
  • journald — central structured log, queryable with journalctl
# Analyse your boot timing:
systemd-analyze

# Which services take the most time:
systemd-analyze blame | head -15

# See the full dependency chain as an HTML graph:
systemd-analyze plot > /tmp/bootchart.html && xdg-open /tmp/bootchart.html

UEFI and Secure Boot — What You Must Know

Important for custom kernel builders: Most x86 hardware since 2012 boots in UEFI mode with Secure Boot enabled. A self-compiled kernel will NOT boot on such a system unless Secure Boot is disabled or the kernel is signed.

Secure Boot works by having the UEFI firmware verify a cryptographic signature on everything it loads — the bootloader, and then the kernel itself. Ubuntu and Fedora ship pre-signed kernels and a shim that carries a Microsoft-trusted certificate, so they boot with Secure Boot on by default.

When you build your own kernel for this course, you have two options:

Options for Custom Kernel + Secure Boot

Option How Downside
Disable Secure Boot Go into UEFI settings and turn it off Reduces firmware-level security
Enroll your own key Generate a key pair, enroll with mokutil, sign kernel with sbsign More steps, must sign every kernel rebuild
# Check if Secure Boot is active:
mokutil --sb-state

# Check if you booted via UEFI or legacy BIOS:
[ -d /sys/firmware/efi ] && echo "UEFI boot" || echo "Legacy BIOS boot"

# Check EFI variables are accessible (should exist on UEFI systems):
ls /sys/firmware/efi/efivars/ | head -5

How Embedded Linux Boot Differs (ARM)

Everything above covers x86 desktop and server hardware. On ARM-based embedded boards — Raspberry Pi, BeagleBone, industrial controllers, automotive ECUs — the boot flow follows the same logical phases but uses completely different software.

Embedded ARM Boot Flow vs x86

x86 Desktop

BIOS / UEFI
ROM firmware
GRUB2
reads grub.cfg
Linux Kernel
ACPI for hardware
initramfs
separate file
systemd

ARM Embedded

SoC ROM Bootloader
baked into chip
SPL / MLO
sets up DDR RAM
U-Boot
loads kernel + DTB
Linux Kernel
Device Tree for hardware
BusyBox init / systemd
Yocto / Buildroot
💡 Key difference: On ARM, there is no BIOS or UEFI. Hardware is described by a Device Tree Blob (DTB) — a compiled binary that tells the kernel which peripherals are on the board, their memory addresses, and interrupts. ACPI (used on x86) serves the same purpose on the PC side. You will work with Device Trees when you write your first device driver for an embedded board.

🎯 Frequently Asked Questions — Linux Boot Process

Commonly tested in Linux kernel, embedded systems, and system software interviews.

Q1. Walk me through the Linux boot sequence from power-on to the login prompt.

Power on → firmware POST → MBR (BIOS) or EFI partition (UEFI) → GRUB2 loads vmlinuz and initrd.img into RAM → kernel decompresses, re-initialises all hardware → kernel detects and unpacks initramfs → initramfs /init script loads FS modules, optionally unlocks LUKS, mounts real root → switch_root frees RAM, hands off to real / → systemd starts as PID 1 → parallel service startup → login prompt.

Q2. What is GRUB2 and why does Linux need a bootloader at all?

GRUB2 is the software that loads the Linux kernel into RAM and jumps to its entry point. We need a bootloader because the kernel binary is large, compressed, and must be placed precisely in memory — the firmware cannot do this directly, especially in legacy BIOS mode where Stage 1 has only 446 bytes. GRUB can read filesystems, present a boot menu, pass kernel parameters, and load both the kernel and initramfs images.

Q3. What is PID 1 and what makes it special?

PID 1 is the very first process the kernel starts in userspace. It is special for three reasons: (1) it is the ancestor of every other process on the system; (2) if PID 1 exits or crashes, the kernel panics; (3) it is responsible for adopting orphaned processes — processes whose parent has died. On modern Linux, PID 1 is systemd, which replaced the older sequential SysV /sbin/init.

Q4. What does “the kernel makes no assumptions about the bootloader” mean in practice?

The Linux kernel re-initialises all hardware from scratch when it starts running, regardless of what GRUB, UEFI, or any other bootloader already set up. It does not rely on the CPU being in a particular state, the memory layout being pre-configured, or any device being already initialised. This design makes the kernel portable across bootloaders and architectures. You can boot it with GRUB, U-Boot, kexec, or directly from UEFI — the kernel always starts fresh.

Q5. What is the difference between BIOS and UEFI booting?

In BIOS boot, firmware reads the first 512-byte MBR sector to find the Stage 1 bootloader. Disks use MBR partition tables (max 2TB, 4 partitions). In UEFI boot, firmware reads the EFI System Partition (FAT32) and loads the bootloader as a proper signed application. UEFI uses GPT partition tables, supports Secure Boot, has a 64-bit pre-boot environment, and can load the bootloader directly without the Stage 1/Stage 2 workaround. Linux kernel initialisation after the bootloader hands off is identical in both cases.

Q6. Why did systemd replace SysV init? Name three concrete improvements.

(1) Parallel startup — systemd can start independent services simultaneously, drastically reducing boot time. SysV started one service at a time. (2) Socket activation — systemd pre-creates sockets and starts the actual service daemon only when the first client connects, eliminating startup order race conditions. (3) Dependency management — unit files declare Requires=, After=, Wants= relationships, so systemd builds and respects a dependency graph automatically. SysV required manual numbering of scripts.

Q7. How does the embedded ARM boot process differ from x86?

Key differences: (1) No BIOS/UEFI — ARM SoCs have a vendor-specific ROM bootloader baked into the chip that loads the SPL (Secondary Program Loader); (2) U-Boot replaces GRUB as the main bootloader; (3) Hardware is described by a Device Tree Blob (DTB) instead of ACPI tables; (4) The initramfs is often compiled into the kernel binary using CONFIG_INITRAMFS_SOURCE rather than being a separate file; (5) The init system may be a lightweight BusyBox init rather than a full systemd, depending on the project (Yocto, Buildroot, etc.).

Q8. What is Secure Boot and how does it affect custom kernel development?

Secure Boot is a UEFI feature that requires every binary the firmware loads — the bootloader, shim, and kernel — to carry a valid cryptographic signature from a trusted Certificate Authority. This prevents bootkits and rootkits from loading before the OS. For custom kernel development it means your self-built kernel will not boot on a Secure Boot system unless you either disable Secure Boot in UEFI settings, or sign your kernel with a key you enroll using mokutil and sbsign.

Free Linux Kernel Programming Course

EmbeddedPathashala is a completely free learning platform covering Linux kernel programming, device drivers, BLE, and embedded systems from the ground up.

Visit EmbeddedPathashala

 

Leave a Reply

Your email address will not be published. Required fields are marked *