# From Power Button to Kernel: How a PC Boots

> At reset, an x86 CPU wakes up pretending it's 1978 — 16-bit mode, 1 MB address space. Everything after that is bootstrapping: firmware, boot sector or EFI application, protected mode, and finally a kernel. I wrote a bootloader to learn the path; here's the map.

URL: https://harshith.in/blog/how-a-pc-boots/
Author: Harshith Sunku (Staff Engineer) — https://harshith.in/about/
Tags: systems, hardware, fundamentals

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](https://harshith.in/projects/simple-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.
