/* 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 service command for injected dispatch */ u64 arg1; /* +0x30 */ u64 arg2; /* +0x38 */ u64 result; /* +0x40 */ char buf[256]; /* +0x48 */ u64 handler_count; /* +0x148 */ u64 handlers[8]; /* +0x150 */ } 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); typedef u64 (*ge_inject_fn)(u64, u64, u64); void rpc_dispatch(u64 cmd, u64 a0, u64 a1, u64 a2) { 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: /* Injected-service dispatch: commands 16..23 call handler slots 0..7. * A debugger can load a new service and store its entry in * gsp_ctrl.handlers[n] while the firmware is running; this is the * demo's stand-in for injecting an RM engine/service. */ if (cmd >= 16 && cmd < 24) { ge_inject_fn injected = (ge_inject_fn)(unsigned long)gsp_ctrl.handlers[cmd - 16]; /* a0 carries the service command; a1/a2 are its parameters. */ result = injected ? injected(a0, a1, a2) : ~cmd; } else { 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->arg2 = 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, 0); rpc_dispatch(2, (u64)(unsigned long)"hello-green-eye", 0, 0); rpc_dispatch(3, 0x5a, 32, 0); rpc_dispatch(4, (u64)(unsigned long)ctl->buf, 16, 0); rpc_dispatch(5, 0, 0, 0); rpc_dispatch(5, 0, 0, 0); rpc_dispatch(5, 0, 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, ctl->arg2); } for (volatile int i = 0; i < 2000; i++) { __asm__ volatile(""); } } }