summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/greeneye/inject.py
diff options
context:
space:
mode:
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/greeneye/inject.py')
-rw-r--r--debug/risc-v-gsp-rm-vm/greeneye/inject.py129
1 files changed, 129 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/greeneye/inject.py b/debug/risc-v-gsp-rm-vm/greeneye/inject.py
new file mode 100644
index 0000000..f9605fc
--- /dev/null
+++ b/debug/risc-v-gsp-rm-vm/greeneye/inject.py
@@ -0,0 +1,129 @@
+"""Frida-like service injection for green-eye.
+
+Loads a position-independent RV64 service blob into a live target, registers
+its dispatch entry with the firmware (on the demo uproc: a handler-table slot
+that commands 16..23 dispatch to), and invokes it either directly through
+``Session.call`` (a debugger-side remote function call) or through the
+firmware's own mailbox dispatcher.
+
+On a future GSP-RM simulator the same shape becomes "inject an RM engine and
+register it with the simulated RM core"; see ``docs/12-INJECTION-SDK.md`` and
+``GREEN-EYE-ROADMAP.md``.
+"""
+from __future__ import annotations
+
+from dataclasses import dataclass
+
+from .elf import load_elf
+from .errors import GreenEyeError
+
+#: Symbol the injector calls in a service blob.
+ENTRY_SYMBOL = "ge_demo_service_dispatch"
+
+#: Demo uproc control-block field offsets (see firmware/uproc_demo).
+CTRL_HANDLERS = 0x150
+CTRL_MAILBOX = 0x20
+CTRL_ARG0 = 0x28
+CTRL_ARG1 = 0x30
+CTRL_ARG2 = 0x38
+CTRL_RESULT = 0x40
+INJECT_CMD_BASE = 16
+
+
+@dataclass
+class InjectedService:
+ path: str
+ name: str
+ load_addr: int
+ entry_addr: int
+ dispatch_offset: int
+ slot: int
+ size: int
+
+ def summary(self) -> str:
+ return (
+ f"{self.name}: {self.size} bytes at {self.load_addr:#x}, "
+ f"entry {self.entry_addr:#x} (offset {self.dispatch_offset:#x}), slot {self.slot}"
+ )
+
+
+def load_service(
+ session,
+ service_elf: str,
+ *,
+ load_addr: int | None = None,
+ slot: int = 6,
+ entry_symbol: str = ENTRY_SYMBOL,
+) -> InjectedService:
+ """Load a service blob and register it with the demo firmware."""
+ image = load_elf(service_elf)
+ entry = None
+ for symbol in image.symbols:
+ if symbol.name == entry_symbol:
+ entry = symbol
+ break
+ if entry is None:
+ available = ", ".join(sorted(s.name for s in image.symbols if s.is_function)) or "<none>"
+ raise GreenEyeError(f"service {service_elf}: symbol {entry_symbol!r} not found (functions: {available})")
+ if not 0 <= slot <= 7:
+ raise GreenEyeError(f"slot must be 0..7 (got {slot})")
+ if load_addr is None:
+ base = getattr(session.target, "ram_base", 0x80000000) if session.target else 0x80000000
+ size = getattr(session.target, "memory_mb", 256) * 1024 * 1024 if session.target else 256 * 1024 * 1024
+ load_addr = base + 0x01000000
+ if load_addr + image.max_vaddr + 0x1000 >= base + size:
+ load_addr = base + max(0x1000, size - image.max_vaddr - 0x2000)
+ if getattr(session.target, "memory_mb", 0):
+ ram_end = getattr(session.target, "ram_base", 0x80000000) + session.target.memory_mb * 1024 * 1024
+ if load_addr + image.max_vaddr > ram_end:
+ raise GreenEyeError(
+ f"service does not fit: load {load_addr:#x} + {image.max_vaddr:#x} exceeds RAM end {ram_end:#x}"
+ )
+
+ total = 0
+ for segment in image.load_segments:
+ data = image.segment_bytes(segment)
+ if data:
+ session.memory.write(load_addr + segment.p_vaddr, data)
+ total += len(data)
+ entry_addr = load_addr + entry.value
+
+ ctrl = session.symbols.get("gsp_ctrl")
+ if ctrl is not None:
+ session.memory.write_u64(ctrl.addr + CTRL_HANDLERS + slot * 8, entry_addr)
+
+ service = InjectedService(
+ path=service_elf,
+ name=service_elf.rsplit("/", 1)[-1],
+ load_addr=load_addr,
+ entry_addr=entry_addr,
+ dispatch_offset=entry.value,
+ slot=slot,
+ size=total,
+ )
+ session.recorder.event(
+ "service_injected",
+ path=service.path,
+ name=service.name,
+ load_addr=load_addr,
+ entry_addr=entry_addr,
+ slot=slot,
+ size=total,
+ )
+ return service
+
+
+def invoke_remote(session, service: InjectedService, cmd: int, a0: int = 0, a1: int = 0, timeout: float = 3.0) -> int:
+ """Call the service directly from the debugger (Frida NativeFunction)."""
+ return session.call(service.entry_addr, (cmd, a0, a1), timeout=timeout)
+
+
+def invoke_via_firmware(session, service: InjectedService, cmd: int, a0: int = 0, a1: int = 0, settle: float = 0.25) -> int:
+ """Invoke the service through the firmware's mailbox dispatcher."""
+ ctrl = session.symbols.lookup("gsp_ctrl")
+ session.memory.write_u64(ctrl + CTRL_ARG0, cmd)
+ session.memory.write_u64(ctrl + CTRL_ARG1, a0)
+ session.memory.write_u64(ctrl + CTRL_ARG2, a1)
+ session.memory.write_u64(ctrl + CTRL_MAILBOX, INJECT_CMD_BASE + service.slot)
+ session.run(duration=settle)
+ return session.memory.read_u64(ctrl + CTRL_RESULT)