- 32-bit x86, Multiboot v1, boots via GRUB into protected mode
- VGA text terminal (80x25, 16 colors, scrolling)
- Serial COM1 output (38400 baud)
- Port I/O helpers (inb/outb)
- Dual-mode test framework (host + QEMU)
- No GDT, IDT, paging, heap, or drivers of its own
These establish control over the CPU that GRUB currently manages for you.
Task 1: String & Memory Utilities (libc subset)
- Implement
memset,memcpy,memmove,memcmp,strlen,strcmp,strncmp,itoa/number formatting - Create
kernel/string.h+kernel/string.c - Why first: nearly every subsequent task needs these
- Test: unit tests for each function in host + QEMU modes
Task 2: Formatted Print (kprintf)
- Implement a kernel printf supporting
%d,%u,%x,%s,%c,%p,%% - Output to both VGA terminal and serial
- Depends on: Task 1 (string/number formatting helpers)
- Why: debugging everything that follows requires formatted output
- Test: print various format strings, verify on serial and VGA
Task 3: Global Descriptor Table (GDT)
- Set up your own GDT with: null descriptor, kernel code (ring 0), kernel data (ring 0), user code (ring 3), user data (ring 3)
- Write
gdt_flushin assembly to load GDTR and reload segment registers - Create
kernel/gdt.h+kernel/gdt.c - Depends on: Task 2 (kprintf for debug output)
- Why: you currently rely on GRUB's GDT which you don't control; needed before IDT
- Test: verify boot still works after loading custom GDT; print segment register values
Task 4: Interrupt Descriptor Table (IDT) + ISR Stubs
- Define 256-entry IDT
- Write assembly ISR stubs for exceptions 0–31 (CPU exceptions) and IRQs 32–47
- Common handler in C that dispatches based on interrupt number
- Create
kernel/idt.h+kernel/idt.c+kernel/isr.asm(or inline asm) - Depends on: Task 3 (GDT must be valid before loading IDT)
- Why: required for hardware interrupts, exception handling, everything beyond polling
- Test: trigger division by zero, verify handler fires and prints message
Task 5: Programmable Interrupt Controller (PIC) Remapping
- Remap PIC so IRQ 0–15 map to interrupts 32–47 (avoiding CPU exception overlap)
- Implement
pic_init(),pic_send_eoi(),irq_set_mask(),irq_clear_mask() - Create
kernel/pic.h+kernel/pic.c - Depends on: Task 4 (IDT must exist to register IRQ handlers)
- Why: hardware interrupts won't work correctly without PIC remapping
- Test: verify no spurious interrupts; PIC responds to EOI
Task 6: Programmable Interval Timer (PIT)
- Configure PIT channel 0 for periodic ticks (e.g., 100 Hz or 1000 Hz)
- Register IRQ 0 handler, maintain a global tick counter
- Implement
pit_init(),timer_get_ticks(),sleep_ms()(busy-wait on tick counter) - Create
kernel/timer.h+kernel/timer.c - Depends on: Task 5 (PIC must be remapped so IRQ0 fires correctly)
- Why: needed for scheduling, timeouts, delays
- Test: print tick count; verify sleep_ms approximation
Task 7: Keyboard Driver (PS/2)
- Register IRQ 1 handler
- Implement scancode-to-ASCII translation (US QWERTY, scan code set 1)
- Handle key press/release, shift state, caps lock
- Ring buffer for key input;
keyboard_getchar()blocking read - Create
kernel/keyboard.h+kernel/keyboard.c - Depends on: Task 5 (PIC for IRQ1), Task 4 (IDT)
- Why: first interactive I/O — makes kernel usable
- Test: type characters, verify they echo on screen
With CPU control established, take ownership of memory.
Task 8: Physical Memory Manager (PMM)
- Parse Multiboot memory map (passed in EBX at boot) to find usable RAM regions
- Bitmap-based frame allocator (4 KiB frames)
pmm_init(),pmm_alloc_frame(),pmm_free_frame()- Create
kernel/pmm.h+kernel/pmm.c - Depends on: Task 1 (memset), Task 2 (kprintf for debug), multiboot info parsing
- Why: paging and heap both need physical frame allocation
- Test: allocate and free frames; verify bitmap state; count available memory
Task 9: Paging (Virtual Memory)
- Set up a page directory and page tables
- Identity-map the first 4 MiB (or however much kernel uses)
- Map VGA buffer at 0xB8000
- Enable paging via CR0/CR3
- Implement
paging_init(),map_page(),unmap_page() - Create
kernel/paging.h+kernel/paging.c - Depends on: Task 8 (PMM to allocate page table frames), Task 4 (IDT for page fault handler)
- Why: memory protection, process isolation, higher-half kernel later
- Test: access mapped memory; trigger page fault on unmapped address; verify handler fires
Task 10: Kernel Heap (kmalloc/kfree)
- Simple heap allocator (e.g., first-fit linked list, or boundary tag)
kmalloc(size),kfree(ptr), optionallykmalloc_aligned()- Heap grows by requesting pages from PMM + mapping them
- Create
kernel/heap.h+kernel/heap.c - Depends on: Task 9 (paging, to map new heap pages), Task 8 (PMM)
- Why: dynamic allocation needed for data structures, drivers, processes
- Test: allocate various sizes; free and reallocate; stress test for fragmentation
With memory management, you can build data structures for storage.
Task 11: ATA/IDE Disk Driver (PIO Mode)
- Detect ATA drives on primary/secondary bus
- Implement PIO read/write of 512-byte sectors
ata_init(),ata_read_sectors(),ata_write_sectors()- Create
kernel/ata.h+kernel/ata.c - Depends on: Task 1 (memcpy), Task 2 (kprintf), Task 5 (PIC for IRQ14/15 optionally)
- Why: need disk access before any filesystem
- Test: read sector 0 (MBR) from a test disk image; verify magic bytes 0x55AA
Task 12: Simple Filesystem (FAT16 or custom read-only fs)
- Parse filesystem structures from disk
- Implement directory listing, file open, file read
- Start read-only; write support later
- Create
kernel/fs.h+kernel/fs.c - Depends on: Task 11 (ATA driver), Task 10 (heap for buffers and directory entries)
- Why: loading programs, config files, enabling initrd
- Test: create a FAT16 disk image with test files; read and print file contents
Task 13: Task State Segment (TSS)
- Add TSS entry to GDT
- Initialize TSS with kernel stack pointer (for ring 3 → ring 0 transitions)
tss_init(),tss_set_kernel_stack()- Create
kernel/tss.h+kernel/tss.c - Depends on: Task 3 (GDT), Task 9 (paging, for per-task stacks)
- Why: required for user mode and context switching
- Test: verify TSS loaded with
ltr; no fault
Task 14: Context Switching & Kernel Threads
- Define a task/process struct (registers, stack pointer, page directory, state)
- Implement
switch_to(old, new)in assembly (save/restore registers + ESP) - Simple round-robin scheduler:
schedule()called from timer IRQ - Task creation:
task_create(entry_function) - Create
kernel/task.h+kernel/task.c+kernel/switch.asm - Depends on: Task 6 (timer for preemption), Task 10 (heap for task structs), Task 9 (per-task page directories)
- Why: multitasking is fundamental to a real OS
- Test: create 2+ kernel threads that print alternating messages
Task 15: User Mode
- Ring 3 transition: set up user code/data segments, jump to user mode via
iret - Separate user and kernel page mappings
- Depends on: Task 13 (TSS), Task 14 (task switching), Task 9 (paging)
- Why: process isolation, security boundary
- Test: user mode program runs; accessing kernel memory causes page fault
Task 16: System Calls
- Use
int 0x80(orsysenter) for user → kernel calls - Implement a syscall dispatcher and initial syscalls:
sys_write,sys_exit,sys_getpid - Create
kernel/syscall.h+kernel/syscall.c - Depends on: Task 15 (user mode), Task 4 (IDT for int 0x80)
- Why: user programs need a safe way to request kernel services
- Test: user program calls sys_write to print to terminal
Task 17: Simple Shell
- Command-line interface reading keyboard input
- Built-in commands:
help,clear,echo,meminfo,uptime,ls,cat - Command parsing (split by spaces)
- Depends on: Task 7 (keyboard), Task 2 (kprintf), Task 6 (timer for uptime)
- Why: interactive interface makes the kernel tangible and testable
- Test: boot kernel, type commands, verify output
Task 18: ELF Binary Loader
- Parse ELF32 headers from filesystem
- Load program segments into user address space
- Set up user stack, jump to entry point
- Create
kernel/elf.h+kernel/elf.c - Depends on: Task 12 (filesystem), Task 15 (user mode), Task 9 (paging)
- Why: run real compiled programs instead of hardcoded functions
- Test: compile a simple C program as freestanding ELF; load and execute it
Task 19: VGA Scrollback Buffer
- Keep a buffer of previous lines for scroll-up
- Keyboard shortcuts (Shift+PgUp/PgDn)
- Depends on: Task 7 (keyboard), Task 10 (heap)
Task 20: Serial Input (bidirectional)
- Read from serial port, enable serial console
- Depends on: Task 4 (IDT for IRQ4)
Task 21: ACPI Power Management
- Parse ACPI tables for shutdown/reboot
- Depends on: Task 9 (paging for ACPI table mapping), Task 1
Task 22: Networking (virtio-net or NE2000)
- Very advanced; needs ring buffers, protocol stack
- Depends on: Task 10 (heap), Task 4 (IDT), Task 5 (PIC)