ADR-021 — Minimal EL0 SVC syscall ABI
- Status: Accepted (ABI mile Verified when the serial / tests pass; Track A / app hosting still Planned)
- Date: 2026-09-11
Context
Track A (issue #31) wants freestanding apps to call the kernel. Issue #32 is the first mile: a documented, probed SVC contract.
ADR-013 already takes lower-EL AArch64 SVC for probe immediates #0 (first-mile return), #1 (standing announce), and #2 (standing restore). Those numbers stay reserved. This ADR adds a tiny public set so a later CRT / libctos (A2) has numbers to call. It does not load ELF, host apps, or claim Linux/POSIX.
Options:
- Linux AArch64 (
svc #0,x8= number). Collides with the first-mileSVC #0probe and invites a Linux-ABI claim. - SVC immediate is the number, public set starts at 16. Leaves 0–2 for ADR-013 probes. Arguments in
x0–x2, return inx0. - Wait for a loader. Would block Track A on A3.
Decision
-
Calling convention. AArch64
SVC #<n>wherenis the syscall number. Arguments inx0,x1,x2. Return value inx0. DAIF stays masked on the existing EL0 path. Not Linux. Not POSIX. -
Reserved 0–2. Probe-only (ADR-013). Not part of the public ABI. A later CRT must not issue them.
-
Public numbers (this tree).
Number Name Args Effect Return 16 exitx0= status (recorded; not a process model)End the EL0 trip; ERETto the EL1 caller(does not return to EL0) 17 uart_writex0= user pointer,x1= lengthCopy up to 64 bytes from a user-mapped and kernel-mapped range; write to PL011 ( \n→\r\n, same as the kernel console)bytes written, or 0if rejected18 yieldnone Record the call; ERETback to EL0 (next insn)0 -
yieldis dispatch-only. It does not callsched::yield_now(ADR-010: that switch is EL1-only and must not run from an exception frame). A later ADR may connect the two. -
uart_writeis fail-closed. Length0or> 64, overflow, a non-canonical or TTBR1 alias, or a range that is not user-mapped or not kernel-mapped returns0and writes nothing. Kernel.datais rejected. Table walks mask to 39 bits — the pointer itself must already be a TTBR0 identity VA, orcopy_userwould load a tagged address. This is not a VFS. -
Unknown immediates stay unhandled (park). Do not silently succeed.
-
Fail-closed probe. Hello serial
svc: yield+ user buffersvc: user-hi+svc: uart+svc: exit+svc: ok.scripts/qemu-smoke.shgreps those and rejectssvc: probe missed.#[test_case]covers the success trip, a kernel-.datareject, and a TTBR1-alias reject. Existingel0:markers stay. -
Honesty. Say “EL0 issued the documented SVC ABI and the kernel performed the documented effect” only when the serial / tests pass. Do not say: app hosting is done, Linux ABI, POSIX, userspace, “EL0 isolated,” or “secure OS.” Standing-as-normal is ADR-024. Thin VFS numbers 19–23 are ADR-027. Track A A7–A9 stay Planned.
-
NFR-10 text is revised in place (ID unchanged) to name this ABI mile. Do not mint FR-16+ or NFR-15+.
Consequences
- Code:
src/syscall.rs. Dispatch is hooked fromhandle_sync_lower_elafter the ADR-013 probe immediates. - Docs: syscall.md, el0.md. Roadmap cites #32 / Track A #31.
- A2 (
libctos) wraps these three numbers (ADR-022). A3 (loader) is not that PR.