From 002e3d7d13c414ea874abbee8d82803dd3a72fe5 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Fri, 2 Oct 2026 10:58:21 -0400 Subject: debug: green-eye wave 3 - RM harness, local simulator, device map, MCP plugin, value-validation demos, CI pipeline, toolchain docs; add host RM tools rmtool/rmtop --- .../harness/value_demos/s07_version_diff.py | 179 +++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 debug/risc-v-gsp-rm-vm/harness/value_demos/s07_version_diff.py (limited to 'debug/risc-v-gsp-rm-vm/harness/value_demos/s07_version_diff.py') diff --git a/debug/risc-v-gsp-rm-vm/harness/value_demos/s07_version_diff.py b/debug/risc-v-gsp-rm-vm/harness/value_demos/s07_version_diff.py new file mode 100644 index 0000000..e4748d7 --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/harness/value_demos/s07_version_diff.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 +"""s07 - version diff: same stimulus against two firmware versions. + +Real scenario (Handoff-vGPU-IO.md PART 91, 2026-10-01 cycles 2-3): the staged +570.211 image and a verified 570.144 image both failed ``GSP_INIT_DONE`` with +``rpc_result=0x59``; disproving the image-pairing hypothesis cost one full +supervised boot cycle per version, and docs/14 section 4.1 exists because +"the code changed" and "the behaviour moved" must be separated before the +emulation is touched. Sorting a new image into the decision table +(static changed x hit/write profile changed) is the H4.1 triage. + +Analog: byte-patch the demo's PING reply constant (0xbeef -> 0xbef0) to make a +labelled "new version", run the *same* recorded stimulus on both, and diff the +sessions and the decoded control block. The answer is one live-path field. +""" +from __future__ import annotations + +import os +import shutil + +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 + +#: addiw a1, a1, -0x111 (the 0xbeef PING key) -> -0x110 (0xbef0) +ADDW_OLD = bytes.fromhex("9b85f5ee") +ADDW_NEW = bytes.fromhex("9b8505ef") +STRING_OLD = b"hello-green-eye" +STRING_NEW = b"hello-green-eyf" + + +def _make_variant(src: str, dst: str) -> dict: + with open(src, "rb") as fh: + data = bytearray(fh.read()) + count = data.count(ADDW_OLD) + if count == 1: + offset = data.find(ADDW_OLD) + old_bytes = bytes(data[offset : offset + 4]) + data[offset : offset + 4] = ADDW_NEW + kind = "ping-key-0xbeef->0xbef0" + else: + count = data.count(STRING_OLD) + if count != 1: + raise C.DemoError(f"no unique patch site (addiw x{data.count(ADDW_OLD)}, string x{count})") + offset = data.find(STRING_OLD) + old_bytes = bytes(data[offset : offset + len(STRING_OLD)]) + data[offset : offset + len(STRING_OLD)] = STRING_NEW + kind = "echo-string" + with open(dst, "wb") as fh: + fh.write(data) + return { + "kind": kind, + "offset": offset, + "old_bytes": old_bytes.hex(), + "source_sha256": C.sha256_file(src), + "variant_sha256": C.sha256_file(dst), + } + + +def run() -> dict: + import greeneye as ge + from greeneye.analyze import compare_sessions + + backend = C.choose_backend("native") + out = C.artifact_dir("s07_version_diff") + va = os.path.join(out, "uproc_demo_vA.elf") + vb = os.path.join(out, "uproc_demo_vB.elf") + shutil.copyfile(C.DEMO_ELF, va) + patch = _make_variant(C.DEMO_ELF, vb) + + commands = [ + "byte-patch PING key 0xbeef -> 0xbef0 to create version B", + 'for v in (A,B): s = ge.launch(v, backend="%s", record=...)' % backend, + "identical stimulus: mailbox cmd=1 arg0=0x1234; snapshot+decode gsp_ctrl", + "compare_sessions(A, B, symbols) + decoded field diff", + ] + scenario = C.scenario( + ids=[ + "Handoff-vGPU-IO.md PART 91 (2026-10-01) cycles 2-3: 570.211 vs 570.144 pairing disproven", + "PART 91 ADDENDUM 6 (2026-10-01): LOGRM id + fn72 identity changed behaviour between cycles", + "docs/14-ADVERSARIAL-INTEROP-GUIDE.md 4.1: version-diff decision table", + ], + baseline=( + "one full supervised boot (~5-6 min) per version plus manual log " + "comparison; the pairing hypothesis took cycles 2 and 3 back-to-back" + ), + projection=( + "green-eye cannot boot the real image without the H2 device model; " + "the same same-stimulus/compare-sessions loop runs on re-hosted " + "firmware and the produced symbol/session deltas are the H4.1 artifact" + ), + blocked=True, + device="H2.2 device model for real .fwimage versions", + ) + C.render_banner( + "s07_version_diff", + "On the same stimulus, which live-path behaviour moved between two firmware versions?", + "570.211 vs 570.144 image pairing; semantic diff before touching the emulation", + scenario["static_baseline"], + commands, + ) + + schema = C.load_schema(ge) + results = {} + for label, path in (("A", va), ("B", vb)): + session = ge.launch(kernel=path, backend=backend, record=os.path.join(out, f"session-{label}"), name=f"vd-s07-{label}") + try: + ctrl = session.symbols.lookup("gsp_ctrl") + dispatches = [] + session.hook( + "rpc_dispatch", + on_enter=lambda hit, d=dispatches: d.append({"cmd": hit.arg(0), "arg0": hit.arg(1)}), + ) + session.watch(ctrl + C.CTRL_RESULT, size=8, kind="w") + session.run(duration=0.25) + session.memory.write_u64(ctrl + C.CTRL_ARG0, 0x1234) + session.memory.write_u64(ctrl + C.CTRL_MAILBOX, 1) + session.run(duration=0.25) + decoded = C.decode_ctrl(ge, session.snapshot(ctrl, C.CTRL_SIZE, f"ctrl-{label}"), schema) + session.note("version", label=label, patch=patch["kind"], reply=decoded.get("result")) + results[label] = { + "path": os.path.relpath(path, C.TOOL_ROOT), + "sha256": C.sha256_file(path), + "dispatches": dispatches, + "reply": decoded.get("result"), + "rpc_count": decoded.get("rpc_count"), + "last_rpc": decoded.get("last_rpc"), + } + finally: + session.close() + + compare_md = compare_sessions( + os.path.join(out, "session-A"), os.path.join(out, "session-B"), symbols=session.symbols, max_rows=20 + ) + C.write_text(os.path.join(out, "compare-sessions.md"), compare_md) + moved = sorted( + key + for key in ("reply", "rpc_count", "last_rpc") + if results["A"][key] != results["B"][key] + ) + payload = { + "success": results["A"]["reply"] == 0xACDB and results["B"]["reply"] == 0xACC4 and moved == ["reply"], + "backend": backend, + "patch": patch, + "version_A": results["A"], + "version_B": results["B"], + "moved_fields": moved, + "decision_table_row": { + "static_code_changed": True, + "live_profile_changed": bool(moved), + "reading": "new logic on a live path - prioritise (docs/14 4.1)", + }, + "compare_report": os.path.relpath(os.path.join(out, "compare-sessions.md"), C.TOOL_ROOT), + "question": "separate 'the code changed' from 'the behaviour moved' on a fixed stimulus", + "answer": ( + f"version A replies {results['A']['reply']:#x}, version B replies {results['B']['reply']:#x}; " + f"moved fields {moved}; the patch site is {patch['kind']} at file offset {patch['offset']:#x} - " + "the decision-table row is 'code changed + profile changed -> prioritise'" + ), + "success_criteria": { + "pass": "exactly the patched reply field moves on the live path; both sessions replay from the same stimulus", + "fail": "unrelated fields move or a version cannot be replayed", + }, + "real_case": scenario, + } + C.write_json(os.path.join(out, "version-diff.json"), payload) + return payload + + +def main() -> int: + return 0 if C.timed_demo(run, C.artifact_dir("s07_version_diff"))["success"] else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) -- cgit v1.2.3