From ba58dcdc5ad6ca021c723d279f33e3f14e1184df Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Fri, 2 Oct 2026 08:36:35 -0400 Subject: debug: add green-eye GSP/uproc RISC-V VM runtime analysis tooling Adds open-iov/debug/risc-v-gsp-rm-vm: a GDB-free instrumentation stack that boots uproc/GSP firmware in qemu-system-riscv64 and provides Frida-like hooks, breakpoints, watchpoints, memory dumps, structure decoding, snapshot diffs, recorded sessions, declarative probes, a plugin API and a Ghidra symbol/hit bridge. Verified: 25 tests green (including a live QEMU smoke test); the demo uproc boots with correct hook/leave/watch/schema/snapshot/fault evidence; the optional TCG trace plugin builds and recorded 15M trace lines in 3 s; the TU104 570.144 fwimage probe reports its first exception (load access fault at firmware vaddr 0x4009014 reading 0x2000000000000A00). Documentation: lean tool docs under debug/risc-v-gsp-rm-vm/docs/00-10, a new docs/05-operations/03-risc-v-gsp-rm-vm.md debugging document, and '# TOOL IN DEVELOPMENT - NOT READY YET!' markers in docs/README.md, the onboarding tooling table and the debugging workflows doc. Vendors qemu-plugin.h from QEMU v8.2.2 (GPL-2.0-or-later), with its license header intact, so the optional TCG plugin builds without a QEMU source tree. --- debug/risc-v-gsp-rm-vm/docs/04-PLUGINS.md | 123 ++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 debug/risc-v-gsp-rm-vm/docs/04-PLUGINS.md (limited to 'debug/risc-v-gsp-rm-vm/docs/04-PLUGINS.md') diff --git a/debug/risc-v-gsp-rm-vm/docs/04-PLUGINS.md b/debug/risc-v-gsp-rm-vm/docs/04-PLUGINS.md new file mode 100644 index 0000000..fa0fe0c --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/docs/04-PLUGINS.md @@ -0,0 +1,123 @@ +# 04 — writing green-eye plugins + +> **# TOOL IN DEVELOPMENT - NOT READY YET!** See 00 for status. + +A plugin is a Python module exposing `register(ge)`. It is loaded before the +session runs (CLI `--plugin PATH`, `GREENEYE_PLUGINS`, or the bundled +`plugins/` directory) and can add instrumentation, CLI commands and harness +logic without touching green-eye itself. + +## 1. The API object + +```python +def register(ge): + # live objects + ge.session # Session + ge.memory # Memory + ge.symbols # SymbolDB + ge.recorder # Recorder | NullRecorder + + # instrumentation + ge.hook(target, on_enter=None, on_leave=None, kind="sw") + ge.watch(target, size=8, kind="w", callback=None) + ge.on_event(kind, callback) # kind in {"*", "bp_hit", "watch_hit", ...} + ge.resolve(target) # symbol/addr -> runtime address + + # CLI + logging + ge.add_command("name", fn, help="…") # fn(session, args: list[str]) -> int + ge.log("message", any="field") # recorded as a "plugin" event + ge.commands # dict of registered commands +``` + +Everything a plugin records lands in the session's `events.jsonl`, so plugin +findings are replayable and comparable like any other event. + +## 2. Lifecycle and safety + +* `register()` runs once, immediately after the session is attached and + symbols are loaded; the target is still stopped, so hooks are in place + before the first instruction. +* A plugin that raises is recorded as `plugin_error` and skipped; it never + aborts the session or the other plugins. +* Session close detaches all hooks/watchpoints, including plugin-installed + ones. +* Plugins must be stdlib-only unless the dependency is documented; they are + development tooling and never shipped. + +## 3. The four plugin classes + +**(a) Runtime RE tools** — add analysis that green-eye does not have: + +```python +def register(ge): + def on_state(hit): + ge.recorder.event("state_machine", result=hit.retval, pc=hit.pc) + ge.hook("rpc_state", on_leave=on_state) +``` + +Useful additions: allocation graphs, ring-buffer watermarks, page-table walks, +coverage collectors built from `on_event("stop")` plus the TCG trace, entropy +or checksum probes over structures. + +**(b) Harnesses** — drive the firmware and assert: + +```python +def register(ge): + def check(session, args): + ctrl = session.symbols.lookup("gsp_ctrl") + before = session.memory.read_u64(ctrl + 0x10) + session.memory.write_u64(ctrl + 0x20, 1) # PING + session.run(duration=0.3) + after = session.memory.read_u64(ctrl + 0x10) + assert after == before + 1, "PING did not dispatch" + print("PING ok") + return 0 + ge.add_command("check-ping", check) +``` + +**(c) Virtualization-environment updates** — alter how the target is built or +driven: extra QEMU args, a rehosting shim for uproc sources, a device-model +stub, a replay layer that feeds recorded events back into a run. Keep such +changes behind plugin-level flags and document them in the session metadata +(`s.recorder.meta`). + +**(d) AI/harness integration** — export sessions/reports to external tools, +generate probe specs, score runs. **MCP servers are planned as a plugin** +(`docs/08-ROADMAP.md`), not part of v0.1; do not add MCP dependencies to the +core package before that rung is scheduled. + +## 4. Example plugin + +`plugins/example_plugin.py` demonstrates (a), (b) and logging: + +```python +def register(ge): + ge.log("example plugin loaded") + def on_state(hit): + ge.recorder.event("plugin_state_transition", result=hit.retval) + if ge.symbols.get("rpc_state"): + ge.hook("rpc_state", on_leave=on_state) + ge.add_command("gehits", lambda session, args: print("…and 0"), help="…") +``` + +Run it with: + +```bash +./greeneye.sh run examples/scripts/trace_rpc.py --plugin plugins/example_plugin.py +``` + +## 5. Testing a plugin + +1. Load it against the demo firmware (`./greeneye.sh demo --plugin …`) — the + session must remain green and the plugin's events must appear in + `events.jsonl`. +2. Unit-test pure logic outside green-eye; keep hooks small. +3. Never let a plugin assume a particular firmware: gate on + `ge.symbols.get(name)` and log a skip instead of failing. + +## 6. Distribution + +Plugins can live anywhere. For in-tree plugins that document the workflow, +put them under `plugins/` in this tool directory; for local or +target-specific tooling, use `GREENEYE_PLUGINS` and keep paths out of tracked +docs (project rule). -- cgit v1.2.3