282 lines
9.2 KiB
Markdown
282 lines
9.2 KiB
Markdown
# 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 <command>` | 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/)
|