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
Hardware wakes up → initializes chipset, memory, storage
Switches CPU modes → loads kernel → jumps to entry point
Sets up stack, paging, interrupts → takes full control
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
hltThis 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
hltFrom 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.