From power-on to a running userspace — firmware, bootloader, kernel initialization, and init. x86-64 and UEFI-first, with BIOS/MBR notes where relevant.
When the CPU is released from reset, it begins executing at a fixed physical address. On x86-64, this is 0xFFFFFFF0 — a 16-byte "reset vector" that immediately jumps into the firmware ROM.
Modern systems use UEFI. Execution proceeds through three phases before the OS loader is invoked:
| SEC (Security) | Runs entirely in cache-as-RAM (CAR). Validates the PEI image, establishes a minimal trusted environment. |
| PEI (Pre-EFI Init) | Initializes DRAM, chipset basics, and any silicon-specific code. Produces a Hand-Off Block (HOB) list passed forward. |
| DXE (Driver Execution) | Full UEFI environment. Loads drivers from the firmware volume, brings up PCIe, storage, and USB. Produces the EFI System Table visible to later stages. |
| BDS (Boot Device Select) | Evaluates the NVRAM boot order, locates an EFI application (\EFI\BOOT\BOOTX64.EFI or a distro-specific path), and transfers control to it. |
db allowlist. Shim (shimx64.efi) is the standard first-stage for distros — it carries its own signature accepted by Microsoft's keys and then verifies the bootloader with its own MOK database.
BIOS loads the first 512 bytes of the boot disk (MBR) at physical address 0x7C00 and jumps to it. The MBR code locates a bootable partition via the partition table and loads a second-stage bootloader. All of this happens in 16-bit real mode with no hardware abstraction.
The dominant bootloader on x86-64 Linux is GRUB2. The EFI binary grubx64.efi is loaded directly by BDS as an EFI application; it runs in 64-bit EFI mode with full access to UEFI Boot Services.
/boot/grub/grub.cfg (or /boot/grub2/grub.cfg). Parses menuentry blocks to present a boot menu./boot, before any OS driver is running.linux command line from the config and any edits made at the menu. This becomes the kernel parameter string at /proc/cmdline.ExitBootServices() to terminate UEFI Boot Services, constructs the Linux Boot Protocol info block, and jumps to the kernel entry point.GRUB places a struct boot_params at a known address and passes it to the kernel. It contains memory map (E820 table), command-line pointer, initrd address and size, video mode info, and EFI-specific data.
0x000 screen_info
0x040 apm_bios_info
0x090 hdr ← setup_header (magic 0xAA55, protocol version, etc.)
0x1E8 e820_entries
0x2D0 e820_table[128]
... efi_info ← EFI memory map, system table pointer
CONFIG_EFI_STUB. systemd-boot (or any EFI loader) loads vmlinuz directly as an EFI binary, passing parameters via EFI LoadOptions. No separate bootloader code path needed.
The file on disk is vmlinuz — a self-decompressing archive. The name derives from virtual memory Linux; the z suffix indicates compression (historically gzip, now commonly zstd or lz4 depending on distro config).
| bzImage header | First ~512 bytes. Contains the setup_header with magic number 0x53726448 ("HdrS"), protocol version, load address hints, and payload offset/size. |
| Real-mode setup code | Historically ran in 16-bit mode; with the boot protocol ≥ 2.02 and a modern bootloader, this is bypassed entirely — the bootloader uses the 32/64-bit entry points directly. |
| Compressed payload | The actual kernel binary, compressed. Preceded by a small decompressor stub. |
Control passes to the decompressor stub at arch/x86/boot/compressed/head_64.S. It sets up a minimal page table, switches to long mode if not already in it, decompresses the kernel image into memory (usually at 0x1000000), and jumps to the uncompressed kernel entry — startup_64 in arch/x86/kernel/head_64.S.
CONFIG_RANDOMIZE_BASE, the decompressor calls into the firmware to get a random physical offset before placing the kernel. The final load address is stored and used to fix up any absolute references before jumping into the kernel proper.
Once at startup_64, the kernel is running its own code with no OS beneath it. The hardware state is still largely as the firmware/bootloader left it.
startup_64 |
Sets up early page tables (identity + kernel mappings). Clears BSS. Calls x86_64_start_kernel(). |
x86_64_start_kernel |
Fixes up CR3 to the real kernel PGD. Calls into start_kernel() in init/main.c. |
start_kernel() |
Main C entry point. Runs hundreds of init calls in a specific order. Never returns. |
memblock) to the buddy allocator (page allocator). Initializes kmalloc/slab.kernel_init (pid 1) and kthreadd (pid 2). CPU 0 becomes the idle thread.After the BSP (Bootstrap Processor) completes start_kernel(), it sends INIT-SIPI-SIPI sequences via the LAPIC to wake Application Processors. Each AP runs start_secondary(), initializes its own per-CPU state, and joins the scheduler runqueue. CPU hotplug controls this process on modern kernels.
# logical CPUs and mapping
$ lscpu --extended
# scheduler domains (NUMA/cache topology seen by the kernel)
$ cat /sys/kernel/debug/sched/domains/cpu0/domain0/name
The kernel cannot mount the real root filesystem without drivers that may themselves be kernel modules stored on that filesystem (disk controller, LUKS, LVM, network). initramfs breaks this chicken-and-egg problem.
A gzip/lz4/zstd-compressed CPIO archive passed to the kernel by the bootloader via the initrd_start / initrd_size fields in boot_params. The kernel unpacks it into a tmpfs instance and mounts it as the initial root (/).
initrd was a block-device image mounted as a ramdisk (required a filesystem driver and a fixed size). initramfs is a CPIO archive unpacked directly into tmpfs — cheaper, no fixed size, no extra filesystem layer. Modern kernels use initramfs; the term "initrd" is colloquially reused for both.
The archive contains a minimal root filesystem sufficient to locate and mount the real root:
systemd or dracut/mkinitcpio generated shell script.Once the real root device is mounted (typically at /sysroot), the initramfs init process calls switch_root /sysroot /sbin/init — a syscall sequence that moves the mount, chroots, and execs the final init binary. The initramfs tmpfs is freed.
# list contents
$ lsinitrd /boot/initramfs-$(uname -r).img # dracut/Fedora
$ lsinitcpio /boot/initramfs-linux.img # mkinitcpio/Arch
# manual unpack (adjust for compression type)
$ mkdir /tmp/ir && cd /tmp/ir
$ file /boot/initramfs-$(uname -r).img # check compression
$ zstdcat /boot/initramfs-$(uname -r).img | cpio -idm
PID 1 in the final rootfs is the init system. It is the ancestor of all userspace processes and must never exit. On the vast majority of current Linux distributions, this is systemd.
default.target |
Typically symlinked to graphical.target or multi-user.target. Defines the set of units that must be active. |
| Unit graph | systemd builds a dependency graph from all unit files in /lib/systemd/system/ and /etc/systemd/system/. Edges are Requires=, Wants=, Before=, After=. |
| Parallel activation | Units with satisfied dependencies are activated concurrently. D-Bus socket activation and device units (.device via udev) allow lazy-start of services. |
| Early targets | sysinit.target → basic.target → network.target → multi-user.target / graphical.target. Each is a synchronization point. |
sysinit.target. Collects logs from the kernel ring buffer and from all subsequent services./dev entries, applies udev rules, emits device units./etc/modules-load.d/ unconditionally at boot./usr/lib/tmpfiles.d/ (e.g. /run, /tmp)./etc/fstab to be mounted. Depends on systemd-fsck@.service for filesystem checks.getty@.service template. Presents a login prompt on the virtual console.# Overall boot time breakdown
$ systemd-analyze
# Per-unit timing (slowest first)
$ systemd-analyze blame
# Critical path through the dependency graph
$ systemd-analyze critical-chain
# Full unit dependency graph (renders with dot)
$ systemd-analyze dot | dot -Tsvg > boot.svg
# Kernel ring buffer from this boot
$ journalctl -k -b 0
# All messages from initramfs phase
$ journalctl -b 0 -o short-monotonic | head -200
/boot/vmlinuz-* | Compressed kernel image loaded by the bootloader. |
/boot/initramfs-*.img | initramfs CPIO archive (dracut naming; initramfs-linux.img on Arch). |
/boot/grub/grub.cfg | GRUB2 generated configuration. Do not edit directly; regenerate with grub-mkconfig. |
/proc/cmdline | Kernel command line as passed by the bootloader. |
/proc/1/exe | Symlink to the init binary (confirms what PID 1 is). |
/sys/firmware/efi | Present only on UEFI boots. Contains EFI variables, runtime services info. |
/sys/firmware/acpi | ACPI tables exposed by the kernel. acpidump extracts them. |
/sys/kernel/boot_params | The raw boot_params struct as seen by the kernel (available with CONFIG_BOOT_CONFIG). |
/etc/systemd/system/ | Local unit files and overrides (higher priority than /lib/systemd/system/). |
/etc/fstab | Filesystem mount table used by systemd-fstab-generator to create mount units. |
root= | Root device. Accepts /dev/sdXN, UUID=, PARTUUID=, LABEL=. |
rootflags= | Mount options for the root filesystem. |
init= | Override PID 1. E.g. init=/bin/bash for emergency shell. |
rd.break | Drop to a shell inside the initramfs before switch_root (dracut). |
systemd.unit= | Boot to a specific target, e.g. rescue.target or emergency.target. |
nomodeset | Disables KMS; kernel stays in VESA/EFI framebuffer mode. Useful for GPU driver debugging. |
iommu=off | Disables IOMMU. Relevant when debugging DMA issues. |
loglevel= | Early printk verbosity (0–7). Default is typically 4 (KERN_WARNING). |
earlyprintk= | Direct early kernel output to a serial port or EFI console before the framebuffer is up. |