summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/harness/value_demos/s05_snapshot_diff.py
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-02 10:58:21 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-02 10:58:21 -0400
commit002e3d7d13c414ea874abbee8d82803dd3a72fe5 (patch)
tree6627c12fa07f6a1cf73e5aaeb7390e698dd3a896 /debug/risc-v-gsp-rm-vm/harness/value_demos/s05_snapshot_diff.py
parent2f249817df327f5dcccf0464ad517fa60d6071d3 (diff)
downloadopen-iov-002e3d7d13c414ea874abbee8d82803dd3a72fe5.tar.gz
open-iov-002e3d7d13c414ea874abbee8d82803dd3a72fe5.zip
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
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/harness/value_demos/s05_snapshot_diff.py')
-rw-r--r--debug/risc-v-gsp-rm-vm/harness/value_demos/s05_snapshot_diff.py143
1 files changed, 143 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/harness/value_demos/s05_snapshot_diff.py b/debug/risc-v-gsp-rm-vm/harness/value_demos/s05_snapshot_diff.py
new file mode 100644
index 0000000..fd89131
--- /dev/null
+++ b/debug/risc-v-gsp-rm-vm/harness/value_demos/s05_snapshot_diff.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python3
+"""s05 - snapshot diff: which control-block fields did one command change?
+
+Real scenario (Handoff-vGPU-IO.md PART 91 ADDENDUM 6, 2026-10-01 cycle 12/13):
+the fn72 ``GSP_SET_SYSTEM_INFO`` payload carried *unpacked* identity fields and
+the reference sends raw config-space dwords; the server read the device id
+back as zero and only a field-level comparison of the live payload/reply
+could confirm the fix ("all zeroed fn72 fields cannot matter to this failure"
+was settled by decoding, not by reading). Static review (REVIEW-FN72.md,
+cycle 13's 1656-byte static-config reply) still needed a live before/after of
+the control block to call the fix verified.
+
+Analog: snapshot the decoded control block before and after one command,
+attribute every changed byte run to its named schema field, and show ECHO's
+buffer side-effect separately.
+"""
+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 _fields_hit(schema, diffs) -> list[str]:
+ names = []
+ for diff in diffs:
+ for field in schema.fields:
+ start = field.offset
+ end = field.offset + field.nbytes
+ if start <= diff.addr < end:
+ if field.name not in names:
+ names.append(field.name)
+ return names
+
+
+def run() -> dict:
+ import greeneye as ge
+ from greeneye.memory import Memory
+
+ backend = C.choose_backend("native")
+ out = C.artifact_dir("s05_snapshot_diff")
+ commands = [
+ 's.snapshot(gsp_ctrl, 0x188, "before")',
+ "write mailbox cmd=2 (ECHO) with a known string; run",
+ 's.snapshot(gsp_ctrl, 0x188, "after")',
+ "Memory.diff(before, after); decode both via gsp_ctrl.schema.json",
+ ]
+ scenario = C.scenario(
+ ids=[
+ "Handoff-vGPU-IO.md PART 91 ADDENDUM 6 cycle 12 (2026-10-01): unpacked fn72 identity fields",
+ "PART 91 ADDENDUM 7 (2026-10-01): 1656-byte static-config reply decoded field-by-field",
+ "docs/06-ACCURACY.md M6: memory side-effect agreement / snapshot hashes",
+ ],
+ baseline=(
+ "the malformed identity value was suspected statically (REVIEW-FN72) "
+ "but accepting the fix required a live boot per iteration; ten NOCAT "
+ "cycles ran before the field-level root cause was visible"
+ ),
+ projection=(
+ "the same before/after diff runs against any firmware region once "
+ "the H2 device model exposes the real RM control pages"
+ ),
+ blocked=True,
+ device="H2.2 RM control/reply pages for the real GSP-RM",
+ )
+ C.render_banner(
+ "s05_snapshot_diff",
+ "Which named control-block fields did this one command change?",
+ "fn72/static-config field-level validation (identity fix, cycle 12/13)",
+ scenario["static_baseline"],
+ commands,
+ )
+
+ schema = C.load_schema(ge)
+ session = ge.launch(kernel=C.DEMO_ELF, backend=backend, name="vd-s05")
+ try:
+ ctrl = session.symbols.lookup("gsp_ctrl")
+ session.run(duration=0.25)
+ before = session.snapshot(ctrl, C.CTRL_SIZE, "before-echo")
+ message = b"hello-green-eye"
+ # Locate the string literal in the loaded image so the stimulus is
+ # self-describing (no hardcoded address).
+ hits = session.memory.search(0x80000000, 0x2000, message)
+ literal = hits[0] if hits else 0
+ session.memory.write_u64(ctrl + C.CTRL_ARG0, literal)
+ session.memory.write_u64(ctrl + C.CTRL_MAILBOX, 2)
+ session.run(duration=0.25)
+ after = session.snapshot(ctrl, C.CTRL_SIZE, "after-echo")
+
+ before_dec = C.decode_ctrl(ge, before, schema)
+ after_dec = C.decode_ctrl(ge, after, schema)
+ diffs = Memory.diff(before, after)
+ changed_fields = _fields_hit(schema, diffs)
+ changed_names = [name for name in changed_fields if before_dec.get(name) != after_dec.get(name)]
+ rendered = Memory.render_diff(diffs, symbols=session.symbols)
+ payload = {
+ "success": bool(changed_names)
+ and set(changed_names) >= {"rpc_count", "last_rpc", "result", "buf"}
+ and after_dec.get("result") == len(message),
+ "backend": backend,
+ "changed_field_names": changed_names,
+ "changed_byte_runs": len(diffs),
+ "before": {k: v for k, v in before_dec.items() if k in changed_names},
+ "after": {k: v for k, v in after_dec.items() if k in changed_names},
+ "rendered_diff": rendered,
+ "question": "attribute every changed byte in one region to a named field",
+ "answer": (
+ f"ECHO changed {len(changed_names)} named fields {changed_names}; "
+ f"buf={after_dec.get('buf')!r} result={after_dec.get('result')} "
+ f"(len={len(message)}); {len(diffs)} byte runs exactly covered by the schema - "
+ "the field-level 'what did the reply actually consume' check"
+ ),
+ "success_criteria": {
+ "pass": "rpc_count/last_rpc/result/buf change, result equals the ECHO length, no unattributed run",
+ "fail": "a region changes with no named field, or the ECHO reply length disagrees",
+ },
+ "real_case": scenario,
+ }
+ C.write_json(os.path.join(out, "snapshot-diff.json"), payload)
+ C.write_text(
+ os.path.join(out, "snapshot-diff.md"),
+ "# snapshot diff (analog)\n\n"
+ f"* changed fields: {changed_names}\n"
+ f"* changed byte runs: {len(diffs)}\n\n"
+ "```\n" + rendered + "\n```\n",
+ )
+ finally:
+ session.close()
+ return payload
+
+
+def main() -> int:
+ return 0 if C.timed_demo(run, C.artifact_dir("s05_snapshot_diff"))["success"] else 1
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())