diff options
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/docs/16-CUSTOM-SIMULATOR.md')
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/docs/16-CUSTOM-SIMULATOR.md | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/docs/16-CUSTOM-SIMULATOR.md b/debug/risc-v-gsp-rm-vm/docs/16-CUSTOM-SIMULATOR.md new file mode 100644 index 0000000..2b77ca3 --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/docs/16-CUSTOM-SIMULATOR.md @@ -0,0 +1,449 @@ +# 16 — Custom simulator: design and implementation plan +> **# TOOL IN DEVELOPMENT - NOT READY YET!** See 00 for status; this is a +> far-later design, do not implement ahead of the H3 gate. + +## 1. Purpose and status + +This document is the design and delivery plan for a green-eye-native execution +backend: a small deterministic RISC-V machine (`libgreenmachine`) implementing +the same `Session` contract as today's QEMU backend. It expands +`GREEN-EYE-ROADMAP.md` §H3 (H3.1–H3.4) and the §H6 soft-processor annex. + +**Nothing here exists today.** The only supported backend is `QemuTarget` + +`RspClient` (`greeneye/target.py`, `greeneye/rsp.py`); native is gated behind +H3 entry — H2.2, the QEMU device model that makes the real GSP image reach its +message loop — and must not be started ad hoc. This document only scopes it. + +## 2. Why a custom simulator + +QEMU is an excellent execution engine and a poor *workflow* engine here. The +limits below are consequences of QEMU being a whole-board emulator with a GDB +stub as its scriptable surface; each maps to a §H3.1 requirement. + +| QEMU property | Consequence for green-eye | Requirement it forces | +|---|---|---| +| RSP serialises every access (hex frame, TCP round-trip, `PacketSize` chunking) | register/memory polling and watchpoint step-over are transport-bound; full-access tracing is impractical | in-process backend ABI with direct memory/register calls | +| No cheap machine snapshots; `savevm` is board-level and not exposed over the stub | region snapshots are copied over RSP; no instant checkpoint exists | copy-on-write full-machine snapshot/restore | +| No in-process plugin ABI: TCG plugins are C objects against QEMU internals; `qRcmd` is stringly | per-access callback *objects*, Python-side devices and deterministic fault injection have no home | native plugin/callback ABI for regions and devices | +| Whole-board device model (`virt` models boards, not a TU104-class firmware environment) | device behaviour lives in QEMU code and cannot be versioned as data with a firmware profile | memory-map DSL + small device modules, versioned as profiles | +| Host clock and iothread drive timers and wakeups | two runs of the same firmware interleave differently; replay is not reproducible | deterministic scheduler, virtual time, event-log replay | +| GDB stub stop model | instructions between stops are invisible unless a trace plugin is built and loaded | event bus emitting `exec`/`access` natively | +| Injection is debugger writes plus `Session.call` | RM-core call interception (stub/log/forward) is impossible at the ABI boundary | injection ABI with service registry and interceptors | + +The §H3.1 contract: RV64IMAC (+GC when needed) interpreter at ≥10–50 MIPS on +one host thread (not realtime); memory map DSL; deterministic scheduler; +snapshot/restore; the existing `events.jsonl` schema; a backend ABI identical +to `Session`'s expectations; an injection ABI; Python-first embedding. §H3.4 +adds: differential tests against QEMU, gate behind H2, minimal `Backend`. + +## 3. Goals and non-goals + +**Goals** + +1. Byte-for-byte compatible `Session` behaviour for every script, probe, + plugin, recording and comparison that works today. +2. Determinism: same image + profile + event log ⇒ identical event stream + (modulo host-only metadata) on every run and host. +3. Cheap snapshot/restore and time-travel from snapshot + replay. +4. Interceptable device and RM-core boundaries: accesses and calls can be + logged, stubbed, forwarded or (later, H4) mutated. +5. Firmware/device knowledge as versioned data (profiles), not code edits. + +**Non-goals** + +* Realtime or wall-clock guarantees; virtual time only. +* Confidentiality/isolation/security claims: a study environment only (§06 §6). +* Replacing QEMU before M4; native is opt-in (`--backend native`, M6). +* Graphics/board emulation, undeclared fidelity claims, or shipping: nothing + here becomes a dependency of `gsp-rm-vfio` or a guest. + +### 3.1 Fidelity claims + +Levels are exactly those of `docs/06-ACCURACY.md`; the native backend does +not get to invent new ones. + +| Level | Property | QEMU backend today | Native target (gated) | +|---|---|---|---| +| L0 | instruction semantics | high (TCG, same ISA family) | parity with QEMU on differential tests (M1) | +| L1 | memory behaviour | high for RAM-resident code | parity per profile; map is versioned data (M2) | +| L2 | time/concurrency | not modelled | deterministic virtual time and event order, **not** cycle accuracy (M3); cycle claims only via H6 | +| L3 | device environment | absent | parity with the H2.2 device model (M4) | + +No L2/L3 conclusion may be published before its gate; timing is out of scope +entirely — the L2 row means reproducibly *ordered* events, not a timing model. + +## 4. Architecture + +The control plane is unchanged: `Session`, hooks, watchpoints, `Memory`, +`SymbolDB`, `Recorder`, `analyze`/`compare`, probes, plugins and the Ghidra +bridge sit above a backend interface. The §H3.2 sketch becomes: + +``` ++-------------------- green-eye Python control plane --------------------+ +| Session API, hooks/watchpoints, recorder, plugins, Ghidra/agent bridge | ++---------------------------- Backend interface -------------------------+ + | QEMU-RSP backend (today) | native backend (planned) + v v + qemu-system-riscv64 libgreenmachine + + greeneye/rsp.py + cpu/ decoder + execute + + greeneye/target.py + mem/ map DSL, COW snapshots + + dev/ falcon, csb, ptimer, dma, pmu + + sched/ cores, timers, event queue + + trace/ event bus, exec/access + + inject/ blob loader, interceptors +``` + +Design rules: (1) the backend implements transport primitives only — +Frida-like `run_until`/`call` stay in `Session` (`greeneye/session.py:373`, +`:403`) and work on every backend for free; (2) device knowledge is data +(profile) plus a small module, never a core special case; (3) every backend +is optional — the Python package stays stdlib-only, and the native artifact +is loaded at session construction with a version check. + +## 5. Backend interface + +`Session` today assumes an RSP-like client. The planned interface is the same +small set of operations; `run_until` and `call` are implemented above it. + +```python +class Backend(Protocol): + def capabilities(self) -> set[str]: ... # machine_snapshot | injection | haystack + def close(self) -> None: ... + def read_registers(self) -> dict[str, int]: ... + def read_reg(self, name: str) -> int: ... + def write_reg(self, name: str, value: int) -> None: ... + @property + def pc(self) -> int: ... + def read_mem(self, addr: int, size: int) -> bytes: ... + def write_mem(self, addr: int, data: bytes) -> None: ... + def step(self) -> StopReply: ... + def continue_(self, budget: Budget | None = None) -> StopReply: ... + def interrupt(self) -> StopReply: ... + def set_sw_break(self, addr: int) -> None: ... + def clear_sw_break(self, addr: int) -> None: ... + def set_hw_break(self, addr: int) -> None: ... + def clear_hw_break(self, addr: int) -> None: ... + def set_watch(self, addr: int, size: int, kind: str) -> None: ... + def clear_watch(self, addr: int, size: int, kind: str) -> None: ... + def save_moment(self, label: str) -> str: ... + def restore_moment(self, moment_id: str) -> None: ... + def inject(self, blob_elf: str, slot: str) -> "InjectionHandle": ... +``` + +| Session need | Backend primitive | QEMU-RSP backend (today) | Native backend (planned) | +|---|---|---|---| +| registers | `read_registers`/`read_reg`/`write_reg` | `g`/`p`/`P` packets | register file + CSRs, direct | +| memory | `read_mem`/`write_mem` | `m`/`M`/`X`, chunked to `PacketSize` | region lookup; RAM direct, MMIO callbacks | +| software break | `set_sw_break`/`clear_sw_break` | `Z0`/`z0`; `Session._step_over` | breakpoint table in the dispatch loop | +| hardware break | `set_hw_break`/`clear_hw_break` | `Z1`/`z1` | PC match | +| watchpoint | `set_watch`/`clear_watch` | `Z2/Z3/Z4`; stops *at* the accessing instruction | access check; contract below | +| step | `step` | `s` | one decoded instruction | +| continue | `continue_(budget)` | `c` + wall timeout + interrupt | run to breakpoint/budget/event | +| stop | `interrupt` | `0x03` | out-of-band flag checked per instruction | +| region snapshot | `read_mem` via `Memory` | copy over RSP | direct map copy | +| machine snapshot | `save_moment`/`restore_moment` | unsupported (raise, never lie) | COW pages + `moment.json` | +| injection | `inject` | docs/12 path: writes + `Session.call` | loader + service registry + interceptors | +| virtual time | `budget`, `StopReply.vt` | absent (host wall clock) | instruction count + timer ticks | + +Compatibility decisions: + +* `Session(client=...)` keeps working; `Session(backend=...)` is added and + `client=` becomes an alias for `RspBackend(client)`. `Memory` is re-typed + against `Backend`; its public methods do not change. +* **Watchpoint contract.** QEMU stops after the watched access and requires + the clear/step/re-arm dance (`greeneye/session.py:538`). Native advertises + `watch_after_access` so the Session skips the dance on that path only; a + `watch_before_access` mode exists for injection experiments but is not the + default, which must reproduce today's event profiles. +* `Budget` is `{instructions, until_event}`; the wall-clock `duration=` + argument is translated via the profile's nominal clock, so native `run` is + deterministic. `StopReply` gains optional `vt` and `access`, defaulting for + old parsers. + +## 6. Core choice: interpreter first, JIT later + +| Option | Determinism | Speed | Debuggability | Cost / risk | +|---|---|---|---|---| +| Pure-Python interpreter | highest | very low (≪1 MIPS) | highest | prototype only; excellent oracle | +| Decode/execute interpreter (recommended M1–M5) | highest | 10–50 MIPS achievable in Rust/C++ | high; per-instruction hooks and single-step are trivial | low-moderate | +| JIT (copy-and-patch / TCG-like) | high if codegen is fixed | ≫100 MIPS | hard: self-modifying code, translation-cache invalidation | deferred to M6, only if profiling demands | +| Existing core (e.g. a small open RISC-V interpreter/JIT) | depends | varies | depends | evaluation only; hooks/snapshots must be retrofit | + +**Decision.** Build a decode/execute interpreter with the ISA behind a +`CpuCore` interface: a decoder mapping instruction words to typed operations, +an execute loop dispatching into the memory map. `MISA` is a profile property +(`rv64imac` default, `rv64gc` selectable, per 07 §P3); compressed +instructions are canonicalised first so breakpoints and single-step work +uniformly. No FPU/vector until a GC profile is needed. An embedded existing +core (public options such as `libriscv`/`rv8`) is an **evaluation note only**: +revisit at M4 as a `CpuCore` if its memory callbacks, watchpoint granularity, +snapshot hooks and licence/provenance fit — it is not the M1 substrate, +because those hooks *are* the product. + +## 7. Memory map DSL and device plugins + +The map is versioned data interpreted by `mem/`. Region kinds are `ram`, +`mmio`, `dma` and `host-window`; each has attributes, logging policy and +either a backing store or a device. Windows resolve to device instances. + +```yaml +schema: greenmachine-profile/1 +profile: tu10x-gsp +isa: rv64imac +devices: + falcon0: { module: falcon, instance: gsp } + csb0: { module: csb, mailbox_depth: 8 } + ptimer0: { module: ptimer, hz: 1000000 } + pmu0: { module: pmu_stub, revision: r1 } + dma0: { module: dma, iommu: pass-through } +regions: + - { name: tcm, base: 0x4000000, size: 0x40000, kind: ram, attrs: [r,w,x] } + - { name: gsp_regs, base: 0x2000000000000000, size: 0x100000, kind: mmio, + log: changes, # off | changes | access + windows: [{ offset: 0x000000, size: 0x1000, device: falcon0 }, + { offset: 0x001000, size: 0x1000, device: csb0, writable: true }, + { offset: 0x002000, size: 0x1000, device: ptimer0 }, + { offset: 0x200a00, size: 0x0200, device: pmu0 }] } + - { name: host_dma, base: 0x100000000, size: 0x10000000, kind: dma, + device: dma0 } +``` + +Device modules are small in-process plugins; the same interface is what makes +`stub`/`log`/`forward` interception possible later: + +```python +class Device(Protocol): + def reset(self, ctx) -> None: ... + def read(self, ctx, offset: int, size: int) -> int: ... + def write(self, ctx, offset: int, size: int, value: int) -> None: ... + def tick(self, ctx, now: int) -> list[Event]: ... + def save_state(self) -> dict: ... + def load_state(self, state: dict) -> None: ... +``` + +Planned modules (all stubs until the H2.2 model defines semantics): + +| Module | Role | Notes | +|---|---|---| +| `falcon` | FALCON/SEC2 register windows, imem/dmem, bootvec | read-back semantics for boot polling | +| `csb` | CSB/TRANSCOM mailbox windows | message queues, doorbells as events | +| `ptimer` | programmable timer | feeds the scheduler; deterministic ticks | +| `dma` | descriptor engine into host/FB windows | completions are scheduled events, not threads | +| `pmu_stub` | identity/clock provisioning reads | canned but recorded; never a timing source | +| `boot_shim` | FWSEC/booter/SEC2 hand-off state | documented shim; assumptions in session metadata | + +Profiles are keyed by `(device, firmware-family, profile-version)` and carry +the firmware hash, provenance, shim assumptions and the validated map. A new +pairing is a new profile, never a core change; profiles are JSON-Schema +validated in CI. + +## 8. Deterministic scheduler + +Virtual time advances only by executed instructions and scheduled events; no +host clock, thread or entropy source may influence guest-visible state. + +* **Cores.** One or more vCPUs with instruction budgets; the default is a + strict, seeded round-robin so interleavings are reproducible. +* **Event queue.** Timers, IRQ lines, DMA completions and external stimuli + are ordered `(vt, kind, source, payload, seq)`; ties break by sequence + number, never host timing. `ptimer` ticks enqueue IRQ events; delivery + defaults to instruction boundaries, and injected interrupts are explicit + and recorded. +* **Replay.** A recording captures a seed plus the ordered external-event + log; re-running the same image, profile and log must reproduce the event + stream and final memory hashes. The determinism test runs every suite twice + and compares a canonical hash. +* **Hygiene (tested).** No `dict` iteration order in device dumps, no + unordered set serialisation, no host paths in semantic fields, fixed page + allocation order, fixed hash seeds where a container affects order. + +## 9. Snapshot/restore and time-travel + +A machine snapshot ("moment") is a new artifact type, not a change to region +snapshots (`snapshots/*.bin.z`, `01-DESIGN.md §6`): + +``` +moments/<id>/ + moment.json # greenmachine-moment/1, profile id+hash, vt, event-log offset, + # register file, CSR/MPU state, scheduler cursor, device state + pages/ # COW page list: base page id + XOR/delta or full page + events.tail # optional copy of events since the previous moment +``` + +* **Memory** is page-granular copy-on-write from a base moment; a 256 MiB + image that changes little is a page list, not a copy. **Device state** + comes from `Device.save_state()`; devices without it are marked "reset + required" and the moment is partial. **Event-log position** indexes + `events.jsonl`; restore replays external events from that index. +* **Use cases (M2/M3):** checkpoint before a fault and explore both branches; + flip one reply byte and compare; restore and re-run a suffix to delta-debug + an injected service; seed a fuzzing harness from a moment. **Limits:** + in-flight DMA and out-of-band input must be in the event log, host/FB + windows backed by real files are excluded, and the profile hash must match. + +## 10. Event bus + +The recorder schema (`greeneye-session/1`, one JSON object per line with `t` +and `type`, `greeneye/record.py`) is preserved exactly. The native bus is the +single producer for stop-based and full-access events: + +| Event type | Status | Fields | +|---|---|---| +| `session_start`, `image_loaded`, `hook`, `watch`, `note`, `exit`, `session_end` | existing | unchanged | +| `bp_hit`, `leave`, `watch_hit`, `stop`, `step`, `mem_read`, `mem_write`, `snapshot*`, `probe*`, `plugin*` | existing | unchanged; `mem_*` may now come from the bus, not only tooling | +| `exec` | new (M1, optional) | `pc`, `insn`, `vt`, `core`, `symbol` | +| `access` | new (M2) | `pc`, `addr`, `size`, `rw`, `region`, `vt`, `core` | +| `device` | new (M2) | `device`, `name`, `rw`, `value`, `vt` | +| `irq` / `schedule` | new (M3) | `line`, `state`, `core`, `vt`, `reason` | +| `inject_*` | existing shape (docs/12) | `service_injected`, `call_enter`, `call_return` unchanged | + +`t` stays the recorder's monotonic seconds **filled from virtual time**, so +the same run yields the same file. `exec`/`access` volume is controlled by +profile logging and a ring budget; the analyzer gains coverage/heatmap +rendering (roadmap R4). `greeneye compare` treats backend identity, `t` and +host paths as non-semantic, so cross-backend comparison is first-class. + +## 11. Injection ABI + +The docs/12 blob contract is reused unchanged: ELF64 `EM_RISCV`, RV64IMAC, +soft-float, PIC, one dispatch entry, optional `ge_service_t` metadata. The +native backend changes *where the blob is registered* and *what can be +intercepted* (H1.3/H3.1): + +* **Loader.** `Backend.inject()` verifies PIC/no-relocations, maps PT_LOAD + segments through the memory map, and records `service_injected`. +* **Service registry.** The demo path registers into the firmware handler + slot as today; the RM path (M5) registers into a service table presented to + the simulated RM core, so the firmware dispatches to the service itself. +* **Interception points** are declared in the profile and matched at runtime: + +| Mode | Behaviour | Intended use | +|---|---|---| +| `log` | real call executes; target, args and return recorded | observation, M5 evidence | +| `stub` | simulator returns a canned reply; real code skipped | isolate the RM front-end from an engine | +| `forward` | call routed to a nominated implementation or injected service | prototype a replacement engine | +| `mutate` (gated H4) | argument or reply deterministically corrupted; mutation id recorded | fuzzing harness | + +**ABI v2 sketch (M5).** `ge_service_fn(cmd, a0, a1)` becomes +`ge_service_v2(ctx, cmd, a0, a1)` where `ctx` carries a call table; blobs +declare their ABI version so loaders can refuse mismatches. Interception +applies to call-table calls, MMIO accesses and the RM dispatch boundary. + +## 12. Language and embedding options + +| Option | Speed | Embedding | Safety/ergonomics | Build & CI cost | Assessment | +|---|---|---|---|---|---| +| C/C++ core | high | CPython C API or pybind11 | manual memory; best if a core is embedded later | compiler matrix; sanitizers essential | viable fallback; no build advantage | +| Rust core + PyO3 | high | first-class via PyO3/maturin | memory-safe; enums/decoders fit the ISA well | cargo + maturin; easy wheel caching | **recommended** | +| Pure Python | lowest | native | fastest iteration; great reference model | none | M0/M1 oracle, not the product core | + +**Recommendation.** Build a pure-Python reference interpreter first as an +executable specification and differential oracle (small, CI-friendly, forces +the memory-map semantics to be testable), then the production core in Rust +with PyO3 behind an identical C-visible ABI. The C ABI lets a C/C++ core or +an existing core slot into `CpuCore` without touching the control plane. + +## 13. Implementation plan (expands H3.3) + +Entry gate for all milestones: **H2.2 exit** and a stable H2.2 device +semantics baseline. Effort is rough engineering-weeks for one developer +familiar with the codebase; milestones are sequential. + +| # | Scope | Exit criteria | Effort | +|---|---|---|---| +| M1 | RV64IMAC interpreter, RAM, NS16550A-compatible UART, loader; `Session` adapter with `Backend` | bundled demo uproc boots unmodified; the canned instrumentation script (`03 §12`) produces the same hook/watch/stop timeline and memory snapshots as QEMU (canonical comparison); determinism test green | 4–6 wk | +| M2 | Memory-map DSL + `falcon`/`csb`/`ptimer` devices + profile validation; native region snapshots; moments v1 | a re-hosted uproc build boots from a profile with no core changes; moment save/restore round-trips; restoring reproduces the event-stream hash | 4–6 wk | +| M3 | Deterministic scheduler (budgets, timers, IRQ events) + event log/replay | multi-task test profile replays identically from the log on two hosts; tie-break and replay tests in CI | 3–4 wk | +| M4 | Device-model parity with H2.2 + `dma`/`pmu_stub` + boot shims | real GSP image reaches the same stable state as the QEMU H2 exit under the same profile; differential suite passes for probe and boot fixtures; L3 parity documented, L2 still not claimed | 6–10 wk | +| M5 | Injection ABI + service registry + `log`/`stub`/`forward` interception | an SDK v1 blob loads and serves a control end-to-end; each interception mode has a recorded acceptance session; fuzzing seed/restore demonstrated | 4–6 wk | +| M6 | Performance/ergonomics: profiling, fast paths, `--backend native`, docs + flag-removal criteria | §14 targets met; `greeneye demo --backend native` in the differential CI job; user docs updated | 3–4 wk | + +Total: roughly 24–36 engineering-weeks, consistent with the roadmap's +"months; explicitly later" classification; none of it starts before the H3 +entry gate, and if H2 slips this plan waits. + +## 14. Differential testing and performance targets + +**Differential testing (the fidelity gate for every milestone).** Fixture: +the bundled demo firmware plus a re-hosted uproc build once M2 exists. The +same script runs on both backends; recordings are canonicalised (drop `t`, +host paths, backend identity, QEMU-log-only fields) and compared: identical +event-type sequence/counts; identical hook/watch hits and `pc` per stop; +byte-identical region snapshots at matched points; `exec`/`access` streams +equal in order, multiplicity and virtual timestamps at equal log levels. + +Divergence blocks the milestone and is filed with both session directories as +evidence (`06 §5`/`§7`); a declared fidelity gap is recorded, not waived. + +**Performance targets** (one host thread; a checked `greeneye-sim bench` +harness, trended in CI): + +| Metric | Target | Gate | +|---|---|---| +| RAM-bound tight loop (no logging) | ≥10 MIPS | M1 | +| Interpreter optimized | ≥50 MIPS | M6 | +| JIT (only if built) | ≥200 MIPS | post-M6, gated | +| Moment save, 256 MiB RAM, sparse changes | ≤100 ms | M3 | +| Moment restore | ≤50 ms | M3 | +| Watchpoint-enabled slowdown | ≤2× | M4 | +| MMIO access vs RAM access | ≤5× | M2 | +| Event bus throughput | ≥1 M events/s buffered, lossless under budget | M3 | + +## 15. CI strategy + +* The Python package and QEMU path remain the default, always-on suite; no + native artifact is required for it. +* Native tests carry a `native` marker and `pytest.importorskip`-style + detection: without the built backend they skip, never fail. A dedicated job + builds the core (cargo/maturin), runs native + differential tests, the + determinism double-run and profile-schema validation; nightly while M1 is + in flight, required at M1 exit. +* Real-image fixtures are opt-in (`GREENEYE_FW`); the default job uses the + bundled demo. Golden sessions are per backend and profile-hash checked. + +## 16. Annex — Verilog soft processor (roadmap H6, far later) + +**Scope (H6.1).** An RTL soft implementation of the GSP core environment: +RV64 core with TCM, MPU/PMP, vectored interrupts and timers; a device fabric +with falcon windows, CSB/TRANSCOM, DMA to host and WPR2/FWSEC state. RTL is +separate from `libgreenmachine`; it only shares profile data. + +**Run modes and integration (H6.2).** Verilator for determinism and +coverage, FPGA for speed, optionally both behind one co-simulation bridge. A +`Backend` talks to the simulation's clock/step and memory API: `step` becomes +clock edges, breakpoints become traps, watchpoints become signal/page +monitors, snapshots use saved simulation state. A new `cycle` event class +makes L2 timing *measurable* — only under docs/06 caveats, and physical +measurement is still required for any silicon claim. + +**Gates (H6.3).** Requires H2/H3 proven, a validated device map, and a funded +reason to care about cycle accuracy; not on the critical path and not before +the baseline project-roadmap goals. **Security posture:** a study +environment, never a confidentiality boundary, never shipped, never in a +guest; no isolation or side-channel claim may be based on it (`06 §6`). + +## 17. Risks and mitigations + +| Risk | Impact | Mitigation | +|---|---|---| +| Fidelity divergence from QEMU/hardware | wrong conclusions | differential suite from M1; milestones gated on canonical event agreement; declared gaps recorded per docs/06 | +| Two backends to maintain | effort/tool decay | minimal `Backend` interface (H3.4); shared `Session` primitives; QEMU stays the reference backend in CI | +| Effort creep before the gate | roadmap damage | explicit H2.2 entry gate; this document is design-only until then | +| Determinism leaks (clock, hashing, dict order, host input) | replay broken, trust lost | double-run determinism test; sequence-number tie-breaks; no host time in semantic fields | +| DSL insufficient for device semantics | profile churn, core edits | `Device` modules own whole windows; escape hatch is a module, never a core special case | +| Snapshot size/I/O blowup | snapshots unusable on real images | page-level COW + sparse encoding; region-scoped logging; moment retention policy | +| Injection misuse (real hardware/guest) | safety/security incident | local-only, dev-only tooling; no guest presence; interception only in the simulator; docs/12 policy checks | + +## 18. Open questions + +1. Which GC extensions must the first `rv64gc` profile cover, and can FPU + instructions be trapped and recorded rather than executed until then? +2. Are before-access watchpoints needed for injection, and how do their + recordings stay distinct from the QEMU-compatible profile? +3. What page size/encoding do moments need, what restore budget, and how much + RTOS semantics must be modelled for M3 (docs/09) without a timing claim? +4. Is Rust + Python sufficient, or does the profile DSL need a native loader, + and does an embedded public core's licence fit this repository (M4)? +5. How are multi-core interleavings represented so "same seed + same log" + stays the only replay contract, and which events must be byte-identical + across backends for the evidence rules? |
