summaryrefslogtreecommitdiffstats
path: root/bins/boom
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-08-21 21:17:44 +0200
committersalmonik <salmonik@tuta.io>2026-08-21 21:17:44 +0200
commit71b6445b048456bf198d8b622570db95f4665cb9 (patch)
tree808e73fa95709ed9c2d409aecaf275fd70365293 /bins/boom
parentinput: total keyboard routing + per-terminal discipline (ADR 0006 ph1) (diff)
downloadrs-dos-71b6445b048456bf198d8b622570db95f4665cb9.tar.gz
rs-dos-71b6445b048456bf198d8b622570db95f4665cb9.zip
exec: PE32+ loader, versioned ABI, first external binaries (v0.6.0)
rsapi: the kernel<->binary ABI crate (no_std, no deps). Versioned Exports table (magic + version header), EntryPoint = extern "C" on x86_64-unknown-uefi (Win64: RCX/RDX/R8), Line/Arg/StreamResult types. Binaries link only this crate. rsnix::load: PE32+ parser — DOS/COFF/optional headers, section copy, BSS zero-fill, .reloc base relocations (DIR64) via rva_to_file mapping (the reloc directory is an RVA, not a file offset). Images are allocated at LOAD_BASE=192MiB inside the page allocator's conventional span and identity-mapped user+writable+nx. rsnix::exec: resolution name -> /disk/<UID>/bin/<kind>/<name>.efi per mounted volume; spawn reserves a sched slot, stores a per-task ExecCtx (exports + argv block + image) before launch_at installs the frame — no adopt race. Trampoline calls entry(&Exports, argc, argv), posts TaskMsg::User(exit_code) to the shell. abort_current notifies the shell with u64::MAX when an exec'd binary dies, so a faulting command cannot leave the shell blocked forever. sched: reserve_slot/launch_at split out of spawn; task_exit made pub. shell: exec_external runs before non-frozen built-ins (ADR 0006 resolution rule); frozen core (help/clear/echo/halt/poweroff/exit/ rescan) is exempt. Exports v1 wires put_line to the terminal and get_line to EOF; stream injection lands in Phase 3. ^C while waiting kills the command task. bins/{hello,boom}: gate binaries. hello prints through the exports table; boom faults on 0x400000000000 to prove containment. The image entry point IS the ABI entry (efi_main carries the Exports signature). Version 0.6.0: exec exists end-to-end. Gates verified in QEMU: 'hello' loads from \\bin\\cli\\hello.efi, calls put_line across the ABI, exits 0, shell unblocks; 'boom' faults, task dies gracefully with full report, shell survives, jobs shows the corpse; built-in fallback works when the file is absent; test-disk PASS.
Diffstat (limited to 'bins/boom')
-rw-r--r--bins/boom/Cargo.toml10
-rw-r--r--bins/boom/src/main.rs31
2 files changed, 41 insertions, 0 deletions
diff --git a/bins/boom/Cargo.toml b/bins/boom/Cargo.toml
new file mode 100644
index 0000000..2d31886
--- /dev/null
+++ b/bins/boom/Cargo.toml
@@ -0,0 +1,10 @@
1[package]
2name = "boom"
3version.workspace = true
4edition.workspace = true
5
6[lints]
7workspace = true
8
9[dependencies]
10rsapi = { path = "../../rsapi" }
diff --git a/bins/boom/src/main.rs b/bins/boom/src/main.rs
new file mode 100644
index 0000000..0c3201b
--- /dev/null
+++ b/bins/boom/src/main.rs
@@ -0,0 +1,31 @@
1//! boom — crash-containment probe (ADR 0006 Phase 2 gate).
2//!
3//! Deliberately faults on an unmapped address. The kernel must kill only
4//! this task; the shell survives and `jobs` shows the corpse.
5
6#![no_std]
7#![no_main]
8
9#[panic_handler]
10fn panic(_info: &core::panic::PanicInfo) -> ! {
11 loop {
12 core::hint::spin_loop();
13 }
14}
15
16/// The image entry point — the loader jumps here with the ABI arguments.
17#[unsafe(no_mangle)]
18pub extern "C" fn efi_main(
19 _exports: &'static rsapi::Exports,
20 _argc: usize,
21 _argv: *const rsapi::Arg,
22) -> i32 {
23 // Canonical-but-unmapped address (below 2^47, above any RAM/MMIO):
24 // the page walk terminates at a not-present entry → #PF with
25 // CR2 = address. Same trick as the built-in `fault` command.
26 unsafe {
27 let p = 0x4000_0000_0000usize as *mut u64;
28 p.write_volatile(0xDEAD_BEEF);
29 }
30 0
31}