aboutsummaryrefslogtreecommitdiff
path: root/PLAN.md
diff options
context:
space:
mode:
authorNatasha Moongrave <natasha@256phi.eu>2026-04-08 16:29:35 +0200
committerNatasha Moongrave <natasha@256phi.eu>2026-04-08 16:29:35 +0200
commiteb61ec76367731579eb585f39b251da629beb871 (patch)
tree3c32261feac8ef615db2772cee2715fd0ea169d1 /PLAN.md
parent0741fe434094aebab684c091e757812bff007a8e (diff)
[Step 0] Add PLAN.md and NOTES.md
PLAN.md: Full 7-phase development roadmap with progress tracker, per-phase tasks, integration test specs, security baseline, and dependency list. NOTES.md: Running developer log for context recovery after session resets. Documents key architecture decisions (GDT segment order, filesystem strategy, heap sizing, syscall ABI). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'PLAN.md')
-rw-r--r--PLAN.md226
1 files changed, 226 insertions, 0 deletions
diff --git a/PLAN.md b/PLAN.md
new file mode 100644
index 0000000..932fad6
--- /dev/null
+++ b/PLAN.md
@@ -0,0 +1,226 @@
+# Strix OS — Full Development Plan
+
+> This file is the authoritative development roadmap. Update the **Status** column as phases complete.
+> See `NOTES.md` for the running developer log (context recovery after resets).
+
+## Filesystem Strategy
+
+| Filesystem | Role | Phase |
+|-----------|------|-------|
+| **ext4** | Persistent root, Nix store | ext2 read-only (4) → ext4 xattrs (5) → write+journal (7) |
+| **ramfs/tmpfs** | Initramfs root, /tmp | Phase 4 |
+| **devfs** | /dev | Phase 4 |
+| **procfs** | /proc | Phase 4 |
+| **sysfs** | /sys skeleton | Phase 6 |
+| **overlayfs** | Nix profile layering | Phase 6 |
+
+## Branch & Commit Policy
+
+- Work on `CLAUDE_TEST` or `CLAUDE_`-prefixed branches only
+- Commit format: `[PhaseX.Y] description`
+- Commit after each sub-task when all tests pass
+- Update `NOTES.md` with every commit
+
+---
+
+## Progress Tracker
+
+| Phase | Status | Description |
+|-------|--------|-------------|
+| Phase 1 | ✅ DONE | Memory management (GDT/IDT/PIC/heap) |
+| Step 0 | ✅ DONE | Repo docs (PLAN.md, NOTES.md) |
+| Phase 2.1 | 🔲 TODO | GDT user space segments |
+| Phase 2.2 | 🔲 TODO | SYSCALL/SYSRET MSR setup |
+| Phase 2.3 | 🔲 TODO | Syscall dispatcher |
+| Phase 2.4 | 🔲 TODO | Process structure |
+| Phase 2.5 | 🔲 TODO | Round-robin scheduler |
+| Phase 2.6 | 🔲 TODO | Ring 3 jump (iretq) |
+| Phase 3.1 | 🔲 TODO | ELF64 parser |
+| Phase 3.2 | 🔲 TODO | Per-process page tables |
+| Phase 3.3 | 🔲 TODO | User stack setup |
+| Phase 3.4 | 🔲 TODO | execve syscall |
+| Phase 3.5 | 🔲 TODO | Busybox embedded initramfs |
+| Phase 4.1 | 🔲 TODO | VFS core abstractions |
+| Phase 4.2 | 🔲 TODO | File descriptor table |
+| Phase 4.3 | 🔲 TODO | ramfs/tmpfs |
+| Phase 4.4 | 🔲 TODO | Initramfs unpacking |
+| Phase 4.5 | 🔲 TODO | devfs |
+| Phase 4.6 | 🔲 TODO | procfs |
+| Phase 4.7 | 🔲 TODO | ATA driver + ext2 read-only |
+| Phase 5.1 | 🔲 TODO | fork/clone |
+| Phase 5.2 | 🔲 TODO | wait/waitpid |
+| Phase 5.3 | 🔲 TODO | Signals |
+| Phase 5.4 | 🔲 TODO | Pipes |
+| Phase 5.5 | 🔲 TODO | Shell syscalls (stat/chdir/symlink/mmap/etc.) |
+| Phase 6.1 | 🔲 TODO | TTY/PTY subsystem |
+| Phase 6.2 | 🔲 TODO | mount/umount/pivot_root/chroot |
+| Phase 6.3 | 🔲 TODO | Init launch (init= param, PID 1) |
+| Phase 6.4 | 🔲 TODO | Mount namespaces (CLONE_NEWNS) |
+| Phase 6.5 | 🔲 TODO | overlayfs |
+| Phase 7.1 | 🔲 TODO | Dynamic linker (PT_INTERP) |
+| Phase 7.2 | 🔲 TODO | File-backed mmap |
+| Phase 7.3 | 🔲 TODO | ext4 write + journal |
+
+---
+
+## Phase 2 — User Space Foundation
+
+**Goal**: Hello World from Ring 3 via SYSCALL.
+
+### 2.1 GDT Extension
+**File**: `StrixKernel/src/gdt.rs`
+- GDT order (required for SYSCALL/SYSRET ABI):
+ - `0x08`: kernel code (DPL=0) ← `STAR[47:32]`
+ - `0x10`: kernel data (DPL=0) ← auto-selected by SYSCALL as CS+8
+ - `0x18`: user data (DPL=3) ← `SYSRET SS = STAR[63:48]+8`
+ - `0x20`: user code (DPL=3) ← `SYSRET CS = STAR[63:48]+16`
+ - `0x28/0x30`: TSS (128-bit)
+- Add `TSS.privilege_stack_table[0]` = 8 KiB static stack (RSP0 for Ring3→Ring0)
+- Expose `Selectors` struct fields as `pub`
+- Add `set_kernel_stack(VirtAddr)` for scheduler to update RSP0
+
+### 2.2 SYSCALL/SYSRET MSR Setup
+**File**: `StrixKernel/src/syscall/mod.rs` (new)
+- `STAR[47:32]` = kernel CS (0x08), `STAR[63:48]` = 0x10 (→ user SS=0x18, user CS=0x20)
+- `LSTAR` = address of `syscall_entry` stub
+- `SFMASK` = clear IF (disable interrupts on SYSCALL entry)
+- Entry stub: save rax/rcx/rdx/rsi/rdi/r8-r11 to kernel stack; call `syscall_handler`
+- ABI: syscall# in rax, args in rdi/rsi/rdx/r10/r8/r9; return in rax; negative = errno
+- Security: ALL user pointers validated to be in range `0..0x0000_8000_0000_0000`
+
+### 2.3 Syscall Dispatcher
+**File**: `StrixKernel/src/syscall/dispatch.rs` (new)
+- `write` (1): fd=1/2 → VGA/serial (no VFS yet)
+- `exit` (60) / `exit_group` (231): mark process exited, schedule next
+- All unknown → return `-ENOSYS` (not halt/panic)
+
+### 2.4 Process Structure
+**File**: `StrixKernel/src/task/process.rs` (new)
+- `ProcessState`: Running, Ready, Zombie
+- `Process`: pid, state, page_table, kernel_stack_top, saved_regs (callee-save)
+- `PROCESS_TABLE`: `spin::Mutex<[Option<Process>; 256]>`; PID 0 = idle, PID 1 = init
+
+### 2.5 Scheduler
+**File**: `StrixKernel/src/task/scheduler.rs` (new)
+- Timer IRQ → `schedule()` → round-robin PROCESS_TABLE
+- `switch_to(next)`: save current callee-saves to current Process, restore next's
+- Before returning to user: `gdt::set_kernel_stack(next.kernel_stack_top)`
+- Grow heap: `HEAP_SIZE` 100 KiB → 4 MiB
+
+### 2.6 Ring 3 Jump
+**File**: `StrixKernel/src/task/spawn.rs` (new)
+- `spawn_user_task(entry: u64, user_stack: u64)` via `iretq`
+- iretq frame: `[user_ss, user_rsp, rflags (IF=1), user_cs, entry_rip]`
+
+### Phase 2 Integration Tests
+- `tests/userspace_syscall.rs`: Ring 3 stub → `write` → serial "RING3" → `exit(0)`
+- `tests/scheduler.rs`: 3 tasks, atomic counter, confirm all run in 1000 ticks
+- `tests/syscall_invalid.rs`: unknown# → `-ENOSYS`; kernel pointer → `-EFAULT`
+
+---
+
+## Phase 3 — ELF Loading & execve
+
+**Goal**: Execute static ELF binaries; embed busybox as rescue shell.
+
+### 3.1 ELF64 Parser
+**File**: `StrixKernel/src/loader/elf.rs` (new)
+**Dep**: `goblin = { version = "0.7", default-features = false, features = ["elf64"] }`
+- Validate magic, e_type; enumerate PT_LOAD; map R/W/X per flags
+- Security: W^X enforced; segment offset/size bounds-checked
+
+### 3.2 Per-Process Page Tables
+**File**: `StrixKernel/src/memory/address_space.rs` (new)
+- `AddressSpace::new()`: new L4, copy kernel high-half
+- `map_range(vaddr, size, flags)`; `switch()` → CR3; frame tracking for cleanup
+
+### 3.3 User Stack Setup
+**File**: `StrixKernel/src/loader/stack.rs` (new)
+- Stack at `0x7fff_f000_0000` (8 MiB)
+- Writes: argc, argv[], envp[], AT_* aux vector (in correct order for musl/glibc)
+
+### 3.4 execve (#59)
+**File**: `StrixKernel/src/syscall/exec.rs` (new)
+- Validate path + argv/envp in user range
+- ELF parse → new address space → stack setup → enter Ring 3
+- PT_INTERP handling: load interpreter binary, pass aux vector
+
+### 3.5 Embedded Busybox
+**Files**: `StrixKernel/build.rs` (new), `StrixKernel/initramfs/`
+- `build.rs` packs `initramfs/` into cpio → `include_bytes!`
+- Applets via symlinks: `sh`, `ls`, `cat`, `echo`, `mv`, `cp`, `rm`, `mkdir`, `pwd`, `mount`, `ln`, `stat`, `env`, `kill`
+
+### Phase 3 Integration Tests
+- `tests/elf_static.rs`: hand-crafted ELF → exit(42) → confirm
+- `tests/elf_wxcheck.rs`: W+X segment → `Err(WxViolation)`
+- `tests/elf_bounds.rs`: out-of-bounds segment → `Err(InvalidSegment)`
+- `tests/address_space.rs`: two spaces, same vaddr → isolation confirmed
+
+---
+
+## Phase 4 — Virtual File System
+
+### 4.1–4.7 (VFS, ramfs, initramfs, devfs, procfs, ext2)
+
+See full plan in `/home/mun/.claude/plans/synthetic-drifting-globe.md`.
+
+### Phase 4 Integration Tests
+- `tests/vfs_ramfs.rs`, `tests/vfs_devnull.rs`, `tests/vfs_procfs.rs`
+- `tests/initramfs_unpack.rs`, `tests/ext2_read.rs`, `tests/fd_table.rs`
+
+---
+
+## Phase 5 — POSIX Essentials
+
+### 5.1–5.5 (fork, wait, signals, pipes, shell syscalls)
+
+### Phase 5 Integration Tests
+- `tests/fork_basic.rs`, `tests/fork_isolation.rs`
+- `tests/signals_basic.rs`, `tests/pipe_basic.rs`, `tests/pipe_shell_pipeline.rs`
+- `tests/symlink.rs`, `tests/ext4_xattr.rs`
+
+---
+
+## Phase 6 — Init System Agnosticism
+
+### 6.1–6.5 (TTY, mount syscalls, init= param, namespaces, overlayfs)
+
+**Init is configurable**: `init=` kernel param; default `/sbin/init`; fallback `/bin/sh`
+
+### Phase 6 Integration Tests
+- `tests/tty_canonical.rs`, `tests/mount_remount.rs`, `tests/pivot_root.rs`
+- `tests/chroot_isolation.rs`, `tests/overlayfs_cow.rs`, `tests/mount_namespace.rs`
+
+---
+
+## Phase 7 — Dynamic Linking & Nix
+
+### 7.1–7.3 (PT_INTERP, file mmap, ext4 write)
+
+### Phase 7 Integration Tests
+- `tests/mmap_file.rs`, `tests/mmap_wx_rejected.rs`
+- `tests/dynamic_elf.rs`, `tests/ext4_write.rs`
+
+---
+
+## Security Baseline (always enforced)
+
+| Rule | Location |
+|------|----------|
+| User pointers validated `0..0x0000_8000_0000_0000` | Every syscall with ptr arg |
+| W^X: no WRITE+EXEC mapping | ELF loader, mmap |
+| No kernel addresses in userspace | procfs output, aux vector |
+| No `unwrap()` in kernel paths | Everywhere — use `?` or explicit match |
+| Signal handler ptr validated | signal.rs |
+| ELF segment bounds checked | elf.rs |
+| ext4 on-disk values validated | ext4.rs |
+| chroot path escape prevention | mount.rs path resolver |
+
+---
+
+## New Dependencies to Add
+
+```toml
+goblin = { version = "0.7", default-features = false, features = ["elf64"] }
+bitflags = "2.4"
+```