-
Notifications
You must be signed in to change notification settings - Fork 69
Bootloader
The first code executed when MentOS starts.
The bootloader is responsible for:
- CPU Initialization - Set up GDT, enable protected mode
- Multiboot Handling - Parse boot information from GRUB
- Kernel Loading - Load the embedded kernel binary into memory
-
Kernel Transfer - Jump to kernel entry point (
kmain())
boot/
├── src/
│ ├── boot.S ← Assembly entry point
│ ├── boot.c ← Main bootloader logic
│ └── multiboot.c ← Multiboot spec handling
├── inc/ ← Headers
└── linker/
├── boot.lds ← Bootloader linker script
└── kernel.lds ← Kernel linker script- BIOS or GRUB loads
bootloader.bininto memory - Execution begins at
_start(defined inboot.S)
; boot/src/boot.S
.section .text
.global _start
_start:
; Disable interrupts
cli
; Set up stack
mov esp, stack_top
; Push multiboot info
push ebx ; Multiboot info structure
push eax ; Multiboot magic number
; Call C code
call boot_main
; Halt if boot_main returns
cli
.hang:
hlt
jmp .hangActions:
- Disables interrupts
- Sets up stack pointer
- Pushes multiboot magic and info pointer
- Calls
boot_main()
// boot/src/boot.c
void boot_main(uint32_t multiboot_magic, multiboot_info_t *multiboot_info)
{
// 1. Parse multiboot information
multiboot_read_info(multiboot_magic, multiboot_info);
// 2. Set up GDT (Global Descriptor Table)
setup_gdt();
// 3. Set up IDT (Interrupt Descriptor Table) - basic
setup_idt();
// 4. Enable paging (virtual memory)
setup_paging();
// 5. Load kernel binary (embedded in bootloader.bin)
load_kernel();
// 6. Jump to kernel entry point
jump_to_kernel();
}// Jump to kernel's kmain()
void (*kernel_entry)() = (void(*)())KERNEL_ENTRY_POINT;
kernel_entry();MentOS uses the Multiboot specification to be compatible with GRUB and other bootloaders.
// boot/src/boot.S
.section .multiboot
.align 4
multiboot_header:
.long MULTIBOOT_MAGIC
.long FLAGS
.long CHECKSUMConstants:
-
MULTIBOOT_MAGIC: 0x1BADB002 -
FLAGS: Requested features -
CHECKSUM: -(MAGIC + FLAGS)
Parsed in multiboot_read_info():
typedef struct multiboot_info {
uint32_t flags; // Available info
uint32_t mem_lower; // Lower memory (KB)
uint32_t mem_upper; // Upper memory (KB)
uint32_t boot_device; // Boot device
uint32_t cmdline; // Kernel command line
uint32_t mods_count; // Number of modules
uint32_t mods_addr; // Modules address
// ... more fields
} multiboot_info_t;Information Provided:
- Memory map (physical RAM available)
- Boot device
- Kernel command line
- Loaded modules (if any)
- Framebuffer information
The Global Descriptor Table (GDT) defines memory segments for protected mode.
// Set up GDT with kernel code/data segments
gdt_entry_t gdt[GDT_SIZE];
// Null descriptor
gdt[0] = {0, 0, 0, 0, 0, 0};
// Kernel code segment (ring 0)
gdt[1] = {0xFFFF, 0, 0, 0x9A, 0xCF, 0};
// Kernel data segment (ring 0)
gdt[2] = {0xFFFF, 0, 0, 0x92, 0xCF, 0};
// User code segment (ring 3)
gdt[3] = {0xFFFF, 0, 0, 0xFA, 0xCF, 0};
// User data segment (ring 3)
gdt[4] = {0xFFFF, 0, 0, 0xF2, 0xCF, 0};
// Load GDT
lgdt(&gdt_ptr);Segments:
- Ring 0 (kernel): Full memory access, all instructions
- Ring 3 (user): Limited access, restricted instructions
Enable virtual memory with identity mapping:
// Set up page directory and page tables
page_directory[0] = &page_table[0] | 0x3; // Present, R/W
// Identity map first 4MB (kernel)
for (int i = 0; i < 1024; i++) {
page_table[i] = (i * 0x1000) | 0x3; // Present, R/W
}
// Load page directory
asm volatile("mov %0, %%cr3" :: "r"(page_directory));
// Enable paging
uint32_t cr0;
asm volatile("mov %%cr0, %0" : "=r"(cr0));
cr0 |= 0x80000000; // PG bit
asm volatile("mov %0, %%cr0" :: "r"(cr0));# Compile bootloader sources
gcc -c boot/src/boot.S -o build/boot/boot.o
gcc -c boot/src/boot.c -o build/boot/boot.o
# Compile kernel (separately)
# ... kernel compilation ...
# Embed kernel.bin as object
objcopy -I binary -O elf32-i386 kernel.bin kernel.bin.o
# Link bootloader + embedded kernel
ld -T boot/linker/boot.lds boot/*.o kernel.bin.o -o bootloader.binbuild/mentos/bootloader.bin:
- Bootloader code
- Embedded
kernel.bin(as data section) - Combined size: ~1.1MB
ENTRY(_start)
SECTIONS
{
. = 0x100000; /* Load at 1MB */
.text : {
*(.multiboot) /* Multiboot header first */
*(.text) /* Code */
}
.rodata : { *(.rodata*) }
.data : { *(.data) }
.bss : { *(.bss) }
/* Embedded kernel binary */
.kernel : {
kernel_start = .;
*(.kernel_binary)
kernel_end = .;
}
}Links the standalone kernel.bin (used by kernel compilation).
Main bootloader entry point from assembly.
Parameters:
-
multiboot_magic: Magic number (0x2BADB002 expected) -
multiboot_info: Pointer to multiboot information structure
Actions:
- Validate multiboot magic
- Parse memory map
- Initialize CPU (GDT, IDT, paging)
- Load kernel
- Jump to kernel
Parse boot information from GRUB.
Extracts:
- Available memory (lower and upper)
- Memory map entries
- Boot device
- Kernel command line
Set up Global Descriptor Table with kernel and user segments.
Enable virtual memory with identity mapping for the first 4MB.
Load the embedded kernel binary into memory at the correct address.
Transfer control to kernel's kmain() function.
# Terminal 1
make qemu-gdb
# Terminal 2
gdb build/mentos/bootloader.bin
(gdb) target remote :1234
(gdb) break boot_main
(gdb) continueAdd debug prints (serial console):
// In boot.c
printk("Bootloader: multiboot_magic = %x\n", multiboot_magic);
printk("Bootloader: memory = %d KB\n", multiboot_info->mem_upper);objdump -d build/mentos/bootloader.bin | less- GRUB didn't load properly
- Multiboot header is missing or incorrect
- Check
.multibootsection in assembly
- Kernel binary wasn't embedded correctly
- Check linker script
- Verify kernel.bin.o creation
- Paging setup incorrect
- Identity mapping missing
- Check page table entries
- CPU exception during boot
- Usually GDT or paging issue
- Use
make qemu-gdbto catch early
- Kernel - What happens after bootloader
- Memory Management - Paging details
- Architecture - Overall system structure
Previous: Architecture | Next: Kernel →