Skip to content

wiki / linux

the boot path, from power button to login

What runs between power-on and a login prompt on a UEFI Linux machine, and the command that shows each stage.

updated · 5 min read

Booting is a relay. Each stage loads the next one and hands over control, and each leaves a trace you can read after the fact. These notes follow a modern x86 machine with UEFI firmware and systemd, which covers most laptops and servers today.

firmware

UEFI ignores boot code in the MBR. It reads boot entries from NVRAM, and each entry names an EFI application plus the disk and partition it lives on. If an entry points at a bare disk, the firmware looks for an EFI system partition on it and tries the fallback path \EFI\BOOT\BOOTx64.EFI. That fallback is how a USB installer boots without any entry of its own. With Secure Boot on, the firmware checks the binary’s signature before running it.

efibootmgr -v

BootCurrent is the entry that started this boot, BootOrder is the order the firmware tries, and each Boot#### line shows the device path it resolves to. When a machine boots the wrong thing, this output usually explains why.

the EFI system partition

The ESP is a FAT32 partition the firmware can read without help. Vendors keep their files under /EFI/<vendor>/. On a running system it is usually mounted at /efi or /boot. /boot/efi still shows up on older installs, but the Arch wiki calls it historical and discouraged.

boot loader

The boot loader loads the kernel with its parameters and any initramfs images. A boot manager such as systemd-boot shows a menu and launches other EFI executables. The kernel can also run straight from the firmware through its EFI boot stub, with no loader at all.

systemd-boot reads entries that follow the Boot Loader Specification, kept under /loader/entries/ on the ESP or on an XBOOTLDR partition. GRUB reads its generated grub.cfg. One constraint applies to every loader: it has to read the file system that holds the kernel and initramfs. Few loaders understand LUKS, LVM or RAID, which is why /boot is so often a plain FAT32 partition.

bootctl status     # firmware, the loader that booted, loaders on the ESP, default entry
bootctl list       # every entry the loader can see
cat /proc/cmdline  # the parameters the kernel actually received

kernel and initramfs

The kernel starts with an empty root file system, a special instance of tmpfs or ramfs called rootfs. It unpacks its built-in initramfs into it, then each external image in the order the loader passed them, later files overwriting earlier ones. If the first image is uncompressed, the kernel looks inside it for CPU microcode under /kernel/x86/microcode/ and ACPI table updates under /kernel/firmware/acpi/ before unpacking the rest.

Then it runs /init as PID 1. This is early userspace, and its whole job is to reach the real root file system: load storage modules, assemble dm-crypt, LVM or RAID, ask for a passphrase, resolve persistent device names through udev. Some work can only happen here, such as fsck on the root partition and resuming from hibernation.

At the end, the real root is mounted at /sysroot and the system switches to it, with systemctl switch-root in a systemd-based initramfs or switch_root in a busybox-based one. The init program on the real root takes over.

lsinitcpio -a /boot/initramfs-linux.img    # Arch/mkinitcpio: human-readable summary
lsinitcpio -c /boot/initramfs-linux.img    # the config this image was built from
lsinitramfs /boot/initrd.img-$(uname -r)   # Debian/Ubuntu initramfs-tools
journalctl -k -b                           # kernel messages for this boot

PID 1 and targets

systemd starts units in parallel, ordered by their dependencies, until it reaches the default target. systemctl get-default prints it, normally graphical.target or multi-user.target.

systemd-analyze                  # time in kernel, initrd and userspace
systemd-analyze blame            # units sorted by how long they took to start
systemd-analyze critical-chain   # the chain of units that gated the default target

Read these with their caveats. The times run until services were spawned, which is earlier than when they finished initializing. blame leaves out Type=simple services, because systemd counts them as started immediately. In critical-chain, the number after @ is when a unit became active and the number after + is how long it took to start. Socket activation and parallel startup can make the chain misleading.

When the loader implements the Boot Loader Interface, it writes LoaderTimeInitUSec and LoaderTimeExecUSec into EFI variables, and systemd-analyze adds firmware and loader time to its first line. systemd-boot does this.

getty and login

Arch enables getty@tty1.service by default. logind spawns gettys on other virtual terminals when you switch to them, up to NAutoVTs in /etc/systemd/logind.conf. The stock getty@.service uses Type=idle, which holds the prompt until boot jobs finish so log lines don’t land on top of it.

agetty prints the prompt and passes the username to login(1), which authenticates the user, sets up the session and starts the shell listed in /etc/passwd. A display manager, if one is enabled, takes the place of getty on its terminal.

when a boot goes wrong

The previous boot’s log survives if the journal is persistent:

journalctl -b -1          # everything from the last boot
journalctl -b -1 -p err   # only errors and worse

Work backwards through the stages. If efibootmgr shows the wrong order, it’s the firmware. If the kernel never logged anything, look at the loader entry and /proc/cmdline. If the log ends inside the initramfs, the root device or its unlock step is usually the culprit.

sources

← all notes