diff options
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/greeneye/inject.py')
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/greeneye/inject.py | 129 |
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) |
