You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Completes T-029: the EL0↔EL1↔EL0 syscall round-trip, timed kernel-side, with no
CNTVCT_EL0 exposure to EL0 (AC#4). Combined with the Phase-1 ctx+IPC benches so
one `perf-bench` measurement build reports all three.
Design (maintainer-approved: hand-assembled image + combined build):
- A hand-assembled 12-byte raw-flat EL0 image (`mov x8,#0; svc #0; b .-4`,
BENCH_EL0_IMAGE) loops a REJECTED syscall (number 0 → BadSyscallNumber →
Resume, before any cap is touched — no yield, no side effect, no log). So each
svc is the pure fixed round-trip overhead (trap + 272-byte frame save + decode
+ return), and the task monopolises the CPU (never switches) → back-to-back
syscall_entry entries.
- perf_bench::run loads it via the existing load_image + task_create_from_image
+ add_user_task (added FIRST, dispatched first), alongside the Phase-1
driver/partner/idle.
- A feature-gated hook in syscall_entry — el0_roundtrip_tick(effect) — times
consecutive entries (safe Timer::now_ns), skips a warm-up, accumulates N
deltas, prints the mean, then returns SyscallEffect::Terminate(0). The
EXISTING (T-028) Terminate arm ends the EL0 bench via task_exit_current and
dispatches the driver → the ctx/IPC benches run next.
- Un-gated USER_TASK_STACK / USER_TASK_TABLE: now the EL0 bench task's SP_EL1 +
an empty cap table (the rejected svc consults no capability).
No new `unsafe` operation: timing reuses safe now_ns (UNSAFE-2026-0015); the EL0
load reuses load_image (UNSAFE-2026-0025/0026/0027) + add_user_task/enter_el0
(UNSAFE-2026-0032); the Terminate is applied by the audited arm
(UNSAFE-2026-0008). UNSAFE-2026-0014 gains a Phase-2 Amendment.
Numbers (QEMU-virt/TCG, relative-only): EL0 syscall round-trip 9 108 ns/syscall
(N=50 000) — ≈3.6x a context switch in the same run.
Gates: fmt clean; kernel-clippy ±feature clean (-D warnings; run() gets the same
too_many_lines allow kernel_main_high uses); host tests pass; build ±feature;
feature-off byte-identity (.text/.data/.bss + footprint identical to base, only
32 .rodata panic-loc bytes shift); feature-off smoke = unchanged demo trace; one
feature-on run captured all three numbers + `perf-bench complete`; Miri
--workspace --exclude bsp 0 UB.
Docs: baseline §Micro-measurements Phase 2 section filled in (section complete);
T-029 AC#4/#6 flipped, all ACs met; UNSAFE-2026-0014 Phase-2 Amendment.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
/// Accumulated ns across the N timed deltas (mean = this / `N_EL0_ROUNDTRIPS`).
129
+
staticEL0_ACCUM_NS:AtomicU64 = AtomicU64::new(0);
130
+
131
+
/// Set up the EL0 bench task + the two kernel bench tasks and start the
132
+
/// scheduler. Never returns: after the driver prints its results it parks, so
133
+
/// the measurement build halts (the QEMU harness captures the `tyrne: perf …`
134
+
/// lines and stops the guest).
135
+
#[allow(
136
+
clippy::too_many_lines,
137
+
reason = "linear measurement-build setup top-to-bottom for auditability — mirrors `kernel_main_high`; splitting into helpers would scatter the load→create→publish→schedule order each step depends on"
**These are QEMU-virt TCG emulated-time figures, not real-silicon latencies.** QEMU's virtual `CNTVCT_EL0` advances on emulated time and TCG executes far slower than hardware, so treat them as **order-of-magnitude + relative** — the same harness-floor caveat the [B5 leg](2026-05-29-B5-closure.md) and §Metric 3 record. They are a baseline for *relative* regression tracking (e.g. "did a scheduler change move ns/switch?"), not absolute performance claims; real-hardware numbers await the Raspberry Pi 4 bring-up. Each figure is the mean over N = 50 000 iterations (after a 256-iteration warm-up), so *within-run* variance is negligible; the 16 ns counter granularity is ≪ the per-op cost. **Cross-run absolutes drift ≈20–25 % with host load under TCG** — a second run measured 2 816 ns/switch and 17 684 ns/cycle — so the figures above are one representative sample, not a stable absolute. The **stable, meaningful** signal is the *ratio* (an IPC round-trip costs ≈4.3–4.5× a context switch across both runs) and same-run deltas, which is what relative regression tracking uses.
73
73
74
-
### Phase 2 — EL0 syscall round-trip (pending)
74
+
### Phase 2 — EL0 syscall round-trip (2026-06-01)
75
75
76
-
The EL0↔EL1↔EL0 syscall round-trip — timed kernel-side around `syscall_entry` for a looping EL0 bench task, still **without** exposing `CNTVCT_EL0` to EL0 — lands in **T-029 Phase 2** (a separate PR), which will complete this section.
76
+
A minimal EL0 bench program (a hand-assembled 12-byte raw-flat image: `mov x8,#0; svc #0; b .-4`) loops a **rejected** syscall (number 0 → `BadSyscallNumber` → `Resume`, before any capability is touched — no yield, no side effect). The kernel times **consecutive `syscall_entry` entries** (the EL0 bench task is the only `SVC` source), reading `CNTVCT_EL0`**kernel-side only** — `CNTVCT_EL0` is **never** exposed to EL0 (no `CNTKCTL_EL1.EL0VCTEN`; AC#4). After N the hook returns `SyscallEffect::Terminate`, ending the EL0 task and handing off to the ctx/IPC benches — so one measurement build reports all three.
77
+
78
+
| primitive | QEMU-virt / TCG | composition |
79
+
|---|---|---|
80
+
|**EL0 syscall round-trip**|**9 108 ns/syscall**| one EL0↔EL1↔EL0 round-trip, hook-to-hook: `SVC` trap → 272-byte frame save → `dispatch` (decode → `BadSyscallNumber`) → `ERET` → 1 EL0 instr (`b`) → next `SVC`; N = 50 000 |
81
+
82
+
This is the **pure fixed overhead** every syscall pays (trap + frame + decode + return), independent of the cap/IPC/console work a real syscall adds on top. In the same run it was ≈**3.6× a context switch** (2 546 ns/switch that run) — the round-trip's 272-byte frame save/restore + `ERET` is heavier than a kernel-internal switch. Same TCG / relative-only caveat as above (cross-run absolutes drift with host load; the *ratio* is the stable signal).
0 commit comments