# 11 — GSP RM firmware service developer guide > Status: implemented, documented and tested (audit: docs/27-TOOL-AUDIT.md; value demos: docs/25-VALUE-VALIDATION.md). > See [06-ACCURACY](06-ACCURACY.md) for the fidelity caveats. ## 1. What this guide is for The GSP runs a resource manager (RM) as **services**: message handlers, state machines and engines that own objects (devices, address spaces, channels, memory, notifiers) and answer controls. Open-IOV emulates those services; the long-term goal of risc-v-gsp-rm-debugger is to let a developer **write, inject, run and debug their own versions of these services** before (or instead of) changing the product emulation. This guide teaches the loop on the demo uproc — which implements the same shape as a GSP service — and maps each step to the future GSP-RM work in [`RISC-V-GSP-RM-DEBUGGER-ROADMAP.md`](../RISC-V-GSP-RM-DEBUGGER-ROADMAP.md) H1.3. ## 2. Service anatomy | Concept | Demo uproc (today) | GSP-RM (roadmap H1.3) | |---|---|---| | Entry point | exported symbol `ge_demo_service_dispatch` | service entry registered with the RM core | | Dispatch ABI | `u64 dispatch(u64 cmd, u64 a0, u64 a1)` | same shape via the service registry shim | | Reachability | firmware mailbox commands 16..23 select handler slot 0..7; `arg0` is the service command, `arg1`/`arg2` are parameters | RM message/control dispatch routed by the simulator's RM-core | | State | service-local (stack/registers) or the control block | RM objects and service-private memory | | Talking to peers | control-block fields + mailbox; debugger calls | RM-core calls and inter-service messages | | Observability | hooks/watchpoints/sessions/TCG trace | same, plus RM-core call interception | A service must be **position-independent**: no globals, no libc, no absolute addresses. Everything it calls is either its own code or an address passed in by the injector (the future RM-core shim will pass a table of callable entry points, which is also the interception point). ## 3. Writing a service 1. Copy the SDK shape from `sdk/`: * `greeneye_service.h` — the ABI header; * `injected_demo.c` — a working multi-command service. 2. Implement `ge_service_fn`: ```c __attribute__((noinline)) ge_u64 my_service_dispatch(ge_u64 cmd, ge_u64 a0, ge_u64 a1) { switch (cmd) { case 1: return a0 ^ 0xbeefu; case 2: return a0 + a1; default: return ~cmd; } } ``` 3. Keep it PIC-clean: no `static` data with pointers, no string globals, no division (the demo build has no libc), no stack beyond a few KiB. 4. Build: ```bash make -C sdk # or point the Makefile at your source make -C sdk relocs # must print "no relocations in this file" ``` 5. Fix any relocation the linker reports before injecting; relocations mean the blob will not run at an arbitrary load address. ## 4. Injecting and invoking ```bash ./risc-v-gsp-rm-debugger.sh inject --service sdk/build/injected_demo.elf \ --cmd 1 --arg0 0x41 --arg1 0x42 ``` What happens (all recorded): 1. Every `PT_LOAD` is written into RAM (default `0x81000000`; auto-sized to fit the target). 2. The service dispatch entry is patched into handler slot 6 (`--slot` selects 0..7). 3. A **direct debugger-side call** runs the service immediately (`greeneye.inject.invoke_remote`): this is the unit-test path, independent of firmware state. 4. After the firmware settles, a **firmware-dispatched call** writes `arg0=cmd, arg1=a0, arg2=a1, mailbox=16+slot` and reads `result` (`greeneye.inject.invoke_via_firmware`): this is the integration path. Programmatic form: ```python from greeneye.inject import load_service, invoke_remote, invoke_via_firmware svc = load_service(s, "sdk/build/injected_demo.elf", slot=6) print(hex(invoke_remote(s, svc, 2, 0x10, 0x20))) # 0x30 s.run(duration=0.3) print(hex(invoke_via_firmware(s, svc, 2, 0x10, 0x20))) # 0x30 ``` ## 5. Talking to other services and the host On the demo: * the **control block** is the shared state (`gsp_ctrl`); services coordinate through its fields and the mailbox command range; * the **host/debugger** is a peer via hooks, watchpoints and `Session.call`; * a service can call another service if it is handed the other entry address (the SDK's second export `ge_demo_service_answer` demonstrates a second callable point; the injector resolves any exported symbol). On real GSP-RM (H1.3) the same roles are filled by RM-core allocation and control calls and by messages between services. The simulator will intercept those calls so they can be logged, stubbed, or forwarded to the real implementation — which is exactly what makes the environment powerful for prototyping: a service you write can be exercised against recorded RM-core behaviour before the product emulation implements it. ## 6. Resource surfaces an engine can use Category map (details are RM-version-specific): | Surface | Purpose | How risc-v-gsp-rm-debugger helps today | |---|---|---| | Memory / descriptors | allocate, map, free, register host or vidmem ranges | watchpoints on allocation state; snapshot/diff; schema decode | | Address spaces / page tables | GPU VA translation, mapping updates | hooks on map/unmap routines; memory diffs; TCG trace of accesses | | Channels / GPFIFO / USERD | submission plumbing | hooks and remote calls; observe ring state via snapshots | | Sync: semaphores, notifiers, interrupts | completion and signalling | watchpoints on sem values; event ordering in sessions | | Engine scheduling / runlists | prioritisation, preemption | call-graph and hook evidence; compare runs | | Controls / messages | configuration and queries | declarative probes; replay/compare across versions | | Device identity / caps / clocks | what the RM reports | schema decode of the static-config block; A/B vs hardware oracle | Everything here is observed through the same primitives, so an engine prototype is analysed exactly like firmware code. ## 7. Debugging workflow 1. **Unit-test with `call`:** breakpoints and hooks around the service entry, `Session.call(entry, (cmd, a0, a1))`, inspect the return and watchpoints on its data. Failures raise `RspTimeout` and leave the session recording. 2. **Integration run:** `invoke_via_firmware` with the mailbox path, while hooks on the firmware's dispatcher and watchpoints on the control block record the full route. 3. **Ghidra-driven:** `./risc-v-gsp-rm-debugger.sh ghidra` + `GreenEyeControl.java` — execute until a service address, set breakpoints from the decompiler, read registers/memory, export hits (docs/13). 4. **Coverage:** run with the TCG plugin filtered to the service load range (`qemu/greeneye_trace.c`, `start=`/`end=` args) to see every access. 5. **Regression:** record a session per command; `./risc-v-gsp-rm-debugger.sh compare` detects changed hit/watch/write profiles when the service or firmware changes. ## 8. Rules and pitfalls * **PIC purity first.** If the blob needs relocation, it is not injectable at an arbitrary address. * **No absolute addresses from the host.** Use exported symbols and `load_service`, never hardcoded addresses. * **No unbounded loops.** A service that never returns blocks the call loop until the timeout and then leaves the target stopped; the recorder shows the last PC. * **Watchpoint the state, hook the entry.** Watchpoints are immediate (one instruction step) and ideal for counters; hooks give arguments/returns. * **Never ship a service.** Injected code is a development experiment; the product changes go through `open-iov-vfio` with the accuracy loop. * **Record everything.** A claim about a service without a session is not evidence. ## 9. Worked example: add a command ```c /* sdk/injected_demo.c */ case 7: return (a0 | (a1 << 8)) & 0xFFFFFFFFull; ``` ```bash make -C sdk ./risc-v-gsp-rm-debugger.sh inject --cmd 7 --arg0 0xab --arg1 0xcd # direct call -> 0xcdab # firmware call -> 0xcdab ./risc-v-gsp-rm-debugger.sh analyze # event/hit report ``` ## 10. What comes next (roadmap) * H1.3: services built against an RM shim, registered with a simulated RM core, calling intercepted RM-core surfaces. * H2: the device model that lets real GSP-RM run in the simulator at all. * H4: adversarial navigation — diff service behaviour across firmware versions, mutate inputs, infer capabilities. * H5: services that interact with PMU/SEC2/ACR models.