# 26 — risc-v-gsp-rm-debugger architecture: full picture, workflow and API surfaces > Status: implemented, documented and tested (audit: docs/27-TOOL-AUDIT.md; value demos: docs/25-VALUE-VALIDATION.md). > The architecture map for the risc-v-gsp-rm-debugger suite: how the pieces fit, which > surface to call, and which document specifies each part. Every claim here is > implemented in the tree or explicitly marked as a gated roadmap design. > Development-only: never shipped, never a driver dependency, never in a guest. ## 1. What risc-v-gsp-rm-debugger is (and is not) risc-v-gsp-rm-debugger runs GSP/uproc firmware in a controllable machine, observes it with Frida-like primitives, records a durable session, changes the machine's inputs (including injected code), and compares runs as evidence for `vgsp` emulation changes. | It is | It is not | |---|---| | A firmware runtime microscope (execution + memory, L0/L1) | A time, interrupt or device model (L2/L3 absent — [06](06-ACCURACY.md)) | | A scriptable `Session` API with two backends | A GDB wrapper; there is no GDB dependency | | A recorded-evidence system with a versioned format | A product component or a guest agent | | A static/dynamic join with Ghidra | A decompiler or a CUDA-corpus replacement | | An automation layer (harnesses, MCP, CI) | A fuzzing service against hardware; simulator/re-hosted code only | ## 2. The suite as a whole ```text front doors control plane evidence CLI/REPL/scripts ─┐ MCP (stdio) ──────┼─► GreenEyeAPI ─► Session ─┬─► QEMU-RSP backend ──┐ Ghidra bridge ────┤ (plugins) Memory │ qemu-system-riscv64│ harness/ ─────────┤ SymbolDB └─► native backend ────┼─► session dir rmharness ────────┘ Recorder sim/machine.py │ events.jsonl └─► analyze / compare / export_hits / bundles ``` ```mermaid flowchart LR CLI["CLI/REPL/scripts"] --> CORE MCP["MCP server"] --> CORE GH["Ghidra bridges"] --> CORE HAR["harness/*.py"] --> CORE RMH["rmharness (read-only)"] --> CORE CORE["GreenEyeAPI + Session
hooks, watchpoints, call, run_until
Memory, SymbolDB, Recorder"] CORE --> QB["QEMU-RSP backend"] --> SESS CORE --> NB["native backend (M1/M1.5)"] --> SESS SESS["session dir, greeneye-session/1"] --> REP["analyze / compare / export_hits / MCP / bundles"] RMH --> SESS ``` Status: transport, core, recording, both backends (native M1 trace included), injection ABI v1, both Ghidra bridges, traces, device map, harnesses, MCP (R3) and `rmharness` are implemented and exercised. The `Backend` protocol refactor (16 §5), the H2.2 QEMU device model, RM-core injection (H1.3) and the H3 M2–M6 milestones are design/roadmap only ([16](16-CUSTOM-SIMULATOR.md)). ## 3. Session core: hooks, watchpoints and dispatch `Session` ([greeneye/session.py](../greeneye/session.py)) owns all stop-driven state and is backend-agnostic: every stop reads registers, resolves the PC, records an event, invokes matching hooks/watchpoints, then resumes. ```mermaid sequenceDiagram S->>FW: set breakpoint / watchpoint loop run(duration, max_stops) FW-->>S: stop reply (T/S/W) S->>S: resolve PC via SymbolDB S->>R: bp_hit / watch_hit / stop / step alt symbol entry S->>H: on_enter(hit) with a0..a7 S->>S: arm return breakpoint from ra (LIFO) S->>FW: continue else return breakpoint S->>H: on_leave(hit), a0 as retval else watchpoint (Z2/Z3/Z4) S->>FW: clear watch, step over store, re-arm S->>H: callback + store PC end S->>R: on_event(kind, hit) end S->>R: session_end on close ``` | Primitive | Semantics / limits | |---|---| | `breakpoint(addr, kind=sw\|hw, temporary=)` | sw is stepped over transparently; hw uses the backend's `Z1` | | `hook(target, on_enter, on_leave)` | leave is a return breakpoint on `ra`; tail calls/interrupt returns are not tracked | | `watch(target, size, kind=w\|r\|a, callback)` | QEMU stops *at* the access; Session clears/steps/re-arms; store PC is recorded | | `run(duration, max_stops)` | services stops until a budget or exit; the deadline path interrupts once | | `run_until(target, timeout)` | temporary breakpoint; raises `RspTimeout` when not reached | | `call(target, args, timeout)` | Frida-like remote call; restores `RESTORE_REGS` around it | | `step(n)`, `note(text)`, `on_event(kind, fn)` | single-step with dispatch; annotate and observe the stream | Instructions between stops are invisible unless the TCG plugin (`qemu/greeneye_trace.c`) or the native trace (`sim/trace.py`) is on (01 §11, 16 §1.1a). ## 4. Backend abstraction: QEMU-RSP and native `Session` assumes a small RSP-shaped primitive set; `run_until` and `call` live above it and work on every backend. M1 implements the same method surface (`greeneye/sim_backend.py`) without the planned `Backend` protocol refactor (16 §5); the table is the M2+ target. ```mermaid flowchart LR S["Session (backend-independent)"] S -->|"read/write reg+mem, step, continue, Z0-Z4"| RC S -->|"same surface"| ST subgraph QEMU["QEMU-RSP backend (default)"] QT["QemuTarget
qemu-system-riscv64 virt"] --- RC["RspClient gdbstub packets"] end subgraph NAT["Native backend (--backend native)"] ST["SimTarget / SimClient"] --- MC["sim.machine.Machine
RAM + UART + decode loop"] MC --- TR["sim.trace.Trace exec/access"] MC --- DM["DeviceMap lenient MMIO"] end ``` | Session need | QEMU-RSP | Native | |---|---|---| | registers/memory | `g`/`p`/`P`, `m`/`M`/`X` chunked to `PacketSize` | direct register file and memory map | | breakpoints/watchpoints | `Z0`–`Z4` | dispatch-loop tables; same stop semantics | | snapshots | region copies over RSP | direct map copy; machine moments are M2+ | | virtual time | host wall clock | deterministic instruction count (`SIM_IPS`) | | full-access trace | TCG plugin (`-plugin`) | in-process `sim/trace.py`, `--trace` | | device environment | QEMU `virt` only (no TU104) | device map + lenient MMIO; real devices H2.2 | | injection | debugger writes + `Session.call` | same SDK ABI; RM interception is M5 | `--backend qemu|native` is accepted by `demo`, `run`, `attach`, `record`, `inject` and `ghidra`; `attach --port` and QEMU `--trace` are QEMU-only; `--trace` is native-only (16 §1.2). ## 5. Recording and the evidence format A session directory is the durable artifact (`greeneye-session/1`, [greeneye/record.py](../greeneye/record.py)). The event stream is the map; the snapshot and blob files are the dump. ```text dumps/-/ session.json # format, name, created, counts, snapshot inventory, meta events.jsonl # append-only JSON lines: {"t": monotonic, "type": ...} snapshots/*.bin.z # zlib-compressed region captures (sha256 recorded) blobs/* # QEMU logs, trace.jsonl, rmharness stdout, plugin dumps summary.md # rendered analysis ``` ```mermaid flowchart LR E["Session stops, memory ops,
probe/plugin/injection events"] --> R["Recorder.event()"] R --> J["events.jsonl (append-only)"] M["Memory.snapshot()"] --> Z["snapshots/*.bin.z + sha256"] B["Recorder.blob()"] --> BD["blobs/*: trace.jsonl, qemu.log"] X["rmharness external runs"] --> J J --> A["analyze: summary.md"] J --> C["compare: A/B delta report"] J --> G["export_hits: greeneye_hits.tsv"] J --> S10["value_demos s10: manifest + M1-M6 skeleton"] ``` Event classes: core stop/memory events (`session_start`, `image_loaded`, `hook`, `watch`, `bp_hit`, `leave`, `watch_hit`, `stop`, `step`, `mem_read`, `mem_write`, `snapshot*`, `probe*`, `plugin*`, `note`, `exit`, `session_end` — 01 §6); injection (`service_injected`, `call_enter`, `call_return` — 12 §6); traces (`exec`/`access`); M2/M3 native bus (`device`/`irq`/`schedule` — 16 §10); `rmharness` (`rm_harness_run`, `t=0.0` with epoch `start`/`end` — 17 §5). Format invariants: append-only (partial sessions stay readable); additive versioning with migration notes (V2); `compare` treats `t`, host paths and backend identity as non-semantic; a claim without a session is not evidence (06 §7, 25 §1). ## 6. Injection SDK lifecycle The SDK (`sdk/greeneye_service.h`, `sdk/injected_demo.c`) builds position-independent ELF64 `EM_RISCV` blobs; `greeneye/inject.py` loads, registers and invokes them. ABI v1: `ge_u64 (*)(ge_u64 cmd, ge_u64 a0, ge_u64 a1)`, magic `0x47455356`, entry `ge_demo_service_dispatch`. ```mermaid sequenceDiagram Dev->>Inj: load_service(elf, slot=6, load_addr) Inj->>FW: write PT_LOAD segments into RAM Inj->>FW: patch gsp_ctrl.handlers[slot] entry Inj->>Rec: service_injected + mem_write events Dev->>Inj: invoke_remote(cmd, a0, a1) Inj->>FW: Session.call(entry, args) Rec-->>Rec: call_enter / call_return Dev->>FW: mailbox = 16 + slot (arg0=cmd, arg1, arg2) FW->>FW: dispatch handlers[slot] FW-->>Rec: hook/watch events + result field ``` Demo registration: mailbox commands 16..23 select slots 0..7; slot 6 is the SDK default; control-block offsets (`mailbox +0x20`, `handlers +0x150`) are demo-specific and must come from `greeneye/inject.py` constants (12 §4). Cleanup is not automatic in v1; record the injection so runs start from a known state. Roadmap: RM-core shim, service registry and `log`/`stub`/ `forward` interception at M5/H1.3 (12 §8, 16 §11). ## 7. Ghidra bridges (two directions) The static/dynamic join uses three address spaces — file vaddr, Ghidra program addr, runtime addr — with a bias per source (01 §5, 05 §1). ```mermaid sequenceDiagram G->>B: {"id":n,"cmd":"run_until","args":{"target":"0x..."}} B->>S: run_until(addr) (temporary breakpoint) S->>T: continue; service intervening stops T-->>S: stop at addr S-->>B: {addr, symbol, kind} B-->>G: JSON response G->>B: read_mem / read_reg / call / snapshot / hits / export_hits B->>R: hits and notes recorded in the session R-->>G: greeneye_hits.tsv (via MarkGreenEyeHits.java) ``` | Tool | Purpose | Doc | |---|---|---| | `symbols export` + `SymbolDB` loaders | Ghidra TSV/JSON → runtime symbol DB | [05](05-GHIDRA-BRIDGE.md) | | `greeneye.ghidra.export_hits` | session hits → `addrhitssymbolannotation` | 05 | | `ExportForGreenEye.java` | Ghidra → `addrnamesize` TSV | 05 | | `MarkGreenEyeHits.java` | annotate functions with runtime hits + bookmarks | 05 | | `GreenEyeControl.java` + `risc-v-gsp-rm-debugger.sh ghidra` | drive a live session from GhidraScripts over localhost JSON-lines | [13](13-GHIDRA-CONTROL.md) | Bridge commands: `ping`, `info`, `resolve`, `bp_set`, `bp_clear`, `bp_list`, `watch_set`, `run_until`, `run`, `step`, `read_mem`, `write_mem`, `read_reg`, `write_reg`, `call`, `snapshot`, `note`, `hits`, `export_hits`, `close` (13 §3). Unauthenticated by design; localhost only. ## 8. MCP server tool surface `plugins/mcp_server.py` serves the active `Session` over JSON-RPC 2.0 on stdio (or loopback TCP) with no MCP library; read/observe by default, `write_mem`/`call_function` require `--allow-write`, `inject_service` requires `--allow-inject` ([20](20-MCP-SERVER.md)). ```mermaid flowchart LR AG["AI agent (Claude Code, opencode)"] -->|"JSON-RPC 2.0 over stdio"| M["plugins/mcp_server.py"] M --> READ["read: session_info, symbols_resolve,
read_mem, hexdump, snapshot, plugins_list"] M --> OBS["observe: bp_set/clear, watch_set,
run, run_until, step (recorded)"] M --> WR["write (gated): write_mem, call_function"] M --> INJ["inject (gated): inject_service"] M --> TR["trace: trace_start/stop"] M --> AN["analysis: analyze_current, compare_sessions, export_hits"] M --> HN["harness: probe_firmware, mutation_run,
portability_matrix_run"] M --> CL["session: close"] READ & OBS & WR & INJ & TR & AN & HN --> S["Session + Recorder"] ``` All results are one JSON text block; absent modules return a structured `not_available` error; stdout carries protocol frames only (20 §4, §8). Full argument schemas for all 31 tools are in 20 §5. ## 9. Automation harness `harness/` turns the playbooks into repeatable, self-describing runs. Modules bootstrap the tool root and use the QEMU backend by default (18, 19, 21, 28, 29); `common.py` provides flat-image relocation, session helpers and bounded log reads. ```mermaid flowchart TB FW["firmware ELF / .fwimage"] --> PM & MU & CD & RE & VD & LV SYM["Ghidra exports + symbol DBs"] --> PM & MU & CD & RE & VD & LV STIM["stimulus / probe spec"] --> PM & MU & CD & RE & VD & LV SEED["seed + budgets"] --> PM & MU & CD & RE & VD & LV subgraph H["harness/"] PM["portability_matrix.py R6/H4.1"] MU["mutation_harness.py R5/H4.3"] CD["control_discovery.py H4"] RE["reach.py H4.1/H4.5"] VD["version_diff.py H4.1"] CR["ci_release.py"] LV["value_demos/ s01..s10"] end VD --> CR PM --> CR MU --> CR PM & MU & CD & RE & VD & LV & CR --> OUT["reports + sessions + findings.jsonl
+ compare/ + bundle-manifest.json"] ``` | Entry point | Purpose | Outputs | Doc | |---|---|---|---| | `portability_matrix.py` | probe + symbols + fixed scenario + compare across targets; M1–M6 skeleton | `portability-report.json/.md`, `symbols/`, `sessions/`, `compare/` | 18 §1 | | `mutation_harness.py` | seeded one-input mutation, outcome classes, per-attempt session | `findings.jsonl`, `summary.md`, `sessions/` | 18 §2 | | `control_discovery.py` | sweep command/call surfaces; classify handler/default/trap/fault | `findings.jsonl`, `discovery-report.json`, `control-discovery.md` | 19 §3 | | `reach.py` | reachability, trigger discovery, required args and effects for one symbol | `reach-report.json/.md`, `probe.json`, sessions | 19 §4 | | `version_diff.py` | old vs new image: shape, symbols, corpus hints, probe, behavior, M1–M6 | `version-diff.json/.md`, `corpus/`, `compare/` | 21 §2 | | `ci_release.py` | version_diff → matrix → mutation smoke, then hash the bundle | `release-report.json`, `bundle-manifest.json` | 21 §4 | | `value_demos/run_all.py` | ten fixed scenarios (reply, counters, discovery, reach, diff, inject, version, mutation, Ghidra, evidence) | `artifacts/results.json` + per-demo JSON | 25 | | `port_driver_versions.py` | version-list sweep of version_diff + matrix + mutation (dry-run default; unadmitted pilot) | per-version bundles + `sweep-summary.json/.md` | 28 §2 | | `port_new_device.py` | device-port scaffold: profile, probe, symbols, matrix, checklist (dry-run default; unadmitted pilot) | `device-port.json/.md`, `port-checklist.json/.md`, bundle | 28 §3 | | `package_sim.py` | build/run a trial simulation package (unadmitted pilot) | package tree + `manifest.json`, `results.json` | 29 | | `common.py` | shared setup (relocate to `0x80000000`, open session, bounded logs) | library | 18 §1.2 | Harness limits are intentional: QEMU/re-hosted/demo code only; no hardware, guest, kernel module or existing gdbstub; bounded budgets; deterministic findings for a fixed seed (18 §5, 19 §8, 21 §7). ## 10. Device map and lenient MMIO data flow `greeneye-device-map/1` is versioned JSON keyed by `(address, width, direction)` with values, hit counts and firmware PCs ([24](24-DEVICE-MAP.md)). It records what the firmware touched first and lets the native interpreter step past unmodelled registers to collect the next ones; it feeds the H2.2 QEMU device model and the H3 profile DSL. ```mermaid flowchart LR P["risc-v-gsp-rm-debugger.sh probe (raw .fwimage in QEMU)"] -->|"blobs/qemu.log + qemu_first_fault"| H["device_map.py harvest"] H --> MAP["greeneye-device-map/1
entries + meta (bias, sha256)"] MAP --> LOOK["DeviceMap.lookup
zero / value / unimplemented"] LOOK --> LEN["Native Machine(unknown_mmio=lenient)"] LEN --> LOG["machine.mmio_log / mmio_unknown"] LEN --> LEARN["learn_mmio=True: source trace entries"] LEARN --> MAP MAP --> PROF["docs/16 profile DSL
falcon/csb/ptimer/pmu stubs"] PROF --> H22["H2.2 QEMU device model"] LOG --> NEXT["next unmapped wall; promote to manual"] NEXT --> MAP ``` Honest scope: lenient execution proves an unmodelled access *can* be served deterministically; it does not model read-back, side effects, interrupts or timing, and a zero that keeps the firmware running is a hypothesis, not a silicon fact (24 §4). ## 11. Workflow architecture: scenario → evidence → emulation change The canonical loop (H4.5, 06 §5, 07 P4, 25 §7). Each stage names its gate. | Stage | Action / instrument | Success criterion | Failure signal | |---|---|---|---| | 1. Scenario | state the firmware question and the smallest stimulus (one command/control/boot phase) | question maps to a symbol or surface | no observable exists → add one (probe/plugin) | | 2. Choose instrument | hooks/watchpoints for semantics; TCG/native trace for coverage; `run_until`/`call` for reachability; injection for new code | stop set answers the question without flooding | session too large/noisy → filter PCs, watch fields only | | 3. Run | fresh bounded session; fixed stimulus, seed and budgets | run completes; UART/banner liveness holds | timeout, trap loop, wedged target → capture and retry clean | | 4. Record | `greeneye-session/1` with snapshots and notes | events + snapshots + hashes written | missing `session.json`/empty analyzer → recording off | | 5. Compare | `compare` against a reference (old firmware, hardware/VFIO capture, self-diff) | deltas explained symbolically; self-diff is zero | unexplained hit/watch/write delta → investigate first | | 6. Decide | fill M1–M6 with the reference method (06 §5); mutation/triage for error paths | metric table improves or stays equal | metric claimed without a reference capture | | 7. Implement | change `vgsp` (never the simulator for a product fix); keep the stimulus | physical confirmation for behaviour changes (06 §6, 14 §7) | product change from a simulator-only observation | | 8. Regression | re-run step 3 with an unchanged stimulus; golden sessions/CI (`make test`, `risc-v-gsp-rm-debugger.yml`) | profile improves or stays equal; CI green | regression → revert and re-derive | ```mermaid flowchart LR Q["Question"] --> OBS["Observe
probe / hooks / watch / trace"] OBS --> ORI["Orient
analyze + symbol decode + snapshots"] ORI --> DEC["Decide
compare + M1-M6 + mutation"] DEC --> ACT["Act
implement in vgsp"] ACT --> REG["Regression
same stimulus, same budgets"] REG -->|"improved / equal"| SHIP["Evidence pack
sessions + report + hashes"] REG -->|"regressed"| Q SHIP --> OBS ``` OODA compression evidence (analog, measured) is in 25 §7: hardware cycles are minutes and campaigns hours/days; the risc-v-gsp-rm-debugger path for the same question class is sub-second to seconds plus scripting. ## 12. API surfaces by tool ### 12.1 CLI (`risc-v-gsp-rm-debugger.sh`, `greeneye/cli.py`; usage in [02](02-USAGE.md)) | Command | Purpose | Not for | |---|---|---| | `info` | environment/tooling check | validating a firmware image | | `demo` | canned boot + hooks + watch + record on the bundled uproc | booting real GSP (use `probe`) | | `run ` | run a Python instrumentation session | shell pipelines (write Python) | | `attach` / `launch` | interactive REPL / keep a target alive by port | injecting into a live product | | `record` | boot and record with watches/snapshots/trace | post-processing an existing session | | `probe ` | bounded run of a raw image; first divergence report | booting the image (no TU104 model) | | `inject` | load/register/invoke an SDK service | shipping or hot-patching signed firmware | | `ghidra` | serve a live session to GhidraScripts | network exposure (localhost only) | | `analyze ` / `compare ` | markdown report / symbolic A/B delta | proving accuracy without a reference | | `symbols export\|resolve` | build/inspect a symbol DB | recovering types/decompilation | | `plugins` | list discoverable plugins | loading untrusted code into the core | | `python3 -m greeneye.device_map harvest\|show\|merge\|demo` | MMIO first-touch map data | a device model or timing source | ### 12.2 Python entry points (recipes in [03](03-SCRIPTING.md)) | Entry point | Purpose | Not for | |---|---|---| | `ge.launch/attach`, `Session.hook/watch/run/run_until/call/step/note` | session lifecycle and instrumentation | in-process C instrumentation | | `Memory.read/write/hexdump/search_*/snapshot/diff/render_diff` | memory inspection and region diff | full-machine snapshots (M2+) | | `SymbolDB.load_elf/*_tsv/*_json`, `resolve/annotate` | address-space-aware symbols | type recovery from Ghidra | | `structs.schema_from_json/decode/render` | declarative struct decoding | automatic struct discovery | | `probes.load_spec/install` | JSON probe specs (no arbitrary code) | complex control flow (write a script) | | `inject.load_service/invoke_remote/invoke_via_firmware` | ABI v1 service load/invoke | product features or persistence | | `ghidra.build_symbol_db/export_hits/corpus_location` | Ghidra TSV in/out and corpus location | replacing Ghidra analysis | | `analyze.render_summary/compare_sessions/parse_qemu_faults/load_trace` | offline reports and QEMU fault parsing | live control | | `record.Recorder/load_events/load_meta` | session writing/reading | a database (files are the interface) | | `device_map.harvest_*/DeviceMap.*` | MMIO map authoring/merge | device semantics | | `sim_backend.launch_native`, `sim.trace.Trace/make_trace` | native backend + trace knobs | timing/concurrency claims | | `errors.*` (`GreenEyeError`, `RspTimeout`, ...) | typed failure surface | retrying a wedged target blindly | ### 12.3 Plugin API (`register(ge)`, [04](04-PLUGINS.md)) `ge` exposes live objects (`session`, `memory`, `symbols`, `recorder`), instrumentation (`hook`, `watch`, `on_event`, `resolve`), CLI extension (`add_command(name, fn, help)`, `commands`) and audit logging (`log`). A raising plugin is recorded as `plugin_error` and skipped. Not for: shipping dependencies, assuming a specific firmware, or hiding unrecorded state. ## 13. Extensibility and invariants Where extension lives: * **Plugins** (`plugins/`, `GREENEYE_PLUGINS`) — runtime RE tools, harnesses, environment updates, agent integrations (04). * **Harness modules** (`harness/`) — new sweeps/reports over the public `Session`/analysis primitives; a new call surface is a `Surface` subclass (19 §5.1). * **SDK services** (`sdk/`) — PIC blobs behind ABI v1; v2 adds an RM-core call table (12 §8). * **Device maps/profiles** — versioned data (`greeneye-device-map/1`, the `greenmachine-profile/1` DSL in 16 §7), never core special cases. * **MCP tools** (`plugins/mcp_server.py`) — add tools in the plugin, keep the core stdlib-only (20 §10). Invariants (enforced by tests or review): 1. **stdlib-only core**: `greeneye/` and `sim/` import only the standard library; optional deps live in plugins/harnesses. 2. **Session format**: `greeneye-session/1` is append-only and evolves additively with migration notes (V2). 3. **Backend parity**: every backend satisfies the same `Session` primitives, so scripts, probes, plugins, recordings and comparisons survive backend changes (16 §4, R0). 4. **ABI**: blobs declare an ABI version and capability metadata; breaking changes need a shim and a doc note (12 §8, V3). 5. **Markers**: the in-development notices were removed on 2026-10-02 after the tool audit (27) and value demos (25); gated items stay marked as such. Never shipped, never in a guest, never a driver dependency. 6. **Evidence**: no accuracy, portability or security claim without a recorded A/B session and an explicit L2/L3 fidelity statement (06 §5–6, 25 §10). 7. **Provenance**: tracked docs describe components and behaviour; conclusions cite recorded evidence (V6). ## 14. Diagram and document index Section diagrams and their backing docs: §2 suite (00, 01, README); §3 dispatch (01, 03); §4 backends (01 §8, 16 §4–5); §5 recording (01 §6, 09, 12 §6, 16 §10, 17 §5); §6 injection (11, 12); §7 Ghidra (05, 13); §8 MCP (20); §9 harness (18, 19, 21, 28, 29); §10 device map (24, 16 §7); §11 OODA (06, 07 P4, 25 §7). Roadmap keys: R3 = MCP (08), R5 = mutation (18), R6 = multi-version (18, 21), H1.3 = RM service injection (RISC-V-GSP-RM-DEBUGGER-ROADMAP), H2.2 = QEMU device model, H3/M1–M6 = native simulator (16), H4 = adversarial navigation (14), H7 = agentic platform (15, 20).