# Simple UEFI Operating System A minimal bootable UEFI operating system written in C that demonstrates UEFI boot loading, memory management, interrupt handling, and cooperative multitasking. ## Features - **UEFI Boot**: Boots directly on UEFI firmware via a PE32+ loader - **ELF64 Kernel Loader**: Reads and maps a standalone kernel from the EFI partition - **Console I/O**: Interactive keyboard input and screen output - **Interrupt Handling**: IDT setup with CPU exception handlers (vectors 0–31) and hardware IRQ dispatch - **Memory Management**: Bitmap-based physical memory manager (PMM), 4-level x86-64 paging, and a first-fit heap allocator with block coalescing - **Cooperative Multitasking**: Process control blocks, assembly context switching, and a round-robin task scheduler - **Interactive Shell**: Command registry with `help`, `man`, built-in commands, and test utilities ## Requirements - GCC compiler - GNU-EFI library - QEMU with OVMF firmware - Make ## Installation ### On Debian/Ubuntu: ```bash make install-deps ``` Or manually: ```bash sudo apt-get install gnu-efi qemu-system-x86 ovmf gcc binutils make xorriso mtools ``` ### On Arch Linux: ```bash sudo pacman -S gnu-efi qemu-system-x86 edk2-ovmf gcc binutils make xorriso mtools ``` ### On Fedora/RHEL: ```bash sudo dnf install gnu-efi qemu-system-x86 edk2-ovmf gcc binutils make xorriso mtools ``` ## Building Build the UEFI loader and kernel: ```bash make ``` This creates `build/EFI/BOOT/BOOTX64.EFI` (UEFI loader) and `build/kernel.elf` (kernel binary). ## Running Test with QEMU (no graphics mode): ```bash make run ``` To build and run from an ISO image: ```bash make runiso ``` The loader expects `kernel.elf` at the root of the EFI partition (next to the `EFI/` directory). ### QEMU Controls: - **Exit QEMU**: Press `Ctrl+A`, then `X` - **Shutdown OS**: Type `shutdown` in the OS ## Commands Once the OS boots, you can use these commands: | Command | Description | |---------|-------------| | `help` | Display all available commands | | `man ` | Display detailed help for a command | | `shutdown` | Shutdown the system | | `clear` | Clear the screen | | `about` | Display system information | | `mem` | Display memory statistics (PMM, heap, paging) | | `ps` | List all active tasks with PID, state, and name | | `spawn [name]` | Create a demo background task | | `memtest` | Run memory allocation/deallocation tests | | `tasktest` | Spawn multiple concurrent tasks to test the scheduler | ### Example Session ``` -> help -> about -> mem -> memtest -> spawn worker1 -> spawn worker2 -> ps -> tasktest -> shutdown ``` ### Testing Memory Management The `memtest` command runs through four test phases: 1. **Heap allocation** — allocates 8 blocks of increasing size (16 to 4096 bytes) via `kmalloc()` 2. **Heap free** — frees all allocated blocks via `kfree()` and verifies coalescing 3. **PMM single page** — allocates and frees a single 4 KB page 4. **PMM multi-page** — allocates and frees 4 contiguous pages Each phase reports addresses, success/failure, and usage statistics. ### Testing Task Scheduling The `tasktest` command: 1. Spawns 3 worker tasks (`worker-A`, `worker-B`, `worker-C`) 2. Each worker prints 3 progress steps, yielding between each 3. The shell yields to let workers run in round-robin order 4. Displays the task list after completion You can also manually test with `spawn` and `ps`: ``` -> spawn myTask Created task 'myTask' (PID 1) -> ps PID STATE SWITCHES NAME --- ---------- -------- ---- 0 RUNNING 5 kernel 1 READY 0 myTask Active tasks: 2 / 32 ``` Background tasks run whenever the shell yields (which happens automatically while waiting for keyboard input). ## Project Structure ``` . ├── main.c # UEFI loader (reads and loads kernel ELF) ├── kernel.c # Kernel entry point and interactive shell loop ├── kernel.ld # Kernel linker script (base address 0x100000) ├── boot_info.h # Shared boot interface (BootInfo struct) ├── commands.c # Command registry and handler implementations ├── commands.h # Command interface definitions ├── string_utils.c # String utility functions (trim, compare, etc.) ├── string_utils.h # String utility declarations ├── idt.c # IDT setup, exception handlers, PIC EOI ├── idt.h # IDT types (ISRFrame) and init declaration ├── isr.S # ISR stub table (vectors 0–255) with common handler ├── memory.c # PMM (bitmap), paging (4-level), heap (first-fit) ├── memory.h # Memory subsystem declarations and constants ├── task.c # Task manager: PCB pool, scheduler, yield/exit ├── task.h # Task types (Task, TaskState) and API ├── context_switch.S # Assembly cooperative context switch (x86-64) ├── Makefile # Build configuration └── README.md # This file ``` ## Architecture ### Boot Flow 1. **UEFI Entry** — `efi_main()` in `main.c` is called by firmware 2. **GNU-EFI Init** — library initialized with the system table 3. **Kernel Load** — `kernel.elf` is read from the EFI partition, ELF64 PT_LOAD segments are mapped into memory 4. **BootInfo Setup** — function pointers for console I/O, shutdown, and page allocation are packaged into a `BootInfo` struct 5. **Kernel Entry** — `kmain()` is called with the `BootInfo` pointer ### Kernel Initialization 1. **IDT** — CPU exception vectors (0–31) are installed; firmware IRQ handlers are preserved 2. **Memory** — PMM allocates a 16 MB page pool via UEFI, bitmap tracks free/used pages; paging reads CR3; heap carves 256 KB from the PMM 3. **Tasks** — scheduler initializes; the running kernel becomes task 0 4. **Shell** — interactive loop with non-blocking input and cooperative yielding ### Memory Management | Component | Description | |-----------|-------------| | **PMM** | Bitmap-based page-frame allocator managing a 16 MB pool (4096 pages); supports single and contiguous multi-page allocation | | **Paging** | Walks/creates 4-level x86-64 page tables (PML4 → PDPT → PD → PT); supports map, unmap, and virtual-to-physical translation | | **Heap** | First-fit free-list allocator with block splitting and bidirectional coalescing; 16-byte alignment; magic number corruption detection | ### Task System | Component | Description | |-----------|-------------| | **PCB** | `Task` struct with PID, state, name, saved RSP, stack, entry function, and scheduling metadata | | **States** | `FREE` → `READY` → `RUNNING` → `TERMINATED` → `FREE` | | **Scheduler** | Round-robin selection among `READY` tasks via `task_yield()` | | **Context Switch** | Assembly routine saves/restores callee-saved registers (`rbp`, `rbx`, `r12`–`r15`) and `RFLAGS`, swaps RSP | | **Stack** | Each task gets 32 KB (8 pages) allocated from PMM; freed on `task_exit()` | | **Limits** | Up to 32 concurrent tasks; cooperative (voluntary yield) only | ### Command System - Commands are registered in a static array with metadata (name, description, usage, handler function) - `execute_command()` parses input, splits command and arguments, and dispatches to the matching handler - `man` provides detailed help by looking up command metadata - New commands are added by writing a handler and appending to the `commands[]` array ## Building for Real Hardware To create a bootable USB drive: 1. Build the project: `make` 2. Format a USB drive with GPT and create an EFI partition (FAT32) 3. Mount the EFI partition 4. Copy `build/EFI/BOOT/BOOTX64.EFI` to `/EFI/BOOT/` on the USB drive 5. Copy `build/kernel.elf` to the root of the EFI partition 6. Boot from the USB drive in UEFI mode **Warning**: Always backup your data before creating bootable media! ## Cleaning Remove build artifacts: ```bash make clean ``` ## Troubleshooting ### GNU-EFI headers not found Make sure GNU-EFI is installed. The headers are typically in `/usr/include/efi`. If installed in a different location, update the `EFI_INC` variable in the Makefile. ### OVMF firmware not found OVMF files might be in different locations depending on your distribution: - `/usr/share/OVMF/` - `/usr/share/qemu/` - `/usr/share/edk2-ovmf/` Update the paths in the Makefile if needed. ### QEMU crashes or fails to start Ensure you have QEMU with x86_64 support and OVMF firmware installed. ## Technical Details - **Architecture**: x86_64 - **Firmware Interface**: UEFI 2.x - **Development Library**: GNU-EFI - **Loader Format**: PE32+ (Portable Executable for EFI) - **Kernel Format**: ELF64 - **Boot Protocol**: UEFI Boot Services - **Memory Model**: Identity-mapped (UEFI), 4-level paging (kernel) - **Scheduling**: Cooperative round-robin multitasking ## License This is a minimal educational example. Feel free to use and modify as needed. ## Adding New Commands 1. Open `commands.c` 2. Add a forward declaration: `static void cmd_yourcommand(BootInfo *Boot, CHAR16 *Args);` 3. Add an entry to the `commands[]` array (before the sentinel) 4. Implement the handler function 5. Rebuild with `make` ## Resources - [UEFI Specification](https://uefi.org/specifications) - [GNU-EFI Documentation](https://sourceforge.net/projects/gnu-efi/) - [OSDev Wiki](https://wiki.osdev.org/)