summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/harness/value_demos/s06_inject_service.py
diff options
context:
space:
mode:
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/harness/value_demos/s06_inject_service.py')
-rw-r--r--debug/risc-v-gsp-rm-vm/harness/value_demos/s06_inject_service.py124
1 files changed, 124 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/harness/value_demos/s06_inject_service.py b/debug/risc-v-gsp-rm-vm/harness/value_demos/s06_inject_service.py
new file mode 100644
index 0000000..04dd001
--- /dev/null
+++ b/debug/risc-v-gsp-rm-vm/harness/value_demos/s06_inject_service.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python3
+"""s06 - inject a service: add an RM engine without rebuilding the firmware.
+
+Real scenario (Handoff-vGPU-IO.md PART 92 ADDENDUM 5-11, 2026-10-02): the P6
+work wired guest-originated CE copies through the real engine; every change to
+the service/wiring path cost a kernel rebuild plus a supervised physical boot
+cycle (P6a/b/c, ~5-8 min each, one device-write experiment per cycle) and was
+only accepted after guest smoke ``ALL OK 64/64``. docs/12-INJECTION-SDK.md
+defines the in-simulator equivalent: load a position-independent service blob
+and register it with the firmware's dispatch table (commands 16..23).
+
+Analog: load ``sdk/build/injected_demo.elf`` into slot 6, invoke it directly
+(Frida NativeFunction) and through the firmware mailbox, and record the
+``service_injected`` evidence event. No kernel rebuild, no hardware.
+"""
+from __future__ import annotations
+
+import os
+
+try: # package import (tests) or direct script execution (run_all)
+ from . import _common as C
+except ImportError:
+ import sys as _sys
+
+ _sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
+ import _common as C
+
+
+def run() -> dict:
+ import greeneye as ge
+ from greeneye.inject import invoke_remote, invoke_via_firmware, load_service
+
+ backend = C.choose_backend("native")
+ out = C.artifact_dir("s06_inject_service")
+ commands = [
+ 'load_service(s, "sdk/build/injected_demo.elf", slot=6)',
+ "invoke_remote(s, svc, cmd=1, a0=0x41) # debugger-side call",
+ "invoke_via_firmware(s, svc, cmd=1, a0=0x41) # mailbox cmd=16",
+ "read gsp_ctrl.result / handlers[6]",
+ ]
+ scenario = C.scenario(
+ ids=[
+ "Handoff-vGPU-IO.md PART 92 ADDENDUM 5-11 (2026-10-02): P6 service/wiring rungs",
+ "PART 92 ADDENDUM 6 (2026-10-02): p6run7 first real engine copy after service wiring",
+ "docs/12-INJECTION-SDK.md: blob/ABI contract for injecting RM services",
+ ],
+ baseline=(
+ "each P6 rung was a kernel rebuild plus a supervised physical cycle "
+ "(~5-8 min boots; P6a->P6c spanned ~02:20-08:30) with fail-safe "
+ "release discipline after every run"
+ ),
+ projection=(
+ "on real GSP-RM the registry is the RM service table (M5), gated on "
+ "H2.2; the load/register/invoke/observe shape is identical to the "
+ "demo handler-table slot"
+ ),
+ blocked=True,
+ device="H2.2 device model + M5 RM service registry",
+ )
+ C.render_banner(
+ "s06_inject_service",
+ "Can a new service be loaded, registered and invoked without rebuilding the firmware?",
+ "guest-originated CE service wiring (P6a-P6c): kernel rebuild + supervised boot per rung",
+ scenario["static_baseline"],
+ commands,
+ )
+
+ if not os.path.isfile(C.SDK_ELF):
+ payload = {
+ "success": False,
+ "skipped": True,
+ "failure": "sdk/build/injected_demo.elf not built (run: make sdk)",
+ "backend": backend,
+ "real_case": scenario,
+ }
+ C.write_json(os.path.join(out, "inject.json"), payload)
+ return payload
+
+ session = ge.launch(kernel=C.DEMO_ELF, backend=backend, record=os.path.join(out, "session"), name="vd-s06")
+ try:
+ ctrl = session.symbols.lookup("gsp_ctrl")
+ session.run(duration=0.25)
+ service = load_service(session, C.SDK_ELF, slot=C.SERVICE_SLOT)
+ remote_ping = invoke_remote(session, service, 1, 0x41)
+ remote_sum = invoke_remote(session, service, 2, 0x10, 0x20)
+ via_fw_ping = invoke_via_firmware(session, service, 1, 0x41)
+ via_fw_sum = invoke_via_firmware(session, service, 2, 0x10, 0x20)
+ handlers7 = session.memory.read_u64(ctrl + 0x150 + 8 * C.SERVICE_SLOT)
+ payload = {
+ "success": remote_ping == (0x41 ^ C.PING_XOR) and remote_sum == 0x30
+ and via_fw_ping == remote_ping and via_fw_sum == remote_sum,
+ "backend": backend,
+ "service": service.summary(),
+ "service_sha256": C.sha256_file(C.SDK_ELF),
+ "handlers_slot6": f"{handlers7:#x}",
+ "remote": {"ping": remote_ping, "sum": remote_sum},
+ "via_firmware": {"ping": via_fw_ping, "sum": via_fw_sum},
+ "mailbox_command": C.INJECT_CMD_BASE + C.SERVICE_SLOT,
+ "session": os.path.relpath(os.path.join(out, "session"), C.TOOL_ROOT),
+ "question": "load/register/invoke a service blob end-to-end, both routes",
+ "answer": (
+ f"service loaded at {service.load_addr:#x}, slot {service.slot} "
+ f"(handlers[{service.slot}]={handlers7:#x}); remote ping={remote_ping:#x} "
+ f"sum={remote_sum:#x}; via mailbox cmd={C.INJECT_CMD_BASE + C.SERVICE_SLOT} "
+ f"ping={via_fw_ping:#x} sum={via_fw_sum:#x} - both paths agree, one session"
+ ),
+ "success_criteria": {
+ "pass": "remote and firmware-mailbox invocations return the SDK ABI values and agree",
+ "fail": "a route disagrees, the slot is not registered, or the blob cannot load",
+ },
+ "real_case": scenario,
+ }
+ C.write_json(os.path.join(out, "inject.json"), payload)
+ finally:
+ session.close()
+ return payload
+
+
+def main() -> int:
+ return 0 if C.timed_demo(run, C.artifact_dir("s06_inject_service"))["success"] else 1
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())