summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/firmware/uproc_demo/main.c
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-02 08:36:35 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-02 08:36:35 -0400
commitba58dcdc5ad6ca021c723d279f33e3f14e1184df (patch)
tree47e43a2dad9a2d4bb40da46243654bff8b1302de /debug/risc-v-gsp-rm-vm/firmware/uproc_demo/main.c
parentfe485080e159c983242d699f4a584659df159818 (diff)
downloadopen-iov-ba58dcdc5ad6ca021c723d279f33e3f14e1184df.tar.gz
open-iov-ba58dcdc5ad6ca021c723d279f33e3f14e1184df.zip
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.
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/firmware/uproc_demo/main.c')
-rw-r--r--debug/risc-v-gsp-rm-vm/firmware/uproc_demo/main.c222
1 files changed, 222 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/firmware/uproc_demo/main.c b/debug/risc-v-gsp-rm-vm/firmware/uproc_demo/main.c
new file mode 100644
index 0000000..d23b00c
--- /dev/null
+++ b/debug/risc-v-gsp-rm-vm/firmware/uproc_demo/main.c
@@ -0,0 +1,222 @@
+/* green-eye uproc demo firmware.
+ *
+ * A small, deterministic "GSP-like" service:
+ * - gsp_ctrl global control block (magic 0x47535031) in .gsp_data;
+ * - mailbox-driven RPC dispatcher with a handler table;
+ * - handlers: PING, ECHO, FILL, SUM, STATE (4-stage machine), FAULT (ebreak);
+ * - a self-test sequence at boot so a freshly attached debugger sees events;
+ * - a free-running heartbeat so memory activity is always present.
+ *
+ * No libc: memcpy/memset are provided for the compiler.
+ */
+#include "ge_uart.h"
+
+#define GSP_MAGIC 0x47535031u
+
+typedef struct {
+ u32 magic; /* +0x00 */
+ u32 version; /* +0x04 */
+ u32 state; /* +0x08 */
+ u32 heartbeat; /* +0x0c */
+ u64 rpc_count; /* +0x10 */
+ u64 last_rpc; /* +0x18 */
+ u64 mailbox; /* +0x20 */
+ u64 arg0; /* +0x28 */
+ u64 arg1; /* +0x30 */
+ u64 result; /* +0x38 */
+ char buf[256]; /* +0x40 */
+ u64 handler_count; /* +0x140 */
+ u64 handlers[8]; /* +0x148 */
+} gsp_ctrl_t;
+
+volatile gsp_ctrl_t gsp_ctrl __attribute__((section(".gsp_data"), aligned(64)));
+
+/* ---- compiler support (freestanding) ---- */
+
+void *memcpy(void *dst, const void *src, u64 n)
+{
+ u8 *d = (u8 *)dst;
+ const u8 *s = (const u8 *)src;
+ while (n--) {
+ *d++ = *s++;
+ }
+ return dst;
+}
+
+void *memset(void *dst, int value, u64 n)
+{
+ u8 *d = (u8 *)dst;
+ while (n--) {
+ *d++ = (u8)value;
+ }
+ return dst;
+}
+
+static u64 strlen_local(const char *s)
+{
+ u64 n = 0;
+ while (s[n]) {
+ n++;
+ }
+ return n;
+}
+
+/* ---- RPC handlers (kept as symbols for green-eye hooks) ---- */
+
+__attribute__((noinline)) u64 rpc_ping(u64 a0, u64 a1)
+{
+ (void)a1;
+ return a0 ^ 0xbeefu;
+}
+
+__attribute__((noinline)) u64 rpc_echo(u64 a0, u64 a1)
+{
+ (void)a1;
+ const char *src = (const char *)a0;
+ u64 n = strlen_local(src);
+ if (n > sizeof(gsp_ctrl.buf) - 1) {
+ n = sizeof(gsp_ctrl.buf) - 1;
+ }
+ memcpy((void *)gsp_ctrl.buf, src, n);
+ gsp_ctrl.buf[n] = '\0';
+ return n;
+}
+
+__attribute__((noinline)) u64 rpc_fill(u64 a0, u64 a1)
+{
+ u64 n = a1 > sizeof(gsp_ctrl.buf) ? sizeof(gsp_ctrl.buf) : a1;
+ memset((void *)gsp_ctrl.buf, (int)(a0 & 0xff), n);
+ return n;
+}
+
+__attribute__((noinline)) u64 rpc_sum(u64 a0, u64 a1)
+{
+ const u8 *p = (const u8 *)a0;
+ u64 total = 0;
+ for (u64 i = 0; i < a1; i++) {
+ total += p[i];
+ }
+ return total;
+}
+
+__attribute__((noinline)) u64 rpc_state(u64 a0, u64 a1)
+{
+ (void)a0;
+ (void)a1;
+ u64 stage = gsp_ctrl.state & 3u;
+ u64 out = 0;
+ switch (stage) {
+ case 0:
+ out = 0x10;
+ break;
+ case 1:
+ out = 0x20;
+ break;
+ case 2:
+ out = 0x30;
+ break;
+ default:
+ out = 0x40;
+ break;
+ }
+ gsp_ctrl.state = (u32)((stage + 1) & 3u);
+ return out;
+}
+
+__attribute__((noinline)) u64 rpc_fault(u64 a0, u64 a1)
+{
+ (void)a0;
+ (void)a1;
+ __asm__ volatile("ebreak"); /* debugger trap: green-eye stops here */
+ return 0xdead;
+}
+
+/* ---- dispatcher ---- */
+
+typedef u64 (*rpc_fn)(u64, u64);
+
+void rpc_dispatch(u64 cmd, u64 a0, u64 a1)
+{
+ u64 result;
+ switch (cmd) {
+ case 1:
+ result = rpc_ping(a0, a1);
+ break;
+ case 2:
+ result = rpc_echo(a0, a1);
+ break;
+ case 3:
+ result = rpc_fill(a0, a1);
+ break;
+ case 4:
+ result = rpc_sum(a0, a1);
+ break;
+ case 5:
+ result = rpc_state(a0, a1);
+ break;
+ case 6:
+ result = rpc_fault(a0, a1);
+ break;
+ default:
+ result = ~cmd;
+ break;
+ }
+ gsp_ctrl.result = result;
+ gsp_ctrl.last_rpc = cmd;
+ gsp_ctrl.rpc_count = gsp_ctrl.rpc_count + 1; /* after the handler */
+}
+
+static void install_handlers(void)
+{
+ gsp_ctrl.handlers[0] = (u64)(unsigned long)rpc_ping;
+ gsp_ctrl.handlers[1] = (u64)(unsigned long)rpc_echo;
+ gsp_ctrl.handlers[2] = (u64)(unsigned long)rpc_fill;
+ gsp_ctrl.handlers[3] = (u64)(unsigned long)rpc_sum;
+ gsp_ctrl.handlers[4] = (u64)(unsigned long)rpc_state;
+ gsp_ctrl.handlers[5] = (u64)(unsigned long)rpc_fault;
+ gsp_ctrl.handler_count = 6;
+}
+
+int main(void)
+{
+ volatile gsp_ctrl_t *ctl = &gsp_ctrl;
+ ctl->magic = GSP_MAGIC;
+ ctl->version = 1;
+ ctl->state = 0;
+ ctl->heartbeat = 0;
+ ctl->rpc_count = 0;
+ ctl->last_rpc = 0;
+ ctl->mailbox = 0;
+ ctl->result = 0;
+ ctl->buf[0] = '\0';
+ install_handlers();
+
+ uart_puts("GREEN-EYE UPROC DEMO v1\n");
+ uart_puts("MAGIC ");
+ uart_hex(ctl->magic, 8);
+ uart_puts(" CTRL@");
+ uart_hex((u64)(unsigned long)ctl, 8);
+ uart_puts("\n");
+
+ /* Deterministic self-test so freshly attached instrumentation sees events. */
+ rpc_dispatch(1, 0x1111, 0);
+ rpc_dispatch(2, (u64)(unsigned long)"hello-green-eye", 0);
+ rpc_dispatch(3, 0x5a, 32);
+ rpc_dispatch(4, (u64)(unsigned long)ctl->buf, 16);
+ rpc_dispatch(5, 0, 0);
+ rpc_dispatch(5, 0, 0);
+ rpc_dispatch(5, 0, 0);
+ uart_puts("SELFTEST DONE\n");
+
+ for (;;) {
+ ctl->heartbeat = ctl->heartbeat + 1;
+ u64 cmd = ctl->mailbox;
+ if (cmd != 0) {
+ ctl->mailbox = 0;
+ rpc_dispatch(cmd, ctl->arg0, ctl->arg1);
+ }
+ for (volatile int i = 0; i < 2000; i++) {
+ __asm__ volatile("");
+ }
+ }
+}