1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
"""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.
"""
from __future__ import annotations
import io
import contextlib
import os
import shutil
import subprocess
import unittest
import greeneye as ge
from greeneye.errors import RspTimeout
HERE = os.path.dirname(os.path.abspath(__file__))
TOOL_ROOT = os.path.join(HERE, "..")
DEMO_ELF = os.path.join(TOOL_ROOT, "firmware", "uproc_demo", "build", "uproc_demo.elf")
SERVICE_ELF = os.path.join(TOOL_ROOT, "sdk", "build", "injected_demo.elf")
def qemu_available() -> bool:
return bool(os.environ.get("GREENEYE_QEMU") or shutil.which("qemu-system-riscv64"))
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")
)
@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):
def test_remote_call(self):
session = ge.launch(kernel=DEMO_ELF, name="unittest-call")
try:
result = session.call("rpc_ping", (0x1111,))
self.assertEqual(result, 0x1111 ^ 0xBEEF)
# registers must be restored around the call
self.assertEqual(session.pc, 0x1000)
finally:
session.close()
def test_run_until(self):
session = ge.launch(kernel=DEMO_ELF, name="unittest-rununtil")
try:
hit = session.run_until("rpc_sum", timeout=5.0)
self.assertEqual(hit.addr, session.symbols.lookup("rpc_sum"))
self.assertEqual(hit.kind, "breakpoint")
finally:
session.close()
def test_run_until_timeout(self):
session = ge.launch(kernel=DEMO_ELF, name="unittest-rununtil-timeout")
try:
with self.assertRaises(RspTimeout):
session.run_until("rpc_fault", timeout=0.2) # self-test never calls it
finally:
session.close()
@unittest.skipUnless(qemu_available(), "qemu-system-riscv64 not installed")
@unittest.skipUnless(os.path.exists(DEMO_ELF), "demo firmware not built (make demo)")
class ReplTests(unittest.TestCase):
def test_repl_commands(self):
from greeneye.cli import GreenEyeRepl
from greeneye.plugins import PluginManager
session = ge.launch(kernel=DEMO_ELF, record=True, name="unittest-repl")
output = io.StringIO()
try:
ctrl = session.symbols.lookup("gsp_ctrl")
repl = GreenEyeRepl(session, PluginManager(session))
with contextlib.redirect_stdout(output):
repl.onecmd("bp rpc_sum")
repl.onecmd("watch gsp_ctrl+0x10 8 w")
repl.onecmd(f"x {ctrl:#x} 16")
repl.onecmd("sym rpc_ping")
repl.onecmd("c 0.6")
repl.onecmd("events 5")
text = output.getvalue()
self.assertIn("rpc_sum", text)
self.assertIn(f"{ctrl:08x}", text)
self.assertIn(f"watch w@{ctrl + 0x10:#x}", text)
finally:
session.close()
@unittest.skipUnless(qemu_available(), "qemu-system-riscv64 not installed")
@unittest.skipUnless(os.path.exists(DEMO_ELF), "demo firmware not built")
@unittest.skipUnless(toolchain_available(), "clang/lld not available")
class InjectionTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
result = subprocess.run(
["make", "-C", os.path.join(TOOL_ROOT, "sdk")],
capture_output=True,
text=True,
)
if result.returncode != 0 or not os.path.exists(SERVICE_ELF):
raise unittest.SkipTest(f"SDK build failed: {result.stderr[-200:]}")
def test_direct_and_firmware_invocation(self):
from greeneye.inject import invoke_remote, invoke_via_firmware, load_service
session = ge.launch(kernel=DEMO_ELF, record=True, name="unittest-inject")
try:
service = load_service(session, SERVICE_ELF, slot=6)
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()
if __name__ == "__main__":
unittest.main()
|