Writing an OS Kernel from Scratch | The Last Jump from BIOS to Kernel — What the CPU Thinks After POST
You press the power button, the screen lights up, the fan spins, BIOS POST completes, GRUB countdown ends — then what?
How does the kernel actually start running? What state is the CPU in at that moment? How does GRUB hand control to a binary .elf file? If this jump doesn’t work, nothing else matters.
This is exactly the topic we’re dissecting today: the last jump from BIOS/UEFI handing control to the OS kernel, and behind it: CPU mode switching, GRUB’s Multiboot protocol, and how a kernel is correctly loaded.
1. Know Where You Are: Real Mode vs Protected Mode
Writing kernel boot code, the most common pitfall is: which address scheme are you using?
Real Mode — CPU’s default state after power-on.
- Address calculation: segment base × 16 + offset
- Max addressing: 20 bits → 1MB (00000h ~ FFFFFh)
- Segment registers: hold segment base (directly the high 16 bits of physical address)
- Register width: 16 bits
- No privilege levels: any code can do anything
Protected Mode — where modern OS actually runs.
- Address calculation: segment selector → segment descriptor → linear address
- Max addressing: 32 bits → 4GB (more with PAE)
- Segment registers: hold segment selectors (index), not addresses
- Register width: 32 bits
- Privilege levels: Ring 0 (kernel) ~ Ring 3 (user mode)
- Paging: optional, maps linear to physical addresses
Key difference: In real mode, addresses are “what you see is what you get” — segment registers directly contain physical addresses. In protected mode, segment registers contain indices into the GDT (Global Descriptor Table) to find the actual segment base.
This matters because when GRUB loads the kernel, the CPU is already in protected mode. Every address you write in kernel code is backed by a table lookup mechanism.
Comments