# 12 — Injection SDK specification (v1, demo ABI) > **# TOOL IN DEVELOPMENT - NOT READY YET!** See 00 for status and > [`GREEN-EYE-ROADMAP.md`](../GREEN-EYE-ROADMAP.md) §H1/H3 for the program. ## 1. Goals and non-goals **Goals** * a repeatable, scriptable way to compile a piece of RV64 code, load it into a live firmware target, register it, invoke it, and observe it with the same hooks/sessions/analysis as firmware; * a clear ABI so services can be written once and run under the demo uproc today and under a simulated RM core later; * an evidence trail: every load, registration and invocation is recorded. **Non-goals (v1)** * shipping any injected code (development-only); * hot-patching signed firmware on hardware; * bypassing security boundaries (the simulator is a study environment). ## 2. Blob contract * **Format:** ELF64 `EM_RISCV`, `RV64IMAC`, soft-float ABI, one or more `PT_LOAD` segments. * **Position independence:** built PIC (`-fPIC`, `-mcmodel=medany`, `-mno-relax`, `-fno-asynchronous-unwind-tables`), no dynamic relocations. The loader verifies by writing segments at the chosen load address; the SDK `make relocs` target must report none. * **Self-sufficiency:** no libc, no global data containing addresses. Caller-provided addresses (future RM-core tables) arrive as arguments. * **Entry symbol:** `ge_demo_service_dispatch` by default (override with `--entry`/`entry_symbol=`). * **Optional metadata:** a `ge_service_t` block can be exported for future loaders (`magic 0x47455356`, version 1, name, dispatch). ## 3. ABI v1 ```c typedef ge_u64 (*ge_service_fn)(ge_u64 cmd, ge_u64 a0, ge_u64 a1); ``` * `a0`, `a1`, `a2` registers carry `(cmd, a0, a1)` on entry. * The return value is `a0`. * The service may use `a3..a7`, `t0..t6`, `s0..s11` and the stack; the injector supplies a private stack below the scratch page. * Traps do not return: the call loop times out, records the stop, restores registers and raises `RspTimeout`. * No calling convention for callbacks in v1; the loader registers a **single dispatch entry** per slot. ## 4. Demo registration model * The firmware exposes eight handler slots (`gsp_ctrl.handlers[0..7]`). * Mailbox commands **16..23** select slot `cmd-16`. * On dispatch the firmware calls `handlers[slot](arg0, arg1, arg2)` — i.e. `cmd = arg0`, parameters `arg1`, `arg2`. * Slots 0..5 are used by the built-in demo RPCs; **slot 6** is the SDK default (`--slot`). ### Control-block fields used (demo) | Field | Offset | Role | |---|---|---| | `mailbox` | `+0x20` | command (`16 + slot` selects an injected service) | | `arg0` | `+0x28` | service command | | `arg1` | `+0x30` | parameter 0 | | `arg2` | `+0x38` | parameter 1 | | `result` | `+0x40` | service return value | | `handlers` | `+0x150` | eight entry slots | Offsets are demo-firmware-specific; use `greeneye/inject.py` constants and the schema at `firmware/uproc_demo/gsp_ctrl.schema.json` rather than hardcoding. ## 5. Lifecycle ``` compile (PIC) ──► verify relocations ──► load PT_LOADs at load_addr │ │ │ ├─► register entry in slot │ └─► record "service_injected" ▼ invoke_remote (direct call) invoke_via_firmware (mailbox dispatch) └──────────────► both record call_enter/call_return events ``` * **Load address:** default `ram_base + 0x01000000`, auto-shrunk to fit if the image is larger; explicit with `--load-addr`. * **Cleanup:** v1 does not unregister automatically. A service is removed by zeroing its slot and (optionally) clearing the blob; sessions record the injection so runs are reproducible from a known state. * **Multiple services:** one per slot; multi-entry services are one dispatch with subcommands. ## 6. Observability contract The injector records, in the session directory: * `service_injected` — path, name, load address, entry address, slot, size; * `mem_write` events for every segment written; * `call_enter` / `call_return` — address, args, return value; * hook/watch events from user instrumentation inside the service; * `qemu.log`-style blobs are not applicable (no probe here), but the TCG trace can be enabled for full coverage. Comparison of injected-service runs uses the standard `greeneye compare` profiles, so a change in a service's behaviour is visible like any firmware change. ## 7. Building (reference) ```makefile CC := clang LLD ?= /usr/lib/llvm-18/bin/ld.lld CFLAGS := --target=riscv64-unknown-elf -march=rv64imac -mabi=lp64 \ -mcmodel=medany -mno-relax -fPIC -ffreestanding -nostdlib \ -fno-builtin -fno-stack-protector -fno-asynchronous-unwind-tables -O2 $(LLD) -m elf64lriscv --no-relax -e ge_demo_service_dispatch -o service.elf service.o ``` The tracked SDK (`sdk/`) is the executable reference. ## 8. Versioning and compatibility * ABI version 1 is the demo contract described here. * Future ABI v2 (RM-core shim) will pass a call table and a service context instead of relying on firmware slots; blobs declare their ABI via the optional metadata block so loaders can refuse mismatches. * Breaking ABI changes require a compatibility shim plus a note in the session metadata and this document. ## 9. Security and safety * Injected code runs with the simulator's full privileges; the bridge and injector are local-only tools. * Never inject into a guest or a shipped environment; never use injection to bypass a hardware protection. * Fuzzing/mutation harnesses (roadmap H4.3) must run against the simulator or re-hosted code, with sessions attached as evidence. * Treat every injected service as untrusted code for the purposes of watching: it may corrupt firmware state, so always record and restart clean after crashes. ## 10. Roadmap hooks * **H1.3** — RM service registration and RM-core call interception. * **H3** — native-simulator loader with snapshots and branch exploration. * **H4** — differential testing of injected services across firmware versions and capability inference from their access patterns. * **H5** — services spanning GSP and adjacent microcontrollers.