GRUB2 Architecture & Boot Stages
GRUB & Multibooting
Chapter 2 · GRUB2 Architecture & Boot Stages
grub1-1 established what a bootloader's job is in general. This chapter goes inside GRUB specifically — why it bootstraps itself in stages, where its pieces actually live on disk, and the one rule the rest of this course depends on: grub.cfg is generated, never hand-edited.
GRUB's Own Multi-Stage Loading Process
A full-featured bootloader — one that understands filesystems, reads a config file, and draws a menu — is far too large to fit in the tiny space firmware actually hands control to (446 usable bytes in a classic MBR, per grub1-1). GRUB solves this by bootstrapping itself in stages, each one slightly more capable than the last:
- boot.img — tiny, fits directly in the MBR itself (or the equivalent UEFI boot stub). Knows almost nothing — just enough to find and load the next stage.
- core.img — larger, contains basic filesystem drivers. Lives in the small gap immediately after the MBR on a BIOS/MBR disk, or on a dedicated BIOS Boot Partition on a GPT disk. Knows enough to read a real filesystem.
- The full GRUB environment — loaded by core.img from
/boot/grub, now capable of readinggrub.cfg, loading additional modules, and presenting the menu you actually see.
Each stage exists purely because the one before it was too small to do the whole job — a tiny bootstrap loading a slightly bigger bootstrap loading the real thing.
Where GRUB Actually Lives on Disk
/boot/grub/ holds grub.cfg, GRUB's own kernel-style modules (.mod files), locale files, and themes. On a BIOS/MBR system, core.img sits in the small unused space right after the MBR (or a dedicated BIOS Boot Partition on GPT), since no real filesystem is available yet at that point in the boot process. On a UEFI/GPT system, the actual GRUB EFI binary (commonly grubx64.efi) lives directly inside the EFI System Partition, alongside its own copy of configuration and modules.
grub.cfg — Generated, Never Hand-Edited
That's the literal top of a real grub.cfg. It's auto-generated by a script (grub-mkconfig, wrapped as update-grub on Debian — grub1-3 covers this fully), built from a set of scripts in /etc/grub.d/ combined with settings in /etc/default/grub. A manual edit to grub.cfg itself is silently overwritten the next time that generator runs — often triggered automatically by a routine kernel package upgrade. This is exactly the "manual fix reverted by the next automated apply" pattern this site's own infrastructure-as-code material already warned about (tf1-9, ansible1-1's own "fix the config, not just the console"), here applied to a bootloader instead of cloud infrastructure. grub1-3 is where the actually-supported editing workflow lives.
/etc/grub.d/ — The Real Building Blocks
A set of numbered scripts, each contributing one piece of the final grub.cfg, run in numeric order: 00_header, 10_linux, 30_os-prober, 40_custom, and others. 10_linux scans /boot for every installed kernel and generates a menu entry for each one. 30_os-prober — previewed here, covered fully in grub1-6/grub1-7 — scans other partitions on the disk for other operating systems and adds entries for them. The numbering isn't cosmetic; it's the literal order these scripts run in, which matters later for menu ordering.
Kernel Modules — Why GRUB Needs Its Own Drivers
GRUB has to understand filesystems, partition schemes, even things like LVM, entirely on its own — independent of, and before, the operating system it's about to boot even exists as a running thing. This is why /boot/grub is full of .mod files like ext2.mod and part_gpt.mod: GRUB is, in effect, its own small, purpose-built environment, with its own drivers, existing solely to find and load a real kernel.
| Stage | Size | Capability |
|---|---|---|
| boot.img | Fits in the MBR itself | Almost none — just enough to find core.img |
| core.img | Small, but real | Basic filesystem drivers — enough to read /boot/grub |
| Full GRUB environment | As large as needed | Reads grub.cfg, loads modules, draws the menu |
ls /boot/grub/*.mod | wc -l counts the filesystem/feature modules GRUB actually ships with on a typical install — often dozens. A concrete way to see, directly, just how much this small pre-OS environment genuinely has to know on its own.
grub.cfg often appears to work immediately — the change is right there, and nothing complains. The problem surfaces later, sometimes days or weeks afterward, when a routine kernel update silently regenerates the file and the edit simply vanishes, with no obvious connection between the kernel update and the "missing" change. grub1-3's own editing workflow avoids this entirely.
Hands-On Exercises
Explain, in your own words, why GRUB can't just be one single piece of code loaded directly from the MBR, and name the two intermediate stages that solve this.
📄 View solutionA new kernel is installed via the package manager. Explain what happens to grub.cfg as a result, and which specific numbered script in /etc/grub.d/ is responsible for that change.
📄 View solutionSomeone hand-edits grub.cfg to change the default boot entry, and it works immediately. Three weeks later, after a routine kernel update, their change is gone. Explain exactly what happened.
📄 View solutionChapter 2 Quick Reference
- GRUB bootstraps itself: boot.img (fits in the MBR) → core.img (basic filesystem drivers) → the full GRUB environment (reads grub.cfg, draws the menu)
/boot/grub/holds grub.cfg, modules, locales, themes; core.img/the EFI binary live in the MBR gap, a BIOS Boot Partition, or the EFI System Partition depending on BIOS vs. UEFI- grub.cfg is generated, never hand-edited — a manual edit is silently overwritten the next time the generator runs, often triggered by a kernel update
- /etc/grub.d/'s numbered scripts (10_linux, 30_os-prober, ...) each contribute one piece of grub.cfg, run in that numeric order
- GRUB ships its own filesystem/feature .mod files — it's effectively a small, self-contained pre-OS environment
- grub1-3 covers the actual, supported way to change GRUB's behavior