summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/tests/test_injection.py
diff options
context:
space:
mode:
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/tests/test_injection.py')
-rw-r--r--debug/risc-v-gsp-rm-vm/tests/test_injection.py61
1 files changed, 61 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/tests/test_injection.py b/debug/risc-v-gsp-rm-vm/tests/test_injection.py
index 5dca9cb..33cc092 100644
--- a/debug/risc-v-gsp-rm-vm/tests/test_injection.py
+++ b/debug/risc-v-gsp-rm-vm/tests/test_injection.py
@@ -1,6 +1,9 @@
"""Integration tests for Frida-like remote calls, run-until and service injection.
Skipped cleanly when QEMU, the demo firmware, clang/lld or the SDK are absent.
+The native simulator is a first-class injection target too: ``NativeInjectionTests``
+proves the demo service blob loads and that direct and firmware-mailbox
+invocations agree without QEMU.
"""
from __future__ import annotations
@@ -24,12 +27,28 @@ def qemu_available() -> bool:
return bool(os.environ.get("GREENEYE_QEMU") or shutil.which("qemu-system-riscv64"))
+def native_available() -> bool:
+ try:
+ import greeneye.sim_backend # noqa: F401
+
+ return True
+ except Exception:
+ return False
+
+
def toolchain_available() -> bool:
return bool(shutil.which("clang")) and (
shutil.which("ld.lld") or os.path.exists("/usr/lib/llvm-18/bin/ld.lld")
)
+def build_sdk() -> bool:
+ if os.path.exists(SERVICE_ELF):
+ return True
+ result = subprocess.run(["make", "-C", os.path.join(TOOL_ROOT, "sdk")], capture_output=True, text=True)
+ return result.returncode == 0 and os.path.exists(SERVICE_ELF)
+
+
@unittest.skipUnless(qemu_available(), "qemu-system-riscv64 not installed")
@unittest.skipUnless(os.path.exists(DEMO_ELF), "demo firmware not built (make demo)")
class CallTests(unittest.TestCase):
@@ -117,5 +136,47 @@ class InjectionTests(unittest.TestCase):
session.close()
+@unittest.skipUnless(native_available(), "native backend unavailable")
+@unittest.skipUnless(os.path.exists(DEMO_ELF), "demo firmware not built (make demo)")
+@unittest.skipUnless(toolchain_available(), "clang/lld not available")
+class NativeInjectionTests(unittest.TestCase):
+ """Injection SDK against the native simulator (no QEMU required)."""
+
+ @classmethod
+ def setUpClass(cls):
+ if not build_sdk():
+ raise unittest.SkipTest("SDK build failed")
+
+ def test_native_direct_and_firmware_invocation(self):
+ from greeneye.inject import invoke_remote, invoke_via_firmware, load_service, services
+
+ session = ge.launch(kernel=DEMO_ELF, backend="native", record=True, name="unittest-native-inject")
+ try:
+ service = load_service(session, SERVICE_ELF, slot=6)
+ self.assertEqual(services(session), [service])
+ self.assertEqual(invoke_remote(session, service, 1, 0x41), 0x41 ^ 0xBEEF)
+ self.assertEqual(invoke_remote(session, service, 2, 0x10, 0x20), 0x30)
+ session.run(duration=0.3)
+ self.assertEqual(invoke_via_firmware(session, service, 1, 0x41), 0x41 ^ 0xBEEF)
+ self.assertEqual(invoke_via_firmware(session, service, 2, 0x10, 0x20), 0x30)
+ finally:
+ session.close()
+
+ def test_native_injection_replay_is_deterministic(self):
+ from greeneye.inject import invoke_remote, load_service
+
+ results = []
+ for _ in range(2):
+ session = ge.launch(kernel=DEMO_ELF, backend="native", record=False, name="unittest-native-inject-replay")
+ try:
+ service = load_service(session, SERVICE_ELF, slot=6)
+ results.append(invoke_remote(session, service, 2, 0x10, 0x20))
+ results.append(session.pc)
+ finally:
+ session.close()
+ self.assertEqual(results[0], 0x30)
+ self.assertEqual(results, results[:2] + results[:2])
+
+
if __name__ == "__main__":
unittest.main()