Writing an OS Kernel from Scratch | Minimal Bootloader — From the Disk’s First Sector to the Kernel Entry Point

Key Insight: After the computer boots, the first instruction executed by the CPU at 0xFFFF0 is neither Windows nor Linux — it’s a piece of code only 446 bytes long. It has no operating system functionality at all. Its only task is: find the next executable program, load it into memory, and jump to it.


1. Problem: After the Computer Boots, Who Runs First?

You press the power button, the CPU resets, and all registers are cleared. But the problem is: Memory has nothing in it. What should the CPU execute?

At this point, the hardware provides a contract: After the CPU powers on, CS:IP is forcibly set to 0xF000:0xFFF0 (x86 architecture), and this address maps to a ROM on the motherboard — the BIOS ROM (now called UEFI Flash). The code in the ROM does two things:

  1. POST (Power-On Self-Test): Detect basic hardware such as memory, GPU, keyboard
  2. Traverse boot devices in order (hard drive, USB drive, optical disc…), find the first one that is bootable

After finding one, the BIOS reads the first sector (512 bytes) of that device into memory at 0x7C00, then jumps there to execute.

These 512 bytes are the starting point of the Bootloader.


2. MBR: What’s Hidden in the Disk’s First Sector

The first sector of a hard drive is called the MBR (Master Boot Record). It’s not the operating system’s code; it’s the first link in the boot chain.

The MBR has a fixed structure:

Bash
┌─────────────────────────────────────────────────────┐
│  0x0000 ~ 0x01BD  (446 bytes)   ← Main boot code      │
│  0x01BE ~ 0x01CD  (16 bytes)    ← Partition entry 1    │
│  0x01CE ~ 0x01DD  (16 bytes)    ← Partition entry 2    │
│  0x01DE ~ 0x01ED  (16 bytes)    ← Partition entry 3    │
│  0x01EE ~ 0x01FD  (16 bytes)    ← Partition entry 4    │
│  0x01FE ~ 0x01FF  (2 bytes)     ← 0x55AA (end marker)  │
└─────────────────────────────────────────────────────┘
           Total: 512 bytes

Structure of a Partition Entry (16 bytes each):

Offset Size Meaning
0x00 1 Active flag (0x80 = bootable, 0x00 = not bootable)
0x01 3 CHS start address (old-style cylinder/head/sector)
0x04 1 Partition type (0x07=NTFS, 0x0C=FAT32, 0x83=Linux ext2…)
0x05 3 CHS end address
0x08 4 Start sector (LBA)
0x0C 4 Partition size (in sectors)

The end marker 0xAA55 is key: After reading 512 bytes, the BIOS checks whether the last two bytes are 0x55AA. If not, it considers this disk non-bootable and moves on to the next device.


3. What Does the Main Boot Code Actually Do?

The MBR’s main boot code is only 446 bytes, with limited capabilities. Its core logic is:

  1. Find the active partition in the partition table (flag = 0x80)
  2. Read the first sector of that partition into memory (e.g., 0x7C00)
  3. Verify the 0xAA55 marker
  4. Jump there to execute

Typical MBR boot code (16-bit real mode):

Asm

; Simplified MBR boot code (16-bit real mode)
org 0x7C00        ; BIOS loads the first sector here

start:
    mov ax, 0x07C0    ; Set data segment
    mov ds, ax
    mov es, ax

    ; Read the first sector of the active partition to 0x8000
    mov bx, 0x8000    ; Buffer address
    mov dl, [boot_device]  ; BIOS-provided boot drive number
    mov dh, 0         ; Head number
    m... [truncated]
Last modified: 2024年5月13日

Author

Comments

Write a Reply or Comment

Your email address will not be published.