diff --git a/Makefile b/Makefile
index de40e2f..ab29e04 100644
--- a/Makefile
+++ b/Makefile
@@ -1,45 +1,56 @@
-# Makefile for UEFI Operating System
+# ==============================================================================
+# Makefile for Simple UEFI Operating System
+# ==============================================================================
-# Architecture
+# ---- Architecture -----------------------------------------------------------
ARCH = x86_64
-# Compiler and tools
-CC = gcc
-LD = ld
+# ---- Tools ------------------------------------------------------------------
+CC = gcc
+LD = ld
OBJCOPY = objcopy
-# Directories
-SRC_DIR = .
+# ---- Directories ------------------------------------------------------------
+SRC_DIR = .
BUILD_DIR = build
-EFI_DIR = $(BUILD_DIR)/EFI/BOOT
+EFI_DIR = $(BUILD_DIR)/EFI/BOOT
IMAGE_DIR = $(BUILD_DIR)/image
-# GNU-EFI paths (common locations)
-EFI_INC = /usr/include/efi
-EFI_INCLUDES = -I$(EFI_INC) -I$(EFI_INC)/$(ARCH) -I$(EFI_INC)/protocol
-EFI_LDS = /usr/lib/elf_$(ARCH)_efi.lds
-EFI_CRT_OBJS = /usr/lib/crt0-efi-$(ARCH).o
+# ---- GNU-EFI paths ---------------------------------------------------------
+EFI_INC = /usr/include/efi
+EFI_INCLUDES = -I$(EFI_INC) -I$(EFI_INC)/$(ARCH) -I$(EFI_INC)/protocol
+EFI_LDS = /usr/lib/elf_$(ARCH)_efi.lds
+EFI_CRT_OBJS = /usr/lib/crt0-efi-$(ARCH).o
EFI_LIB_PATHS = -L/usr/lib
-# Compiler flags
-CFLAGS = -ffreestanding -fno-stack-protector -fpic \
- -fshort-wchar -mno-red-zone -Wall -Wextra \
- $(EFI_INCLUDES) -DEFI_FUNCTION_WRAPPER
+# ---- Compiler / linker flags ------------------------------------------------
+
+# Flags shared by both the UEFI loader and the kernel
+COMMON_CFLAGS = -ffreestanding -fno-stack-protector -fshort-wchar \
+ -mno-red-zone -Wall -Wextra $(EFI_INCLUDES)
+
+# UEFI loader: position-independent (shared object → PE32+ conversion)
+LOADER_CFLAGS = $(COMMON_CFLAGS) -fpic -DEFI_FUNCTION_WRAPPER
+
+# Kernel: position-dependent static ELF
+KERNEL_CFLAGS = $(COMMON_CFLAGS) -fno-pic
-# Linker flags
LDFLAGS = -nostdlib -znocombreloc -T $(EFI_LDS) -shared \
-Bsymbolic $(EFI_LIB_PATHS) $(EFI_CRT_OBJS)
-# Libraries
LIBS = -lefi -lgnuefi
-# Target
-TARGET = BOOTX64.EFI
-TARGET_SO = bootx64.so
-OBJ = $(BUILD_DIR)/main.o
+# ---- Targets / objects ------------------------------------------------------
+TARGET = BOOTX64.EFI
+TARGET_SO = bootx64.so
+LOADER_OBJ = $(BUILD_DIR)/main.o
+
KERNEL_TARGET = kernel.elf
-KERNEL_OBJS = $(BUILD_DIR)/kernel.o $(BUILD_DIR)/string_utils.o $(BUILD_DIR)/commands.o $(BUILD_DIR)/idt.o $(BUILD_DIR)/isr.o $(BUILD_DIR)/memory.o $(BUILD_DIR)/task.o $(BUILD_DIR)/context_switch.o
-KERNEL_LD = kernel.ld
+KERNEL_LD = kernel.ld
+KERNEL_C_SRCS = kernel.c string_utils.c commands.c idt.c memory.c task.c
+KERNEL_S_SRCS = isr.S context_switch.S
+KERNEL_OBJS = $(patsubst %.c,$(BUILD_DIR)/%.o,$(KERNEL_C_SRCS)) \
+ $(patsubst %.S,$(BUILD_DIR)/%.o,$(KERNEL_S_SRCS))
# QEMU settings
QEMU = qemu-system-x86_64
@@ -76,17 +87,23 @@ QEMU_FLAGS = -machine q35 \
-drive if=pflash,format=raw,file=$(BUILD_DIR)/OVMF_VARS.fd \
-drive format=raw,file=fat:rw:$(BUILD_DIR) \
-net none \
- -no-reboot
+ -no-reboot
- # Phony targets
-.PHONY: all clean run setup install-deps iso runiso
+# ==============================================================================
+# Phony targets
+# ==============================================================================
+
+.PHONY: all clean run setup install-deps iso runiso help
+
+# ---- Default ----------------------------------------------------------------
- # Default target
all: setup $(EFI_DIR)/$(TARGET) $(BUILD_DIR)/$(KERNEL_TARGET)
-# ISO target
-ISO_FILE = $(IMAGE_DIR)/os.iso
+# ==============================================================================
+# ISO image
+# ==============================================================================
-ESP_IMG = $(IMAGE_DIR)/staging/esp.img
+ISO_FILE = $(IMAGE_DIR)/os.iso
+ESP_IMG = $(IMAGE_DIR)/staging/esp.img
iso: all
@echo "Creating FAT ESP image for UEFI boot..."
@@ -107,6 +124,10 @@ iso: all
$(IMAGE_DIR)/staging
@echo "ISO image created: $(ISO_FILE)"
+# ==============================================================================
+# Run targets
+# ==============================================================================
+
# Run from ISO with QEMU
runiso: iso
@echo "Starting QEMU from ISO..."
@@ -124,77 +145,62 @@ runiso: iso
-nographic \
-no-reboot
-# Create necessary directories
+# ==============================================================================
+# Directory setup
+# ==============================================================================
+
setup:
@mkdir -p $(BUILD_DIR)
@mkdir -p $(EFI_DIR)
@mkdir -p $(IMAGE_DIR)
-# Compile source files
+# ==============================================================================
+# Compilation rules
+# ==============================================================================
+
+# ---- UEFI loader object (PIC, linked as shared object → PE32+) ----
+
+$(BUILD_DIR)/main.o: $(SRC_DIR)/main.c
+ @echo " CC $<"
+ @$(CC) $(LOADER_CFLAGS) -c $< -o $@
+
+# ---- Kernel objects (position-dependent, linked into static ELF) ----
+# Pattern rules replace the per-file rules for every kernel source.
+
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
- @echo "Compiling $<..."
- $(CC) $(CFLAGS) -c $< -o $@
+ @echo " CC $<"
+ @$(CC) $(KERNEL_CFLAGS) -c $< -o $@
-# Compile kernel source
-$(BUILD_DIR)/kernel.o: $(SRC_DIR)/kernel.c
- @echo "Compiling kernel.c..."
- $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \
- -mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@
+$(BUILD_DIR)/%.o: $(SRC_DIR)/%.S
+ @echo " AS $<"
+ @$(CC) $(KERNEL_CFLAGS) -c $< -o $@
-$(BUILD_DIR)/string_utils.o: $(SRC_DIR)/string_utils.c
- @echo "Compiling string_utils.c..."
- $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \
- -mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@
+# ==============================================================================
+# Linking
+# ==============================================================================
-$(BUILD_DIR)/commands.o: $(SRC_DIR)/commands.c
- @echo "Compiling commands.c..."
- $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \
- -mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@
+# ---- Kernel ELF ----
-$(BUILD_DIR)/idt.o: $(SRC_DIR)/idt.c
- @echo "Compiling idt.c..."
- $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \
- -mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@
-
-$(BUILD_DIR)/isr.o: $(SRC_DIR)/isr.S
- @echo "Compiling isr.S..."
- $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \
- -mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@
-
-$(BUILD_DIR)/memory.o: $(SRC_DIR)/memory.c
- @echo "Compiling memory.c..."
- $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \
- -mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@
-
-$(BUILD_DIR)/task.o: $(SRC_DIR)/task.c
- @echo "Compiling task.c..."
- $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \
- -mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@
-
-$(BUILD_DIR)/context_switch.o: $(SRC_DIR)/context_switch.S
- @echo "Assembling context_switch.S..."
- $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \
- -mno-red-zone -Wall -Wextra -c $< -o $@
-
-# Link kernel ELF
$(BUILD_DIR)/$(KERNEL_TARGET): $(KERNEL_OBJS) $(KERNEL_LD)
- @echo "Linking kernel ELF..."
- $(LD) -nostdlib -T $(KERNEL_LD) $(KERNEL_OBJS) -o $@
+ @echo " LD $@"
+ @$(LD) -nostdlib -T $(KERNEL_LD) $(KERNEL_OBJS) -o $@
-# Link to create shared object
-$(BUILD_DIR)/$(TARGET_SO): $(OBJ)
- @echo "Linking $@..."
- $(LD) $(LDFLAGS) $(OBJ) -o $@ $(LIBS)
+# ---- UEFI loader shared object ----
+
+$(BUILD_DIR)/$(TARGET_SO): $(LOADER_OBJ)
+ @echo " LD $@"
+ @$(LD) $(LDFLAGS) $(LOADER_OBJ) -o $@ $(LIBS)
+
+# ---- Convert to PE32+ EFI application ----
-# Convert to EFI application
$(EFI_DIR)/$(TARGET): $(BUILD_DIR)/$(TARGET_SO)
- @echo "Creating EFI application..."
- $(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic \
- -j .dynsym -j .rel -j .rela -j .reloc \
- --target=efi-app-$(ARCH) $< $@
+ @echo " OBJCOPY $@"
+ @$(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic \
+ -j .dynsym -j .rel -j .rela -j .reloc \
+ --target=efi-app-$(ARCH) $< $@
@echo "Build complete: $@"
-# Run with QEMU
+# Run with QEMU (FAT directory)
run: all
@echo "Starting QEMU..."
@echo "Using OVMF firmware: $(OVMF_CODE)"
@@ -210,20 +216,27 @@ run: all
@echo "================================================"
$(QEMU) $(QEMU_FLAGS)
+# ==============================================================================
+# Maintenance
+# ==============================================================================
+
# Clean build artifacts
clean:
@echo "Cleaning build directory..."
rm -rf $(BUILD_DIR)
@echo "Clean complete."
-# Install dependencies (for Debian/Ubuntu)
+# Install dependencies (Debian/Ubuntu)
install-deps:
@echo "Installing dependencies..."
sudo apt-get update
sudo apt-get install -y gnu-efi qemu-system-x86 ovmf gcc binutils make xorriso mtools
@echo "Dependencies installed."
- # Help target
+# ==============================================================================
+# Help
+# ==============================================================================
+
help:
@echo "UEFI Operating System Makefile"
@echo ""
diff --git a/README.md b/README.md
index 1434193..aa23ea1 100644
--- a/README.md
+++ b/README.md
@@ -4,24 +4,30 @@ A minimal bootable UEFI operating system written in C that demonstrates UEFI boo
## 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
+- **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
+## Getting Started
+
+### Requirements
+
+- GCC cross-compiler (or native x86-64 GCC)
+- GNU-EFI library and headers
- QEMU with OVMF firmware
-- Make
+- GNU Make
+- `xorriso` and `mtools` (for ISO builds only)
-## Installation
+### Installation
-### On Debian/Ubuntu:
+
+Debian / Ubuntu
```bash
make install-deps
@@ -33,57 +39,78 @@ Or manually:
sudo apt-get install gnu-efi qemu-system-x86 ovmf gcc binutils make xorriso mtools
```
-### On Arch Linux:
+
+
+
+Arch Linux
```bash
sudo pacman -S gnu-efi qemu-system-x86 edk2-ovmf gcc binutils make xorriso mtools
```
-### On Fedora/RHEL:
+
+
+
+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:
+### Building
```bash
-make
+make # build both UEFI loader and kernel
```
-This creates `build/EFI/BOOT/BOOTX64.EFI` (UEFI loader) and `build/kernel.elf` (kernel binary).
+This produces two binaries:
-## Running
+| Output | Description |
+|--------|-------------|
+| `build/EFI/BOOT/BOOTX64.EFI` | UEFI loader (PE32+) |
+| `build/kernel.elf` | Kernel binary (ELF64) |
-Test with QEMU (no graphics mode):
+### Running
```bash
-make run
-```
-
-To build and run from an ISO image:
-
-```bash
-make runiso
+make run # build and run with QEMU (FAT directory, nographic)
+make runiso # build, create ISO, and run with QEMU
```
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
+| Key / Command | Action |
+|---------------|--------|
+| `Ctrl+A`, then `X` | Exit QEMU |
+| `shutdown` (in shell) | Power off the OS |
-## Commands
+### Make Targets
-Once the OS boots, you can use these commands:
+| Target | Description |
+|--------|-------------|
+| `all` | Build the UEFI loader and kernel (default) |
+| `run` | Build and run with QEMU |
+| `iso` | Create a bootable ISO image |
+| `runiso` | Build, create ISO, and run in QEMU |
+| `clean` | Remove all build artifacts |
+| `install-deps` | Install required packages (Debian/Ubuntu) |
+| `help` | Print target summary |
+
+---
+
+## Usage
+
+### Shell Commands
+
+Once the OS boots, an interactive prompt (`->`) is displayed. The following commands are available:
| Command | Description |
|---------|-------------|
-| `help` | Display all available commands |
-| `man ` | Display detailed help for a command |
-| `shutdown` | Shutdown the system |
+| `help` | List all available commands |
+| `man ` | Show detailed help for a command |
+| `shutdown` | Shut down the system |
| `clear` | Clear the screen |
| `about` | Display system information |
| `mem` | Display memory statistics (PMM, heap, paging) |
@@ -108,10 +135,10 @@ Once the OS boots, you can use these commands:
### Testing Memory Management
-The `memtest` command runs through four test phases:
+The `memtest` command runs four 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
+1. **Heap allocation** — allocates 8 blocks of increasing size (16–4096 bytes) via `kmalloc()`
+2. **Heap free** — frees all 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
@@ -126,7 +153,7 @@ The `tasktest` command:
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`:
+You can also test manually with `spawn` and `ps`:
```
-> spawn myTask
@@ -142,140 +169,154 @@ Created task 'myTask' (PID 1)
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
+```
+UEFI Firmware
+ └─ efi_main() [main.c] PE32+ UEFI application
+ ├─ InitializeLib() GNU-EFI setup
+ ├─ read_file_to_buffer() read kernel.elf from ESP
+ ├─ load_elf_kernel() parse ELF64, map PT_LOAD segments
+ ├─ populate BootInfo wrap UEFI services as fn pointers
+ └─ kmain(&Boot) [kernel.c] jump to kernel
+ ├─ idt_init() install exception handlers
+ ├─ memory_init() PMM → paging → heap
+ ├─ task_init() scheduler + task 0
+ └─ shell loop read-eval-print with cooperative yield
+```
### 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 |
+| Layer | Description |
+|-------|-------------|
+| **PMM** | Bitmap-based page-frame allocator managing a 16 MB pool (4096 pages). Supports single and contiguous multi-page allocation. |
+| **Paging** | Walks and creates 4-level x86-64 page tables (PML4 → PDPT → PD → PT). Supports map, unmap, and virtual-to-physical translation for 4 KB, 2 MB, and 1 GB page sizes. |
+| **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 |
+| Aspect | Detail |
+|--------|--------|
+| **PCB** | `Task` struct: PID, state, name, saved RSP, stack base, entry function, 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()` |
+| **Scheduler** | Round-robin among `READY` tasks via `task_yield()` |
+| **Context Switch** | Assembly routine saves/restores callee-saved registers (`rbp`, `rbx`, `r12`–`r15`) and `RFLAGS`, then swaps RSP |
+| **Stack** | 32 KB (8 pages) per task, 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
+Commands are registered in a static array in `commands.c`:
-## Building for Real Hardware
+```c
+static Command commands[] = {
+ { L"name", L"description", L"usage", handler_fn },
+ ...
+ { NULL, NULL, NULL, NULL } /* sentinel */
+};
+```
-To create a bootable USB drive:
+`execute_command()` splits user input into command + arguments and dispatches to the matching handler. `man` provides detailed help by looking up command metadata.
+
+---
+
+## Project Structure
+
+```
+.
+├── main.c # UEFI loader — reads kernel.elf, sets up BootInfo
+├── kernel.c # Kernel entry point (kmain) and interactive shell
+├── kernel.ld # Linker script — kernel loaded at 0x100000
+├── boot_info.h # BootInfo struct shared between loader and kernel
+│
+├── commands.c # Command registry and all handler implementations
+├── commands.h # Command type and dispatch API
+├── string_utils.c # CHAR16 string helpers (trim, compare)
+├── string_utils.h # String utility declarations
+│
+├── idt.c # IDT setup, exception handlers, PIC EOI
+├── idt.h # ISRFrame layout and idt_init() declaration
+├── isr.S # ISR stub table (vectors 0–255) and common handler
+│
+├── memory.c # PMM (bitmap), paging (4-level), heap (first-fit)
+├── memory.h # Memory subsystem constants, types, and API
+│
+├── task.c # PCB pool, round-robin scheduler, yield/exit
+├── task.h # Task types (Task, TaskState) and task API
+├── context_switch.S # Cooperative context switch (x86-64 assembly)
+│
+├── Makefile # Build system
+├── README.md # This file
+└── build/ # Build output (generated)
+ ├── EFI/BOOT/BOOTX64.EFI
+ ├── kernel.elf
+ └── image/ # ISO staging area
+```
+
+---
+
+## Development
+
+### Adding a New Command
+
+1. Open `commands.c`
+2. Add a forward declaration:
+ ```c
+ static void cmd_foo(BootInfo *Boot, CHAR16 *Args);
+ ```
+3. Append an entry to the `commands[]` array (before the `NULL` sentinel):
+ ```c
+ { L"foo", L"Short description", L"Usage: foo [args]\n\r Details.", cmd_foo },
+ ```
+4. Implement `cmd_foo`
+5. Rebuild with `make`
+
+### Building for Real Hardware
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
+2. Format a USB drive with GPT and a FAT32 EFI System Partition
+3. Mount the ESP
+4. Copy `build/EFI/BOOT/BOOTX64.EFI` → `/EFI/BOOT/BOOTX64.EFI`
+5. Copy `build/kernel.elf` → `/kernel.elf`
+6. Boot from the USB in UEFI mode
-**Warning**: Always backup your data before creating bootable media!
+> **Warning:** Always back up your data before creating bootable media.
-## Cleaning
-
-Remove build artifacts:
-
-```bash
-make clean
-```
+---
## Troubleshooting
-### GNU-EFI headers not found
+| Problem | Solution |
+|---------|----------|
+| **GNU-EFI headers not found** | Install GNU-EFI. Headers are typically at `/usr/include/efi`. Update `EFI_INC` in the Makefile if installed elsewhere. |
+| **OVMF firmware not found** | Check `/usr/share/OVMF/`, `/usr/share/qemu/`, or `/usr/share/edk2-ovmf/`. The Makefile auto-detects common paths. |
+| **QEMU crashes or won't start** | Ensure `qemu-system-x86_64` and OVMF are installed. Verify with `qemu-system-x86_64 --version`. |
-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`
+| Property | Value |
+|----------|-------|
+| Architecture | x86-64 (long mode) |
+| Firmware Interface | UEFI 2.x |
+| Development Library | GNU-EFI |
+| Loader Format | PE32+ (EFI application) |
+| Kernel Format | ELF64 (static, loaded at 0x100000) |
+| Boot Protocol | UEFI Boot Services |
+| Memory Model | Identity-mapped (UEFI), 4-level paging (kernel) |
+| Scheduling | Cooperative round-robin multitasking |
## Resources
- [UEFI Specification](https://uefi.org/specifications)
- [GNU-EFI Documentation](https://sourceforge.net/projects/gnu-efi/)
- [OSDev Wiki](https://wiki.osdev.org/)
+
+## License
+
+This is a minimal educational example. Feel free to use and modify as needed.
diff --git a/boot_info.h b/boot_info.h
index 219bccb..0f6e64f 100644
--- a/boot_info.h
+++ b/boot_info.h
@@ -1,22 +1,45 @@
+/*
+ * boot_info.h – Shared interface between the UEFI loader and the kernel.
+ *
+ * The BootInfo struct is populated by the loader (main.c) and passed to
+ * the kernel entry point (kmain). It provides function pointers that
+ * abstract UEFI Boot/Runtime Services so the kernel can use console I/O,
+ * memory allocation, and system control without linking against GNU-EFI.
+ */
+
#ifndef BOOT_INFO_H
#define BOOT_INFO_H
#include
+/* Printf-style print function (backed by UEFI ConOut). */
typedef UINTN (*KernelPrintFn)(const CHAR16 *Format, ...);
+/*
+ * BootInfo – everything the kernel needs from the UEFI environment.
+ *
+ * All function pointers are optional: the kernel must NULL-check before
+ * calling. If a service is unavailable the corresponding field is NULL.
+ */
typedef struct {
- EFI_SYSTEM_TABLE *SystemTable;
- KernelPrintFn print;
- EFI_STATUS (*clear_screen)(void);
- EFI_STATUS (*set_attribute)(UINTN Attribute);
- EFI_STATUS (*read_key)(EFI_INPUT_KEY *Key);
- EFI_STATUS (*try_read_key)(EFI_INPUT_KEY *Key); /* non-blocking */
- void (*shutdown)(void);
+ EFI_SYSTEM_TABLE *SystemTable; /* UEFI System Table */
+
+ /* Console I/O */
+ KernelPrintFn print; /* formatted text output */
+ EFI_STATUS (*clear_screen)(void); /* clear the console */
+ EFI_STATUS (*set_attribute)(UINTN Attribute); /* set text colour/attr */
+ EFI_STATUS (*read_key)(EFI_INPUT_KEY *Key); /* blocking key read */
+ EFI_STATUS (*try_read_key)(EFI_INPUT_KEY *Key); /* non-blocking key read */
+
+ /* System control */
+ void (*shutdown)(void); /* power-off the machine */
+
+ /* Physical memory */
EFI_STATUS (*alloc_pages)(UINTN pages, EFI_PHYSICAL_ADDRESS *addr);
EFI_STATUS (*free_pages)(EFI_PHYSICAL_ADDRESS addr, UINTN pages);
} BootInfo;
+/* Kernel entry point signature (called by the UEFI loader). */
typedef void (*KernelEntryFn)(BootInfo *Boot);
-#endif
+#endif /* BOOT_INFO_H */
diff --git a/commands.c b/commands.c
index 8ce2ebd..e3f762f 100644
--- a/commands.c
+++ b/commands.c
@@ -1,9 +1,23 @@
+/*
+ * commands.c – Shell command registry and handler implementations.
+ *
+ * Each command is a { name, description, usage, handler } entry in the
+ * static `commands[]` table. execute_command() splits user input into
+ * command + arguments and dispatches to the matching handler.
+ *
+ * To add a new command:
+ * 1. Write a static handler cmd_foo(BootInfo *Boot, CHAR16 *Args)
+ * 2. Add a forward declaration above the table
+ * 3. Append an entry to commands[] (before the sentinel)
+ */
+
#include
#include "commands.h"
#include "string_utils.h"
#include "memory.h"
#include "task.h"
+/* Null-safe print helper used throughout the kernel. */
#define SAFE_PRINT(Boot, ...) \
do { \
if ((Boot) != NULL && (Boot)->print != NULL) { \
@@ -11,7 +25,10 @@
} \
} while (0)
-// Forward declarations
+/* ================================================================
+ * Forward declarations
+ * ================================================================ */
+
static void cmd_shutdown(BootInfo *Boot, CHAR16 *Args);
static void cmd_help(BootInfo *Boot, CHAR16 *Args);
static void cmd_man(BootInfo *Boot, CHAR16 *Args);
@@ -23,7 +40,10 @@ static void cmd_spawn(BootInfo *Boot, CHAR16 *Args);
static void cmd_memtest(BootInfo *Boot, CHAR16 *Args);
static void cmd_tasktest(BootInfo *Boot, CHAR16 *Args);
-// Command registry
+/* ================================================================
+ * Command registry
+ * ================================================================ */
+
static Command commands[] = {
{
L"shutdown",
@@ -85,9 +105,17 @@ static Command commands[] = {
L"Usage: tasktest\n\r Spawns several concurrent tasks that run cooperatively\n\r and report their progress, demonstrating context switching.",
cmd_tasktest
},
- {NULL, NULL, NULL, NULL} // Sentinel
+ {NULL, NULL, NULL, NULL} /* sentinel */
};
+/* ================================================================
+ * System control
+ * ================================================================ */
+
+/*
+ * Try Boot->shutdown first, then fall back to RuntimeServices, then
+ * print an error if neither is available.
+ */
static void request_shutdown(BootInfo *Boot)
{
if (Boot == NULL) {
@@ -110,16 +138,20 @@ static void request_shutdown(BootInfo *Boot)
SAFE_PRINT(Boot, L"Shutdown service unavailable.\n\r");
}
+/* ================================================================
+ * Built-in command handlers
+ * ================================================================ */
+
static void cmd_shutdown(BootInfo *Boot, CHAR16 *Args)
{
- (void)Args; // Unused
+ (void)Args;
SAFE_PRINT(Boot, L"Shutting down...\n\r");
request_shutdown(Boot);
}
static void cmd_help(BootInfo *Boot, CHAR16 *Args)
{
- (void)Args; // Unused
+ (void)Args;
show_help(Boot);
}
@@ -154,7 +186,7 @@ static void cmd_man(BootInfo *Boot, CHAR16 *Args)
static void cmd_clear(BootInfo *Boot, CHAR16 *Args)
{
EFI_STATUS Status;
- (void)Args; // Unused
+ (void)Args;
if (Boot != NULL && Boot->clear_screen != NULL) {
Status = Boot->clear_screen();
@@ -168,8 +200,8 @@ static void cmd_clear(BootInfo *Boot, CHAR16 *Args)
static void cmd_about(BootInfo *Boot, CHAR16 *Args)
{
- (void)Args; // Unused
-
+ (void)Args;
+
SAFE_PRINT(Boot, L"\n\r");
SAFE_PRINT(Boot, L"================================================\n\r");
SAFE_PRINT(Boot, L" Simple UEFI Operating System\n\r");
@@ -378,6 +410,11 @@ static void cmd_tasktest(BootInfo *Boot, CHAR16 *Args)
SAFE_PRINT(Boot, L"Task scheduler test completed.\n\r\n\r");
}
+/* ================================================================
+ * Public API
+ * ================================================================ */
+
+/* Print a formatted table of all registered commands. */
void show_help(BootInfo *Boot)
{
UINTN i = 0;
@@ -389,7 +426,7 @@ void show_help(BootInfo *Boot)
SAFE_PRINT(Boot, L"Available commands:\n\r");
SAFE_PRINT(Boot, L"\n\r");
- // Find longest command name for formatting
+ /* Find the longest command name for column alignment */
for (i = 0; commands[i].name != NULL; i++) {
name_len = 0;
while (commands[i].name[name_len] != L'\0') {
@@ -403,7 +440,7 @@ void show_help(BootInfo *Boot)
for (i = 0; commands[i].name != NULL; i++) {
SAFE_PRINT(Boot, L" %s", commands[i].name);
- // Add padding
+ /* Pad to align descriptions */
name_len = 0;
while (commands[i].name[name_len] != L'\0') {
name_len++;
@@ -420,6 +457,10 @@ void show_help(BootInfo *Boot)
SAFE_PRINT(Boot, L"\n\r");
}
+/*
+ * Parse a line of user input into command + arguments and dispatch
+ * to the matching handler. Unknown commands print an error.
+ */
void execute_command(BootInfo *Boot, CHAR16 *Input)
{
CHAR16 *cmd_start = NULL;
@@ -436,27 +477,27 @@ void execute_command(BootInfo *Boot, CHAR16 *Input)
return;
}
- // Split command and arguments
+ /* Split input into command and argument strings */
cmd_start = Input;
args_start = Input;
-
- // Find first space
+
+ /* Advance past the command keyword */
while (*args_start != L'\0' && !is_space16(*args_start)) {
args_start++;
}
- // If we found a space, null-terminate command and advance to args
+ /* NUL-terminate the command and skip leading whitespace in args */
if (*args_start != L'\0') {
*args_start = L'\0';
args_start++;
- // Skip leading spaces in args
+ /* skip leading whitespace in args */
while (*args_start != L'\0' && is_space16(*args_start)) {
args_start++;
}
}
- // Search for command
+ /* Look up and dispatch the command */
for (i = 0; commands[i].name != NULL; i++) {
if (ascii_streq_ci(cmd_start, commands[i].name)) {
commands[i].handler(Boot, args_start);
@@ -464,7 +505,7 @@ void execute_command(BootInfo *Boot, CHAR16 *Input)
}
}
- // Command not found
+ /* Command not found */
SAFE_PRINT(Boot, L"Unknown command: %s\n\r", cmd_start);
SAFE_PRINT(Boot, L"Type 'help' for a list of available commands.\n\r");
}
diff --git a/commands.h b/commands.h
index 5e35f97..77135ac 100644
--- a/commands.h
+++ b/commands.h
@@ -1,18 +1,35 @@
+/*
+ * commands.h – Interactive shell command interface.
+ *
+ * Defines the Command structure used by the command registry and the
+ * public API for command execution and help display.
+ */
+
#ifndef COMMANDS_H
#define COMMANDS_H
#include "boot_info.h"
+/* Handler function signature: receives BootInfo and any argument text. */
typedef void (*CommandHandlerFn)(BootInfo *Boot, CHAR16 *Args);
+/*
+ * Command – describes a single shell command.
+ *
+ * An array of these (terminated by a sentinel with name == NULL) forms
+ * the command registry in commands.c.
+ */
typedef struct {
- const CHAR16 *name;
- const CHAR16 *description;
- const CHAR16 *usage;
- CommandHandlerFn handler;
+ const CHAR16 *name; /* command keyword (e.g. L"help") */
+ const CHAR16 *description; /* one-line summary for `help` */
+ const CHAR16 *usage; /* detailed text shown by `man` */
+ CommandHandlerFn handler; /* function that executes the cmd */
} Command;
+/* Parse and dispatch a line of user input. */
void execute_command(BootInfo *Boot, CHAR16 *Input);
+
+/* Print a formatted list of all registered commands. */
void show_help(BootInfo *Boot);
-#endif
+#endif /* COMMANDS_H */
diff --git a/context_switch.S b/context_switch.S
index dfafcd7..93c6539 100644
--- a/context_switch.S
+++ b/context_switch.S
@@ -1,19 +1,27 @@
/*
- * context_switch.S – cooperative context switch for x86_64
+ * context_switch.S – Cooperative context switch for x86-64.
*
* void context_switch(UINT64 *old_rsp, UINT64 new_rsp);
*
* %rdi = pointer to save current RSP (old task's PCB field)
* %rsi = RSP value to restore (new task's saved RSP)
*
- * Saves callee-saved registers and RFLAGS on the current stack,
- * stores RSP, loads the new stack, restores registers, and returns
- * into the new task's execution context.
+ * Saves callee-saved registers (rbp, rbx, r12-r15) and RFLAGS on
+ * the current stack, stores RSP into *old_rsp, loads new_rsp into
+ * RSP, restores registers, and returns into the new task's context.
+ *
+ * This is a standard cooperative switch: only callee-saved state
+ * needs preserving because the C calling convention guarantees that
+ * caller-saved registers are already handled by the compiler.
*/
.text
.global context_switch
+/* ----------------------------------------------------------------
+ * Entry: save old context, load new context, return
+ * ---------------------------------------------------------------- */
+
context_switch:
/* Save callee-saved registers and flags on the current stack */
pushq %rbp
@@ -24,10 +32,10 @@ context_switch:
pushq %r15
pushfq
- /* Save current stack pointer into *old_rsp */
+ /* Store current RSP into *old_rsp */
movq %rsp, (%rdi)
- /* Load new task's stack pointer */
+ /* Switch to the new task's stack */
movq %rsi, %rsp
/* Restore callee-saved registers and flags from the new stack */
diff --git a/idt.c b/idt.c
index 89c1168..75a7b03 100644
--- a/idt.c
+++ b/idt.c
@@ -1,28 +1,55 @@
+/*
+ * idt.c – Interrupt Descriptor Table setup and exception handling.
+ *
+ * On initialisation the kernel copies the firmware's IDT (preserving
+ * its hardware IRQ handlers for vectors 32-47), then overrides CPU
+ * exception vectors 0-31 with our own stubs from isr.S.
+ *
+ * Hardware IRQs receive an EOI and return. CPU exceptions print
+ * diagnostic information (including CR2 for page faults) and halt.
+ */
+
#include "idt.h"
-#define IDT_SIZE 256
-#define IDT_TYPE_INTERRUPT 0x8E
+/* ================================================================
+ * Constants and internal types
+ * ================================================================ */
+#define IDT_SIZE 256
+#define IDT_TYPE_INTERRUPT 0x8E /* P=1, DPL=0, 64-bit interrupt gate */
+
+/* Single 16-byte IDT entry (x86-64 long mode). */
typedef struct {
- UINT16 offset_low;
- UINT16 selector;
- UINT8 ist;
- UINT8 type_attr;
- UINT16 offset_mid;
- UINT32 offset_high;
- UINT32 zero;
+ UINT16 offset_low; /* bits 0-15 of handler address */
+ UINT16 selector; /* code segment selector */
+ UINT8 ist; /* interrupt stack table index */
+ UINT8 type_attr; /* type and attributes */
+ UINT16 offset_mid; /* bits 16-31 of handler address */
+ UINT32 offset_high; /* bits 32-63 of handler address */
+ UINT32 zero; /* reserved, must be zero */
} __attribute__((packed)) IdtEntry;
+/* IDTR register layout for the LIDT instruction. */
typedef struct {
UINT16 limit;
UINT64 base;
} __attribute__((packed)) IdtPtr;
-static IdtEntry idt[IDT_SIZE];
+/* ================================================================
+ * Module state
+ * ================================================================ */
+
+static IdtEntry idt[IDT_SIZE];
static BootInfo *gBoot = NULL;
+/* Defined in isr.S – one stub function per vector (0-255). */
extern void (*isr_stub_table[])(void);
+/* ================================================================
+ * IDT gate helpers
+ * ================================================================ */
+
+/* Install a single IDT gate pointing at `handler`. */
static void idt_set_gate(UINTN index, void (*handler)(void))
{
UINT64 addr = (UINT64)(UINTN)handler;
@@ -39,11 +66,17 @@ static void idt_set_gate(UINTN index, void (*handler)(void))
idt[index].zero = 0;
}
+/* Load a new IDT register value. */
static void lidt(const IdtPtr *idtr)
{
__asm__ __volatile__("lidt (%0)" :: "r"(idtr));
}
+/* ================================================================
+ * Exception name lookup
+ * ================================================================ */
+
+/* Return a human-readable name for CPU exception vectors 0-31. */
static const CHAR16 *exception_name(UINTN vector)
{
switch (vector) {
@@ -83,11 +116,17 @@ static const CHAR16 *exception_name(UINTN vector)
}
}
+/* ================================================================
+ * PIC and low-level helpers
+ * ================================================================ */
+
+/* Write a byte to an I/O port. */
static inline void outb(UINT16 port, UINT8 value)
{
__asm__ __volatile__("outb %0, %1" :: "a"(value), "Nd"(port));
}
+/* Send End-Of-Interrupt to the 8259 PIC(s). */
static void pic_eoi(UINTN vector)
{
if (vector >= 40) {
@@ -96,6 +135,7 @@ static void pic_eoi(UINTN vector)
outb(0x20, 0x20); /* EOI to master PIC */
}
+/* Disable interrupts and halt forever (unrecoverable fault). */
static void halt_forever(void)
{
for (;;) {
@@ -103,6 +143,10 @@ static void halt_forever(void)
}
}
+/* ================================================================
+ * ISR dispatcher (called from isr_common in isr.S)
+ * ================================================================ */
+
void isr_handler(ISRFrame *frame)
{
UINT64 cr2 = 0;
@@ -132,6 +176,10 @@ void isr_handler(ISRFrame *frame)
halt_forever();
}
+/* ================================================================
+ * IDT initialisation
+ * ================================================================ */
+
void idt_init(BootInfo *Boot)
{
IdtPtr old_idtr;
diff --git a/idt.h b/idt.h
index 4c4fd06..c5bce38 100644
--- a/idt.h
+++ b/idt.h
@@ -1,10 +1,22 @@
+/*
+ * idt.h – Interrupt Descriptor Table interface.
+ *
+ * Declares the ISR stack frame layout (pushed by isr_common in isr.S)
+ * and the IDT initialisation function.
+ */
+
#ifndef IDT_H
#define IDT_H
#include
#include "boot_info.h"
+/*
+ * ISRFrame – register state saved by isr_common before calling
+ * isr_handler(). The order must match the push sequence in isr.S.
+ */
typedef struct {
+ /* General-purpose registers (pushed by isr_common) */
UINT64 r15;
UINT64 r14;
UINT64 r13;
@@ -20,13 +32,21 @@ typedef struct {
UINT64 rcx;
UINT64 rbx;
UINT64 rax;
+
+ /* Pushed by the ISR stub macros */
UINT64 vector;
UINT64 error_code;
+
+ /* Pushed by the CPU on interrupt entry */
UINT64 rip;
UINT64 cs;
UINT64 rflags;
} ISRFrame;
+/*
+ * Install our IDT: copies firmware entries for vectors 32+,
+ * overrides vectors 0-31 with kernel exception handlers.
+ */
void idt_init(BootInfo *Boot);
-#endif
+#endif /* IDT_H */
diff --git a/isr.S b/isr.S
index 5b9b68d..ab19f69 100644
--- a/isr.S
+++ b/isr.S
@@ -1,8 +1,28 @@
+/*
+ * isr.S – Interrupt Service Routine stubs for x86-64.
+ *
+ * Provides 256 thin stub functions (isr0 – isr255) and a common body
+ * (isr_common) that saves all general-purpose registers, calls the C
+ * handler isr_handler(ISRFrame *), restores registers, and returns
+ * via IRETQ.
+ *
+ * Two macros generate the stubs:
+ * ISR_NOERR – pushes a dummy error code (0) for vectors that don't
+ * ISR_ERR – the CPU already pushed an error code
+ *
+ * A pointer table (isr_stub_table) is emitted at the end so that
+ * idt.c can look up the stub address for each vector by index.
+ */
+
.text
.global isr_handler
.global isr_stub_table
+/* ----------------------------------------------------------------
+ * Stub macros
+ * ---------------------------------------------------------------- */
+
.macro ISR_NOERR num
.global isr\num
isr\num:
@@ -18,6 +38,10 @@ isr\num:
jmp isr_common
.endm
+/* ----------------------------------------------------------------
+ * Common ISR body – saves state, calls C handler, restores state
+ * ---------------------------------------------------------------- */
+
isr_common:
cld
@@ -59,6 +83,15 @@ isr_common:
addq $16, %rsp
iretq
+/* ----------------------------------------------------------------
+ * Stub instantiation (vectors 0-255)
+ *
+ * Vectors 0-31: CPU exceptions (some push error codes)
+ * Vectors 32-47: legacy 8259 PIC hardware IRQs
+ * Vectors 48+: available for software / APIC use
+ * ---------------------------------------------------------------- */
+
+/* CPU exceptions */
ISR_NOERR 0
ISR_NOERR 1
ISR_NOERR 2
@@ -316,6 +349,11 @@ ISR_NOERR 253
ISR_NOERR 254
ISR_NOERR 255
+/* ----------------------------------------------------------------
+ * Stub pointer table – indexed by vector number (0-255)
+ * Used by idt_set_gate() in idt.c to populate the IDT.
+ * ---------------------------------------------------------------- */
+
isr_stub_table:
.quad isr0
.quad isr1
diff --git a/kernel.c b/kernel.c
index dadcbc7..d8bf497 100644
--- a/kernel.c
+++ b/kernel.c
@@ -1,3 +1,15 @@
+/*
+ * kernel.c – Kernel entry point and interactive shell loop.
+ *
+ * kmain() is called by the UEFI loader (main.c) after the ELF kernel
+ * has been mapped into memory. It initialises subsystems (IDT, memory,
+ * tasks), prints a welcome banner, and enters an interactive read-
+ * eval-print loop that dispatches typed commands via commands.c.
+ *
+ * While waiting for keyboard input, the shell yields to the cooperative
+ * scheduler so that background tasks can make progress.
+ */
+
#include
#include "boot_info.h"
@@ -6,6 +18,7 @@
#include "memory.h"
#include "task.h"
+/* Null-safe print helper used throughout the kernel. */
#define SAFE_PRINT(Boot, ...) \
do { \
if ((Boot) != NULL && (Boot)->print != NULL) { \
@@ -13,6 +26,10 @@
} \
} while (0)
+/* ================================================================
+ * Kernel entry point
+ * ================================================================ */
+
void kmain(BootInfo *Boot)
{
EFI_INPUT_KEY Key;
@@ -37,11 +54,12 @@ void kmain(BootInfo *Boot)
}
}
+ /* ---- Subsystem initialisation ---- */
idt_init(Boot);
memory_init(Boot);
task_init(Boot);
- SAFE_PRINT(Boot, L"================================================\n\r");
+ /* ---- Welcome banner ---- */
SAFE_PRINT(Boot, L" Welcome to Simple UEFI Operating System!\n\r");
SAFE_PRINT(Boot, L"================================================\n\r");
SAFE_PRINT(Boot, L"\n\r");
@@ -69,7 +87,7 @@ void kmain(BootInfo *Boot)
SAFE_PRINT(Boot, L"\n\r");
SAFE_PRINT(Boot, L"Type 'help' for a list of commands.\n\r\n\r");
- // Simple line buffer
+ /* ---- Interactive shell loop ---- */
CHAR16 line[128];
UINTN len = 0;
@@ -100,21 +118,22 @@ void kmain(BootInfo *Boot)
read_errors = 0;
if (Key.UnicodeChar == L'\r' || Key.UnicodeChar == L'\n') {
- // Enter pressed: terminate string and handle
+ /* Enter pressed: execute the buffered command */
line[len] = L'\0';
SAFE_PRINT(Boot, L"\n\r");
execute_command(Boot, line);
- // Reset for next command
+ /* Reset for next command */
len = 0;
SAFE_PRINT(Boot, L"-> ");
} else if (Key.ScanCode == 0x08 || Key.UnicodeChar == L'\b' || Key.UnicodeChar == 0x7F) {
- // Backspace (ScanCode 0x08 is backspace in UEFI)
+ /* Backspace */
if (len > 0) {
len--;
SAFE_PRINT(Boot, L"\b \b");
}
} else if (Key.UnicodeChar >= 32 && Key.UnicodeChar < 127) {
+ /* Printable ASCII */
if (len < (sizeof(line) / sizeof(line[0]) - 1)) {
line[len++] = Key.UnicodeChar;
SAFE_PRINT(Boot, L"%c", Key.UnicodeChar);
diff --git a/kernel.ld b/kernel.ld
index 66829c4..506a4b1 100644
--- a/kernel.ld
+++ b/kernel.ld
@@ -1,25 +1,33 @@
+/*
+ * kernel.ld – Linker script for the ELF64 kernel.
+ *
+ * The kernel is loaded at physical address 0x100000 (1 MB) by the
+ * UEFI loader. Each section is page-aligned (4 KB) so that page-
+ * level permissions can be applied later if desired.
+ */
+
ENTRY(kmain)
SECTIONS
{
- . = 0x100000;
+ . = 0x100000; /* load address: 1 MB */
- .text : ALIGN(0x1000)
+ .text : ALIGN(0x1000) /* executable code */
{
*(.text*)
}
- .rodata : ALIGN(0x1000)
+ .rodata : ALIGN(0x1000) /* read-only data / string literals */
{
*(.rodata*)
}
- .data : ALIGN(0x1000)
+ .data : ALIGN(0x1000) /* initialised read-write data */
{
*(.data*)
}
- .bss : ALIGN(0x1000)
+ .bss : ALIGN(0x1000) /* zero-initialised data */
{
*(COMMON)
*(.bss*)
diff --git a/main.c b/main.c
index e67ef89..e40cf16 100644
--- a/main.c
+++ b/main.c
@@ -1,45 +1,70 @@
+/*
+ * main.c – UEFI boot loader.
+ *
+ * This is the first code that runs. efi_main() is called by UEFI
+ * firmware via the PE32+ entry point. It:
+ * 1. Reads kernel.elf from the EFI System Partition
+ * 2. Parses the ELF64 headers and maps PT_LOAD segments into memory
+ * 3. Populates a BootInfo struct with UEFI service wrappers
+ * 4. Jumps to the kernel entry point (kmain)
+ */
+
#include
#include
#include "boot_info.h"
-#define ELF_MAGIC 0x464c457f
-#define PT_LOAD 1
+/* ================================================================
+ * ELF64 constants and types
+ * ================================================================ */
+#define ELF_MAGIC 0x464c457f /* "\x7fELF" as a little-endian UINT32 */
+#define PT_LOAD 1 /* loadable program segment */
+
+/* Minimal ELF64 file header (enough to locate program headers). */
typedef struct {
- UINT32 e_magic;
- UINT8 e_class;
- UINT8 e_data;
- UINT8 e_version;
- UINT8 e_osabi;
- UINT8 e_abiversion;
- UINT8 e_pad[7];
- UINT16 e_type;
- UINT16 e_machine;
+ UINT32 e_magic; /* must be ELF_MAGIC */
+ UINT8 e_class; /* 2 = 64-bit */
+ UINT8 e_data; /* 1 = little-endian */
+ UINT8 e_version;
+ UINT8 e_osabi;
+ UINT8 e_abiversion;
+ UINT8 e_pad[7];
+ UINT16 e_type; /* ET_EXEC = 2 */
+ UINT16 e_machine; /* EM_X86_64 = 62 */
UINT32 e_version2;
- UINT64 e_entry;
- UINT64 e_phoff;
+ UINT64 e_entry; /* virtual address of entry point */
+ UINT64 e_phoff; /* file offset to program header table */
UINT64 e_shoff;
UINT32 e_flags;
UINT16 e_ehsize;
- UINT16 e_phentsize;
- UINT16 e_phnum;
+ UINT16 e_phentsize; /* size of one program header entry */
+ UINT16 e_phnum; /* number of program header entries */
UINT16 e_shentsize;
UINT16 e_shnum;
UINT16 e_shstrndx;
} Elf64_Ehdr;
+/* ELF64 program header (describes one segment). */
typedef struct {
- UINT32 p_type;
- UINT32 p_flags;
- UINT64 p_offset;
- UINT64 p_vaddr;
- UINT64 p_paddr;
- UINT64 p_filesz;
- UINT64 p_memsz;
- UINT64 p_align;
+ UINT32 p_type; /* segment type (PT_LOAD, etc.) */
+ UINT32 p_flags; /* segment flags (R/W/X) */
+ UINT64 p_offset; /* file offset of segment data */
+ UINT64 p_vaddr; /* virtual address to map at */
+ UINT64 p_paddr; /* physical address (usually == vaddr) */
+ UINT64 p_filesz; /* bytes of segment data in file */
+ UINT64 p_memsz; /* total bytes in memory (>= filesz) */
+ UINT64 p_align; /* alignment requirement */
} Elf64_Phdr;
+/* ================================================================
+ * EFI file-system helpers
+ * ================================================================ */
+
+/*
+ * Open the root directory of the volume from which this loader was
+ * loaded (i.e. the EFI System Partition).
+ */
static EFI_STATUS open_root_volume(EFI_HANDLE ImageHandle, EFI_FILE_PROTOCOL **Root)
{
EFI_STATUS Status;
@@ -61,6 +86,10 @@ static EFI_STATUS open_root_volume(EFI_HANDLE ImageHandle, EFI_FILE_PROTOCOL **R
return uefi_call_wrapper(FileSystem->OpenVolume, 2, FileSystem, Root);
}
+/*
+ * Read an entire file from the EFI System Partition into a pool buffer.
+ * Caller must free *Buffer with BS->FreePool() when done.
+ */
static EFI_STATUS read_file_to_buffer(EFI_HANDLE ImageHandle, CHAR16 *Path,
VOID **Buffer, UINTN *Size)
{
@@ -127,6 +156,15 @@ static EFI_STATUS read_file_to_buffer(EFI_HANDLE ImageHandle, CHAR16 *Path,
return EFI_SUCCESS;
}
+/* ================================================================
+ * ELF64 kernel loader
+ * ================================================================ */
+
+/*
+ * Parse an ELF64 image in memory and load all PT_LOAD segments to
+ * their specified virtual (== physical) addresses. On success,
+ * *EntryOut is set to the kernel entry point.
+ */
static EFI_STATUS load_elf_kernel(VOID *Image, UINTN Size, UINT64 *EntryOut)
{
Elf64_Ehdr *Ehdr = (Elf64_Ehdr *)Image;
@@ -185,6 +223,10 @@ static EFI_STATUS load_elf_kernel(VOID *Image, UINTN Size, UINT64 *EntryOut)
return EFI_SUCCESS;
}
+/* ================================================================
+ * UEFI service wrappers (passed to the kernel via BootInfo)
+ * ================================================================ */
+
static EFI_STATUS loader_clear_screen(void)
{
return uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
@@ -223,6 +265,10 @@ static EFI_STATUS loader_free_pages(EFI_PHYSICAL_ADDRESS addr, UINTN pages)
return uefi_call_wrapper(BS->FreePages, 2, addr, pages);
}
+/* ================================================================
+ * UEFI application entry point
+ * ================================================================ */
+
EFI_STATUS
EFIAPI
efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
@@ -233,10 +279,10 @@ efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
UINT64 KernelEntry = 0;
BootInfo Boot;
KernelEntryFn EntryFn = NULL;
-
- // Initialize the GNU-EFI library
+
+ /* Initialise the GNU-EFI library */
InitializeLib(ImageHandle, SystemTable);
-
+
Print(L"Loading kernel...\n\r");
Status = read_file_to_buffer(ImageHandle, L"\\kernel.elf", &KernelImage, &KernelSize);
@@ -251,16 +297,18 @@ efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
return Status;
}
- Boot.SystemTable = ST;
- Boot.print = Print;
+ /* Populate the BootInfo struct with UEFI service wrappers */
+ Boot.SystemTable = ST;
+ Boot.print = Print;
Boot.clear_screen = loader_clear_screen;
Boot.set_attribute = loader_set_attribute;
- Boot.read_key = loader_read_key;
+ Boot.read_key = loader_read_key;
Boot.try_read_key = loader_try_read_key;
- Boot.shutdown = loader_shutdown;
- Boot.alloc_pages = loader_alloc_pages;
- Boot.free_pages = loader_free_pages;
+ Boot.shutdown = loader_shutdown;
+ Boot.alloc_pages = loader_alloc_pages;
+ Boot.free_pages = loader_free_pages;
+ /* Jump to the kernel – this should not return */
EntryFn = (KernelEntryFn)(UINTN)KernelEntry;
EntryFn(&Boot);
diff --git a/memory.c b/memory.c
index 658df7e..26b54d7 100644
--- a/memory.c
+++ b/memory.c
@@ -1,5 +1,18 @@
+/*
+ * memory.c – Kernel memory management.
+ *
+ * Implements three layers:
+ * PMM – bitmap-based physical page-frame allocator backed by a
+ * 16 MB pool obtained from UEFI at boot.
+ * Paging – walks and creates 4-level x86-64 page tables; supports
+ * map, unmap, and virtual-to-physical translation.
+ * Heap – first-fit free-list allocator with block splitting and
+ * bidirectional coalescing; 16-byte aligned.
+ */
+
#include "memory.h"
+/* Null-safe print helper used throughout the kernel. */
#define SAFE_PRINT(Boot, ...) \
do { \
if ((Boot) != NULL && (Boot)->print != NULL) { \
@@ -17,21 +30,36 @@ static UINTN pmm_free_count = 0;
static UINT8 pmm_bitmap[PMM_POOL_PAGES / 8];
static BOOLEAN pmm_ready = FALSE;
+/* ================================================================
+ * PMM – bitmap helpers
+ * ================================================================ */
+
+/* Mark page `idx` as allocated. */
static void pmm_set_bit(UINTN idx)
{
pmm_bitmap[idx / 8] |= (UINT8)(1U << (idx % 8));
}
+/* Mark page `idx` as free. */
static void pmm_clear_bit(UINTN idx)
{
pmm_bitmap[idx / 8] &= (UINT8)~(1U << (idx % 8));
}
+/* Return TRUE if page `idx` is currently allocated. */
static BOOLEAN pmm_test_bit(UINTN idx)
{
return (pmm_bitmap[idx / 8] & (1U << (idx % 8))) != 0;
}
+/* ----------------------------------------------------------------
+ * PMM – public interface
+ * ---------------------------------------------------------------- */
+
+/*
+ * Initialise the PMM: request PMM_POOL_PAGES from UEFI and set up
+ * the bitmap with all pages marked free.
+ */
void pmm_init(BootInfo *Boot)
{
EFI_STATUS Status;
@@ -66,6 +94,7 @@ void pmm_init(BootInfo *Boot)
pmm_pool_base);
}
+/* Allocate a single 4 KB page. Returns physical address or 0. */
UINT64 pmm_alloc_page(void)
{
UINTN i;
@@ -85,6 +114,7 @@ UINT64 pmm_alloc_page(void)
return 0;
}
+/* Free a single page previously returned by pmm_alloc_page(). */
void pmm_free_page(UINT64 phys_addr)
{
UINTN idx;
@@ -100,6 +130,7 @@ void pmm_free_page(UINT64 phys_addr)
pmm_free_count++;
}
+/* Allocate `count` physically contiguous pages (first-fit). */
UINT64 pmm_alloc_pages(UINTN count)
{
UINTN i, j;
@@ -131,6 +162,7 @@ UINT64 pmm_alloc_pages(UINTN count)
return 0;
}
+/* Free `count` contiguous pages starting at phys_addr. */
void pmm_free_pages(UINT64 phys_addr, UINTN count)
{
UINTN i;
@@ -146,6 +178,11 @@ UINTN pmm_get_total_pages(void) { return pmm_total_pages; }
* Paging – manipulate the live 4-level x86-64 page tables
* ================================================================ */
+/* ================================================================
+ * Paging – low-level helpers
+ * ================================================================ */
+
+/* Read the CR3 register (physical address of PML4). */
static UINT64 read_cr3(void)
{
UINT64 cr3;
@@ -153,11 +190,13 @@ static UINT64 read_cr3(void)
return cr3;
}
+/* Invalidate the TLB entry for virtual address `addr`. */
static void invlpg(UINT64 addr)
{
__asm__ __volatile__("invlpg (%0)" :: "r"(addr) : "memory");
}
+/* Return a pointer to the current PML4 table. */
static UINT64 *get_pml4(void)
{
return (UINT64 *)(UINTN)(read_cr3() & PTE_ADDR_MASK);
@@ -197,12 +236,22 @@ static UINT64 *paging_walk_level(UINT64 *table, UINTN index, BOOLEAN create)
return next;
}
+/* ----------------------------------------------------------------
+ * Paging – public interface
+ * ---------------------------------------------------------------- */
+
+/* Log the current CR3 value (identity-mapped by UEFI). */
void paging_init(BootInfo *Boot)
{
SAFE_PRINT(Boot, L" Page: CR3 = 0x%lx (UEFI identity-mapped)\n\r",
read_cr3());
}
+/*
+ * Map a single 4 KB page: virt → phys with the given flags.
+ * Returns TRUE on success, FALSE if a huge page is in the way or
+ * page-table allocation failed.
+ */
BOOLEAN paging_map_page(UINT64 virt, UINT64 phys, UINT64 flags)
{
UINT64 *pml4, *pdpt, *pd, *pt;
@@ -235,6 +284,7 @@ BOOLEAN paging_map_page(UINT64 virt, UINT64 phys, UINT64 flags)
return TRUE;
}
+/* Remove the mapping for a single 4 KB page and flush the TLB. */
void paging_unmap_page(UINT64 virt)
{
UINT64 *pml4, *pdpt, *pd, *pt;
@@ -262,6 +312,10 @@ void paging_unmap_page(UINT64 virt)
invlpg(virt);
}
+/*
+ * Translate a virtual address to its physical counterpart.
+ * Handles 4 KB, 2 MB, and 1 GB page sizes. Returns 0 if unmapped.
+ */
UINT64 paging_get_phys(UINT64 virt)
{
UINT64 *pml4, *pdpt, *pd, *pt;
@@ -302,11 +356,16 @@ UINT64 paging_get_phys(UINT64 virt)
static HeapBlock *heap_start = NULL;
static BOOLEAN heap_ready = FALSE;
+/* Round `val` up to the next multiple of `align`. */
static UINTN align_up(UINTN val, UINTN align)
{
return (val + align - 1) & ~(align - 1);
}
+/*
+ * Initialise the heap: allocate HEAP_INITIAL_PAGES from the PMM
+ * and set up a single free block spanning the entire region.
+ */
void heap_init(BootInfo *Boot)
{
UINT64 phys;
@@ -333,6 +392,11 @@ void heap_init(BootInfo *Boot)
heap_size / 1024, phys);
}
+/*
+ * Allocate `size` bytes from the heap (first-fit).
+ * The returned pointer is aligned to HEAP_ALIGN. Returns NULL on
+ * failure or heap corruption.
+ */
void *kmalloc(UINTN size)
{
HeapBlock *block, *split;
@@ -377,6 +441,10 @@ void *kmalloc(UINTN size)
return NULL; /* out of heap memory */
}
+/*
+ * Free a previously kmalloc'd pointer. Coalesces adjacent free
+ * blocks to reduce fragmentation.
+ */
void kfree(void *ptr)
{
HeapBlock *block;
@@ -416,6 +484,7 @@ void kfree(void *ptr)
}
}
+/* Gather aggregate heap statistics. */
void heap_get_stats(UINTN *total, UINTN *used, UINTN *free_mem,
UINTN *num_blocks)
{
@@ -441,6 +510,7 @@ void heap_get_stats(UINTN *total, UINTN *used, UINTN *free_mem,
* Top-level helpers
* ================================================================ */
+/* Initialise all memory subsystems in order. */
void memory_init(BootInfo *Boot)
{
SAFE_PRINT(Boot, L"Initializing memory management...\n\r");
@@ -450,6 +520,7 @@ void memory_init(BootInfo *Boot)
SAFE_PRINT(Boot, L"Memory management ready.\n\r\n\r");
}
+/* Print a summary of PMM, heap, and paging state to the console. */
void memory_print_stats(BootInfo *Boot)
{
UINTN h_total, h_used, h_free, h_blocks;
diff --git a/memory.h b/memory.h
index 1453014..534563f 100644
--- a/memory.h
+++ b/memory.h
@@ -1,41 +1,51 @@
+/*
+ * memory.h – Memory management subsystem declarations.
+ *
+ * Three layers:
+ * PMM – bitmap-based physical page-frame allocator (16 MB pool)
+ * Paging – walks / creates 4-level x86-64 page tables
+ * Heap – first-fit free-list allocator with block coalescing
+ */
+
#ifndef MEMORY_H
#define MEMORY_H
#include
#include "boot_info.h"
-/* Page size constants */
+/* ================================================================
+ * Page-level constants
+ * ================================================================ */
+
#define PAGE_SIZE 4096
#define PAGE_SHIFT 12
#define PAGE_MASK (~(UINT64)(PAGE_SIZE - 1))
-/* Page table entry flags */
-#define PTE_PRESENT (1ULL << 0)
-#define PTE_WRITABLE (1ULL << 1)
-#define PTE_USER (1ULL << 2)
+/* ================================================================
+ * Page-table entry flags (x86-64 4-level paging)
+ * ================================================================ */
+
+#define PTE_PRESENT (1ULL << 0)
+#define PTE_WRITABLE (1ULL << 1)
+#define PTE_USER (1ULL << 2)
#define PTE_WRITETHROUGH (1ULL << 3)
-#define PTE_NOCACHE (1ULL << 4)
-#define PTE_ACCESSED (1ULL << 5)
-#define PTE_DIRTY (1ULL << 6)
-#define PTE_HUGE (1ULL << 7)
-#define PTE_GLOBAL (1ULL << 8)
-#define PTE_NX (1ULL << 63)
+#define PTE_NOCACHE (1ULL << 4)
+#define PTE_ACCESSED (1ULL << 5)
+#define PTE_DIRTY (1ULL << 6)
+#define PTE_HUGE (1ULL << 7) /* 2 MB or 1 GB page */
+#define PTE_GLOBAL (1ULL << 8)
+#define PTE_NX (1ULL << 63) /* No-Execute */
-#define PTE_ADDR_MASK 0x000FFFFFFFFFF000ULL
+#define PTE_ADDR_MASK 0x000FFFFFFFFFF000ULL
-/* PMM pool: 16 MB managed by the kernel */
-#define PMM_POOL_SIZE (16ULL * 1024 * 1024)
+/* ================================================================
+ * Physical Memory Manager (PMM)
+ * ================================================================ */
+
+/* Size of the PMM-managed pool (allocated from UEFI at boot). */
+#define PMM_POOL_SIZE (16ULL * 1024 * 1024) /* 16 MB */
#define PMM_POOL_PAGES (PMM_POOL_SIZE / PAGE_SIZE)
-/* Heap constants */
-#define HEAP_INITIAL_PAGES 64 /* 256 KB initial heap */
-#define HEAP_BLOCK_MAGIC 0xDEADBEEFU
-#define HEAP_BLOCK_FREE 0
-#define HEAP_BLOCK_USED 1
-#define HEAP_ALIGN 16
-
-/* -------- Physical Memory Manager -------- */
-
void pmm_init(BootInfo *Boot);
UINT64 pmm_alloc_page(void);
void pmm_free_page(UINT64 phys_addr);
@@ -44,19 +54,33 @@ void pmm_free_pages(UINT64 phys_addr, UINTN count);
UINTN pmm_get_free_pages(void);
UINTN pmm_get_total_pages(void);
-/* -------- Paging -------- */
+/* ================================================================
+ * Paging
+ * ================================================================ */
void paging_init(BootInfo *Boot);
BOOLEAN paging_map_page(UINT64 virt, UINT64 phys, UINT64 flags);
void paging_unmap_page(UINT64 virt);
UINT64 paging_get_phys(UINT64 virt);
-/* -------- Heap Allocator -------- */
+/* ================================================================
+ * Heap allocator
+ * ================================================================ */
+#define HEAP_INITIAL_PAGES 64 /* 256 KB initial heap */
+#define HEAP_BLOCK_MAGIC 0xDEADBEEFU /* corruption-detection canary */
+#define HEAP_BLOCK_FREE 0
+#define HEAP_BLOCK_USED 1
+#define HEAP_ALIGN 16 /* minimum allocation alignment */
+
+/*
+ * HeapBlock – header placed immediately before each allocation.
+ * Blocks form a doubly-linked free-list.
+ */
typedef struct HeapBlock {
- UINT32 magic;
- UINT32 state;
- UINTN size; /* usable bytes (excludes header) */
+ UINT32 magic; /* must be HEAP_BLOCK_MAGIC */
+ UINT32 state; /* HEAP_BLOCK_FREE / _USED */
+ UINTN size; /* usable bytes (excludes hdr) */
struct HeapBlock *next;
struct HeapBlock *prev;
} HeapBlock;
@@ -67,9 +91,14 @@ void kfree(void *ptr);
void heap_get_stats(UINTN *total, UINTN *used, UINTN *free_mem,
UINTN *num_blocks);
-/* -------- Top-level helpers -------- */
+/* ================================================================
+ * Top-level helpers
+ * ================================================================ */
+/* Initialise all memory subsystems (PMM → paging → heap). */
void memory_init(BootInfo *Boot);
+
+/* Print PMM, heap, and paging statistics to the console. */
void memory_print_stats(BootInfo *Boot);
-#endif
+#endif /* MEMORY_H */
diff --git a/string_utils.c b/string_utils.c
index 377a0a2..2ed3b63 100644
--- a/string_utils.c
+++ b/string_utils.c
@@ -1,5 +1,16 @@
+/*
+ * string_utils.c – Wide-character (CHAR16) string helpers.
+ *
+ * Provides basic string operations used throughout the kernel,
+ * particularly by the command parser in commands.c.
+ */
+
#include "string_utils.h"
+/* ----------------------------------------------------------------
+ * Character classification / conversion
+ * ---------------------------------------------------------------- */
+
BOOLEAN is_space16(CHAR16 Ch)
{
return (Ch == L' ' || Ch == L'\t');
@@ -13,6 +24,10 @@ CHAR16 ascii_lower16(CHAR16 Ch)
return Ch;
}
+/* ----------------------------------------------------------------
+ * String comparison
+ * ---------------------------------------------------------------- */
+
BOOLEAN ascii_streq_ci(const CHAR16 *A, const CHAR16 *B)
{
if (A == NULL || B == NULL) {
@@ -30,6 +45,10 @@ BOOLEAN ascii_streq_ci(const CHAR16 *A, const CHAR16 *B)
return (*A == L'\0' && *B == L'\0');
}
+/* ----------------------------------------------------------------
+ * In-place trimming
+ * ---------------------------------------------------------------- */
+
void trim_spaces_inplace(CHAR16 *Cmd)
{
UINTN start = 0;
@@ -40,19 +59,23 @@ void trim_spaces_inplace(CHAR16 *Cmd)
return;
}
+ /* Find first non-space character */
while (Cmd[start] != L'\0' && is_space16(Cmd[start])) {
start++;
}
+ /* Find end of string */
end = start;
while (Cmd[end] != L'\0') {
end++;
}
+ /* Trim trailing spaces */
while (end > start && is_space16(Cmd[end - 1])) {
end--;
}
+ /* Shift trimmed content to the beginning */
while (i < (end - start)) {
Cmd[i] = Cmd[start + i];
i++;
diff --git a/string_utils.h b/string_utils.h
index bb89c3d..21481b5 100644
--- a/string_utils.h
+++ b/string_utils.h
@@ -1,11 +1,25 @@
+/*
+ * string_utils.h – Wide-character (CHAR16) string helpers.
+ *
+ * Utility functions for the kernel's interactive shell: whitespace
+ * detection, case-insensitive comparison, and in-place trimming.
+ */
+
#ifndef STRING_UTILS_H
#define STRING_UTILS_H
#include
+/* Return TRUE if Ch is a space or horizontal tab. */
BOOLEAN is_space16(CHAR16 Ch);
+
+/* Return the ASCII lower-case equivalent of Ch (A-Z only). */
CHAR16 ascii_lower16(CHAR16 Ch);
+
+/* Case-insensitive equality test for two NUL-terminated CHAR16 strings. */
BOOLEAN ascii_streq_ci(const CHAR16 *A, const CHAR16 *B);
+
+/* Strip leading and trailing whitespace from Cmd in place. */
void trim_spaces_inplace(CHAR16 *Cmd);
-#endif
+#endif /* STRING_UTILS_H */
diff --git a/task.c b/task.c
index 4b74231..f08e239 100644
--- a/task.c
+++ b/task.c
@@ -1,6 +1,21 @@
+/*
+ * task.c – Cooperative multitasking: PCB pool, scheduler, yield/exit.
+ *
+ * Task 0 is the kernel/shell thread (uses the boot stack).
+ * Additional tasks are created with task_create(), which allocates a
+ * stack from the PMM and sets up a fake context-switch frame so that
+ * context_switch() can "return" into a trampoline that calls the real
+ * entry function.
+ *
+ * Scheduling is purely cooperative: tasks must call task_yield() to
+ * let others run. The scheduler uses round-robin selection among
+ * READY tasks.
+ */
+
#include "task.h"
#include "memory.h"
+/* Null-safe print helper used throughout the kernel. */
#define SAFE_PRINT(Boot, ...) \
do { \
if ((Boot) != NULL && (Boot)->print != NULL) { \
@@ -9,14 +24,14 @@
} while (0)
/* ================================================================
- * Task / Process Control Block pool
+ * Module state
* ================================================================ */
-static Task tasks[TASK_MAX];
+static Task tasks[TASK_MAX]; /* PCB pool (static array) */
static Task *current_task = NULL;
-static UINT32 next_pid = 0;
-static BootInfo *task_boot = NULL;
-static BOOLEAN task_ready = FALSE;
+static UINT32 next_pid = 0;
+static BootInfo *task_boot = NULL;
+static BOOLEAN task_ready = FALSE;
/* Forward declaration */
static void task_trampoline(void);
@@ -25,6 +40,7 @@ static void task_trampoline(void);
* Helpers
* ---------------------------------------------------------------- */
+/* Copy a wide string with a maximum length (including NUL). */
static void wstrcpy16(CHAR16 *dst, const CHAR16 *src, UINTN max)
{
UINTN i = 0;
@@ -39,6 +55,10 @@ static void wstrcpy16(CHAR16 *dst, const CHAR16 *src, UINTN max)
* Initialisation – make the current (kernel) thread task 0
* ---------------------------------------------------------------- */
+/*
+ * Initialise the scheduler: clear all PCB slots and register the
+ * currently running kernel thread as task 0.
+ */
void task_init(BootInfo *Boot)
{
UINTN i;
diff --git a/task.h b/task.h
index 1c80949..e700793 100644
--- a/task.h
+++ b/task.h
@@ -1,56 +1,80 @@
+/*
+ * task.h – Cooperative multitasking interface.
+ *
+ * Provides process control blocks (PCBs), a round-robin scheduler,
+ * and voluntary yield/exit primitives. Context switching is performed
+ * by the assembly routine in context_switch.S.
+ */
+
#ifndef TASK_H
#define TASK_H
#include
#include "boot_info.h"
-/* Maximum number of concurrent tasks */
-#define TASK_MAX 32
+/* ================================================================
+ * Configuration
+ * ================================================================ */
-/* Stack size per task: 32 KB (8 pages) */
-#define TASK_STACK_PAGES 8
+#define TASK_MAX 32 /* max concurrent tasks */
+#define TASK_STACK_PAGES 8 /* 32 KB stack per task */
#define TASK_STACK_SIZE (TASK_STACK_PAGES * 4096)
+#define TASK_NAME_LEN 32 /* max name length (CHAR16) */
-/* Maximum task name length */
-#define TASK_NAME_LEN 32
+/* ================================================================
+ * Task states
+ * ================================================================ */
-/* Task states */
typedef enum {
- TASK_STATE_FREE = 0, /* PCB slot is unused */
- TASK_STATE_READY, /* Runnable, waiting for CPU */
- TASK_STATE_RUNNING, /* Currently executing on CPU */
- TASK_STATE_TERMINATED /* Finished execution */
+ TASK_STATE_FREE = 0, /* PCB slot is unused */
+ TASK_STATE_READY, /* runnable, waiting to be scheduled */
+ TASK_STATE_RUNNING, /* currently executing on the CPU */
+ TASK_STATE_TERMINATED /* finished; slot will be recycled */
} TaskState;
-/* Task entry function signature */
+/* ================================================================
+ * Task entry function
+ * ================================================================ */
+
+/* Signature for the function a new task begins executing. */
typedef void (*TaskEntryFn)(void *arg);
/* ================================================================
* Process Control Block (PCB)
* ================================================================ */
+
typedef struct Task {
- UINT32 pid; /* Process ID */
- TaskState state; /* Current state */
- CHAR16 name[TASK_NAME_LEN]; /* Human-readable name */
- UINT64 saved_rsp; /* Saved stack pointer (context switch) */
- UINT64 stack_base; /* Base address of allocated stack */
- UINTN stack_pages; /* Stack size in pages */
- TaskEntryFn entry; /* Entry function pointer */
- void *arg; /* Argument passed to entry */
- UINTN switches; /* Number of times scheduled */
+ UINT32 pid; /* unique process ID */
+ TaskState state; /* current lifecycle state */
+ CHAR16 name[TASK_NAME_LEN]; /* human-readable label */
+
+ /* Context switch state */
+ UINT64 saved_rsp; /* RSP saved by context_switch() */
+ UINT64 stack_base; /* base phys addr of stack alloc */
+ UINTN stack_pages; /* stack size in pages */
+
+ /* Entry point */
+ TaskEntryFn entry; /* function pointer */
+ void *arg; /* argument passed to entry() */
+
+ /* Scheduling metadata */
+ UINTN switches; /* number of times scheduled */
} Task;
-/* -------- Task API -------- */
+/* ================================================================
+ * Public API
+ * ================================================================ */
-void task_init(BootInfo *Boot);
-Task *task_create(const CHAR16 *name, TaskEntryFn entry, void *arg);
-void task_yield(void);
-void task_exit(void);
-Task *task_current(void);
-UINTN task_count(void);
-void task_print_list(BootInfo *Boot);
+void task_init(BootInfo *Boot); /* initialise scheduler */
+Task *task_create(const CHAR16 *name, /* spawn a new task */
+ TaskEntryFn entry, void *arg);
+void task_yield(void); /* voluntarily give up the CPU */
+void task_exit(void); /* terminate the current task */
+Task *task_current(void); /* return the running task's PCB */
+UINTN task_count(void); /* number of non-FREE tasks */
+void task_print_list(BootInfo *Boot); /* print task table (for `ps`) */
-/* Assembly context switch (defined in context_switch.S) */
+/* Assembly context switch (defined in context_switch.S). */
extern void context_switch(UINT64 *old_rsp, UINT64 new_rsp);
-#endif
+#endif /* TASK_H */