summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/harness/value_demos/s04_run_until_function.py
blob: a74c83fb13384a40dd4ff1d017eae1d01ec47db4 (plain)
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#!/usr/bin/env python3
"""s04 - run-until a function: is this code on the live path, and with what args?

Real scenario (docs/19-CONTROL-DISCOVERY.md 4.4 and
Handoff-vGPU-IO.md PART 91 ADDENDUM 9, 2026-10-01 cycles 18-25): the
``gsp-phys-rm-call`` RM-client ladder asked, per cycle, whether a symbolized
function had executed under the current stimulus (VAS commit, token, BIND,
fetch, semaphore).  Cycles 18->25 (~1 h 41 m, eight supervised boots) walked
the ladder one reachability question at a time; ``reach.py`` answers that
question in ~0.5 s per target, and a ``run_until`` timeout means "not executed
under this stimulus at this fidelity", never "unreachable".

Analog: run to the self-test's ``rpc_sum``, show ``rpc_fault`` is *not* on the
natural path (timeout), then trigger it through the mailbox and reach it with
the dispatch arguments captured; finally call ``rpc_ping`` directly
(Frida NativeFunction) to show the unit-test route.
"""
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.errors import RspTimeout

    backend = C.choose_backend("native")
    out = C.artifact_dir("s04_run_until_function")
    commands = [
        's.run_until("rpc_sum", timeout=5.0)                      # natural path',
        's.run_until("rpc_fault", timeout=0.2)                    # not on natural path',
        'write mailbox cmd=6; s.run_until("rpc_fault")            # trigger route',
        's.call("rpc_ping", (0x41,))                              # Frida-like direct call',
    ]
    scenario = C.scenario(
        ids=[
            "docs/19-CONTROL-DISCOVERY.md 4.4 (2026-10-02): reach.py measured ~0.5 s per target",
            "Handoff-vGPU-IO.md PART 91 ADDENDUM 9 (2026-10-01): RM-client ladder cycles 18-25",
            "docs/14-ADVERSARIAL-INTEROP-GUIDE.md 5: run_until timeouts are not unreachability",
        ],
        baseline=(
            "cycles 18-25 took ~1 h 41 m of supervised hardware cycles (full "
            "boot + RPC ladder per hypothesis) to answer the same class of "
            "'did this function execute under this stimulus' question"
        ),
        projection=(
            "real RM ladder reachability needs the live GSP-RM (H1.3/H2.2); "
            "run_until/hook/call already work against the re-hosted firmware "
            "shape and every probe spec emitted here replays"
        ),
        blocked=True,
        device="H1.3 live RM method entry / H2.2 device model",
    )
    C.render_banner(
        "s04_run_until_function",
        "Is a symbolized function on the live path under this stimulus, what args does it take, and can it be called directly?",
        "RM-client ladder: reachability of each rung, one supervised boot per question",
        scenario["static_baseline"],
        commands,
    )

    session = ge.launch(kernel=C.DEMO_ELF, backend=backend, name="vd-s04")
    try:
        ctrl = session.symbols.lookup("gsp_ctrl")
        dispatch_args = []
        session.hook(
            "rpc_dispatch",
            on_enter=lambda hit: dispatch_args.append(
                {"cmd": hit.arg(0), "a0": hit.arg(1), "a1": hit.arg(2), "a2": hit.arg(3)}
            ),
        )
        natural_hit = session.run_until("rpc_sum", timeout=5.0)
        natural = {
            "target": "rpc_sum",
            "method": "run",
            "reachable": True,
            "pc": f"{natural_hit.addr:#x}",
            "dispatch_args": list(dispatch_args),
        }
        timeout_hit = None
        try:
            timeout_hit = session.run_until("rpc_fault", timeout=0.2)
            timeout_seen = False
        except RspTimeout:
            timeout_seen = True
        fault_before = {
            "target": "rpc_fault",
            "method": "run_until",
            "reachable": timeout_hit is not None,
            "timeout": timeout_seen,
            "reading": "not executed under this stimulus at this fidelity (docs/14 section 5)",
        }
        session.memory.write_u64(ctrl + C.CTRL_MAILBOX, 6)
        trigger_hit = session.run_until("rpc_fault", timeout=5.0)
        triggered = {
            "target": "rpc_fault",
            "method": "trigger (mailbox cmd=6)",
            "reachable": True,
            "pc": f"{trigger_hit.addr:#x}",
            "dispatch_args": list(dispatch_args[-1:]),
        }
        direct = session.call("rpc_ping", (0x41,))
        payload = {
            "success": natural["reachable"] and fault_before["timeout"] and triggered["reachable"]
            and direct == (0x41 ^ C.PING_XOR),
            "backend": backend,
            "natural_path": natural,
            "before_trigger": fault_before,
            "after_trigger": triggered,
            "direct_call": {"target": "rpc_ping", "args": [0x41], "result": direct, "expected": 0x41 ^ C.PING_XOR},
            "question": "reachability and required arguments per symbolized function",
            "answer": (
                f"rpc_sum reached on the self-test path (pc {natural['pc']}); rpc_fault "
                f"times out (reachable=False) until mailbox cmd=6 triggers it "
                f"(pc {triggered['pc']}, args {triggered['dispatch_args']}); direct call "
                f"rpc_ping(0x41)={direct:#x} - three reachability answers, one session"
            ),
            "success_criteria": {
                "pass": "natural target reached, un-triggered target times out, trigger route reaches it, direct call matches",
                "fail": "a route reports reachable without a hit, or the direct call disagrees",
            },
            "real_case": scenario,
        }
        C.write_json(os.path.join(out, "reach.json"), payload)
    finally:
        session.close()
    return payload


def main() -> int:
    return 0 if C.timed_demo(run, C.artifact_dir("s04_run_until_function"))["success"] else 1


if __name__ == "__main__":
    raise SystemExit(main())