2026-02-26 21:08:06 +00:00
2026-02-26 17:37:57 +00:00
2026-02-26 21:08:06 +00:00
2026-02-26 21:08:06 +00:00
2026-02-26 18:11:24 +00:00
2026-02-26 21:08:06 +00:00
2026-02-26 20:12:45 +00:00
2026-02-26 20:12:45 +00:00
2026-02-26 20:12:45 +00:00
2026-02-26 21:08:06 +00:00
2026-02-26 17:41:53 +00:00
2026-02-26 21:08:06 +00:00
2026-02-26 21:08:06 +00:00
2026-02-26 20:24:56 +00:00
2026-02-26 20:24:56 +00:00
2026-02-26 21:08:06 +00:00
2026-02-26 18:11:24 +00:00
2026-02-26 18:11:24 +00:00
2026-02-26 21:08:06 +00:00
2026-02-26 21:08:06 +00:00

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 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

  • GCC compiler
  • GNU-EFI library
  • QEMU with OVMF firmware
  • Make

Installation

On Debian/Ubuntu:

make install-deps

Or manually:

sudo apt-get install gnu-efi qemu-system-x86 ovmf gcc binutils make xorriso mtools

On Arch Linux:

sudo pacman -S gnu-efi qemu-system-x86 edk2-ovmf gcc binutils make xorriso mtools

On Fedora/RHEL:

sudo dnf install gnu-efi qemu-system-x86 edk2-ovmf gcc binutils make xorriso mtools

Building

Build the UEFI loader and kernel:

make

This creates build/EFI/BOOT/BOOTX64.EFI (UEFI loader) and build/kernel.elf (kernel binary).

Running

Test with QEMU (no graphics mode):

make run

To build and run from an ISO image:

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 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

Architecture

Boot Flow

  1. UEFI Entryefi_main() in main.c is called by firmware
  2. GNU-EFI Init — library initialized with the system table
  3. Kernel Loadkernel.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 Entrykmain() 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 FREEREADYRUNNINGTERMINATEDFREE
Scheduler Round-robin selection among READY tasks via task_yield()
Context Switch Assembly routine saves/restores callee-saved registers (rbp, rbx, r12r15) 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:

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

Description
No description provided
Readme 257 KiB
Languages
C 82%
Assembly 10%
Makefile 8%