diff options
| author | Natasha Moongrave <natasha@256phi.eu> | 2026-04-08 16:29:35 +0200 |
|---|---|---|
| committer | Natasha Moongrave <natasha@256phi.eu> | 2026-04-08 16:29:35 +0200 |
| commit | eb61ec76367731579eb585f39b251da629beb871 (patch) | |
| tree | 3c32261feac8ef615db2772cee2715fd0ea169d1 | |
| parent | 0741fe434094aebab684c091e757812bff007a8e (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>
| -rw-r--r-- | NOTES.md | 78 | ||||
| -rw-r--r-- | PLAN.md | 226 |
2 files changed, 304 insertions, 0 deletions
diff --git a/NOTES.md b/NOTES.md new file mode 100644 index 0000000..d140620 --- /dev/null +++ b/NOTES.md @@ -0,0 +1,78 @@ +# Strix OS β Developer Notes + +> **READ THIS FIRST after a context reset.** Then read `PLAN.md` for the full roadmap. +> Updated at every commit. + +--- + +## How to Resume After Context Reset + +1. Read this file top-to-bottom +2. Read `PLAN.md` β check the Progress Tracker table for current status +3. Run `cargo test` from `StrixKernel/` to confirm current build state +4. Continue from the first `π² TODO` item in PLAN.md + +--- + +## Current Status + +**Branch**: `CLAUDE_TEST` +**Phase**: Starting Phase 2 β User Space Foundation +**Last commit**: `[Step 0] Add PLAN.md and NOTES.md` +**Next task**: `[Phase 2.1]` β Extend GDT with user space segments + +--- + +## Key Decisions & Rationale + +| Decision | Rationale | +|----------|-----------| +| ext4 as primary filesystem | Nix requires symlinks, xattrs, POSIX perms; ext4 covers all. Impl as ext2 first | +| SYSCALL/SYSRET for syscall interface | Faster than int 0x80; Linux ABI compatible | +| GDT segment order: kcode/kdata/udata/ucode/tss | Required for STAR MSR arithmetic (see Phase 2.1 notes) | +| Heap 100 KiB β 4 MiB | Process table (256 entries) + 64 KiB kernel stacks needs > 16 MiB; start at 4 MiB | +| goblin crate for ELF parsing | no_std compatible, well-maintained, handles ELF64 | +| Embedded busybox initramfs | Self-contained rescue shell; no disk dependency for Phase 3 testing | +| OpenRC default init, `init=` configurable | Lightweight; swappable without kernel changes | +| W^X enforced on all mappings | Prevents code injection via data segments | +| All user pointers validated | Prevents kernel memory disclosure/corruption via syscalls | + +--- + +## Architecture Notes + +### GDT Segment Order (Critical for SYSCALL/SYSRET) +``` +0x00: null +0x08: kernel code (DPL=0) β STAR[47:32] +0x10: kernel data (DPL=0) β auto: SYSCALL sets SS = 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) +``` +STAR MSR values: `STAR[47:32]=0x08`, `STAR[63:48]=0x10` + +### Heap Layout +``` +HEAP_START = 0x4444_4444_0000 +HEAP_SIZE = 4 MiB (was 100 KiB) +``` + +### Key Virtual Address Constants +``` +Physical memory offset: 0x0000_2560_0000_0000 (from bootloader) +Heap start: 0x4444_4444_0000 +User stack top: 0x7fff_f000_0000 (8 MiB stack) +User address limit: 0x0000_8000_0000_0000 (canonical boundary) +``` + +--- + +## Log + +### [Step 0] 2026-04-08 β Bootstrap repo docs +**Done**: Created `PLAN.md` (full roadmap with progress tracker) and `NOTES.md` (this file). +**Next**: Phase 2.1 β Extend GDT. +**Decisions**: None new. + +--- @@ -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" +``` |
