summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/docs/21-CI-RELEASE-PIPELINE.md
diff options
context:
space:
mode:
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/docs/21-CI-RELEASE-PIPELINE.md')
-rw-r--r--debug/risc-v-gsp-rm-vm/docs/21-CI-RELEASE-PIPELINE.md320
1 files changed, 320 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/docs/21-CI-RELEASE-PIPELINE.md b/debug/risc-v-gsp-rm-vm/docs/21-CI-RELEASE-PIPELINE.md
new file mode 100644
index 0000000..d687cca
--- /dev/null
+++ b/debug/risc-v-gsp-rm-vm/docs/21-CI-RELEASE-PIPELINE.md
@@ -0,0 +1,320 @@
+# 21 — CI and release pipeline (version diff + evidence bundle)
+
+> **# TOOL IN DEVELOPMENT - NOT READY YET!** See 00 for status.
+
+## 1. Purpose
+
+This is the release-triage path for a new GSP firmware image (roadmap R6 /
+H4.1, docs/23 C5/C7). Two stdlib-only tools turn the manual docs/07 P1/P2
+playbook into one repeatable run and one attachable bundle:
+
+| Tool | What it does |
+|---|---|
+| `harness/version_diff.py` | old vs new target, section by section: image/shape, symbol inventory, decompiled corpus hints, bounded QEMU probe, shared-scenario behavior, M1–M6 skeleton |
+| `harness/ci_release.py` | runs version_diff → portability matrix → mutation smoke and hashes every artifact into `bundle-manifest.json` |
+
+`.github/workflows/greeneye.yml` runs the tool's test suite on pushes/PRs that
+touch `debug/risc-v-gsp-rm-vm/**` or `tools/rm*/**`. The repository-level
+`.github/workflows/ci.yml` is independent and unchanged.
+
+Everything here is local: the tools never download firmware, never attach to
+hardware, a guest or a kernel module, and use their own QEMU processes.
+
+---
+
+## 2. Version diff
+
+### 2.1 Usage
+
+Self-diff of the demo (a determinism/regression check; must be all-zero):
+
+```bash
+python3 harness/version_diff.py \
+ --old firmware/uproc_demo/build/uproc_demo.elf \
+ --new firmware/uproc_demo/build/uproc_demo.elf \
+ --out dumps/vdiff-selfcheck \
+ --probe-instructions 200 --behavior-duration 0.2
+```
+
+Old vs new firmware with per-version symbol DBs and Ghidra exports:
+
+```bash
+python3 harness/version_diff.py \
+ --old firmware/57X-GSP/570.144/gsp-570.144.fwimage.bin \
+ --new firmware/57X-GSP/570.211/gsp-570.211.fwimage.bin \
+ --old-label 570.144 --new-label 570.211 \
+ --old-ghidra-exports analysis/570.144/symbols.tsv \
+ --new-ghidra-exports analysis/570.211/symbols.tsv \
+ --corpus-diff analysis/570.144/decompiled_index.tsv analysis/570.211/decompiled_index.tsv \
+ --out dumps/vdiff-570
+```
+
+Attaching a fixed stimulus and a pre-produced corpus report:
+
+```bash
+python3 harness/version_diff.py --old <rehosted-old.elf> --new <rehosted-new.elf> \
+ --stimulus examples/scripts/trace_rpc.py \
+ --corpus-report run/corpus-delta-570.144-570.211.txt \
+ --out dumps/vdiff-rehosted
+```
+
+Targets are RISC-V ELF files or raw images (`.fwimage`). A raw target is
+relocated to `0x80000000` as one implicit load; its firmware-vaddr translation
+is `runtime - 0x80000000`. Symbols may be absent for raw images; the symbol
+section then reports `skipped` with a reason unless Ghidra/JSON sources are
+supplied.
+
+Flags: `--old/--new` (required), `--out` (required), `--old-label/--new-label/
+--release`, `--old-symbols/--new-symbols` (green-eye JSON), `--old-ghidra-names`,
+`--new-ghidra-names`, `--old-ghidra-index`, `--new-ghidra-index`,
+`--old-ghidra-exports`, `--new-ghidra-exports` (`--old-ghidra-bias`/
+`--new-ghidra-bias`), `--corpus-diff OLD NEW`, `--corpus-report FILE`,
+`--stimulus`, `--probe-instructions` (`0` = `--probe-duration` wall clock),
+`--behavior-duration`, `--max-stops`, `--max-rows`, `--approval-token`.
+
+### 2.2 Sections
+
+Every section reports `status: ok | skipped | error`, and a skipped section
+never aborts the others. A failing QEMU probe or scenario is recorded as
+`error` for that section only.
+
+1. **Image / shape.** Entry, span, file size/hash, PT_LOAD layout (vaddr,
+ filesz/memsz, flags), and the ELF section table (name/type/addr/size).
+ Raw images have no section table; that is stated, not guessed.
+2. **Symbol inventory.** ELF `.symtab` plus Ghidra `names.tsv`,
+ `decompiled_index.tsv`/bucket TSVs, 4-column `exports.tsv` and the
+ 3-column `ExportForGreenEye` form (`addr<TAB>name<TAB>size`), plus
+ green-eye `greeneye-symbols/1` JSON DBs. Matching follows
+ `userspace/cuda/tools/diff_corpus.py`: named functions match by name
+ (duplicates in address order), the address shift is the median of named
+ deltas, and `FUN_*` functions match at the shifted address. The report
+ lists added/removed/moved/resized (named and anonymous), the shift and its
+ dominance, match rates and the named-transfer rate. Merged per-side DBs are
+ saved under `<out>/symbols/`.
+3. **Decompiled-change hints.** `--corpus-diff A B` runs the same matching
+ discipline over two corpus indexes and writes `<out>/corpus/corpus-diff.json`;
+ `--corpus-report FILE` attaches a previously produced report (hash +
+ preview). With neither flag the section is `skipped` with reason
+ `not supplied: ...`.
+4. **Probe first-touch.** Both targets are flattened, relocated to
+ `0x80000000`, stepped for the bounded budget, and their QEMU logs parsed
+ with `greeneye.analyze.parse_qemu_faults`. The first fault is reported with
+ cause/desc, `epc` **and** `epc_firmware`, the access address **and**
+ `access_firmware` (translated only when the access address falls inside the
+ image). The diff says whether the stopped PC/first fault changed.
+5. **Behavior.** The shared demo scenario from `portability_matrix` (hooks on
+ every resolvable `rpc_*`, a write watchpoint on `gsp_ctrl.rpc_count`, the
+ self-test, one deterministic PING) or a `--stimulus` script is run on both
+ targets and recorded as normal `greeneye-session/1` directories. The report
+ includes `analyze.compare_sessions` output under `<out>/compare/`, explicit
+ hit/watch/written-page deltas, `all_zero`, and the docs/06 M1–M6 skeleton
+ with every agreement metric marked `not measured` (a reference capture is
+ required, docs/06 §5).
+
+### 2.3 Output
+
+```text
+<out>/
+ version-diff.json stable machine-readable report
+ version-diff.md rendered report
+ symbols/<target>.json merged symbol DB per side (when non-empty)
+ sessions/<target>-probe/ recorded probe session
+ sessions/<target>-behavior/ recorded scenario session
+ compare/<old>--<new>.md compare_sessions report
+ corpus/corpus-diff.json when --corpus-diff is used
+```
+
+Exit status: `0` for a normal report (skips included), `2` for a tool error,
+`3` for an RM 610 gate refusal.
+
+---
+
+## 3. RM 610 gate (hard, testable)
+
+The gate is enforced by `version_diff` and `ci_release` **before any target
+file is opened** and before `--out` is created:
+
+* a target/release label matching `rm610` / `RM-610` / `rm_610` etc. (the
+ `--release`, `--old-label`, `--new-label` values and the target basenames)
+ is refused unless
+ * `--approval-token RM610-APPROVED` is passed, or
+ * `GREENEYE_RM610_APPROVED=1` is set;
+* the refusal text points at [`docs/23-RM610-READINESS.md`](23-RM610-READINESS.md)
+ and states that nothing was downloaded and no target file was opened;
+* the same check runs in `--dry-run` mode.
+
+Nothing in either tool performs network access, and `--dry-run` never touches
+a target. Current-version rehearsal runs use `570.144`-labelled inputs; 610
+inputs require the §6 approval message in docs/23.
+
+---
+
+## 4. Release pipeline
+
+### 4.1 Dry run first
+
+```bash
+python3 harness/ci_release.py --old <old> --new <new> --out dumps/release-570 \
+ --old-label 570.144 --new-label 570.211 --dry-run
+```
+
+`--dry-run` prints a deterministic JSON plan (the three step commands and the
+artifact paths) and exits `0` without creating `--out` or opening a target.
+This is the docs/23 §4 step 6 rehearsal.
+
+### 4.2 Real run
+
+```bash
+python3 harness/ci_release.py --old <old> --new <new> --out dumps/release-570 \
+ --old-label 570.144 --new-label 570.211 \
+ --old-symbols symbols-570.144.json --new-symbols symbols-570.211.json \
+ --stimulus examples/scripts/trace_rpc.py \
+ --mutation-kernel firmware/uproc_demo/build/uproc_demo.elf \
+ --mutation-iterations 8 --mutation-seed 7
+```
+
+Steps, in order:
+
+1. **version_diff** — writes `<out>/version-diff/` (section flags above);
+2. **portability_matrix** — old and new as two targets, same budgets, writes
+ `<out>/portability-matrix/portability-report.json|.md` and per-pair compare
+ reports; the self-comparison must show `all_zero`;
+3. **mutation_smoke** — `mutation_harness` on the demo (or `--mutation-kernel`,
+ with `--allow-foreign-mutation` for a re-hosted ELF) with a small budget;
+ writes `<out>/mutation-smoke/findings.jsonl|summary.md`.
+
+A step that raises is recorded as `status: error` and the later steps still
+run; the process exits `1` when any step errored, `0` otherwise (`skipped`
+steps do not fail the run, but their reasons are in the report). Exit `3` is
+the RM 610 refusal.
+
+`ci_release` flags: the `version_diff` inputs above plus `--matrix-symbols`
+(one JSON DB applied to both matrix targets), `--mutation-kernel` (default:
+the bundled demo), `--mutation-iterations`/`--mutation-seed`/
+`--mutation-timeout`/`--mutation-settle`, and `--allow-foreign-mutation` for a
+non-demo re-hosted ELF.
+
+### 4.3 Evidence bundle format
+
+```text
+<out>/
+ release-report.json pipeline summary (steps, gate, labels)
+ bundle-manifest.json sha256 + byte size of every other file
+ version-diff/ docs/21 §2.3 tree
+ portability-matrix/ portability-report.json|.md, symbols/, sessions/, compare/
+ mutation-smoke/ findings.jsonl, summary.md, sessions/
+```
+
+`bundle-manifest.json`:
+
+```json
+{
+ "format": "greeneye-release-bundle/1",
+ "tool": "harness/ci_release.py",
+ "files": [{"path": "version-diff/version-diff.json", "bytes": 12345, "sha256": "..."}]
+}
+```
+
+`release-report.json` shape:
+
+```json
+{
+ "format": "greeneye-ci-release/1",
+ "dry_run": false,
+ "rm610_gate": {"status": "passed", "labels": ["570.144", "570.211"]},
+ "old": {"label": "570.144", "path": "gsp-570.144.fwimage.bin"},
+ "new": {"label": "570.211", "path": "gsp-570.211.fwimage.bin"},
+ "steps": [{"name": "version_diff", "status": "ok",
+ "sections": {"image": "ok", "symbols": "ok", "corpus": "ok",
+ "probe": "ok", "behavior": "ok"}}],
+ "errors": [],
+ "manifest": "bundle-manifest.json"
+}
+```
+
+The manifest is written last, so its hashes cover the final release report.
+Attach the directory (or its manifest) to the docs/07 evidence checklist and
+the docs/14 §6 pack; the docs/23 §4 step 7 expected-artifact list maps 1:1.
+
+---
+
+## 5. CI workflow
+
+`.github/workflows/greeneye.yml` (new; `ci.yml` is untouched):
+
+* triggers on push/PR paths `debug/risc-v-gsp-rm-vm/**` and `tools/rm*/**`;
+* `ubuntu-latest`, no secrets, no hardware;
+* installs `qemu-system-misc clang lld-18`;
+* builds `make -C debug/risc-v-gsp-rm-vm sdk demo`;
+* runs `python3 -m unittest discover -s tests -t .` from the tool root (QEMU
+ integration tests run; if QEMU were absent they skip cleanly).
+
+The intended release gates this workflow covers are the fast inventory smoke
+from docs/19 §6 (`control_discovery --max-id 32 --patterns zero`), the harness
+suites, and the docs/21 dry-run. A full `ci_release` run against real firmware
+is driven manually/agentically because it needs the versioned inputs and
+symbol DBs; CI-adjacent automation should call it with `--dry-run` first.
+
+---
+
+## 6. MCP monitoring
+
+`plugins/mcp_server.py` (docs/20) exposes the same machinery to an agent:
+`probe_firmware`, `portability_matrix_run`, `mutation_run`, `compare_sessions`
+and `analyze_current`. The agent pattern for a release:
+
+1. `portability_matrix_run` with the old/new targets and fixed budgets;
+2. `mutation_run` on the demo with the pinned seed and budget;
+3. `compare_sessions` for the recorded evidence pair;
+4. read `version-diff.json` / `release-report.json` / `bundle-manifest.json`
+ from the run directory (agent reads the bundle, it does not recompute it).
+
+The MCP server remains read/observe by default and simulator-only (docs/20 §7).
+
+---
+
+## 7. Determinism, limits and safety
+
+* Fixed probe budgets, fixed scenario phases and the same stimulus make the
+ self-diff zero: an unchanged target reports zero symbol/probe/behavior
+ deltas (verified by `tests/test_version_diff.py`).
+* Agreement metrics M1–M6 are **not measured** without a reference capture
+ (docs/06 §5); the skeleton carries local `observed` facts only. L2/L3 claims
+ (timing, interrupts, device model) are out of scope.
+* Raw `.fwimage` targets execute as flat blobs; a firmware linked differently
+ may fault earlier than on silicon — that is first-touch data, not a boot
+ (docs/06 §3).
+* Only temporary work directories and `<out>/` are written; QEMU is always
+ launched and killed by the tools; log reads are bounded
+ (`--log-bytes`).
+* No downloads, no `/dev`, no kernel module, no existing gdbstub, no physical
+ GPU. The 610 gate is part of both CLIs, not documentation-only.
+
+---
+
+## 8. Tests
+
+```bash
+python3 -m unittest tests.test_version_diff -v
+make test # includes these with the rest of the suite
+```
+
+`tests/test_version_diff.py` covers the gate (default refusal, token/env
+approval, gate-before-open), the `diff_corpus.py` matching discipline
+(moved/resized/added/removed, anonymous shift), the 3-column export parser,
+missing/incompatible/raw target handling, corpus hints, `ci_release --dry-run`
+side-effect freedom, clean `--help`, and a demo self-diff asserting zero
+inventory and behavior deltas. QEMU integration tests skip cleanly when QEMU
+or the demo ELF is absent.
+
+---
+
+## 9. Roadmap mapping
+
+| Item | Where this doc/tool contributes |
+|---|---|
+| **R6 — multi-version orchestration** | `version_diff` + `ci_release` prefix the matrix with a version-scoped diff |
+| **H4.1 — semantic diffing** | symbol/corpus inventory deltas and behavior profiles for two firmware versions |
+| **V1 — metrics/golden data** | deterministic self-diff and stable JSON reports support golden comparisons |
+| **docs/23 C5/C7** | release pipeline implemented and exercisable; dry-run rehearsal path |
+| **H7 — agentic harness** | self-describing bundle + docs/20 MCP tools for monitored runs |