1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
# Strix OS — Developer Notes
> **READ THIS FIRST after a context reset.** Then read `PLAN.md` for the full roadmap.
> Updated at every commit.
---
## Environment
All `cargo run`, `cargo test`, and QEMU commands **must** be run inside the Nix
development environment. From the repo root:
```
nix develop
cd StrixKernel
cargo test # or cargo run
```
---
## 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**: Phase 3 implementation done, awaiting test run + commit
**Last commit**: `[Phase 2.4-2.6] Process structure, scheduler, Ring 3 spawn`
**Next task**: Run `cargo test` in `nix develop`, then commit Phase 3.1–3.5; then write integration tests
---
## 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.
### [Phase 2.2/2.3] 2026-04-08 — SYSCALL/SYSRET MSR setup + dispatcher
**Done**:
- `src/syscall/mod.rs`: configure STAR/LSTAR/SFMASK/EFER.SCE MSRs
- Naked assembly `syscall_entry_asm`: swapgs, save user RSP, load kernel RSP, remap r10→rcx for arg4, call `syscall_handler`, restore, sysretq
- `src/syscall/dispatch.rs`: dispatcher with `write(1)`, `exit(60)`, `exit_group(231)`; unknown → `-ENOSYS`
- `sys_write`: validates user pointer before dereference; uses `read_volatile` to avoid aliasing UB
- Registered `syscall::init()` + `syscall::init_kernel_stack()` in `strix_os::init()`
- All existing integration tests still pass in QEMU
**Next**: Phase 2.4 — Process structure
**Decisions**:
- Use `swapgs` pattern for user/kernel GS switching (per-CPU scratch for user RSP)
- `sys_write` uses raw pointer + `read_volatile` loop (not `&[u8]` slice) on user memory
- `sys_exit` currently halts; Phase 2.5 will add proper process termination
### [Phase 3.1-3.5] 2026-04-08 — ELF loader, address spaces, execve, initramfs
**Done**:
- `src/loader/elf.rs`: ELF64 parser via goblin; validates magic/class/type; W^X + bounds enforcement; iterator over PT_LOAD segments; interpreter detection
- `src/loader/stack.rs`: SysV AMD64 initial user stack builder (argc/argv/envp/auxv)
- `src/memory/address_space.rs`: per-process PML4; copies kernel high-half; `alloc_and_map`, `map_range`, `switch` (CR3 write), `write_bytes`
- `src/memory/mod.rs` → `src/memory/` directory module; added `PHYS_MEM_OFFSET` AtomicU64 set by `init()`
- `src/initramfs.rs`: newc CPIO parser; `lookup(path)` → `Option<&'static [u8]>`; INITRAMFS static (empty until build.rs is added)
- `src/syscall/exec.rs`: `sys_execve` (#59); loads from initramfs, builds address space, sets up stack, switches CR3, jumps to Ring 3
- Added goblin (`elf32+elf64+endian_fd`) and bitflags to Cargo.toml
- Build is clean (zero warnings)
**Next**: Run `cargo test` in `nix develop`; write Phase 3 integration tests; add `build.rs` + initramfs content
**Decisions**:
- goblin needs `elf32+elf64+endian_fd` features together for the combined `Elf` struct (elf64-only is gated behind elf32 too)
- `PHYS_MEM_OFFSET` stored as AtomicU64 in `memory/mod.rs` so submodules can access it without threading VirtAddr through every call
- `INITRAMFS` is an empty static for now; build.rs + cpio generation deferred to Phase 3.5 follow-up
### [Phase 2.1] 2026-04-08 — GDT user space segments + heap growth
**Done**:
- Restructured `StrixKernel/src/gdt.rs`: added `kernel_data`, `user_data`, `user_code` segments in the correct order for SYSCALL/SYSRET ABI
- Added `TSS.privilege_stack_table[0]` (RSP0) with an 8 KiB static initial stack
- Exposed `GDT` static and `Selectors` fields as `pub` for use by syscall setup
- Added `set_kernel_stack(VirtAddr)` for the scheduler to update RSP0 per-process
- Grew `HEAP_SIZE` from 100 KiB → 4 MiB in `allocator.rs` (needed for process table)
- Fixed pre-existing lifetime lint in `allocator.rs`
- Updated `flake.nix` to add `cpio`, `busybox`, `gdb`, `binutils`, `e2fsprogs`
- `basic_boot` integration test passes in QEMU via `nix develop`
**Next**: Phase 2.2 — SYSCALL/SYSRET MSR setup (`src/syscall/mod.rs`)
**Decisions**:
- GDT order: kernel_code(0x08) / kernel_data(0x10) / user_data(0x18) / user_code(0x20) / TSS(0x28)
- STAR MSR: `[47:32]=0x08`, `[63:48]=0x10` → SYSRET CS=0x20, SS=0x18
- `set_kernel_stack` uses raw pointer write inside `unsafe {}` block; safe when interrupts disabled
---
|