# 48 — GMMU walk & launch memory-contract audit (in development) > **Status: in development (2026-10-06, engine-path wave).** Documents the > functional page-table walker (`sim/gmmu_walk.py`) and the launch > memory-contract auditor (`harness/launch_audit.py`). Together they answer, > offline and deterministically, *where the engine would read/write each > address of a submission* — the class of question behind "the front-end never > wrote the QMD" and "the release never fired". > > Development-only tooling: never a shipped component, never in a guest, > never touching hardware. Fidelity is the address view (L1); no timing or > engine-behaviour claim. > > **See also:** `43-GMMU-PTE-DECODER.md` (entry decoding), `42`/`44` > (observe-only GMMU/FBIF windows and the oracle workflow), `46`/`47` > (surgical rehost and predicate probes), `06-ACCURACY.md` (fidelity levels). ## 1. What the two tools are * **Walker** (`sim.gmmu_walk`): given a page-directory base PA and a sparse physical-memory view of the table pages, it follows VER2 entries through all five levels and reports the leaf mapping — PA, page size, aperture, writable/read-only — or the exact fault (`table_missing`, `invalid_aperture`, `pte_invalid`, `unsupported`, `va_out_of_range`). Every level's raw entry and decode is kept in the report. * **Auditor** (`harness/launch_audit.py`): takes a submission's **memory contract** — the labelled VAs a launch requires (ring, inline-QMD destination, parameter/program pages, release slot, report semaphore, ctx buffers) — walks each leg, and classifies the result against what the host believes: | verdict | meaning | |---|---| | `ok` | the engine can reach the leg with the required access | | `pa_mismatch` | the walk resolves to a different PA than the host's bookkeeping (the engine writes the shadow while you check the guest page, or vice versa) | | `unmapped` | no mapping/valid leaf: the engine's access is dropped or faults | | `access_denied` | the leaf is read-only for a write leg | Schemas: `greeneye-gmmu-walk/1`, `greeneye-launch-audit-spec/1`, `greeneye-launch-audit/1`, `greeneye-launch-audit-check/1`. ```bash python3 -m sim.gmmu_walk demo # captured channel-PDB vector python3 -m sim.gmmu_walk run --pdb-pa 0x12f16e000 \ --va 0x100000 --mem 0x12f16e000= … # ad-hoc walk python3 harness/launch_audit.py demo # captured contract + fork classes python3 harness/launch_audit.py run --spec audit.json python3 harness/launch_audit.py check --spec partial.json # pages to dump next python3 harness/launch_audit.py capture-hints # exact live dump list + skeleton ``` ## 2. Evidence and validation * Geometry, field positions and encodings come from the same evidence base as the PTE decoder (`docs/43`); the walker composes the decoder rather than re-implementing it. * The 4K and 2M paths are **validated against a live captured channel PDB**: `sim.gmmu_walk demo` walks five VAs and must reproduce the DMA addresses the capture logged (5/5, deterministic). The capture lives as numeric entries in the module (`channel_pdb_fixture`). * The 64K dual path is **modelled** from the format definitions and reported with `evidence: "modeled"` until a capture validates it. Video-memory leaf addresses are reported with `pa_complete: false` (the decoder does not claim the high bits there). * Unsupported encodings fail closed; an incomplete table view reports `unmapped`, never "unreachable". ## 3. Using it on the live launch wall `harness/launch_audit.py capture-hints` prints the capture recipe plus the exact page list and a copy-paste `greeneye-launch-audit-spec/1` skeleton for the current wall. In short: the channel's PDB base PA, a raw PA→bytes dump of the table pages reachable from it for the contract VAs, the submitted stream's pointers (GPFIFO ring, `SET_INLINE_QMD_ADDRESS_A/B` destination, QMD parameter/program/release-slot addresses, report semaphore), and the host's shadow bookkeeping for the same VAs. Put them in one spec and run the audit. ### 3.1 `check` — iterate the capture with an empty or partial dump `check` runs the same walks but reports, per contract leg, the FIRST table page the `memory[]` view is missing — the page to dump next — with the level/index path that reached it, and a consolidated `needed_pages[]` list (sorted, de-duplicated, each entry naming the legs that need it). A leg whose walk faults on entries that are present (invalid aperture/PTE, unsupported encoding) is `fault` and needs no more pages; `complete` means every table page the walk needed is in the spec (not that any mapping is correct). Start with only `pdb_pa` + `contract` (empty `memory`) and let the tool drive one dump level per iteration; then run the verdict audit on the completed spec: ```bash python3 harness/launch_audit.py check --spec m7-launch.json # -> needed_pages python3 harness/launch_audit.py check --spec m7-launch.json --out ./out python3 harness/launch_audit.py run --spec m7-launch.json ``` ### 3.2 Current live wall dump list (m7 evidence, 2026-10-06) The m7 launch silence needs these raw pages (`capture-hints` prints the same list with the spec skeleton): * host-side chain (the validated 5/5 fixture): root `0x12f16e000`, pd2 `0x12f16f000`, pd1 `0x11065e000`, pd0 `0x110663000`, pt `0x15908d000`; * forward-window PT pages `0x1b8706000` (pdeIndex 4128) and `0x1197a6000` (pdeIndex 4131); the geometry rule `pdeIndex = (pd1_idx << 8) | pd0_idx` places the forward windows at pd1 index 16, and their pd1/pd0-level chain entries are server-written (in no host log) — dump them too if they fall outside the host pages above; * the 16 guest-scratch-PT page DMAs (`vaBase=0x200000`, capacity 512) — the Tier-2 shadow legs resolve through them; * the `rmcall_gr_shadow` chunk DMAs — the engine-write shadow pages for the high-VA legs. Contract VAs: QMD destination `0x204608000` (write), release slot `0x204027f7c` (write), low-window ring/push-buffer legs `0x100000..0x104000`, and the high-VA param/program legs, which are NOT directly mapped — they resolve through the shadow aliases (`0x210000`/`0x220000`/`0x260000`) whose PTEs come from the guest scratch pool. Reading the result: * `pa_mismatch` on the inline-QMD destination or the release slot means the engine operates on a different PA than the one the post-mortem checks — fix the bookkeeping/observation point, not the engine path. * `unmapped` means the engine's access is dropped at translation; the fault names the exact missing level/table — `check` names the page to dump. * all-`ok` with a silent launch means the address path is complete: the silence is downstream (front-end/engine/context), and the next suspect is the context program itself (see docs/46/47 for the commit-gate probes). ### 3.3 Demo scenarios and expected verdicts (pinned by tests) `demo` audits the fork classes and prints every verdict: | scenario | mapping (VA → PA) | host bookkeeping | expected verdict | |---|---|---|---| | captured channel contract | 5 low-window legs (validated fixture) | matches capture | all `ok` | | `qmd-dest-shadow-trusted` | 0x204608000 → shadow 0x15e8fa000 | shadow PA | `ok` | | `qmd-dest-host-checks-guest` | 0x204608000 → shadow | guest page 0x204608000 | `pa_mismatch` | | `qmd-dest-unmapped` | 0x204608000 → unmapped | n/a | `unmapped` | | `engine-write-shadow-host-checks-guest` | alias 0x210000 → shadow PA | guest PA | `pa_mismatch` | | `engine-write-shadow-bookkeeping-aligned` | alias 0x210000 → shadow PA | shadow PA | `ok` | The last two are the next wall class after dispatch clears: CUDA output buffers at high VAs, where Tier-1 is refused (0x1f) and the leg resolves through the guest scratch PT pool to a shadow page. The shadow/guest PAs in the demo are synthetic (documented in the `run_demo` docstring), not m7 captures. ## 4. Reuse across firmware/driver version upgrades Both tools are data-driven and fail closed; nothing about them encodes a specific firmware version except the entry-encoding family: 1. On a new version, re-run `python3 -m sim.gmmu_walk demo` and the test module. If the encoding family changed, the walker refuses with `unsupported` rather than guessing. 2. Extend the evidence base (decoder field tables/encoders) and the LEVELS geometry in one place (`sim/gmmu_pte.py` / `sim/gmmu_walk.py`), add one captured golden vector per new format, and pin it in tests. 3. Keep `firmware`/`gmmu` fields in audit specs so reports are self-describing across versions; the audit refuses an unknown `gmmu` kind. 4. Re-capture the contract data from the new version (the capture recipe is version-independent) and re-run; compare the two audit reports for behaviour drift. ## 5. Tests `tests/test_gmmu_walk.py` (15) and `tests/test_launch_audit.py` (20): captured golden vector, synthetic round-trips, 2M/64K paths, all fault classes, read-only leaves, mismatch/unmapped/access-denied verdicts, check-mode page needs (empty memory, partial memory, complete+fault legs, needed-pages dedup/ordering, CLI exit codes/artifact), engine-write shadow demo verdicts, determinism, spec errors and CLI exit codes. ```bash python3 -m unittest tests.test_gmmu_walk tests.test_launch_audit -v ```