summaryrefslogtreecommitdiffstats
path: root/docs/plan-executable-binaries.md
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-08-21 17:18:09 +0200
committersalmonik <salmonik@tuta.io>2026-08-21 17:18:09 +0200
commitfc4c563ca14ddec3de3aeef5a861f9707eaae389 (patch)
treee88483e79ca3ca6d980cdbf984cf9278ccef1d27 /docs/plan-executable-binaries.md
parentwm: fix usize underflow in move_window at screen edges (diff)
downloadrs-dos-fc4c563ca14ddec3de3aeef5a861f9707eaae389.tar.gz
rs-dos-fc4c563ca14ddec3de3aeef5a861f9707eaae389.zip
docs: ADR 0006 (executable binaries) + implementation plan + glossary terms
ADR 0006: PE32+ loader in rsnix, versioned exports-table ABI constructed per invocation, task-per-binary, single dd-able system image with /bin/cli and /bin/gui, shell-owned grammar with injected line streams, per-terminal input discipline as prerequisite phase, frozen built-in core, 0.6.0 at first exec. Plan: five phases (image → input discipline → loader/exec → streams + migration → GUI binaries), each with a QEMU verification gate. Glossary: Binary, Loader, Exports table, System image; kind = launch surface.
Diffstat (limited to 'docs/plan-executable-binaries.md')
-rw-r--r--docs/plan-executable-binaries.md140
1 files changed, 140 insertions, 0 deletions
diff --git a/docs/plan-executable-binaries.md b/docs/plan-executable-binaries.md
new file mode 100644
index 0000000..cd6f1d0
--- /dev/null
+++ b/docs/plan-executable-binaries.md
@@ -0,0 +1,140 @@
1# Implementation plan — executable binaries and the /bin tree
2
3Companion to ADR 0006. Phases are strictly ordered where dependencies
4say so; each phase ends with a QEMU verification gate. Version bumps per
5the glossary rules.
6
7---
8
9## Phase 0 — System image (build only, no kernel changes)
10
11The build produces one dd-able FAT32 image instead of the current
12two-source setup (vvfat `esp/` directory + separate `/tmp/disk.img`).
13
14- [ ] Extend `scripts/mkdisk.sh` (or add `scripts/mksystem.sh`) to create
15 `system.img`: `mkfs.fat -C`, then `mcopy` in
16 `\bzImage`, `\EFI\` (as rsbl requires), `\etc\*` (motd moves out of
17 the `fs.rs` code literal? — optional here), seed content.
18- [ ] Stage `\bin\cli\` and `\bin\gui\` directories (empty for now).
19- [ ] Makefile: build kernel → stage image; `run`/`run-gui`/`debug`
20 attach **only** `system.img` on ide.0 (delete the
21 `-drive file=fat:rw:$(ESP_DIR)` vvfat lines).
22- [ ] Verify OVMF boots the single image end-to-end.
23
24**Gate:** image boots; `ls /disk/<UID>/` shows `\bin`, `\etc`, seed files;
25writes under `/disk/<UID>/` survive poweroff (existing FAT write-back).
26
27---
28
29## Phase 1 — Per-terminal input discipline (prerequisite)
30
31Keyboard routing becomes total; canonical mode becomes per-terminal shell
32property. This is the original "per-instance shell" step, completed.
33
34- [ ] WM pump routes keys unconditionally: IRQ ring → pump → focused
35 window's queue. No task ever polls the global ring directly.
36- [ ] Home terminal (window 0) becomes an ordinary focusable window:
37 clicking it focuses it; retire the `FOCUSED == CONSOLE` sentinel
38 and the `focused() == 0` special cases from commit `09a44f1`.
39 `focus_console()` becomes `focus(0)`.
40- [ ] Each terminal's shell assembles lines canonically from its own
41 queue (`tty::read_line_from_queue` path); delete the global-ring
42 path through `tty::poll_key`.
43- [ ] Raw-mode consumers that remain built-ins (`edit`, pager) read keys
44 via the same focused-window queue, not the ring.
45- [ ] Two terminals + home terminal: input always lands in the focused
46 one; unfocused terminals keep rendering output from running
47 commands.
48
49**Gate:** three terminals open; typing switches cleanly with focus; no
50keystroke ever appears in two terminals; `make test-disk` regression
51passes (it drives the shell via injected keys).
52
53---
54
55## Phase 2 — Loader, Exports table, first exec (**bump 0.6.0**)
56
57- [ ] New crate `rsapi` (or `rsnix::abi`): the versioned `Exports` struct,
58 entry-point signature, ABI event/argv types shared by kernel and
59 binaries. Header carries `version: u32`.
60- [ ] `rsnix::load`: PE32+ parser (headers, sections, `.reloc`
61 application), alloc-copy-relocate, entry lookup. ~300–500 lines.
62- [ ] Spawn path: load → new task (own stack, guard page) → jump to entry
63 with `(&Exports, argc, argv)`; completion message to spawner;
64 pages freed on exit.
65- [ ] Resolution: name → scan mounted volumes' `\bin\<kind>\<name>` →
66 else built-in → else unknown.
67- [ ] argv block: shell copies parsed positional args into the binary's
68 memory; `argc/argv` C-style.
69- [ ] First binary: `hello.efi` in `/bin/cli/` — calls one export
70 (`put_line("hello")`) and returns 0.
71- [ ] Crash containment check: a `boom.efi` that faults kills only its
72 task; shell survives; `jobs` shows the corpse.
73
74**Gate:** `hello` runs from the image, prints via exports, exits;
75`boom` crashes gracefully; built-in fallback still works when the file
76is absent; **version bump to 0.6.0**.
77
78---
79
80## Phase 3 — Streams, pipeline integration, line-tool migration
81
82- [ ] Per-invocation line channel: bounded queue between owning shell and
83 command task; shell pumps its key queue → canonical assembly →
84 channel while a command runs (shell main loop gains a "pumping" mode).
85- [ ] Exports streams: `get_line() -> {Line, Eof, Abort}`,
86 `put_line(line) -> bool` (false = stop). Shell-side implementations
87 feed the filter pipeline / sink / tty exactly as built-ins do today.
88- [ ] Grammar translation shell-side: USE/TIL resolve into the injected
89 source; `<path> TIL <marker>` sugar strips paths from argv;
90 filters/sinks wrap `put_line`. Binaries never see keywords.
91- [ ] ^C semantics: shell sees ^C while pumping → marks channel aborted →
92 command's next stream call returns Abort/stop → completion → shell
93 flushes `-t` windows. `-p` pager blocks inside `put_line`.
94- [ ] Migrate simple tools to `/bin/cli/`: `echo` stays frozen; migrate
95 `ls`, `mem`, `pci`, `date`, `info`, `mkdir`, `touch`, `rm`,
96 `hexdump`, `klog`, … Each keeps its grammar behaviour verbatim.
97- [ ] Migrate `cat` last (input consumer of every corner: heredoc, USE,
98 TIL sugar, multi-path).
99
100**Gate:** full `scripts/test-disk.sh` grammar suite passes unchanged with
101external binaries; `-h/-t/-g/-p` chains work across the task boundary;
102^C flushes a tail window on an infinite stream.
103
104---
105
106## Phase 4 — GUI binaries
107
108- [ ] Window/event exports: `create_window(title,w,h) -> id`,
109 `window_buffer(id) -> (ptr,w,h)`, `flush(id)`, `destroy(id)`,
110 `recv_event(&mut Ev) -> bool` (Ev mirrors Key/AppMouse/Focus/Close).
111- [ ] Fire-and-forget spawn: shell returns immediately after spawning;
112 exit = destroy window + task done.
113- [ ] Migrate paint → `/bin/gui/paint`; `draw` resolves through the
114 normal rule (no special case).
115- [ ] F1-spawned terminals keep working alongside GUI binaries.
116
117**Gate:** `draw` launches the migrated binary; pen draws; F4/close button
118closes it; focus cycle and title-bar drag unaffected.
119
120---
121
122## Frozen built-in core (permanent)
123
124`help`, `clear`, `echo`, `poweroff`/`exit`, `halt`, `rescan`.
125
126Rationale: needed before disks exist, or exist to fix them. `edit` also
127stays built-in until the deferred raw-mode CLI ABI exists.
128
129## Explicitly deferred (non-goals for this rework)
130
131- Raw-mode surface access in the CLI ABI (needed before `edit` migrates).
132- FAT shadowing of `/bin` beyond the system image.
133- Boot-volume identity / root-path unification (`\etc` at `/etc`).
134- Launcher (will simply enumerate `/bin/gui` once Phase 4 lands).
135- Fixing compute-bound-binary ^C blindness (accepted policy).
136
137## Version story
138
139- Phases 0–1 land as 0.5.x increments if separate.
140- **0.6.0 at Phase 2** — exec exists (glossary class `b`: big feature).