1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
# 02 — risc-v-gsp-rm-debugger usage
> Status: implemented, documented and tested (audit: docs/27-TOOL-AUDIT.md; value demos: docs/25-VALUE-VALIDATION.md).
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
./risc-v-gsp-rm-debugger.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
./risc-v-gsp-rm-debugger.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
./risc-v-gsp-rm-debugger.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
./risc-v-gsp-rm-debugger.sh launch --kernel <elf> # prints: gdbstub: 127.0.0.1:<port>
./risc-v-gsp-rm-debugger.sh attach --port <port> --kernel <elf>
```
REPL commands:
| Command | Meaning |
|---|---|
| `regs` / `reg <name> [value]` | read/write registers |
| `x <addr> [len]` | annotated hexdump |
| `mem <addr> <len>` | raw bytes (hex) |
| `w <addr> <hex>` / `wstr <addr> <text>` | write memory |
| `bp <addr\|symbol> [sw\|hw]` | breakpoint that prints on hit |
| `hook <addr\|symbol>` | enter/leave hook with printing |
| `watch <addr\|symbol> [size] [w\|r\|a]` | watchpoint |
| `s [n]` / `c [seconds]` | step n; continue for N seconds |
| `snap <label> <addr> <len>` | snapshot a region into the session |
| `sym <addr\|name>` | symbol resolution |
| `events [n]` | tail the recorded event stream |
| `note <text>` | annotate the session |
| `quit` | close and exit |
Plugin-provided commands appear automatically (e.g. `gehits` from the example
plugin when loaded with `--plugin plugins/example_plugin.py`).
## 3. Scripted sessions
```bash
./risc-v-gsp-rm-debugger.sh run examples/scripts/trace_rpc.py
./risc-v-gsp-rm-debugger.sh run examples/scripts/dump_diff.py
./risc-v-gsp-rm-debugger.sh run my_script.py --kernel <elf> --plugin plugins/example_plugin.py
```
The script runs with `ge`, `s`/`session`, `memory`, `symbols`, `recorder` in
scope and is recorded by default. See [03-SCRIPTING](03-SCRIPTING.md).
## 4. Recording, analysis, comparison
```bash
./risc-v-gsp-rm-debugger.sh record --duration 2.0 --watch gsp_ctrl+0x10:8 --snapshot gsp_ctrl:392
./risc-v-gsp-rm-debugger.sh analyze dumps/20261002-082721-demo
./risc-v-gsp-rm-debugger.sh analyze dumps/<a> --symbols symbols.json > a.md
./risc-v-gsp-rm-debugger.sh compare dumps/<run-a> dumps/<run-b> --symbols
```
`analyze` prints a markdown report: event counts, breakpoint hits by symbol,
watchpoint hits, written pages, stepped coverage, snapshot inventory and a
timeline. `compare` diffs two sessions symbolically (hit-count deltas, watch
deltas, written-page deltas) — the A/B primitive for bare-metal vs
vfio-mdev and old-firmware vs new-firmware runs.
## 5. Probing an arbitrary firmware image
```bash
./risc-v-gsp-rm-debugger.sh probe # the project's TU104 570.144 fwimage
./risc-v-gsp-rm-debugger.sh probe path/to/fw.elf --duration 0.5
./risc-v-gsp-rm-debugger.sh probe --instructions 2000 --no-relocate
```
Segments are flattened into a raw kernel loaded at `0x80000000`; because QEMU
starts raw kernels at the load base, a tiny PC-relative trampoline at offset 0
jumps to the image's real (relocated) entry. The run is bounded and the report
shows the **first exception** (faulting instruction, access address/size,
cause) plus the QEMU log tail. For the real GSP image expect an early
load-access fault on an unmodelled GPU register — that is the honest result
(see [06-ACCURACY](06-ACCURACY.md)).
## 6. Symbol databases
```bash
./risc-v-gsp-rm-debugger.sh symbols export \
--names ../../userspace/cuda/570.144/annotations/names.tsv \
--index <run>/analysis/decompiled/decompiled_index.tsv \
--callgraph <run>/analysis/elf-index/callgraph.tsv \
--bias 0x100000 -o symbols-570.json
./risc-v-gsp-rm-debugger.sh symbols resolve --db symbols-570.json 0x453ef0
```
`--bias` normalises file vaddrs to runtime addresses (`0x100000` is the
libcuda Ghidra image base; use 0 for firmware linked at its load address).
## 7. Plugins
```bash
./risc-v-gsp-rm-debugger.sh plugins
./risc-v-gsp-rm-debugger.sh run examples/scripts/trace_rpc.py --plugin plugins/example_plugin.py
GREENEYE_PLUGINS=/path/to/plugins ./risc-v-gsp-rm-debugger.sh demo
```
See [04-PLUGINS](04-PLUGINS.md).
## 8. Make targets
| Target | Action |
|---|---|
| `make demo` | build the demo firmware |
| `make test` | build + unit tests + live QEMU smoke |
| `make demo-run` | `./risc-v-gsp-rm-debugger.sh demo --no-build` |
| `make probe-real` | run the TU104 fwimage probe |
| `make plugin` | build the optional TCG plugin |
| `make clean` | remove builds, dumps and `__pycache__` |
## 9. Troubleshooting
* **`qemu-system-riscv64 not found`** — install `qemu-system-misc` or set
`GREENEYE_QEMU`.
* **`gdbstub port not ready`** — QEMU exited early; the error includes its
stderr. Check the ELF actually loads (`./risc-v-gsp-rm-debugger.sh info`).
* **A run seems stuck** — sessions never `pkill -f greeneye` (it matches the
invoking shell). Use `pkill -9 -f '[q]emu-system-riscv64'` to clean up
leftover emulators.
* **Target stops immediately at `0x1000`** — the firmware was not passed to
QEMU; check the `-kernel` path (known historical bug, fixed).
* **`Permission denied` on `/dev/kvm`** — not needed here; QEMU `virt` may
fall back to TCG. For speed, ensure the user can use KVM.
## 10. Safety
risc-v-gsp-rm-debugger touches no GPU, no mdev, no kernel module and no host driver. It
runs unprivileged QEMU instances on loopback gdbstub ports. The only host
side effects are temporary directories under `/tmp` and `dumps/` under the
tool tree. It is safe to run alongside the CUDA/PLAN-B work, but keep an eye
on RAM/CPU if large snapshots are recorded.
|