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/.gitignore | 8 + debug/risc-v-gsp-rm-vm/Makefile | 31 + debug/risc-v-gsp-rm-vm/README.md | 77 +++ debug/risc-v-gsp-rm-vm/docs/00-OVERVIEW.md | 98 +++ debug/risc-v-gsp-rm-vm/docs/01-DESIGN.md | 179 ++++++ debug/risc-v-gsp-rm-vm/docs/02-USAGE.md | 168 +++++ debug/risc-v-gsp-rm-vm/docs/03-SCRIPTING.md | 219 +++++++ debug/risc-v-gsp-rm-vm/docs/04-PLUGINS.md | 123 ++++ debug/risc-v-gsp-rm-vm/docs/05-GHIDRA-BRIDGE.md | 105 +++ debug/risc-v-gsp-rm-vm/docs/06-ACCURACY.md | 136 ++++ .../docs/07-PORTABILITY-PLAYBOOKS.md | 126 ++++ debug/risc-v-gsp-rm-vm/docs/08-ROADMAP.md | 116 ++++ .../risc-v-gsp-rm-vm/docs/09-GSP-FIRMWARE-NOTES.md | 104 +++ debug/risc-v-gsp-rm-vm/docs/10-RELATED-WORK.md | 76 +++ .../examples/probes/demo_probes.json | 25 + .../risc-v-gsp-rm-vm/examples/scripts/dump_diff.py | 33 + .../risc-v-gsp-rm-vm/examples/scripts/trace_rpc.py | 29 + .../risc-v-gsp-rm-vm/firmware/uproc_demo/Makefile | 34 + .../risc-v-gsp-rm-vm/firmware/uproc_demo/ge_uart.h | 38 ++ .../firmware/uproc_demo/gsp_ctrl.schema.json | 19 + .../risc-v-gsp-rm-vm/firmware/uproc_demo/linker.ld | 54 ++ debug/risc-v-gsp-rm-vm/firmware/uproc_demo/main.c | 222 +++++++ debug/risc-v-gsp-rm-vm/firmware/uproc_demo/start.S | 10 + .../risc-v-gsp-rm-vm/ghidra/ExportForGreenEye.java | 46 ++ .../risc-v-gsp-rm-vm/ghidra/MarkGreenEyeHits.java | 80 +++ debug/risc-v-gsp-rm-vm/ghidra/README.md | 39 ++ debug/risc-v-gsp-rm-vm/greeneye.sh | 8 + debug/risc-v-gsp-rm-vm/greeneye/__init__.py | 115 ++++ debug/risc-v-gsp-rm-vm/greeneye/__main__.py | 3 + debug/risc-v-gsp-rm-vm/greeneye/analyze.py | 293 +++++++++ debug/risc-v-gsp-rm-vm/greeneye/cli.py | 688 ++++++++++++++++++++ debug/risc-v-gsp-rm-vm/greeneye/elf.py | 322 ++++++++++ debug/risc-v-gsp-rm-vm/greeneye/errors.py | 30 + debug/risc-v-gsp-rm-vm/greeneye/ghidra.py | 80 +++ debug/risc-v-gsp-rm-vm/greeneye/memory.py | 190 ++++++ debug/risc-v-gsp-rm-vm/greeneye/plugins.py | 121 ++++ debug/risc-v-gsp-rm-vm/greeneye/probes.py | 166 +++++ debug/risc-v-gsp-rm-vm/greeneye/record.py | 156 +++++ debug/risc-v-gsp-rm-vm/greeneye/rsp.py | 583 +++++++++++++++++ debug/risc-v-gsp-rm-vm/greeneye/session.py | 562 ++++++++++++++++ debug/risc-v-gsp-rm-vm/greeneye/structs.py | 197 ++++++ debug/risc-v-gsp-rm-vm/greeneye/symbols.py | 313 +++++++++ debug/risc-v-gsp-rm-vm/greeneye/target.py | 223 +++++++ debug/risc-v-gsp-rm-vm/plugins/README.md | 34 + debug/risc-v-gsp-rm-vm/plugins/example_plugin.py | 45 ++ debug/risc-v-gsp-rm-vm/qemu/Makefile | 16 + debug/risc-v-gsp-rm-vm/qemu/README.md | 28 + debug/risc-v-gsp-rm-vm/qemu/greeneye_trace.c | 128 ++++ debug/risc-v-gsp-rm-vm/qemu/launch.sh | 5 + debug/risc-v-gsp-rm-vm/qemu/qemu-plugin.h | 711 +++++++++++++++++++++ debug/risc-v-gsp-rm-vm/tests/__init__.py | 0 debug/risc-v-gsp-rm-vm/tests/test_core.py | 131 ++++ debug/risc-v-gsp-rm-vm/tests/test_memory_record.py | 101 +++ debug/risc-v-gsp-rm-vm/tests/test_probe.py | 65 ++ debug/risc-v-gsp-rm-vm/tests/test_rsp.py | 175 +++++ debug/risc-v-gsp-rm-vm/tests/test_smoke_qemu.py | 78 +++ 56 files changed, 7762 insertions(+) create mode 100644 debug/risc-v-gsp-rm-vm/.gitignore create mode 100644 debug/risc-v-gsp-rm-vm/Makefile create mode 100644 debug/risc-v-gsp-rm-vm/README.md create mode 100644 debug/risc-v-gsp-rm-vm/docs/00-OVERVIEW.md create mode 100644 debug/risc-v-gsp-rm-vm/docs/01-DESIGN.md create mode 100644 debug/risc-v-gsp-rm-vm/docs/02-USAGE.md create mode 100644 debug/risc-v-gsp-rm-vm/docs/03-SCRIPTING.md create mode 100644 debug/risc-v-gsp-rm-vm/docs/04-PLUGINS.md create mode 100644 debug/risc-v-gsp-rm-vm/docs/05-GHIDRA-BRIDGE.md create mode 100644 debug/risc-v-gsp-rm-vm/docs/06-ACCURACY.md create mode 100644 debug/risc-v-gsp-rm-vm/docs/07-PORTABILITY-PLAYBOOKS.md create mode 100644 debug/risc-v-gsp-rm-vm/docs/08-ROADMAP.md create mode 100644 debug/risc-v-gsp-rm-vm/docs/09-GSP-FIRMWARE-NOTES.md create mode 100644 debug/risc-v-gsp-rm-vm/docs/10-RELATED-WORK.md create mode 100644 debug/risc-v-gsp-rm-vm/examples/probes/demo_probes.json create mode 100644 debug/risc-v-gsp-rm-vm/examples/scripts/dump_diff.py create mode 100644 debug/risc-v-gsp-rm-vm/examples/scripts/trace_rpc.py create mode 100644 debug/risc-v-gsp-rm-vm/firmware/uproc_demo/Makefile create mode 100644 debug/risc-v-gsp-rm-vm/firmware/uproc_demo/ge_uart.h create mode 100644 debug/risc-v-gsp-rm-vm/firmware/uproc_demo/gsp_ctrl.schema.json create mode 100644 debug/risc-v-gsp-rm-vm/firmware/uproc_demo/linker.ld create mode 100644 debug/risc-v-gsp-rm-vm/firmware/uproc_demo/main.c create mode 100644 debug/risc-v-gsp-rm-vm/firmware/uproc_demo/start.S create mode 100644 debug/risc-v-gsp-rm-vm/ghidra/ExportForGreenEye.java create mode 100644 debug/risc-v-gsp-rm-vm/ghidra/MarkGreenEyeHits.java create mode 100644 debug/risc-v-gsp-rm-vm/ghidra/README.md create mode 100755 debug/risc-v-gsp-rm-vm/greeneye.sh create mode 100644 debug/risc-v-gsp-rm-vm/greeneye/__init__.py create mode 100644 debug/risc-v-gsp-rm-vm/greeneye/__main__.py create mode 100644 debug/risc-v-gsp-rm-vm/greeneye/analyze.py create mode 100644 debug/risc-v-gsp-rm-vm/greeneye/cli.py create mode 100644 debug/risc-v-gsp-rm-vm/greeneye/elf.py create mode 100644 debug/risc-v-gsp-rm-vm/greeneye/errors.py create mode 100644 debug/risc-v-gsp-rm-vm/greeneye/ghidra.py create mode 100644 debug/risc-v-gsp-rm-vm/greeneye/memory.py create mode 100644 debug/risc-v-gsp-rm-vm/greeneye/plugins.py create mode 100644 debug/risc-v-gsp-rm-vm/greeneye/probes.py create mode 100644 debug/risc-v-gsp-rm-vm/greeneye/record.py create mode 100644 debug/risc-v-gsp-rm-vm/greeneye/rsp.py create mode 100644 debug/risc-v-gsp-rm-vm/greeneye/session.py create mode 100644 debug/risc-v-gsp-rm-vm/greeneye/structs.py create mode 100644 debug/risc-v-gsp-rm-vm/greeneye/symbols.py create mode 100644 debug/risc-v-gsp-rm-vm/greeneye/target.py create mode 100644 debug/risc-v-gsp-rm-vm/plugins/README.md create mode 100644 debug/risc-v-gsp-rm-vm/plugins/example_plugin.py create mode 100644 debug/risc-v-gsp-rm-vm/qemu/Makefile create mode 100644 debug/risc-v-gsp-rm-vm/qemu/README.md create mode 100644 debug/risc-v-gsp-rm-vm/qemu/greeneye_trace.c create mode 100755 debug/risc-v-gsp-rm-vm/qemu/launch.sh create mode 100644 debug/risc-v-gsp-rm-vm/qemu/qemu-plugin.h create mode 100644 debug/risc-v-gsp-rm-vm/tests/__init__.py create mode 100644 debug/risc-v-gsp-rm-vm/tests/test_core.py create mode 100644 debug/risc-v-gsp-rm-vm/tests/test_memory_record.py create mode 100644 debug/risc-v-gsp-rm-vm/tests/test_probe.py create mode 100644 debug/risc-v-gsp-rm-vm/tests/test_rsp.py create mode 100644 debug/risc-v-gsp-rm-vm/tests/test_smoke_qemu.py (limited to 'debug') diff --git a/debug/risc-v-gsp-rm-vm/.gitignore b/debug/risc-v-gsp-rm-vm/.gitignore new file mode 100644 index 0000000..3a97916 --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/.gitignore @@ -0,0 +1,8 @@ +# Recorded sessions and built artifacts are never tracked. +dumps/ +firmware/uproc_demo/build/ +qemu/*.so +qemu/*.o +__pycache__/ +*.pyc +.run/ diff --git a/debug/risc-v-gsp-rm-vm/Makefile b/debug/risc-v-gsp-rm-vm/Makefile new file mode 100644 index 0000000..a5103f5 --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/Makefile @@ -0,0 +1,31 @@ +# green-eye convenience targets. The real work lives in the package. +PYTHON ?= python3 + +.PHONY: all demo test demo-run plugin probe-real clean info + +all: demo + +demo: + $(MAKE) -C firmware/uproc_demo + +test: demo + $(PYTHON) -m unittest discover -s tests -t . + +demo-run: demo + ./greeneye.sh demo --no-build + +info: + ./greeneye.sh info + +# Optional TCG plugin (needs the QEMU plugin headers; see qemu/README.md). +plugin: + $(MAKE) -C qemu + +# Run the real GSP fwimage for a bounded window and report first divergence. +probe-real: + ./greeneye.sh probe + +clean: + $(MAKE) -C firmware/uproc_demo clean + rm -rf dumps/* qemu/*.so qemu/*.o + find . -name __pycache__ -type d -exec rm -rf {} + 2>/dev/null || true diff --git a/debug/risc-v-gsp-rm-vm/README.md b/debug/risc-v-gsp-rm-vm/README.md new file mode 100644 index 0000000..66f53fa --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/README.md @@ -0,0 +1,77 @@ +# green-eye — GSP runtime analysis in a RISC-V QEMU VM + +> **# TOOL IN DEVELOPMENT - NOT READY YET!** +> Experimental tooling for simulating and inspecting the GSP firmware's +> runtime memory, for `gsp-rm-vfio` accuracy measurements and GSP/uproc +> reverse engineering. It is a development-only, out-of-tree tool: never a +> dependency of the shipped driver and never present in a guest. + +green-eye boots GSP/uproc firmware in `qemu-system-riscv64`, attaches over +QEMU's gdbstub with a GDB-free Python client, and exposes Frida-like +instrumentation: hooks, breakpoints, watchpoints, memory dumping, structure +decoding, snapshot diffing, session recording, plugins and a Ghidra bridge. + +Start with [`docs/00-OVERVIEW.md`](docs/00-OVERVIEW.md). + +## Quick start + +```bash +cd open-iov/debug/risc-v-gsp-rm-vm + +make demo # build the bundled RV64 uproc demo (clang + lld) +./greeneye.sh demo # boot it, hook handlers, watch counters, record + analyze +./greeneye.sh info # environment / tooling check +``` + +Interactively instrument a session: + +```bash +./greeneye.sh attach --kernel firmware/uproc_demo/build/uproc_demo.elf +greeneye> bp rpc_sum +greeneye> watch gsp_ctrl+0x10 8 w +greeneye> x 0x80000840 64 +greeneye> c 2 +``` + +Or launch a target separately and attach to it by port: + +```bash +./greeneye.sh launch --kernel # prints the gdbstub port, keeps QEMU alive +./greeneye.sh attach --port --kernel +``` + +Requirements: QEMU (`qemu-system-misc` on Ubuntu gives +`qemu-system-riscv64`), Python 3.10+, clang + lld for the demo firmware. +Everything else is Python stdlib. + +## Layout + +``` +greeneye/ Python package: RSP client, session, memory, symbols, + structures, recording, analysis, probes, plugins, Ghidra bridge +firmware/uproc_demo/RV64 bare-metal demo "uproc" (mailbox RPC, state machine, trap) +qemu/ launch helper + optional TCG plugin for full memory tracing +ghidra/ Ghidra scripts (export symbols, annotate hit addresses) +plugins/ example plugin + authoring guide +examples/ Python instrumentation scripts and declarative probe specs +tests/ unit tests + QEMU integration smoke test +docs/ full documentation set (design, scripting, plugins, accuracy, + portability playbooks, roadmap, GSP firmware notes) +dumps/ recorded sessions (gitignored) +``` + +## Documentation map + +| Doc | Content | +|---|---| +| [00-OVERVIEW](docs/00-OVERVIEW.md) | purpose, status, quickstart | +| [01-DESIGN](docs/01-DESIGN.md) | architecture and data model | +| [02-USAGE](docs/02-USAGE.md) | CLI and REPL recipes | +| [03-SCRIPTING](docs/03-SCRIPTING.md) | Python API + probe specs | +| [04-PLUGINS](docs/04-PLUGINS.md) | writing green-eye plugins | +| [05-GHIDRA-BRIDGE](docs/05-GHIDRA-BRIDGE.md) | static/dynamic address join | +| [06-ACCURACY](docs/06-ACCURACY.md) | simulation fidelity and measurement | +| [07-PORTABILITY-PLAYBOOKS](docs/07-PORTABILITY-PLAYBOOKS.md) | new GSP/device/arch runs | +| [08-ROADMAP](docs/08-ROADMAP.md) | MCP, custom simulator, device model | +| [09-GSP-FIRMWARE-NOTES](docs/09-GSP-FIRMWARE-NOTES.md) | uproc/falcon/NV-RISCV facts | +| [10-RELATED-WORK](docs/10-RELATED-WORK.md) | Frida/GDB/QEMU-plugin comparison | diff --git a/debug/risc-v-gsp-rm-vm/docs/00-OVERVIEW.md b/debug/risc-v-gsp-rm-vm/docs/00-OVERVIEW.md new file mode 100644 index 0000000..a9f4251 --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/docs/00-OVERVIEW.md @@ -0,0 +1,98 @@ +# 00 — green-eye overview + +> **# TOOL IN DEVELOPMENT - NOT READY YET!** +> Experimental tooling for simulating and inspecting the GSP firmware's +> runtime memory, for `gsp-rm-vfio` accuracy measurements and GSP/uproc +> reverse engineering. Interfaces and file formats may change without notice. +> It is development-only: never shipped, never a dependency of the emulation, +> never present in a guest. + +## What problem it solves + +Open-IOV emulates the GSP interface and (in the physical backend) boots the +real GSP firmware. Both efforts need the same missing capability: to *watch +the firmware do things* — which handler runs, what it reads and writes, what +it returns, how its state machine evolves — without a physical GPU, without +WPR2/hazard exposure, and repeatably. + +green-eye runs uproc/GSP firmware in `qemu-system-riscv64` and gives the +developer Frida-like instrumentation over its memory and execution: + +* breakpoints and enter/leave **hooks** (RV64 ABI aware), +* **watchpoints** on firmware variables, +* **memory** read/write/hexdump/search/scan, +* **structure decoding** from schemas, +* **snapshot + diff** of memory regions, +* **sessions**: a recorded, replayable dump of runtime memory activity, +* **post-run analysis** with symbol annotation, +* **plugins** for new tools, harnesses and virtualization updates, +* a **Ghidra bridge** joining static addresses to runtime hits, +* A/B tooling for **bare-metal vs vfio-mdev** result comparison. + +It is deliberately not a GDB wrapper: a pure-Python RSP client makes the +whole stack scriptable, and QEMU is just the first execution backend. + +## Status (v0.1, 2026-10-02) + +| Capability | State | +|---|---| +| RV64 QEMU boot + gdbstub transport (no GDB) | working, live-verified | +| Hooks / breakpoints / watchpoints / step / continue | working, live-verified | +| Memory tools + struct decode + snapshot diff | working, live-verified | +| Session recording + `analyze` report | working, live-verified | +| Declarative probe specs | working, live-verified | +| Plugin loader + example plugin | working | +| Test suite | 22 tests green (live QEMU smoke included) | +| Ghidra bridge | scripts + docs | +| Real GSP `.fwimage` probe (first-fault report) | working (honest: it does not boot without a TU104 model — see 06) | +| TCG full-memory tracing plugin | source + optional build | +| MCP server / custom simulator / full device model | roadmap only (08) | + +## Quick start + +```bash +cd open-iov/debug/risc-v-gsp-rm-vm +make demo # build the bundled RV64 uproc demo +./greeneye.sh demo # canned instrumentation pass, records a session +./greeneye.sh info # environment check +./greeneye.sh probe # run the real GSP fwimage and report first divergence +``` + +Interactively: + +```bash +./greeneye.sh demo --no-build --duration 0 # launches; prints the gdbstub port +./greeneye.sh attach --port --kernel firmware/uproc_demo/build/uproc_demo.elf +``` + +## Document map + +| Doc | Read it for | +|---|---| +| [01-DESIGN](01-DESIGN.md) | how the pieces fit, transport, data model, limits | +| [02-USAGE](02-USAGE.md) | CLI and REPL recipes | +| [03-SCRIPTING](03-SCRIPTING.md) | writing programs against the API | +| [04-PLUGINS](04-PLUGINS.md) | extending green-eye | +| [05-GHIDRA-BRIDGE](05-GHIDRA-BRIDGE.md) | static/dynamic address join | +| [06-ACCURACY](06-ACCURACY.md) | what the simulation does and does not prove | +| [07-PORTABILITY-PLAYBOOKS](07-PORTABILITY-PLAYBOOKS.md) | new GSP / device / architecture | +| [08-ROADMAP](08-ROADMAP.md) | MCP, custom simulator, device model | +| [09-GSP-FIRMWARE-NOTES](09-GSP-FIRMWARE-NOTES.md) | uproc/falcon/NV-RISCV facts used here | +| [10-RELATED-WORK](10-RELATED-WORK.md) | Frida/GDB/QEMU-plugin comparison | + +## Where this fits in the Open-IOV workflow + +green-eye is a **development and research** tool, alongside the Ghidra corpus +and the guest diagnostics. It does not replace either: + +* **Ghidra** answers *what is the code*; green-eye answers *what did it do*. +* **Guest diagnostics** (`tools/guest-diag`) answer *what did the guest + driver do*; green-eye answers *what did the firmware server do*. +* **The physical backend** (PLAN B) answers *what does real silicon do*; + green-eye provides a safe place to repeat, mutate and single-step the same + firmware when the hardware path is hazardous or unavailable. + +The intended accuracy loop is: run the same workload against the real +GSP-RM (hardware or mdev), record its RPC/state behaviour, then run the +firmware in green-eye, record the same events, and compare with `compare` +(see 06 and 07). diff --git a/debug/risc-v-gsp-rm-vm/docs/01-DESIGN.md b/debug/risc-v-gsp-rm-vm/docs/01-DESIGN.md new file mode 100644 index 0000000..c5de2da --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/docs/01-DESIGN.md @@ -0,0 +1,179 @@ +# 01 — green-eye design + +> **# TOOL IN DEVELOPMENT - NOT READY YET!** See 00 for status. + +## 1. Why this shape + +Two facts drive the design: + +1. **Frida does not run here.** Frida upstream ships no RISC-V target + (verified against the 17.20 release assets: x86/x86_64, arm/arm64, mips, + macOS/Windows/QNX/Prospero — no riscv64), and its agent model requires a + hosted process. The GSP image is bare-metal RV64IMAC with its own libos. + So the instrumentation layer is built from QEMU's gdbstub and QEMU TCG + plugins instead (see 10 for the full comparison). +2. **The project needs scripting, not a debugger.** The recurring workflow is + "run firmware, capture a symbol-annotated map of behaviour, diff it against + another run". A pure-Python RSP client makes that a library call; GDB + would make it a subprocess and a parsing problem. + +Design goals, in order: direct time-to-value, scriptability, extensibility, +robustness, and honest fidelity reporting. + +## 2. Component map + +``` + +-----------------------------+ + | qemu-system-riscv64 (virt) | + | -bios none -kernel | + | gdbstub <-- TCP | + +--------------+--------------+ + | RSP + greeneye/rsp.py pure-Python RSP client + | | + | registers, memory, Z0-Z4, continue/step/interrupt, qRcmd, qXfer + v + greeneye/target.py QEMU process lifecycle, loader segments, UART/qemu logs + greeneye/elf.py ELF64/32 parse: segments, symbols, entry + greeneye/symbols.py symbol DB (ELF + Ghidra TSV + JSON) + address translation + greeneye/session.py hooks, breakpoints, watchpoints, stop dispatch, run loop + greeneye/memory.py read/write/hexdump/search/snapshot/diff + greeneye/structs.py declarative structure schemas + render + greeneye/probes.py declarative probe specs + conditions + greeneye/record.py session dir: events.jsonl + snapshots + session.json + greeneye/analyze.py summary/compare reports (symbol annotated) + greeneye/plugins.py plugin discovery + API + greeneye/ghidra.py Ghidra export/import bridge + greeneye/cli.py info/demo/run/attach/probe/record/analyze/compare/symbols +``` + +## 3. Transport: the RSP client + +`rsp.py` implements the gdbstub subset QEMU needs: + +* packet framing `$#`, ack/no-ack, retransmit on bad checksum; +* `QStartNoAckMode` (QEMU acknowledges with an **empty packet**, then + switches once the client acks — a bring-up bug worth remembering); +* `qSupported`, including the stub's `PacketSize` (memory payloads are chunked + so hex expansion never exceeds it); +* `qXfer:features:read:target.xml`, recursively inlining `xi:include` register + files; RV64 register layout with a 33×8-byte fallback; +* `g`/`p`/`P`, `m`/`M`/`X`, `Z0-Z4`, `c`/`s`, interrupt `0x03`, `qRcmd`; +* stop-reply parsing (`T`/`S`/`W`/`X`, `watch:`/`rwatch:`/`awatch:`, + `thread:`). + +The client is deliberately blocking and simple; async/concurrency belongs in +the plugin layer, not the transport. + +## 4. Session semantics (the Frida-like core) + +* **Breakpoint kinds**: `sw` (default; stepped over transparently) and `hw`. +* **Hooks**: `hook(target, on_enter, on_leave)`. `on_leave` is implemented by + reading `ra` on entry and arming a *return breakpoint*; nested calls at the + same return address are queued (LIFO). Tail calls and asynchronous + interrupts are not tracked — documented limitation. +* **Watchpoints**: `Z2/Z3/Z4` (write/read/access). QEMU stops *at* the + accessing instruction, so green-eye temporarily clears the watch, steps one + instruction, and re-arms it. Without this the target livelocks on the same + store. +* **Stop dispatch**: every stop reads the register file, resolves the PC to a + symbol, records an event, invokes matching hooks/watchpoints, and resumes. +* **Run loop**: `run(duration=…, max_stops=…)` services events until the + wall-clock budget, stop budget, or target exit; the deadline path interrupts + **once**, dispatches that stop, and returns (never interrupt twice). +* **Faults**: `ebreak`/traps surface as plain stops; `step()` walks past them. + The demo uses this to demonstrate trap capture. + +## 5. Address spaces and the symbol layer + +Three address spaces must never be confused (the CUDA docs learned this the +hard way): + +| Space | Meaning | Example | +|---|---|---| +| file vaddr | offset in the binary file (`libcuda+0x…`) | `0x353ef0` | +| program addr | what Ghidra shows (file vaddr + image base) | `0x453ef0` (base `0x100000`) | +| runtime addr | link address + relocation bias | `0x80000048` | + +`SymbolDB` stores runtime addresses. Loaders take a `bias`: + +```python +db.load_elf(image, bias=relocation) # demo firmware: bias 0 +db.load_names_tsv("names.tsv", bias=0x100000) # libcuda: file vaddr -> runtime +db.load_index_tsv("decompiled_index.tsv") # program+file addr columns +db.load_callgraph("callgraph.tsv") # caller/callee extras +``` + +`resolve()` returns the containing symbol (or nearest preceding within a +delta), and `annotate()` renders `addr name+0xoff`. + +## 6. Recording format (`greeneye-session/1`) + +A session directory is the durable artifact: + +``` +dumps/-/ + session.json # format, name, created, counts, snapshot inventory, meta + events.jsonl # one JSON object per event, monotonic t + fields + snapshots/*.bin.z # zlib-compressed region captures (sha256 recorded) + blobs/* # plugin/probe dumps + summary.md # rendered analysis (analyze/render_summary) +``` + +Event types: `session_start`, `image_loaded`, `hook`, `watch`, `bp_hit`, +`leave`, `watch_hit`, `stop`, `step`, `mem_read`, `mem_write`, `snapshot`, +`snapshot_saved`, `probe_log`, `probe_dump`, `plugin*`, `note`, `exit`, +`session_end`. The format is append-only JSON lines so partial sessions stay +readable, and `compare` can profile two sessions symbolically. + +This is the "complete map and recorded dump of runtime memory activity": the +event stream is the map, the snapshot/blob files are the dump. + +## 7. Structures + +Schemas are JSON data (`structs.schema_from_json`) with integer, float, +`bytes`, `cstr` and `ptr` fields plus optional `enum` names and notes. The +demo control block schema lives at +`firmware/uproc_demo/gsp_ctrl.schema.json`. Schemas are intentionally +declarative so they can come from a Ghidra type export or a plugin later. + +## 8. Execution backends + +* `QemuTarget` (`virt`, RAM, NS16550A UART) is the default and the only + supported backend today. +* The **kernel fast path** passes `-kernel `; QEMU loads segments and + sets PC. +* The **probe path** passes each PT_LOAD segment as a raw loader device and + green-eye sets PC through the gdbstub — this lets a firmware linked for GPU + physical addresses be relocated into RAM for observation. +* A **custom simulator** can be added as a session backend later (roadmap, + not v0.1) because `Session` only assumes the RSP-like operations. + +## 9. Ghidra bridge + +* Out: `greeneye symbols export --names … --index … --exports … --callgraph …` + builds `greeneye-symbols.json` from the project's existing TSV exports. +* In: `greeneye.ghidra.export_hits(session_dir, out_tsv)` writes + `addrhitssymbol`, and `MarkGreenEyeHits.java` annotates those + functions in a Ghidra project (plate comments + bookmarks). +* Mapping a hit to source: symbols carrying `bucket`/`line` extras resolve to + the decompiled corpus via `greeneye.ghidra.corpus_location()`. + +## 10. Plugins + +`plugins.py` discovers modules and calls `register(ge)` with a `GreenEyeAPI` +(`session`, `memory`, `symbols`, `recorder`, `hook`, `watch`, `on_event`, +`add_command`, `log`). Errors are recorded as `plugin_error` and never break a +session. MCP is a planned plugin, not an implementation (08). + +## 11. Known limits (be honest) + +1. QEMU `virt` models **no TU104**: GPU MMIO, falcon windows, WPR2, PMU, + timers and FB DMA do not exist. The real GSP image will fault early; the + `probe` command reports exactly where. Not a boot environment (06). +2. The transport is blocking; no replay of async interrupts mid-run. +3. Return hooks miss tail calls and interrupt-driven returns. +4. `qRcmd` monitor commands are host-tooling only and not portable. +5. Recorded events describe *observed* stops; instructions executed between + stops are invisible unless the TCG plugin is enabled or a step trace is + taken. diff --git a/debug/risc-v-gsp-rm-vm/docs/02-USAGE.md b/debug/risc-v-gsp-rm-vm/docs/02-USAGE.md new file mode 100644 index 0000000..53cf19f --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/docs/02-USAGE.md @@ -0,0 +1,168 @@ +# 02 — green-eye usage + +> **# TOOL IN DEVELOPMENT - NOT READY YET!** See 00 for status. + +All commands are run from `open-iov/debug/risc-v-gsp-rm-vm/`. + +## 0. Requirements + +* `qemu-system-riscv64` (Ubuntu: `apt install qemu-system-misc`); +* Python 3.10+ (stdlib only); +* for the demo firmware: `clang` and `lld` (`apt install lld-18` provides + `/usr/lib/llvm-18/bin/ld.lld`). + +Check everything: + +```bash +./greeneye.sh info +make test # builds the demo then runs unit + QEMU smoke tests +``` + +## 1. The canned demo (fastest way to see it work) + +```bash +make demo +./greeneye.sh demo +``` + +It boots `firmware/uproc_demo/build/uproc_demo.elf`, installs hooks on all +`rpc_*` handlers and a watchpoint on `rpc_count`, runs the firmware self-test, +injects a `PING` from the host, decodes and diffs the control block, then +injects `FAULT` to capture the `ebreak` trap. A session is recorded under +`dumps/` and a `summary.md` is written. + +Options: `--no-build`, `--duration SECONDS`, `--kernel PATH`, `--record PATH`. + +## 2. Interactive REPL + +```bash +./greeneye.sh attach --kernel firmware/uproc_demo/build/uproc_demo.elf +``` + +`attach` launches the target itself when `--port` is omitted. To share a +target between terminals, launch it separately and attach by port: + +```bash +./greeneye.sh launch --kernel # prints: gdbstub: 127.0.0.1: +./greeneye.sh attach --port --kernel +``` + +REPL commands: + +| Command | Meaning | +|---|---| +| `regs` / `reg [value]` | read/write registers | +| `x [len]` | annotated hexdump | +| `mem ` | raw bytes (hex) | +| `w ` / `wstr ` | write memory | +| `bp [sw\|hw]` | breakpoint that prints on hit | +| `hook ` | enter/leave hook with printing | +| `watch [size] [w\|r\|a]` | watchpoint | +| `s [n]` / `c [seconds]` | step n; continue for N seconds | +| `snap