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:

asm
    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:0x7E00

Hence 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):

  1. A20 line — enable address bit 20, disabling a wraparound quirk kept for 8086 compatibility.
  2. 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.”
  3. 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.
  4. 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.