Better docs and structure
This commit is contained in:
161
Makefile
161
Makefile
@@ -1,45 +1,56 @@
|
||||
# Makefile for UEFI Operating System
|
||||
# ==============================================================================
|
||||
# Makefile for Simple UEFI Operating System
|
||||
# ==============================================================================
|
||||
|
||||
# Architecture
|
||||
# ---- Architecture -----------------------------------------------------------
|
||||
ARCH = x86_64
|
||||
|
||||
# Compiler and tools
|
||||
# ---- Tools ------------------------------------------------------------------
|
||||
CC = gcc
|
||||
LD = ld
|
||||
OBJCOPY = objcopy
|
||||
|
||||
# Directories
|
||||
# ---- Directories ------------------------------------------------------------
|
||||
SRC_DIR = .
|
||||
BUILD_DIR = build
|
||||
EFI_DIR = $(BUILD_DIR)/EFI/BOOT
|
||||
IMAGE_DIR = $(BUILD_DIR)/image
|
||||
|
||||
# GNU-EFI paths (common locations)
|
||||
# ---- 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
|
||||
# ---- Targets / objects ------------------------------------------------------
|
||||
TARGET = BOOTX64.EFI
|
||||
TARGET_SO = bootx64.so
|
||||
OBJ = $(BUILD_DIR)/main.o
|
||||
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_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
|
||||
@@ -78,14 +89,20 @@ QEMU_FLAGS = -machine q35 \
|
||||
-net none \
|
||||
-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
|
||||
# ==============================================================================
|
||||
|
||||
ISO_FILE = $(IMAGE_DIR)/os.iso
|
||||
ESP_IMG = $(IMAGE_DIR)/staging/esp.img
|
||||
|
||||
iso: all
|
||||
@@ -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 \
|
||||
@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 ""
|
||||
|
||||
325
README.md
325
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:
|
||||
<details>
|
||||
<summary><strong>Debian / Ubuntu</strong></summary>
|
||||
|
||||
```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:
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Arch Linux</strong></summary>
|
||||
|
||||
```bash
|
||||
sudo pacman -S gnu-efi qemu-system-x86 edk2-ovmf gcc binutils make xorriso mtools
|
||||
```
|
||||
|
||||
### On Fedora/RHEL:
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Fedora / RHEL</strong></summary>
|
||||
|
||||
```bash
|
||||
sudo dnf install gnu-efi qemu-system-x86 edk2-ovmf gcc binutils make xorriso mtools
|
||||
```
|
||||
|
||||
## Building
|
||||
</details>
|
||||
|
||||
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 <command>` | Display detailed help for a command |
|
||||
| `shutdown` | Shutdown the system |
|
||||
| `help` | List all available commands |
|
||||
| `man <command>` | 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` → `<mount>/EFI/BOOT/BOOTX64.EFI`
|
||||
5. Copy `build/kernel.elf` → `<mount>/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.
|
||||
|
||||
39
boot_info.h
39
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 <efi.h>
|
||||
|
||||
/* 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 */
|
||||
|
||||
71
commands.c
71
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 <efi.h>
|
||||
#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,7 +200,7 @@ 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");
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
27
commands.h
27
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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
66
idt.c
66
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;
|
||||
|
||||
/* ================================================================
|
||||
* 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;
|
||||
|
||||
22
idt.h
22
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 <efi.h>
|
||||
#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 */
|
||||
|
||||
38
isr.S
38
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
|
||||
|
||||
29
kernel.c
29
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 <efi.h>
|
||||
|
||||
#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);
|
||||
|
||||
18
kernel.ld
18
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*)
|
||||
|
||||
88
main.c
88
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 <efi.h>
|
||||
#include <efilib.h>
|
||||
|
||||
#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;
|
||||
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;
|
||||
UINT16 e_machine;
|
||||
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)
|
||||
@@ -234,7 +280,7 @@ efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
|
||||
BootInfo Boot;
|
||||
KernelEntryFn EntryFn = NULL;
|
||||
|
||||
// Initialize the GNU-EFI library
|
||||
/* Initialise the GNU-EFI library */
|
||||
InitializeLib(ImageHandle, SystemTable);
|
||||
|
||||
Print(L"Loading kernel...\n\r");
|
||||
@@ -251,6 +297,7 @@ efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* Populate the BootInfo struct with UEFI service wrappers */
|
||||
Boot.SystemTable = ST;
|
||||
Boot.print = Print;
|
||||
Boot.clear_screen = loader_clear_screen;
|
||||
@@ -261,6 +308,7 @@ efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
|
||||
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);
|
||||
|
||||
|
||||
71
memory.c
71
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;
|
||||
|
||||
73
memory.h
73
memory.h
@@ -1,15 +1,30 @@
|
||||
/*
|
||||
* 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 <efi.h>
|
||||
#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 */
|
||||
/* ================================================================
|
||||
* Page-table entry flags (x86-64 4-level paging)
|
||||
* ================================================================ */
|
||||
|
||||
#define PTE_PRESENT (1ULL << 0)
|
||||
#define PTE_WRITABLE (1ULL << 1)
|
||||
#define PTE_USER (1ULL << 2)
|
||||
@@ -17,25 +32,20 @@
|
||||
#define PTE_NOCACHE (1ULL << 4)
|
||||
#define PTE_ACCESSED (1ULL << 5)
|
||||
#define PTE_DIRTY (1ULL << 6)
|
||||
#define PTE_HUGE (1ULL << 7)
|
||||
#define PTE_HUGE (1ULL << 7) /* 2 MB or 1 GB page */
|
||||
#define PTE_GLOBAL (1ULL << 8)
|
||||
#define PTE_NX (1ULL << 63)
|
||||
#define PTE_NX (1ULL << 63) /* No-Execute */
|
||||
|
||||
#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 */
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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 <efi.h>
|
||||
|
||||
/* 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 */
|
||||
|
||||
24
task.c
24
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,10 +24,10 @@
|
||||
} 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;
|
||||
@@ -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;
|
||||
|
||||
84
task.h
84
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 <efi.h>
|
||||
#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_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 */
|
||||
|
||||
Reference in New Issue
Block a user