# 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; 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" ```