Skip to content

Bootloader

Frank edited this page Jan 18, 2026 · 1 revision

Bootloader

The cupid-os bootloader is a critical component that bridges the gap between the BIOS and the 32-bit kernel. This 16-bit assembly program handles the transition from real mode to protected mode while loading the kernel into memory.

Overview

The bootloader performs three main tasks:

  1. Initialization: Sets up the environment and segments
  2. Kernel Loading: Reads the kernel from disk into memory
  3. Mode Transition: Switches from 16-bit real mode to 32-bit protected mode

Boot Process

BIOS Handover

When the system powers on, the BIOS performs POST (Power-On Self Test) and initializes hardware. It then looks for a bootable device and loads the first 512 bytes (one sector) from the boot device into memory at address 0x7C00.

[org 0x7c00]  ; Tell NASM our code will be loaded at 0x7C00

Real Mode Initialization

The bootloader starts in 16-bit real mode with limited capabilities:

start:
    ; Set up segments
    xor ax, ax
    mov ds, ax    ; Data segment = 0
    mov es, ax    ; Extra segment = 0
    mov ss, ax    ; Stack segment = 0
    mov sp, 0x9000 ; Stack pointer = 0x9000
  • Segments: All segment registers are set to 0 for flat memory access
  • Stack: Set up a small stack at 0x9000 (above the bootloader)

Loading the Kernel

The kernel is stored on disk starting from sector 2 (sector 1 is the bootloader). The bootloader uses BIOS interrupt 0x13 to read disk sectors:

load_kernel:
    mov bx, KERNEL_OFFSET   ; Load to 0x1000
    mov dh, 50             ; Load 50 sectors
    mov dl, [BOOT_DRIVE]   ; From boot drive
    mov ah, 0x02          ; BIOS read function
    mov al, dh            ; Sectors to read
    mov ch, 0             ; Cylinder 0
    mov dh, 0             ; Head 0
    mov cl, 2             ; Start from sector 2
    int 0x13
  • Destination: Kernel loaded to 0x1000 (KERNEL_OFFSET)
  • Size: 50 sectors (25KB) - adjust as kernel grows
  • Location: Starting from sector 2 on the boot disk
  • Error Handling: Jumps to disk_error on failure

Protected Mode Transition

Disabling Interrupts

Before switching modes, interrupts must be disabled:

switch_to_pm:
    cli                   ; Clear Interrupt Flag (disable interrupts)

Interrupts are disabled because the interrupt table (IVT) is about to become invalid during the mode switch.

Global Descriptor Table (GDT)

The GDT defines memory segments for protected mode:

gdt_start:
    dq 0                  ; Null descriptor (required)

gdt_code:
    dw 0xffff            ; Limit (0-15)
    dw 0                 ; Base (0-15)
    db 0                 ; Base (16-23)
    db 10011010b         ; Access byte
    db 11001111b         ; Flags + Limit (16-19)
    db 0                 ; Base (24-31)

gdt_data:
    dw 0xffff            ; Limit (0-15)
    dw 0                 ; Base (0-15)
    db 0                 ; Base (16-23)
    db 10010010b         ; Access byte
    db 11001111b         ; Flags + Limit (16-19)
    db 0                 ; Base (24-31)

Code Segment Descriptor

  • Base: 0 (starts at beginning of memory)
  • Limit: 0xFFFFF (4GB, with 4KB granularity)
  • Access: Present, Ring 0, Code, Executable, Readable
  • Flags: 32-bit, Page granularity

Data Segment Descriptor

  • Base: 0 (starts at beginning of memory)
  • Limit: 0xFFFFF (4GB, with 4KB granularity)
  • Access: Present, Ring 0, Data, Writable
  • Flags: 32-bit, Page granularity

Enabling Protected Mode

The transition to protected mode happens in two steps:

lgdt [gdt_descriptor]   ; Load Global Descriptor Table

mov eax, cr0
or eax, 0x1            ; Set PE (Protection Enable) bit
mov cr0, eax           ; Enable protected mode

The CR0.PE bit enables protected mode. Once set, the CPU operates in 32-bit protected mode.

Far Jump

A far jump is required to flush the instruction pipeline and reload segment registers:

jmp CODE_SEG:init_pm   ; Far jump to 32-bit code
  • CODE_SEG: Selector for code segment (offset into GDT)
  • init_pm: 32-bit initialization function

32-Bit Initialization

Segment Setup

In protected mode, segment registers contain selectors (not physical addresses):

[bits 32]
init_pm:
    mov ax, DATA_SEG
    mov ds, ax    ; Data segment selector
    mov es, ax    ; Extra segment selector
    mov fs, ax    ; F segment selector
    mov gs, ax    ; G segment selector
    mov ss, ax    ; Stack segment selector
    mov esp, 0x90000  ; Set up 32-bit stack
    mov ebp, esp       ; Base pointer = stack top

Verification

The bootloader prints a character to video memory to verify 32-bit mode:

mov ebx, 0xb8000    ; VGA text buffer address
mov al, 'P'         ; Character to display
mov ah, 0x0f        ; White on black attribute
mov [ebx], ax       ; Write to video memory

This displays a white 'P' in the top-left corner of the screen.

Kernel Handover

Finally, the bootloader jumps to the kernel:

jmp CODE_SEG:KERNEL_OFFSET  ; Jump to kernel at 0x1000
  • CODE_SEG: Ensures we're in the correct code segment
  • KERNEL_OFFSET: 0x1000 where the kernel was loaded

Bootloader Structure

Memory Layout

0x00000 - 0x003FF: Real Mode IVT (Interrupt Vector Table)
0x00400 - 0x004FF: BIOS Data Area
0x00500 - 0x07BFF: Free memory
0x07C00 - 0x07DFF: Bootloader (512 bytes)
0x07E00 - 0x7FFFF: Free memory
0x80000 - 0x9FFFF: Extended BIOS Data Area
0xA0000 - 0xFFFFF: Video memory, ROM, etc.

Disk Layout

Sector 0: Master Boot Record (MBR) - unused
Sector 1: Bootloader (512 bytes)
Sector 2+: Kernel binary

Build Process

The bootloader is assembled with NASM:

boot/boot.bin: boot/boot.asm
    $(ASM) -f bin boot/boot.asm -o boot/boot.bin
  • Input: boot/boot.asm
  • Output: boot/boot.bin (flat binary)
  • Format: Raw binary (no ELF headers)

Debugging the Bootloader

Common Issues

"No bootable device found"

  • Ensure the disk image was created correctly
  • Check that the boot signature (0xAA55) is present

"Disk error!"

  • The BIOS int 0x13 call failed
  • Check disk geometry parameters
  • Verify kernel sectors exist on disk

Triple fault after mode switch

  • GDT not loaded correctly
  • Invalid segment selectors
  • Stack corruption during transition

Debugging Techniques

QEMU Debugging

# Run QEMU with GDB
qemu-system-i386 -fda cupidos.img -s -S

# In another terminal
gdb
(gdb) target remote localhost:1234
(gdb) break *0x7c00  # Break at bootloader entry
(gdb) continue

Visual Debugging

  • Add int 0x10 calls to print characters during boot
  • Use different colors to mark progress points
  • Check video memory contents with GDB

Memory Inspection

# Check if kernel was loaded
(gdb) x/32xb 0x1000

# Examine GDT
(gdb) x/8xb gdt_start

# Check segment registers
(gdb) info registers

Extending the Bootloader

Adding Features

Multistage Loading

  • Load additional stages if bootloader grows too large
  • Implement better disk drivers (LBA addressing)

Hardware Detection

  • Detect available memory with int 0x15
  • Probe for CPU features (CPUID)
  • Identify attached hardware

Advanced GDT

  • Add more segment descriptors
  • Implement task state segments (TSS)
  • Set up separate user/kernel segments

Limitations

Sector Count

The current bootloader loads exactly 50 sectors. As the kernel grows, this limit may need to be increased:

mov dh, 50  ; Increase this value

Error Handling

Currently errors result in infinite loops. Consider implementing retry logic or fallback mechanisms.

Memory Constraints

The bootloader must fit in 512 bytes. Complex features may require a multistage design.

Technical Details

BIOS Interrupts Used

Interrupt Function Purpose
0x10 Video Services Printing characters
0x13 Disk Services Reading kernel sectors

CPU State Changes

Register Real Mode Protected Mode
CS 0x0000 CODE_SEG (0x08)
DS 0x0000 DATA_SEG (0x10)
SS 0x0000 DATA_SEG (0x10)
ESP 0x9000 0x90000

Memory Addressing

  • Real Mode: Segment:Offset (physical = segment × 16 + offset)
  • Protected Mode: Selector:Offset with GDT translation

References

  • OSDev Wiki: Comprehensive bootloader tutorials
  • Intel Manuals: CPU architecture documentation
  • BIOS Specifications: Interrupt calling conventions

For more information about the boot process:

Clone this wiki locally