From fc4c563ca14ddec3de3aeef5a861f9707eaae389 Mon Sep 17 00:00:00 2001 From: salmonik Date: Fri, 21 Aug 2026 17:18:09 +0200 Subject: docs: ADR 0006 (executable binaries) + implementation plan + glossary terms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- docs/plan-executable-binaries.md | 140 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 docs/plan-executable-binaries.md (limited to 'docs/plan-executable-binaries.md') 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 @@ +# Implementation plan — executable binaries and the /bin tree + +Companion to ADR 0006. Phases are strictly ordered where dependencies +say so; each phase ends with a QEMU verification gate. Version bumps per +the glossary rules. + +--- + +## Phase 0 — System image (build only, no kernel changes) + +The build produces one dd-able FAT32 image instead of the current +two-source setup (vvfat `esp/` directory + separate `/tmp/disk.img`). + +- [ ] Extend `scripts/mkdisk.sh` (or add `scripts/mksystem.sh`) to create + `system.img`: `mkfs.fat -C`, then `mcopy` in + `\bzImage`, `\EFI\` (as rsbl requires), `\etc\*` (motd moves out of + the `fs.rs` code literal? — optional here), seed content. +- [ ] Stage `\bin\cli\` and `\bin\gui\` directories (empty for now). +- [ ] Makefile: build kernel → stage image; `run`/`run-gui`/`debug` + attach **only** `system.img` on ide.0 (delete the + `-drive file=fat:rw:$(ESP_DIR)` vvfat lines). +- [ ] Verify OVMF boots the single image end-to-end. + +**Gate:** image boots; `ls /disk//` shows `\bin`, `\etc`, seed files; +writes under `/disk//` survive poweroff (existing FAT write-back). + +--- + +## Phase 1 — Per-terminal input discipline (prerequisite) + +Keyboard routing becomes total; canonical mode becomes per-terminal shell +property. This is the original "per-instance shell" step, completed. + +- [ ] WM pump routes keys unconditionally: IRQ ring → pump → focused + window's queue. No task ever polls the global ring directly. +- [ ] Home terminal (window 0) becomes an ordinary focusable window: + clicking it focuses it; retire the `FOCUSED == CONSOLE` sentinel + and the `focused() == 0` special cases from commit `09a44f1`. + `focus_console()` becomes `focus(0)`. +- [ ] Each terminal's shell assembles lines canonically from its own + queue (`tty::read_line_from_queue` path); delete the global-ring + path through `tty::poll_key`. +- [ ] Raw-mode consumers that remain built-ins (`edit`, pager) read keys + via the same focused-window queue, not the ring. +- [ ] Two terminals + home terminal: input always lands in the focused + one; unfocused terminals keep rendering output from running + commands. + +**Gate:** three terminals open; typing switches cleanly with focus; no +keystroke ever appears in two terminals; `make test-disk` regression +passes (it drives the shell via injected keys). + +--- + +## Phase 2 — Loader, Exports table, first exec (**bump 0.6.0**) + +- [ ] New crate `rsapi` (or `rsnix::abi`): the versioned `Exports` struct, + entry-point signature, ABI event/argv types shared by kernel and + binaries. Header carries `version: u32`. +- [ ] `rsnix::load`: PE32+ parser (headers, sections, `.reloc` + application), alloc-copy-relocate, entry lookup. ~300–500 lines. +- [ ] Spawn path: load → new task (own stack, guard page) → jump to entry + with `(&Exports, argc, argv)`; completion message to spawner; + pages freed on exit. +- [ ] Resolution: name → scan mounted volumes' `\bin\\` → + else built-in → else unknown. +- [ ] argv block: shell copies parsed positional args into the binary's + memory; `argc/argv` C-style. +- [ ] First binary: `hello.efi` in `/bin/cli/` — calls one export + (`put_line("hello")`) and returns 0. +- [ ] Crash containment check: a `boom.efi` that faults kills only its + task; shell survives; `jobs` shows the corpse. + +**Gate:** `hello` runs from the image, prints via exports, exits; +`boom` crashes gracefully; built-in fallback still works when the file +is absent; **version bump to 0.6.0**. + +--- + +## Phase 3 — Streams, pipeline integration, line-tool migration + +- [ ] Per-invocation line channel: bounded queue between owning shell and + command task; shell pumps its key queue → canonical assembly → + channel while a command runs (shell main loop gains a "pumping" mode). +- [ ] Exports streams: `get_line() -> {Line, Eof, Abort}`, + `put_line(line) -> bool` (false = stop). Shell-side implementations + feed the filter pipeline / sink / tty exactly as built-ins do today. +- [ ] Grammar translation shell-side: USE/TIL resolve into the injected + source; ` TIL ` sugar strips paths from argv; + filters/sinks wrap `put_line`. Binaries never see keywords. +- [ ] ^C semantics: shell sees ^C while pumping → marks channel aborted → + command's next stream call returns Abort/stop → completion → shell + flushes `-t` windows. `-p` pager blocks inside `put_line`. +- [ ] Migrate simple tools to `/bin/cli/`: `echo` stays frozen; migrate + `ls`, `mem`, `pci`, `date`, `info`, `mkdir`, `touch`, `rm`, + `hexdump`, `klog`, … Each keeps its grammar behaviour verbatim. +- [ ] Migrate `cat` last (input consumer of every corner: heredoc, USE, + TIL sugar, multi-path). + +**Gate:** full `scripts/test-disk.sh` grammar suite passes unchanged with +external binaries; `-h/-t/-g/-p` chains work across the task boundary; +^C flushes a tail window on an infinite stream. + +--- + +## Phase 4 — GUI binaries + +- [ ] Window/event exports: `create_window(title,w,h) -> id`, + `window_buffer(id) -> (ptr,w,h)`, `flush(id)`, `destroy(id)`, + `recv_event(&mut Ev) -> bool` (Ev mirrors Key/AppMouse/Focus/Close). +- [ ] Fire-and-forget spawn: shell returns immediately after spawning; + exit = destroy window + task done. +- [ ] Migrate paint → `/bin/gui/paint`; `draw` resolves through the + normal rule (no special case). +- [ ] F1-spawned terminals keep working alongside GUI binaries. + +**Gate:** `draw` launches the migrated binary; pen draws; F4/close button +closes it; focus cycle and title-bar drag unaffected. + +--- + +## Frozen built-in core (permanent) + +`help`, `clear`, `echo`, `poweroff`/`exit`, `halt`, `rescan`. + +Rationale: needed before disks exist, or exist to fix them. `edit` also +stays built-in until the deferred raw-mode CLI ABI exists. + +## Explicitly deferred (non-goals for this rework) + +- Raw-mode surface access in the CLI ABI (needed before `edit` migrates). +- FAT shadowing of `/bin` beyond the system image. +- Boot-volume identity / root-path unification (`\etc` at `/etc`). +- Launcher (will simply enumerate `/bin/gui` once Phase 4 lands). +- Fixing compute-bound-binary ^C blindness (accepted policy). + +## Version story + +- Phases 0–1 land as 0.5.x increments if separate. +- **0.6.0 at Phase 2** — exec exists (glossary class `b`: big feature). -- cgit v1.2.3