Task scheduler

This commit is contained in:
2026-02-26 21:08:06 +00:00
parent 6658e4314b
commit d449150169
9 changed files with 836 additions and 62 deletions

201
README.md
View File

@@ -1,14 +1,16 @@
# Simple UEFI Operating System
A minimal bootable UEFI operating system written in C that demonstrates basic UEFI functionality.
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
- **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
- **System Information**: Displays firmware details
- **Kernel Loader**: Loads an ELF64 kernel from the EFI partition
- **Simple Commands**: Minimal interactive command interface
- **Interrupt Handling**: IDT setup with CPU exception handlers (vectors 031) 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
@@ -28,30 +30,30 @@ make install-deps
Or manually:
```bash
sudo apt-get install gnu-efi qemu-system-x86 ovmf gcc binutils make
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
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
sudo dnf install gnu-efi qemu-system-x86 edk2-ovmf gcc binutils make xorriso mtools
```
## Building
Build the UEFI application and kernel:
Build the UEFI loader and kernel:
```bash
make
```
This will create `build/EFI/BOOT/BOOTX64.EFI` and `build/kernel.elf`.
This creates `build/EFI/BOOT/BOOTX64.EFI` (UEFI loader) and `build/kernel.elf` (kernel binary).
## Running
@@ -61,63 +63,151 @@ Test with QEMU (no graphics mode):
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 in the OS
## Commands
Once the OS boots, you can use these commands:
- `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
| 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
Example usage:
```
-> help
-> man shutdown
-> clear
-> 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 main loop
├── commands.c # Command registry and handlers
├── commands.h # Command interface definitions
├── string_utils.c # String utility functions
├── string_utils.h # String utility declarations
├── kernel.ld # Kernel linker script
├── boot_info.h # Shared boot interface
├── Makefile # Build configuration
── README.md # This file
├── 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 0255) 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
```
## How It Works
## Architecture
1. **UEFI Entry Point**: The `efi_main` function is the entry point called by UEFI firmware
2. **Initialization**: GNU-EFI library is initialized with the system table
3. **Console Setup**: Output is configured and screen is cleared
4. **Kernel Load**: The loader reads `kernel.elf` and maps its loadable segments
5. **Main Loop**: The kernel enters a loop waiting for keyboard input
6. **Command Processing**: User commands are parsed and dispatched to registered handlers
7. **Command Execution**: Each command is executed via its handler function
8. **Shutdown**: UEFI runtime services are used to shutdown the system
### Boot Flow
**Command System Architecture:**
- Commands are registered in a static array with metadata (name, description, usage, handler)
- The `execute_command()` function parses input, splits command and arguments, and dispatches to handlers
- The `man` command provides detailed help by looking up command metadata
- New commands can be easily added by implementing a handler and registering it
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 (031) 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
@@ -166,30 +256,23 @@ Ensure you have QEMU with x86_64 support and OVMF firmware installed.
- **Architecture**: x86_64
- **Firmware Interface**: UEFI 2.x
- **Development Library**: GNU-EFI
- **Executable Format**: PE32+ (Portable Executable for 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.
## Further Development
## Adding New Commands
The codebase is now modular and easy to extend:
**Adding New Commands:**
1. Open `commands.c`
2. Create a new handler function: `static void cmd_yourcommand(BootInfo *Boot, CHAR16 *Args)`
3. Add your command to the `commands[]` array with name, description, usage, and handler
4. Rebuild with `make`
**Other Ideas for expansion:**
- File system support
- Memory management
- Graphics output
- Network stack
- Device drivers
- Multi-tasking
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