Every x86 CPU ever shipped wakes up the same way: 16-bit real mode, 1 MB of addressable memory, executing at a hardwired address near the top of it — a compatibility contract with the 8086 that modern silicon still honors for about a microsecond before the bootstrapping begins. I wrote a real-mode bootloader to internalize this path, and the main discovery was how much of “booting” is just the same problem solved repeatedly at increasing scale: code that loads bigger code, then gets out of the way.
Stage 0: Firmware
The first instruction executes from the reset vector (0xFFFFFFF0, mapped to flash ROM). Firmware — BIOS in the classic scheme, UEFI on anything recent — initializes the hardware that software normally takes for granted; the big-ticket item is DRAM training, calibrating the memory controller against physical signal timing. (Until RAM works, early firmware famously runs with the CPU cache configured as RAM.) Then it enumerates PCIe, runs option ROMs, and goes hunting for something to boot.
Stage 1: 512 Bytes of Runway (BIOS/MBR)
Legacy BIOS reads sector 0 of the boot disk into memory at 0x7C00 and jumps —
if the sector ends with 0x55AA. That’s the whole contract: 446 bytes of
code (the rest is partition table), no filesystem, no memory map, CPU in
real mode with the wonderful segment:offset addressing scheme
(segment × 16 + offset; multiple names for every address). What can you do
in 446 bytes? Exactly one thing: use BIOS interrupt services —
INT 0x13 (read disk sectors), INT 0x10 (print) — to fetch a bigger
loader and jump to it:
mov ah, 0x02 ; BIOS: read sectors
mov al, 16 ; count
mov ch, 0 ; cylinder
mov cl, 2 ; start at sector 2 (sector 1 = us)
mov dh, 0 ; head
mov bx, 0x7E00 ; load right after ourselves
int 0x13
jc disk_error
jmp 0x0000:0x7E00Hence GRUB’s stage architecture: a boot-sector stub loads a “core image”
stashed in the gap after the MBR, which contains filesystem drivers, which
can finally read /boot like a civilized program and present menus.
Bootstrapping, recursively.
The Mode Ladder
The loaded code is still in a 1978-shaped CPU. Getting to a modern one is a ritual with exact steps — miss one and the machine silently triple-faults and reboots (the real-mode debugging experience is “it rebooted again,” which teaches careful reading of manuals like nothing else):
- A20 line — enable address bit 20, disabling a wraparound quirk kept for 8086 compatibility.
- GDT — load a Global Descriptor Table defining flat 4 GB code/data segments; segmentation must be configured even though everyone configures it to “off, effectively.”
- PE bit — set bit 0 of CR0; the next far jump flushes the pipeline and lands in 32-bit protected mode. BIOS services are now gone forever — you’re on your own for I/O.
- For 64-bit: build initial page tables (paging is mandatory in long mode), set CR4.PAE, EFER.LME, enable paging — welcome to long mode.
UEFI: Skipping the Archaeology
UEFI replaces most of this with something almost anticlimactic: firmware that
understands FAT filesystems and PE executables. The “bootloader” is just a
file (\EFI\BOOT\BOOTX64.EFI on the EFI System Partition), loaded with the
CPU already in long mode, with rich boot services — filesystem access,
memory map, graphics — available by function call, plus Secure Boot signature
checks over the whole chain. The 512-byte heroics survive only in legacy CSM
mode and in hobby bootloaders, which remain the single best way to learn
what the machine is really like under all the layers.
Either way, the endgame is identical: load kernel + initramfs into memory,
fill in a boot-protocol structure (memory map, command line, framebuffer
info), and jump. Decompression stub, early page tables, start_kernel(),
PID 1 — and the part of boot that userspace sees begins.
Takeaways
- x86 boots as an 8086 by contract; the reset vector, real mode, and the MBR 0x55AA sector are compatibility archaeology that still executes today.
- 446 bytes buys exactly one ability: BIOS INT 0x13 loads the next stage — boot is chain-loading all the way up.
- Real→protected→long mode is a precise ritual (A20, GDT, CR0.PE, page tables, EFER.LME); the failure mode is a silent triple-fault reboot.
- UEFI boots a PE file from a FAT partition in long mode with services and Secure Boot — same job, modern interface.
- Writing a toy bootloader is the fastest route to understanding segments, modes, and why firmware exists; the QEMU+NASM loop takes minutes to set up.