Loading...

Kernel Build Understanding

How a real kernel actually comes to life — from silicon to Ring 0. No shortcuts. No abstractions. Just the raw truth of booting and controlling hardware.

The Real Boot-to-Kernel Flow

BIOS / UEFI

Hardware wakes up → initializes chipset, memory, storage

Bootloader (ASM)

Switches CPU modes → loads kernel → jumps to entry point

Kernel Entry (ASM + C)

Sets up stack, paging, interrupts → takes full control

→ From here: everything is kernel space — Ring 0 — no safety net

1. Assembly — The Only Language That Talks to the CPU

Kernel development starts in assembly because:

  • CPU boots in 16-bit real mode (BIOS legacy)
  • Bootloader must switch to 32-bit protected mode, then 64-bit long mode
  • Only ASM can directly manipulate CR0/CR3/CR4 (control registers) and GDT/IDT
  • Kernel entry point must be written in ASM to set up stack, segment registers, and jump to C
section .text
global _start
extern kernel_main

_start:
    ; Switch to long mode (simplified)
    mov eax, cr4
    or  eax, 1 << 5          ; enable PAE
    mov cr4, eax

    ; Load page tables (PML4, PDP, PD, PT)
    mov eax, pml4
    mov cr3, eax

    ; Enable long mode
    mov ecx, 0xC0000080
    rdmsr
    or  eax, 1 << 8          ; set LM bit
    wrmsr

    ; Enable paging
    mov eax, cr0
    or  eax, 1 << 31
    mov cr0, eax

    ; Far jump to 64-bit code
    lgdt [gdt64_pointer]
    jmp 0x08:long_mode_start

long_mode_start:
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

    ; Now in 64-bit long mode — call C kernel
    call kernel_main
    hlt

This is the real first code that runs after bootloader. No C yet — only raw CPU control.

2. Bootloader — From Reset Vector to Kernel Handover

Bootloader does the dirty, low-level work no C code can do:

  • Enable A20 line (access memory above 1MB)
  • Build GDT (Global Descriptor Table) for protected/long mode
  • Setup page tables (identity mapping + kernel mapping)
  • Load kernel binary into memory (from disk/network)
  • Pass boot parameters (Multiboot, Limine, etc.)
  • Jump to kernel entry point

Common Bootloaders

  • • GRUB — most common, supports many formats
  • • Limine — modern, clean, 64-bit friendly
  • • U-Boot — embedded systems
  • • Custom bootloader — full control (used in OS dev)

3. Kernel Entry Point — Where C Takes Control

After bootloader jumps, kernel starts here (usually in entry.S or start.S):

// entry.S (x86_64 example)
.section .text
.global kernel_entry
.extern kernel_main
.extern stack_top

kernel_entry:
    cli                         # disable interrupts
    mov rsp, stack_top          # set up stack

    # Zero BSS section
    xor rax, rax
    mov rdi, bss_start
    mov rcx, bss_end - bss_start
    rep stosb

    # Call C main
    call kernel_main

    # Should never reach here
    hlt

From this point: you are in Ring 0. Full hardware control. One wrong pointer = crash.

4. First Things Kernel Must Do

Must-Have Initialization Steps

  • 1. Setup interrupt descriptor table (IDT)
  • 2. Initialize memory management (page allocator)
  • 3. Setup timer (PIT/APIC/HPET)
  • 4. Probe hardware (PCI, ACPI, etc.)
  • 5. Start scheduler & multitasking
  • 6. Mount root filesystem
  • 7. Launch init process (user space)

Critical Warnings

  • Never trust user input in kernel
  • Every pointer must be validated
  • Interrupts can happen anytime
  • Concurrency bugs = race conditions
  • One bad driver = entire system down

Final Kernel Mental Model

Bootloader → CPU mode switch & kernel load

Kernel Entry → Take Ring 0 control

Subsystems → Memory, interrupts, drivers, scheduler

User Space → Only when everything else is safe

Building a kernel is not about writing more code — it's about gaining total control and understanding every failure mode.