summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/docs/48-GMMU-WALK-AND-LAUNCH-AUDIT.md
blob: c412f215e57561a2e0521a2da8c0b83d359b41fe (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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# 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=<hex> …      # 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
```