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/greeneye/__init__.py | 115 ++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 debug/risc-v-gsp-rm-vm/greeneye/__init__.py (limited to 'debug/risc-v-gsp-rm-vm/greeneye/__init__.py') diff --git a/debug/risc-v-gsp-rm-vm/greeneye/__init__.py b/debug/risc-v-gsp-rm-vm/greeneye/__init__.py new file mode 100644 index 0000000..8b1c1e5 --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/greeneye/__init__.py @@ -0,0 +1,115 @@ +"""green-eye: Frida-like runtime instrumentation for GSP/uproc firmware in a +RISC-V QEMU VM. + +green-eye talks to QEMU's gdbstub directly (no GDB dependency) and layers a +scriptable instrumentation API on top: breakpoints, hooks, watchpoints, +memory inspection, structure decoding, session recording and post-run analysis. + +Public API (stable-ish for v0.1): + + import greeneye as ge + s = ge.launch(kernel="firmware/uproc_demo/build/uproc_demo.elf") + fn = s.symbols.lookup("rpc_sum") + h = s.hook(fn, on_enter=lambda hit: print(hit)) + s.run(duration=2.0) + s.close() + +Everything is development tooling; see docs/00-OVERVIEW.md. +""" +from __future__ import annotations + +__version__ = "0.1.0" + +from .errors import ( # noqa: F401 + GreenEyeError, + RspError, + RspTimeout, + RspProtocolError, + SymbolNotFound, + TargetError, + StructError, +) +from .rsp import RspClient, StopReply, Register # noqa: F401 +from .elf import FirmwareImage, Segment, ElfSymbol, load_elf # noqa: F401 +from .symbols import Symbol, SymbolDB # noqa: F401 +from .memory import Memory # noqa: F401 +from .structs import Field, Schema, decode, schema_from_json # noqa: F401 +from .record import Recorder, NullRecorder, load_events # noqa: F401 +from .session import Session, Hit, HookHandle, Breakpoint, Watchpoint # noqa: F401 +from .target import QemuTarget # noqa: F401 + +__all__ = [ + "attach", + "launch", + "Session", + "Hit", + "HookHandle", + "Breakpoint", + "Watchpoint", + "QemuTarget", + "RspClient", + "SymbolDB", + "Symbol", + "Memory", + "Recorder", +] + + +def attach( + host: str = "127.0.0.1", + port: int = 4444, + *, + timeout: float = 10.0, + record: str | None = None, + symbols: SymbolDB | None = None, + name: str = "attach", +) -> Session: + """Attach to an already-running QEMU gdbstub and return a Session.""" + client = RspClient(host, port, timeout=timeout) + client.connect() + client.sync() + return Session(client=client, symbols=symbols, record=record, name=name) + + +def launch( + *, + kernel: str | None = None, + machine: str = "virt", + memory_mb: int = 256, + gdb_port: int = 0, + bios: str = "none", + extra_args: list[str] | None = None, + timeout: float = 10.0, + record: str | None = None, + symbols: SymbolDB | None = None, + name: str = "session", + load_symbols: bool = True, + keep_alive: bool = False, +) -> Session: + """Launch QEMU (paused), attach, and return a ready Session. + + The target starts paused at its reset vector; use Session.load_image() to + load an ELF (which also sets PC), or QemuTarget.run_kernel() style helper + launch with kernel= for the fast `-kernel` path. + """ + target = QemuTarget( + machine=machine, + memory_mb=memory_mb, + gdb_port=gdb_port, + bios=bios, + extra_args=extra_args, + kernel=kernel, + ) + if keep_alive: + target.keep_alive = True + target.start(paused=True) + client = RspClient(target.host, target.port, timeout=timeout) + client.connect() + client.sync() + + symdb = symbols or SymbolDB() + sess = Session(client=client, target=target, symbols=symdb, record=record, name=name) + if kernel: + image = load_elf(kernel) + sess.load_image(image, load_symbols=load_symbols) + return sess -- cgit v1.2.3