diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-04 18:42:36 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-04 18:42:36 -0400 |
| commit | e214609c2f60e35254c243a806a82a0f1d4ec3ae (patch) | |
| tree | 1731b30a65f1cd45ebea9155d1bf882017b17dc3 /debug/risc-v-gsp-rm-vm/docs | |
| parent | 09b50bfee94fe21088fa1449e16153b9a413b44b (diff) | |
| download | open-iov-e214609c2f60e35254c243a806a82a0f1d4ec3ae.tar.gz open-iov-e214609c2f60e35254c243a806a82a0f1d4ec3ae.zip | |
greeneye: GMMU/FBIF observability, PTE decoder and oracle, MPU/libos simulator wave
Simulator development wave (docs 42-44): GMMU page-table entry decode with its oracle/workflow harness, FBIF observability, and MPU/LIBOS device surfaces in the device model, plus refreshed boot/machine/profile data (tu104-570.144), the qemu-line results, and the new unit tests. Docs 00/06/35-41 updated to match; harness helpers and .gitignore included.
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/docs')
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/docs/00-OVERVIEW.md | 12 | ||||
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/docs/06-ACCURACY.md | 8 | ||||
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/docs/35-GREENEYE-SIMULATOR-DESIGN.md | 20 | ||||
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/docs/36-GSP-DEVICE-MODEL.md | 127 | ||||
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/docs/37-SIM-QEMU-LINE.md | 47 | ||||
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/docs/39-RE-WORKFLOW-REVIEW.md | 2 | ||||
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/docs/40-NEW-VERSION-TRIAGE.md | 8 | ||||
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/docs/41-STABILITY-AND-TIME-TO-VALUE.md | 45 | ||||
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/docs/42-GMMU-FBIF-OBSERVABILITY.md | 203 | ||||
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/docs/43-GMMU-PTE-DECODER.md | 206 | ||||
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/docs/44-GMMU-ORACLE-AND-WORKFLOW.md | 254 |
11 files changed, 862 insertions, 70 deletions
diff --git a/debug/risc-v-gsp-rm-vm/docs/00-OVERVIEW.md b/debug/risc-v-gsp-rm-vm/docs/00-OVERVIEW.md index 50b4428..a5f14ab 100644 --- a/debug/risc-v-gsp-rm-vm/docs/00-OVERVIEW.md +++ b/debug/risc-v-gsp-rm-vm/docs/00-OVERVIEW.md @@ -44,12 +44,12 @@ whole stack scriptable, and QEMU is just the first execution backend. | Plugin loader + example plugin | working | | Native RV64IMAC simulator (M1, `--backend native`) | working; QEMU-parity differential test | | Native exec/access trace (M1.5, R4): `--trace`, analyzer coverage | working; trace blob + determinism test | -| Test suite | full suite green on the 2026-10-03 review host (298 tests incl. live QEMU + native differential; count varies with local prerequisites) | +| Test suite | full suite green on the 2026-10-03 review host (417 tests incl. live QEMU + native differential; count varies with local prerequisites) | | Ghidra bridge | scripts + docs | | Real GSP `.fwimage` probe (first-fault report) | working (first fault + lenient continuation) | -| GSP device model + versioned profiles (S1) | in development: `sim/devices/`, `sim/profiles/`, `sim/gsp_boot.py`; B0–B2 on the real 570.144 image with 0 unknown MMIO (36) | +| GSP device model + versioned profiles (S1) | in development: `sim/devices/`, `sim/profiles/`, `sim/gsp_boot.py`, XMPU model `sim/mpu.py`, libos service model `sim/libos.py`; B0–B3 on the real 570.144 image with 0 unknown MMIO (36) | | Modeled cycle accounting | increment 0: `sim/cycles.py`; modeled defaults, not silicon timing (32) | -| Simulator-vs-QEMU A/B + new-version triage | working: frozen thresholds, verdict `above` on the S1 path per docs/37 §4.3; `harness/triage.py` (37, 40) | +| Simulator-vs-QEMU A/B + new-version triage | working: frozen thresholds, verdict `above` on the S1 path per docs/37 §4.4; `harness/triage.py` (37, 40) | | GPGPU-Sim overlay | Phase 0 scaffold + CUDA-12 build port record; Phase G gated (30) | | TCG full-memory tracing plugin | source + optional build (verified live) | | Automation harness (mutation/portability/version diff) + value demos | working (18/19/21/25) | @@ -63,7 +63,7 @@ The in-development notices were removed on 2026-10-02 after the tool audit end-to-end s01–s10 value demonstrations ([25-VALUE-VALIDATION](25-VALUE-VALIDATION.md)). Open items stay listed as open: docs 28/29 are unadmitted pilots, the device model is partial (S1, -B0–B2; docs/36) and custom-simulator M2+ remains roadmap, and nothing here is +B0–B3; docs/36) and custom-simulator M2+ remains roadmap, and nothing here is a stability guarantee beyond the audited v0.1 surface. ## Quick start @@ -127,6 +127,10 @@ Interactively: | [38-GSP-EM-API](38-GSP-EM-API.md) | GSP-emulator/system-simulator TLV boundary (design) | | [39-RE-WORKFLOW-REVIEW](39-RE-WORKFLOW-REVIEW.md) | reverse-engineering workflow review | | [40-NEW-VERSION-TRIAGE](40-NEW-VERSION-TRIAGE.md) | one-command new-version triage | +| [41-STABILITY-AND-TIME-TO-VALUE](41-STABILITY-AND-TIME-TO-VALUE.md) | formal stability and value report | +| [42-GMMU-FBIF-OBSERVABILITY](42-GMMU-FBIF-OBSERVABILITY.md) | observe-only GMMU/FBIF windows and trace (in development) | +| [43-GMMU-PTE-DECODER](43-GMMU-PTE-DECODER.md) | TU104 GMMU PTE/PDE decoder (in development) | +| [44-GMMU-ORACLE-AND-WORKFLOW](44-GMMU-ORACLE-AND-WORKFLOW.md) | GMMU/FBIF oracle comparison and static<->dynamic join (in development) | | [RISC-V-GSP-RM-DEBUGGER-ROADMAP](../RISC-V-GSP-RM-DEBUGGER-ROADMAP.md) | long-term program H1–H7 | ## Where this fits in the Open-IOV workflow diff --git a/debug/risc-v-gsp-rm-vm/docs/06-ACCURACY.md b/debug/risc-v-gsp-rm-vm/docs/06-ACCURACY.md index a9a91aa..1a85ec5 100644 --- a/debug/risc-v-gsp-rm-vm/docs/06-ACCURACY.md +++ b/debug/risc-v-gsp-rm-vm/docs/06-ACCURACY.md @@ -124,9 +124,11 @@ Before trusting a risc-v-gsp-rm-debugger session: ## 8. Known accuracy gaps (living list) 1. Partial TU104 device model (S1, docs/36): the real 570.144 image is now - served past the first unmapped access to B2 (relocation copy, 0 unknown - MMIO) with a recorded boot shim; B3/B4 (CSB poll, message loop) are not - reached, and the model is uncalibrated and partial. + served past the first unmapped access to B3 (libos init complete; the + message-loop task blocks in the port wait, 0 unknown MMIO) with the XMPU + model, a recorded boot shim and the libos service model; B4/B5 + (host-traffic-triggered CSB poll, scripted message) are not reached, and + the model is uncalibrated and partial. 2. No time/concurrency model; multi-core and partition-switch behaviour is approximated. Cycles are modeled defaults, not measured (docs/32). 3. PMU/SEC2/ACR/Booter behaviour is message-level only. diff --git a/debug/risc-v-gsp-rm-vm/docs/35-GREENEYE-SIMULATOR-DESIGN.md b/debug/risc-v-gsp-rm-vm/docs/35-GREENEYE-SIMULATOR-DESIGN.md index 83954d4..d5bf4a4 100644 --- a/debug/risc-v-gsp-rm-vm/docs/35-GREENEYE-SIMULATOR-DESIGN.md +++ b/debug/risc-v-gsp-rm-vm/docs/35-GREENEYE-SIMULATOR-DESIGN.md @@ -129,11 +129,20 @@ reproducible twice identically and have its device requirements listed. |---|---|---| | B0 | flat image loads; first MMIO observed at `0x2000000000000A00` | done (native, lenient) | | B1 | device model serves the whole boot path: 0 unknown MMIO, 0 unhandled in windows; deterministic 2M-instruction run stopping at the boot-halt/report loop (`0x4010928`) | done (S1 device model; sessions `dumps/gsp-boot-b1-run1/2`) | -| B2 | boot stage progress: vector table/TCM/MPU/trap handling exercised with the shim; passes the bootargs consumer and reaches the relocation copy, then stops on the known DMEM-runspace unknown (`fault @ 0x400EA0C`; docs/36 §4) | done (shim) | -| B3 | first CSB/TRANSCOM mailbox poll with correct read-back semantics | target | -| B4 | message loop reached (`appMain`/equivalent); waits on host message deterministically | target | +| B2 | boot stage progress: vector table/TCM/MPU/trap handling exercised with the shim; passes the bootargs consumer and reaches the relocation copy | done (S1 device model + XMPU model + bootstrap-args shim; reaches B3) | +| B3 | libos init completes (TCM registration, pagetable registration, DMA flush, external-interrupt arming) and the message-loop task blocks in the port wait | done (sessions `dumps/gsp-boot-b3-run1/2`: 474 043 insns, stop `ecall` at `0x4018038`, two-run identical) | +| B4 | first CSB/TRANSCOM mailbox poll served with correct read-back semantics (host-traffic-triggered doorbell) | target | | B5 | one scripted host-side RM message served end-to-end with a recorded reply | target (gated by S3 injection) | +**Ladder revision (2026-10-03, B3).** The original B3 wording assumed the +firmware polls CSB/TRANSCOM during a silent boot. The 570.144 image does not: +after arming interrupts and completing its libos services it blocks in the +message-loop port wait, and the CSB/TRANSCOM poll is *triggered by host +traffic*. B3 is therefore anchored on the observed wait (a real, reproducible +milestone) and the CSB poll moved to B4, where a scripted doorbell will +produce it. The pre-revision text is kept in history; this note is the +operative definition. + The ladder is deliberately versioned-data driven: a new firmware version is a new profile + shim record, not a code fork. @@ -211,7 +220,7 @@ flags; every call attributable to a session. | Probe/record/plugins | yes | — | backend peer | yes | | Ghidra symbol/control bridges | yes | watch/heat-map/nav | protocol-compatible | full (S2/S3) | | Injection SDK | demo | RM-core interception H1.3 | demo backend | services vs GSP model (S3) | -| Device model / real boot | no (faults) | H2.2/H2.3/H2.4 planned | ladder B0–B2 (S1, device model + shim) | B4/B5 (S1/S3) | +| Device model / real boot | no (faults) | H2.2/H2.3/H2.4 planned | ladder B0–B3 (S1, device model + XMPU + libos services) | B4/B5 (S1/S3) | | Determinism/replay | wall-clock | H2.4 budgets | instruction-count | event replay (S2) | | MCP | yes (QEMU) | — | native sim tools + backend selection (20) | wider GSP-model tools (S3) | | Tiering | — | — | modeled cycles | calibrated bands (S4) | @@ -277,12 +286,13 @@ The comparison and decision are recorded in the private handoff. | Wave | Date | Result | |---|---|---| -| Recon + master handoff | 2026-10-03 | `handoff-simulator.md`, local sources register | +| Recon + master handoff | 2026-10-03 | master handoff; source register | | W1 GPGPU-Sim build | 2026-10-03 | CUDA 12 port patches; `vectorAdd` functional+perf green | | W1 TU104 config/overlay | 2026-10-03 | config scaffold + sanity 26/26 + admissions draft | | W1 GSP cycle-0 | 2026-10-03 | modeled cycles; real image 200k instr deterministic; 298 tests green (2026-10-03 suite run; count varies with prerequisites) | | W1 dossier | 2026-10-03 | Turing/Ampere/Blackwell parameter docs + cross-generation survey | | W2 Phase S (S1 + S4) | 2026-10-03 | `sim/devices` + `sim/profiles` device model: B0–B2 on the real image (0 unknown MMIO), 2-run determinism; A/B harness discovers the profile and records verdict `above` on the frozen checks (docs/36, docs/37 §4.3, docs/40) | +| W2 Phase S (4c: XMPU + libos) | 2026-10-03 | `sim/mpu.py` + `sim/libos.py` + bootstrap-args shim: B3 on the real image (474 043 instr, message-loop port wait, 0 unknown MMIO, 2-run identical); A/B re-run at `--instructions 2000000` verdict `above` (docs/37 §4.4); 417 tests green | | W2 Phase S | launched | S2 parity, S3 MCP/injection, S4 validation | Backlog (recorded, not started): Ampere/Blackwell models; fabric (PCIe, diff --git a/debug/risc-v-gsp-rm-vm/docs/36-GSP-DEVICE-MODEL.md b/debug/risc-v-gsp-rm-vm/docs/36-GSP-DEVICE-MODEL.md index 9516fd5..2741748 100644 --- a/debug/risc-v-gsp-rm-vm/docs/36-GSP-DEVICE-MODEL.md +++ b/debug/risc-v-gsp-rm-vm/docs/36-GSP-DEVICE-MODEL.md @@ -7,7 +7,7 @@ > `06-ACCURACY.md`. Development-only: nothing here authorises GPU hardware, > kernel modules, mdev, guest access or reboots. > -> **See also:** `37-SIM-QEMU-LINE.md` (the B0–B2 device model on the frozen +> **See also:** `37-SIM-QEMU-LINE.md` (the B0–B3 device model on the frozen > simulator-vs-QEMU checks) and `40-NEW-VERSION-TRIAGE.md` (one-command triage > of a new image through this ladder). @@ -47,13 +47,15 @@ sim/devices/__init__.py registry_from_profile(profile, machine) │ FBIF control/transcfg ├─ priscv.py RISC-V-local block: IRQ mask, trace, error regs ├─ timer.py PTIMER (modeled virtual time; opt-in) - ├─ csb.py CSB/TRANSCOM message window seam (B3 landing) + ├─ csb.py CSB/TRANSCOM message window seam (B4 landing) ├─ fwsec.py WPR2 address pair (FWSEC state unmodeled) ├─ pmu.py PMU stub: observes, never fakes ├─ dma.py aperture → RAM translation seam (FBGPA/SYSGPA) └─ irq.py falcon + RISC-V-local IRQ aggregation ▼ sim/machine.py Machine(devices=..., translator=..., reset_regs=...) + │ + sim/mpu.py: XMPU entry model (fetch/load/store) + │ + sim/libos.py: libos syscall service model ▼ sim/gsp_boot.py bounded real-firmware run + session + rung ``` @@ -80,7 +82,7 @@ noted; every unlisted offset is unhandled and logged. | RISC-V local | `+0x111000`, alias `0x300001000` | `IRQMSET` 0x2ac, `IRQMCLR` 0x2b0, `IRQMASK` 0x2b4 (read), `IRQDEST` 0x2b8, `IRQTYPE` 0x2bc, `TRACECTL` 0x344, `TRACE_RDIDX` 0x348, `TRACE_WTIDX` 0x34c, `PRIV_ERR_STAT` 0x360, `PRIV_ERR_INFO` 0x364 (read), `PRIV_ERR_ADDR` 0x368 (read), `HUB_ERR_STAT` 0x36c | header + observed | | PTIMER | `+0x9000` | `TIME_0` aperture 0x9400 / block `+0x400` (NSEC 31:5), `TIME_1` aperture 0x9410 / block `+0x410` (NSEC 28:0); value = instruction count × `ns_per_insn` (**modeled**, not calibrated) | header + **modeled** time | | WPR2 | `+0x1FA000` | `WPR2_ADDR_LO` 0x1FA824, `WPR2_ADDR_HI` 0x1FA828 (4 KiB alignment; HI points at the last 128 KiB block) | header + firmware use | -| CSB/TRANSCOM | profile-declared | none by default; a profile may declare individual registers with width/value and `rw`/`ro`/`wo`/`w1c`/`set`/`clear` semantics | none yet (B3 landing point) | +| CSB/TRANSCOM | profile-declared | none by default; a profile may declare individual registers with width/value and `rw`/`ro`/`wo`/`w1c`/`set`/`clear` semantics | none yet (B4 landing point) | | PMU | profile-declared | none; the stub records accesses and returns unhandled | none yet | | DMA seam | FBGPA/SYSGPA (profile) | GPU-physical → RAM windows for falcon DMA and aperture loads/stores; unmapped transfers are refused and recorded | address-map header + firmware use | @@ -99,10 +101,15 @@ and records each as an assumption in `session.json.boot_shim.assumptions`: * `NV_RISCV_BOOT_PARAMS` at a RAM address (bootType `NVRM`, size 120, version 1, core-dump buffer, one `WPRM` libos init arg); * a `GspFwWprMeta` (magic/revision/verified and a self-consistent FB layout); -* raw-image mirror at the DMEM aperture base so the app's own relocation copy - is self-consistent (it is not: see §6); -* `a0`–`a5` register arguments (bootargs PA/VA, ELF PA/size, load base, WPR - id). +* a `libos_bootstrap_args_t` — six `u64` fields (IMEM and DMEM + `base`/`size`/`bs`) the real bootloader builds from its TCM configuration + and passes in `a0`; the M-mode TCM configuration is declared in the + profile (offsets 0, 64 KiB windows). This replaces the earlier + raw-image mirror at the DMEM aperture: with the XMPU model (§3.2) the + resident-section copy reads its real backing and no image mirror is needed; +* `a0`–`a5` register arguments (a0 = bootstrap-args pointer; the rest are + declared assumptions the boot path only uses where its own computation is + not self-sufficient). The DEBUGINFO **host-consume** behaviour is also a shim: the firmware writes a boot-stage marker and polls bits 22:20 until they clear, and no self-clearing @@ -110,28 +117,86 @@ path exists in the firmware, so the host side is modelled as consuming the stage marker on read. The device never changes a read value except for this documented shim, and the shim is profile-enabled and session-recorded. +### 3.2 CPU protection unit (XMPU) — core model, `sim/mpu.py` + +The GSP core boots in bare mode and then enables a small protection unit +through M-mode CSRs; the firmware programs entries for its code/data ranges +and, during init, for a scratch window it copies resident sections through. +Evidence kind: **hardware CSR/field header + firmware use + observed access +sequence** (the 570.144 bootstrap writes are visible in any run's device/CSR +trace). Modelled CSRs: + +| CSR | value | semantics | +|---|---|---| +| `MMPUIDX` (0x7C8) | entry index | INDEX field 5:0 (64 entries) | +| `MMPUVA` (0x7C9) | virtual base | BASE 63:12, `VLD` bit 0 | +| `MMPUPA` (0x7CA) | physical base | BASE 63:12 | +| `MMPURNG` (0x7CB) | range (a size) | RANGE 63:12 | +| `MMPUATTR` (0x7CC) | attributes | recorded, not enforced | + +Translation is active only when `mstatus.VM == NVMPU` (`0x1E`; field bits +28:24); lookup is lowest-index-first (the firmware's own bootstrap comment +documents the precedence). An entry maps `addr` to `pa + (addr - va_base)` +when `va_base <= addr < va_base + range`. The model applies to instruction +fetch, loads, stores and atomics; permissions/cache attributes are recorded +but not enforced, and **no match is treated as identity** (more lenient than +silicon) because the simulator keeps local memory flat and the boot path's +mappings are self-consistent. `read_mem`/`write_mem` (debugger surface) +remain raw and untranslated. + +Evidence in the session: `session.json.mpu` carries the enabled flag, entry +table and `mstatus`. The model removes the B2 ceiling: with the correct +bootstrap args and the scratch mapping, the resident-section copy reads the +in-place image and the run continues (no DMEM image mirror, no faked copy). + +### 3.3 libos syscall surface — service model, `sim/libos.py` + +The application is a libos task that requests kernel services with `ecall` +(the syscall number in `t0`). The native machine stops on `ecall` unless a +handler is installed, so the boot runner attaches a deterministic service +model that: + +* records every request (number, name, arguments, pc, instruction count) in + `session.json.libos`; +* acknowledges only no-ops and modelled services — task yield, cache + invalidation, external-interrupt arming, profiler enable/query, the + modeled timer read, DMA bounce-buffer registration (`TCM` region), + inverted-pagetable registration, and DMA copy/flush (an in-RAM copy in the + flat model: the real kernel drives the falcon DMA through TCM, the copy + effect is identical and is not faked — both sides must resolve or the run + stops); +* **never fakes waits**: `processor_suspend`, `task_port_send_recv_wait`, + `task_panic` and every unimplemented syscall stop the run with `R_ECALL` + and are reported in `session.json.libos.stopped`. + +Task switching, interrupt delivery, pagination, partition switch and memory +queries are not modelled (force-stopped, not invented). The B3 result is +exactly this stop: the message-loop task blocks in `task_port_send_recv_wait` +with `armed_mask=0xFF`. + ## 4. Boot ladder results (observed, 2026-10-03) | Rung | Claim | Evidence | |---|---|---| | **B0** | flat image loads; first MMIO is the unmodelled PMC `BOOT_42` read at `0x2000_0000_0000_0A00` | `tests/test_sim_devices.py` (B0 classification) | | **B1** | device model serves the whole boot path: **0 unknown MMIO, 0 unhandled in windows**, deterministic 2M-instruction run stopping at the boot-halt/report loop (`0x4010928`) | `run_boot(use_shim=False)`; sessions `dumps/gsp-boot-b1-run1`, `dumps/gsp-boot-b1-run2`; test `test_no_shim_b1_device_model_serves_boot` | -| **B2** | with the shim, the firmware passes the chip gate, MPU/vector setup, the bootargs consumer (`0x4007A30`) and reaches its **relocation copy** (`0x4007B7C`), then stops at a known unknown (DMEM runspace) | sessions `dumps/gsp-boot-b2-run1`, `dumps/gsp-boot-b2-run2`; test `test_shim_reaches_relocation_b2` | +| **B2** | with the shim, the firmware passes the chip gate, XMPU setup, the bootstrap-args consumer and its **relocation copy** (`0x4007B7C`); the XMPU model resolves the copy's real backing and the run continues into libos init | sessions `dumps/gsp-boot-b2-run1`, `dumps/gsp-boot-b2-run2`; test `test_shim_reaches_relocation_plus_mmu` | +| **B3** | libos init completes (TCM registration `0x5B22000`/`0x1000`, pagetable registration, DMA flush, external-interrupt arming `0xFF`) and the message-loop task blocks in the port wait | sessions `dumps/gsp-boot-b3-run1`, `dumps/gsp-boot-b3-run2`; test `test_shim_reaches_message_loop_b3` | Reproduction (read-only; no hardware): ```bash cd open-iov/debug/risc-v-gsp-rm-vm -python3 -m sim.gsp_boot --instructions 2000000 --record dumps/gsp-boot-b2-run1 -python3 -m unittest tests.test_sim_devices -v +python3 -m sim.gsp_boot --instructions 2000000 --record dumps/gsp-boot-b3-run1 +python3 -m unittest tests.test_sim_devices tests.test_mpu -v ``` Two-run determinism is asserted by `test_two_runs_identical` and was checked -at the recorded B2 sessions: identical instruction count (14 682), stop -reason/PC (`fault @ 0x400EA0C`), device-state SHA-256 -(`3fa264b9…`) and register file. The B1 no-shim run is likewise identical -across runs (2 000 000 instructions, budget stop at `0x4010928`, device-state -SHA-256 `88eb07b7…`). +at the recorded B3 sessions: identical instruction count (474 043), stop +reason/PC (`ecall @ 0x4018038`, syscall +`task_port_send_recv_wait`), device-state SHA-256 (`5b3cc7e4…`) and register +file. The B1 no-shim run is likewise identical across runs (2 000 000 +instructions, budget stop at `0x4010928`, device-state SHA-256 `88eb07b7…`). ## 5. Session evidence @@ -139,21 +204,24 @@ Each `run_boot` invocation writes a `greeneye-session/1` directory with: * `session.json` — firmware SHA-256, profile name, load layout, rung, milestones, stop reason/PC, unknown-MMIO counts, device-state SHA-256, - device-access counts and the full boot-shim assumption list; + device-access counts, the full boot-shim assumption list, the XMPU state + (`mpu`: enabled, entries, mstatus) and the libos service log (`libos`: + calls, counts, TCM/pagetable registrations, the stopping request); * `blobs/device-state.json` — the `greeneye-sim-devices/1` snapshot of every device (registers, IRQ state, DMA windows/counters, shim flags); * `events.jsonl` — the normal session event stream (`gsp_boot` note). ## 6. Unknowns (explicitly not modeled / not verified) -1. **DMEM runspace content.** The app's relocation copy reads the DMEM - aperture (`0x1_0000_0000+`) and writes it to the `0x400E000` runtime - region; the bootloader-provided content is not reconstructed, so the copy - writes zeros and the run stops at `0x400EA0C` (illegal instruction). This - is the B2 ceiling today. -2. **CSB/TRANSCOM register semantics.** No message-window poll has been - observed yet, so no register is modeled. B3 waits for the first observed - poll; the `csb.py` seam and profile register declarations are in place. +1. **CSB/TRANSCOM register semantics.** No message-window poll has been + observed yet, so no register is modeled. The 570.144 firmware blocks in + the message-loop port wait before any CSB access; B4 waits for the first + host-traffic-triggered poll; the `csb.py` seam and profile register + declarations are in place. +2. **Libos services.** Task switching, interrupt delivery, pagination, + partition switch and memory queries are not modelled: the service model + records and force-stops on them (never invents progress). DMA copies are + modelled as in-RAM copies in the flat memory model. 3. **PMU.** No PMU register access observed; stub only. 4. **FWSEC state machine.** Only the WPR2 address pair is modeled; FWSEC execution is not. @@ -161,14 +229,15 @@ Each `run_boot` invocation writes a `greeneye-session/1` directory with: not proven; only the profile's DMA windows decide what an address means. 6. **Interrupt delivery.** Falcon/RISC-V IRQ state is aggregated and serialisable, but mstatus/mie/mip/mtvec trap entry and interrupt injection - are not modelled. + are not modelled (the firmware arms external interrupts with mask `0xFF` + and then waits forever in the flat model). 7. **PTIMER calibration.** Values are modeled from the instruction count with a placeholder `ns_per_insn`; no firmware PTIMER read has been observed and no timing claim is made. -8. **MPU translation.** The firmware programs its MPU CSRs (observed), but - the machine does not enforce translations; the libos private mapping is - served by explicit device aliases and the FBGPA/SYSGPA seam, not by an MPU - model. +8. **XMPU enforcement.** Translation, VLD gating, mstatus enable and + lowest-index precedence are modelled; permissions, cache/coherence + attributes and WPR IDs are recorded but not enforced, and an unmatched + access passes through as identity (lenient by design, listed here). Nothing in this page is a silicon-accuracy claim; `06-ACCURACY.md` fidelity levels apply (L0/L1 modelled, L2 modelled-not-calibrated, L3 partial and diff --git a/debug/risc-v-gsp-rm-vm/docs/37-SIM-QEMU-LINE.md b/debug/risc-v-gsp-rm-vm/docs/37-SIM-QEMU-LINE.md index 42c76cc..06fa943 100644 --- a/debug/risc-v-gsp-rm-vm/docs/37-SIM-QEMU-LINE.md +++ b/debug/risc-v-gsp-rm-vm/docs/37-SIM-QEMU-LINE.md @@ -29,11 +29,12 @@ and analyze/compare, probes/plugins, Ghidra bridges, trace, injection SDK and `Session.call`/`run_until`, plus the documented first fault at firmware vaddr `0x4009014` (4-byte read of `0x2000000000000A00`). -The boot ladder (docs/35 §3.3) is `pre-B0` (image loads, first MMIO observed -*as a fault*) < `B0` (first MMIO observed; lenient) < `B1` (first fault past -the QEMU point, explained by device semantics) < `B2` (boot-stage progress) < -`B3` (first mailbox poll) < `B4` (message loop) < `B5` (one scripted message -served). +The boot ladder (docs/35 §3.3, revised 2026-10-03) is `pre-B0` (image loads, +first MMIO observed *as a fault*) < `B0` (first MMIO observed; lenient) < +`B1` (first fault past the QEMU point, explained by device semantics) < +`B2` (boot-stage progress: relocation copy) < `B3` (libos init complete; +message-loop task blocks in the port wait) < `B4` (first CSB/TRANSCOM poll +served, host-traffic-triggered) < `B5` (one scripted message served). **"Above the QEMU line"** requires *all* of the frozen checks in §3, including the docs/35 §1.1 requirement that the milestone be reached "with @@ -196,6 +197,33 @@ MMIO. Frozen thresholds unchanged. Measured run on the same image `harness/qemu_line/sim-qemu-line.json|.md`). Wall figures are host-load dependent. +### 4.4 Re-measurement after the XMPU/libos wave (2026-10-03, session 4c) + +The native side now runs the real image with the XMPU (MPU) translation model +and the libos service model (docs/36 §3.2/§3.3), so the DMEM-runspace unknown +is gone. Fresh run, frozen thresholds unchanged, same image +(sha256 `2d1bb5c60b208cfd…`), `--instructions 2000000`: + +| side | ran | rung | instructions | cycles (modeled) | stop | wall s | evidence | +|---|---|---|---:|---:|---|---:|---| +| QEMU probe | yes | `pre-B0` | – | – | `fault_load` fw `0x4009014` -> `0x2000000000000A00` | 0.98 | `harness/qemu_line/sessions/qemu-probe` | +| native run 1 | yes | `B3` | 474,043 | 871,904 | `ecall@0x4018038` (`task_port_send_recv_wait`) | 0.65 | `harness/qemu_line/sessions/native-1` | +| native run 2 | yes | `B3` | 474,043 | 871,904 | `ecall@0x4018038` | 0.66 | `harness/qemu_line/sessions/native-2` | + +* all five frozen checks pass: `qemu_first_fault_reproduced`, + `native_above_qemu`, `identical_runs` (device-state sha256 + `5b3cc7e4af933e01…` on both runs), `native_pair_wall_s`, and + `device_model_documented`; **verdict `above`**. +* milestones observed: `bootargs_consumer`, `relocation_copy`; + `unknown MMIO = 0`, `device_unhandled = 0`; `armed_mask=0xFF`. +* What this does **not** claim: B4/B5 are not reached (the message-loop task + blocks before any CSB access without host traffic); the shim values are + declared assumptions; cycles are modeled (docs/32); no timing or + device-environment claim. +* Command: `python3 harness/sim_qemu_line.py --instructions 2000000` + (regenerates `harness/qemu_line/sim-qemu-line.json|.md`; the tracked report + is this run). Wall figures are host-load dependent. + ## 5. Failure signals (when to disable, per U48/U56) * Any frozen check false **with QEMU available and the firmware present**: @@ -227,10 +255,11 @@ report is emitted with `unverified_qemu_absent`. ## 7. Honest limits -* **S1 partial** — the device model reaches B2 (relocation copy) with the - recorded boot shim; B3/B4 (CSB poll, message loop) are not reached and the - DMEM-runspace unknown is the documented ceiling (docs/36 §6). The §4.1 - lenient record predates S1; §4.3 is the post-S1 measurement. +* **S1 partial** — the device model reaches B3 (libos init complete; + message-loop port wait) with the XMPU model, the recorded boot shim and the + libos service model (docs/36); B4/B5 (host-traffic-triggered CSB poll and a + scripted message) are not reached. §4.1/§4.3 are the pre-XMPU records; + §4.4 is the current measurement. * **L2/L3 partial** — cycles are modeled (`rv64imac-modeled/1`, docs/32), not measured; the device model is partial and carries per-register evidence (docs/36). No timing or full device-environment claim is made. diff --git a/debug/risc-v-gsp-rm-vm/docs/39-RE-WORKFLOW-REVIEW.md b/debug/risc-v-gsp-rm-vm/docs/39-RE-WORKFLOW-REVIEW.md index 2b94746..d21957f 100644 --- a/debug/risc-v-gsp-rm-vm/docs/39-RE-WORKFLOW-REVIEW.md +++ b/debug/risc-v-gsp-rm-vm/docs/39-RE-WORKFLOW-REVIEW.md @@ -83,7 +83,7 @@ but unfinished; docs 30–38 and the untracked `sim/devices`/`gpgpu-sim` trees), | 4 | Native RV64IMAC backend (M1) | `sim/decode.py`, `sim/machine.py`, `greeneye/sim_backend.py` | deterministic in-process backend; QEMU parity | tests, demos, new sim work | differential test (`docs/16` §1.1); `sim_diff.py` `PARITY: OK` | verified useful (reference core) | | 5 | Native exec/access trace (M1.5/R4) | `sim/trace.py` | full instruction/access trace as session blob | record/analyze, value demos | `docs/16` §1.1a; trace tests | verified useful | | 6 | Modeled cycle counter | `sim/cycles.py` | table-driven modeled cycles (explicitly not silicon timing) | 21 offline tests | `docs/32`; `results.json` of `test_sim_cycles` | in development (increment 0) | -| 7 | GSP device model + profiles + bounded boot runner | `sim/devices/`, `sim/profiles/`, `sim/gsp_boot.py`, `sim/gsp_profile.py` | real 570.144 image past the QEMU first fault (B0–B2) | S1 workstream | `dumps/gsp-boot-b1-run1/2`, `b2-run1/2`; 2-run determinism asserted (`docs/36` §4) | in development (S1) | +| 7 | GSP device model + profiles + bounded boot runner | `sim/devices/`, `sim/profiles/`, `sim/gsp_boot.py`, `sim/gsp_profile.py`, `sim/mpu.py`, `sim/libos.py` | real 570.144 image past the QEMU first fault (B0–B3) | S1 workstream | `dumps/gsp-boot-b1-run1/2`, `b3-run1/2`; 2-run determinism asserted (`docs/36` §4) | in development (S1) | | 8 | Declarative probe specs | `greeneye/probes.py`, `examples/probes/` | JSON hooks/conditions without scripts | audit smoke; documented pattern | `docs/27` §3 (3 probes fired) | verified useful | | 9 | Plugin loader + example plugin | `greeneye/plugins.py`, `plugins/example_plugin.py` | extension point | audit smoke | `plugins` CLI lists all three | verified useful | | 10 | MCP server | `plugins/mcp_server.py` | agent/CI machine surface (JSON-RPC over stdio/loopback) | no live agent consumer found | 55 tests (`docs/20` §9); tool catalogue 31 (`docs/20` §5) | implemented; **unused in real workflow** (F1 `partially used`) | diff --git a/debug/risc-v-gsp-rm-vm/docs/40-NEW-VERSION-TRIAGE.md b/debug/risc-v-gsp-rm-vm/docs/40-NEW-VERSION-TRIAGE.md index 613e966..aadae37 100644 --- a/debug/risc-v-gsp-rm-vm/docs/40-NEW-VERSION-TRIAGE.md +++ b/debug/risc-v-gsp-rm-vm/docs/40-NEW-VERSION-TRIAGE.md @@ -194,6 +194,14 @@ the QEMU line" next-action line no longer appears. Wall ≈2.7 s (host-load dependent). The pre-S1 trace above is kept as the recorded before/after evidence. +Post-XMPU/libos wave (2026-10-03, session 4c; same command): with the default +budget the `native_boot` step reports rung **B2** and a budget stop at +200,000 instructions (no fault anymore — the former `0x400EA0C` wall is +gone); with `--budget 2000000` it reports rung **B3** (474,043 instructions, +`ecall` port wait, 0 unknown MMIO) and `ab_eval` verdict `above` (measured +wall ≈4.1 s, 434× the frozen 1800 s manual baseline). The numbers in the +example and the post-S1 paragraph above are the pre-wave record. + Baseline variant (same image both sides, an unchanged-version rehearsal with the docs/21 path): diff --git a/debug/risc-v-gsp-rm-vm/docs/41-STABILITY-AND-TIME-TO-VALUE.md b/debug/risc-v-gsp-rm-vm/docs/41-STABILITY-AND-TIME-TO-VALUE.md index 77e1298..57ee33b 100644 --- a/debug/risc-v-gsp-rm-vm/docs/41-STABILITY-AND-TIME-TO-VALUE.md +++ b/debug/risc-v-gsp-rm-vm/docs/41-STABILITY-AND-TIME-TO-VALUE.md @@ -3,15 +3,16 @@ > **Date:** 2026-10-03. **Scope:** the simulator/debugger workstream inside > `debug/risc-v-gsp-rm-vm/` only. **Verdict:** the minimum bar for marking the > *tool and standalone GSP simulator* as **stable (conditional)** is passed: -> 298 tests green twice, deterministic real-firmware runs, an independently +> 417 tests green (298 at the first review; the XMPU/libos wave added the +> rest), deterministic real-firmware runs, an independently > reproduced A/B verdict "above the QEMU line", and reproducible value > measurements. Conditions are listed in §7 — chiefly that the device model -> currently reaches boot rung B2 (not the message loop) and cycle numbers are +> currently reaches boot rung B3 (message-loop port wait; the CSB poll awaits +> host traffic) and cycle numbers are > modelled, not silicon-calibrated. > -> Local notes: `../../../../handoff-simulator.md` (directive ledger, top work), -> `../LOCAL-REVIEW-NOTES.md` (local provenance). Public text describes -> components and behaviour only. +> Companion: `../../../../handoff-simulator.md` (directive ledger, top work). +> Public text describes components and behaviour only. ## 1. What we were doing before (the QEMU baseline) @@ -29,8 +30,8 @@ | Phase | Improvement | Status | |---|---|---| -| P1 | **GSP device model** (`sim/devices/`, `sim/profiles/`) with PMC/FALCON/PRISCV/timer/CSB/FWSEC/PMU/DMA/IRQ windows, versioned profile, boot shims | B2: 14,682 instr, fault `0x400EA0C`, 0 unknown MMIO, two-run identical | -| P2 | **`sim.gsp_boot` boot-ladder runner** with rung classification and serialisable device state | B0–B2 (B3/B4 targets) | +| P1 | **GSP device model** (`sim/devices/`, `sim/profiles/`) with PMC/FALCON/PRISCV/timer/CSB/FWSEC/PMU/DMA/IRQ windows, versioned profile, boot shims, **XMPU model** (`sim/mpu.py`) and **libos service model** (`sim/libos.py`) | B3: 474,043 instr, `ecall` port wait, 0 unknown MMIO, two-run identical | +| P2 | **`sim.gsp_boot` boot-ladder runner** with rung classification and serialisable device state | B0–B3 (B4/B5 targets) | | P3 | **One-command release triage** (`./risc-v-gsp-rm-debugger.sh triage`) orchestrating identity/QEMU probe/native boot/A-B eval/version diff/readiness | 570.144 in ~2.7 s; baseline pair ~11.4 s | | P4 | **QEMU-line A/B evaluation harness** (`harness/sim_qemu_line.py`, frozen checks) | verdict `above`, all threshold checks computed from live runs | | P5 | **Real RE demonstration** (`harness/re_demo.py`) answering a concrete bootstrap question with simulator tools | PASS 5/5, 0.13 s vs 601 s baseline floor | @@ -41,7 +42,7 @@ | Scenario | Before | Now | Evidence | |---|---|---|---| -| What happens in GSP boot before the first wall? | QEMU: fault at `0x4009014`, stop | Device model serves the path; deterministic B2 fault `0x400EA0C`; unknown accesses 0 | `python3 -m sim.gsp_boot`; sessions `dumps/gsp-boot-b2-run1/2` | +| What happens in GSP boot before the first wall? | QEMU: fault at `0x4009014`, stop | Device model serves the path; deterministic B3 port wait (`ecall @ 0x4018038`); unknown accesses 0 | `python3 -m sim.gsp_boot`; sessions `dumps/gsp-boot-b3-run1/2` | | Triage a new firmware image | manual probe + reading, ~1800 s estimated | one command, **2.68 s**, JSON+MD report with hashes | `./risc-v-gsp-rm-debugger.sh triage --firmware … --version 570.144` | | Is the emulator better than QEMU? | no mechanism | validators run both, compute rung/feature deltas; verdict `above` | `python3 harness/sim_qemu_line.py` | | Answer a bootstrap RE question | static reading only | 5/5 recorded answers in 0.13 s | `python3 harness/re_demo.py` | @@ -54,10 +55,10 @@ one-line command in the report/doc that produced it. ## 4. Stability evidence -* `python3 -m unittest discover -s tests -t .` → **298 tests, OK**, twice - (rounded identity across runs); MCP 55, triage 14, device model 34 new - checks included. -* Determinism: B1/B2 device-state SHA identical across runs; triage sessions +* `python3 -m unittest discover -s tests -t .` → **417 tests, OK** (298 at + the first review; the XMPU/libos wave added 13 boot/MPU checks plus WPR2 + and trace-number updates); MCP 55, triage 14, device model 35 included. +* Determinism: B1/B3 device-state SHA identical across runs; triage sessions hashed; A/B harness repeats native runs and compares. * Back-compat: `Session`, `RspClient`, `SimClient` and CLI keep prior behaviour; additions are keyword/default forms; QEMU path untouched @@ -79,15 +80,20 @@ one-line command in the report/doc that produced it. derivation, DMA refusal counting, PMC chip-ID duplication, overlapping window fallback, stale doc counts, `session_launch` documentation, docs/40 measurement labels). One latent item recorded: WPR2 window offsets versus - profile base (firmware never touches it in B0–B2). + profile base — left unserved because the old B2 run never reached the read; + the XMPU/libos wave reached it and the offset defect was fixed (relative and + absolute forms both accepted, docs/36 §3). * Verdict after fixes: conditional stability as stated in §7; no unverified "beats silicon" or "boots RM" statement anywhere. ## 6. Top work for the next session (time-to-value first) -1. **Push the device model to B3/B4** (CSB/TRANSCOM mailbox semantics, - DMEM runspace source, PMU/FWSEC stubs) — this is the gate to serving a - real RM message and the largest remaining time-to-value lever. +1. **Push the device model to B4/B5** (host-traffic-triggered CSB/TRANSCOM + poll semantics, one scripted message served end-to-end) — this is the gate + to serving a real RM message and the largest remaining time-to-value + lever. *Update 2026-10-03, session 4c: the XMPU model, bootstrap-args shim + and libos service model landed; the ladder is now B3 (was B2), with the + former DMEM-runspace unknown resolved.* 2. **Surgical rehost of the live GR engine-context promote/commit wall** (docs/39 top-3): convert the manual, multi-cycle physical campaign into device-free hook/watch/compare sessions. @@ -99,7 +105,8 @@ one-line command in the report/doc that produced it. ## 7. Conditions on the stability claim -1. Device model ceiling is B2; B3/B4 pending profile semantics. +1. Device model ceiling is B3 (libos init complete; message-loop port wait); + B4/B5 (host-traffic-triggered CSB poll, scripted message) pending. 2. Cycle counts are modelled defaults, not calibrated bands. 3. Manual-baseline speedups use labelled estimates, not timed human studies. 4. Cross-version triage was exercised as a self-diff rehearsal (no second @@ -111,8 +118,8 @@ one-line command in the report/doc that produced it. ```bash cd open-iov/debug/risc-v-gsp-rm-vm -python3 -m unittest discover -s tests -t . # 298 tests -python3 -m sim.gsp_boot # B2 +python3 -m unittest discover -s tests -t . # 417 tests +python3 -m sim.gsp_boot # B3 (port wait) python3 -m sim.gsp_boot --no-shim # B1 budget stop python3 harness/sim_qemu_line.py # verdict "above" python3 harness/re_demo.py # PASS 5/5 diff --git a/debug/risc-v-gsp-rm-vm/docs/42-GMMU-FBIF-OBSERVABILITY.md b/debug/risc-v-gsp-rm-vm/docs/42-GMMU-FBIF-OBSERVABILITY.md new file mode 100644 index 0000000..4a2d6ee --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/docs/42-GMMU-FBIF-OBSERVABILITY.md @@ -0,0 +1,203 @@ +# 42 — GMMU/FBIF observability (in development) + +> **Status: in development (2026-10-03, G1 work package).** This page +> documents the observe-only GMMU and FBIF/FBPA register windows added to the +> device model, the `greeneye-gmmu-fbif-trace/1` document and the boot-runner +> flags that produce it. Development-only: nothing here authorises GPU +> hardware, kernel modules, mdev, guest access or reboots, and nothing here is +> a silicon-accuracy claim. +> +> **See also:** `36-GSP-DEVICE-MODEL.md` (the device model this plugs into), +> `06-ACCURACY.md` (fidelity levels), `24-DEVICE-MAP.md` (harvested access +> maps), `35-GREENEYE-SIMULATOR-DESIGN.md` §4.1 (the Phase G inventory this +> observability de-risks). + +## 1. What this is + +The Phase G board model needs the GMMU and the FB interface eventually. Before +modelling either, we need to know **what the firmware actually touches** in +those register windows, with which width and direction, from which PC, and +without pretending to know register behaviour we have not established. + +`sim/devices/observe.py` adds **observer windows**: device-model windows that +record every access and then **decline** it, so execution follows the existing +device-map / lenient / unknown path exactly as it did before the window +existed. They never invent a read value. + +## 2. What is observed + +The TU104 profile (`sim/profiles/tu104-570.144.json`) declares 23 observer +windows, all `observe-only`. Bases are runtime addresses in the GSP RISC-V +PRIV aperture (`0x2000_0000_0000_0000` + BAR0 offset); offsets/names come from +the hardware register/field headers (evidence kind: **header**). The B0–B2 +runs did not touch any of them; the B3 wave reaches the WPR2 read pair +(`0x1FA824/0x1FA828`), which falls inside the `gmmu-wpr-lock` window and is +recorded observe-only (2 accesses, 0 served; the fwsec device answers). + +| Window label | Runtime base | Size | Covers | +|---|---|---|---| +| `gmmu-pri-mmu` | `0x2000000000100C80` | `0x280` | `NV_PFB_PRI_MMU_*` cluster (`NV_PFB_PRI_MMU_CTRL` first) | +| `gmmu-wpr-lock` | `0x20000000001FA7D8` | `0x5C` | MMU per-engine PRIV_LEVEL masks + VPR/WPR/lock address pair | +| `gmmu-hsmmu` | `0x20000000001FAC00` | `0x400` | `NV_PFB_HSMMU_PRI_MMU_*` (secondary MMU block) | +| `fbpa` | `0x20000000009A0000` | `0x4000` | main FBPA block (`NV_PFB_FBPA_*`) | +| `fbpa-partition-0..15` | `0x2000000000900000` + i·`0x4000` | `0x4000` | per-partition FBPA instances | +| `fbpa-mc-0..2` | `0x2000000000980000` + i·`0x4000` | `0x4000` | FBPA MC instances | + +Register names (and, for a few control/status registers, header field names +with bit positions) are declared per window so traces are readable; offsets +inside a window that the profile does not name are still recorded and land +under `unclassified`. `gmmu-pri-mmu` spans the `NV_PFB_PRI_MMU_*` cluster +`0x100C80–0x100EFC`; the few non-MMU PFB registers interleaved in that span +(e.g. the PRI_FB clock/power-gating registers) are recorded as unclassified, +never renamed. + +## 3. Mechanism: record, then decline + +Each `ObservedWindow` is a normal device-model window and is consulted by the +registry like any other. Its `read`/`write` always record: + +```text +addr, offset, size, rw, value-if-known, pc, vt, window, register, served +``` + +and then return `None`/`False` unless the window is declared `storage` **and** +the accessed offset is a declared plain read/write register **and** (for +reads) that register has been written before. Declining means the registry +falls through to the next matching window and the machine's existing path +applies unchanged: + +* lenient mode: the access is logged/counted as before and reads 0; +* fault mode: the same `Fault(cause, tval)` as without the window; +* a modelled window underneath (e.g. the PMC block) is consulted exactly as + before and serves the access if it models it, and the observer records that + it happened. (The `gmmu-wpr-lock` window overlaps the fwsec WPR2 window, so + it exercises this fall-through; it asserts nothing about whether the + underlying WPR2 path serves — that path is unchanged by the observer and + currently serves nothing through the registry, a pre-existing condition.) + +**No-fake-read guarantee.** An observe-only read records `"value": null` and +returns `None`; a storage read before any write is declined the same way; no +reset value is synthesised. The only read value a storage window can return +is one this same run wrote to that register. All profile windows for +GMMU/FBIF are `observe-only`; the `storage` mode exists for surfaces whose +header proves plain read/write behaviour and is exercised by tests. + +`pc` is the faulting instruction address (the registry forwards it only to +devices that opt in). `vt` is the machine instruction counter at the access; +it advances at `Machine.run` boundaries, so accesses inside one bounded run +share the run-start value — a documented granularity, not a timing claim +(fidelity L2 is not modelled). + +**Size limit.** Each window's access log is capped (`max_access_log`, default +65536 entries); accesses beyond the cap are counted, not written. The trace +document reports `dropped_accesses` per window and in the summary and +separates `accesses` (total observed) from `retained_accesses` (present in +the document), so truncation is never silent. + +## 4. Trace document: `greeneye-gmmu-fbif-trace/1` + +`--trace-out <path>` writes a trace file: a path that is an existing +directory (or ends in `/`, which makes that directory) gets the canonical +filename `gmmu-fbif-trace.json` inside; anything else is used as the file +name. Accesses are grouped per window and per declared register; undeclared +offsets go to `unclassified`. Shape: + +```json +{ + "schema": "greeneye-gmmu-fbif-trace/1", + "image_sha256": "…", + "profile": "tu104-570.144.json", + "profile_name": "tu104-570.144", + "symbols": "symbols.json", + "summary": {"windows": 23, "windows_touched": 0, "accesses": 0, + "retained_accesses": 0, "dropped_accesses": 0, + "reads": 0, "writes": 0, "served": 0, "registers_touched": 0}, + "windows": [ + {"label": "gmmu-pri-mmu", "base": "0x2000000000100c80", "size": "0x280", + "mode": "observe-only", "evidence": "…", + "counts": {"r": 0, "w": 0, "served": 0, "declined": 0}, + "retained_accesses": 0, "dropped_accesses": 0, + "registers": [ + {"offset": "0x0", "name": "NV_PFB_PRI_MMU_CTRL", "access": "rw", + "counts": {"r": 0, "w": 1, "served": 0}, + "accesses": [ + {"addr": "0x2000000000100c80", "offset": "0x0", "size": 4, + "rw": "w", "value": "0x1", "pc": "0x4001234", + "pc_symbol": "rm_init+0x20", "vt": 0, + "window": "gmmu-pri-mmu", "register": "NV_PFB_PRI_MMU_CTRL", + "served": false, "decoded": {"VM_PG_SIZE": 1}} + ]} + ], + "unclassified": {"counts": {"r": 0, "w": 0, "served": 0}, + "accesses": []}} + ] +} +``` + +`pc_symbol` is `null` unless `--symbols <greeneye-symbols/1 JSON>` is given; +`decoded` is present only when a value is known and the register declares +fields (bit positions from the header). `value`/`addr`/`offset`/`pc` are hex +strings; `vt` is an integer. The document is deterministic for a fixed image, +profile, flags and stimulus. + +The **count summary is always in the boot report** (and session metadata under +`gmmu_fbif`) even when `--trace-out` is off; the trace blob is added to the +recorded session only when the flag is on. + +## 5. Commands + +```bash +cd open-iov/debug/risc-v-gsp-rm-vm + +# B2 run with the observer summary, trace written to a directory: +python3 -m sim.gsp_boot --instructions 20000 --record dumps/gsp-boot-b2-observe \ + --trace-out dumps/gsp-boot-b2-observe + +# Resolve recorded PCs through a symbol database: +python3 -m sim.gsp_boot --trace-out /tmp/trace.json --symbols symbols.json + +# Ad-hoc window whose base is not established yet (repeatable, no guessing): +python3 -m sim.gsp_boot --observe-window 0x20000000009A0000:0x4000:fbpa \ + --observe-window 0x2000000000100C80:0x280:gmmu +``` + +`--observe-window BASE:SIZE[:LABEL[:MODE]]` is the escape hatch: the trace +accepts a runtime window list, so an unknown surface can be observed before +any profile entry exists. A runtime window is `observe-only` unless +`MODE=storage` is given (storage without declared registers still records and +declines). + +## 6. What is NOT modelled + +* **No GMMU page walk, no PTE/PDE decode, no TLB, no fault handling, no ATS.** + Window accesses are recorded; their semantics are not interpreted. +* **No FBIF transaction semantics, compression, ECC or partition hashing.** + FBPA register offsets/names are labels, not behaviour. +* **No interrupt or timing (L2) claims.** `vt` is the modeled instruction + counter, not cycles. +* **No device-environment (L3) claims.** The windows exist because the + simulator serves them; silicon behaviour is not asserted. +* **Storage is not semantics.** A storage window only echoes values written in + the same run; it is not evidence that the real register stores them. +* **No product or hardware action.** Nothing in this workstream touches + `vgsp`, a GPU, a module, mdev, or a guest. + +## 7. Evidence, limits and tests + +* Evidence kind for every window/offset is the hardware register/field header + (block ranges and register offsets); observed firmware use in B0–B2 was + **zero accesses** (measured at the B2 stop: 14 682 instructions, 0 unknown + MMIO, 1 device access — the PMC chip-ID read). The B3 wave's WPR2 reads are + the first observed accesses (2 reads inside `gmmu-wpr-lock`, observe-only, + 0 served). The mechanism is still tested end-to-end with runtime windows. +* Boot numbers are unchanged by the observer windows (instructions, stop + PC/reason, unknown MMIO, unhandled-in-window, served accesses); the only + session difference is the added device-state entries for the observers. +* Tests: `tests/test_gmmu_fbif_trace.py` — no-semantic-change (lenient and + fault paths match a registry without the window), storage read-back, + field decode, profile expansion, trace schema, access-log-cap reporting, + WPR2-overlap pass-through, B2 invariance, symbol resolution, runtime + windows. +* Public artifacts carry only component register names/offsets and behaviour; + the window/register tables come from the tracked analysis and recorded + sessions. diff --git a/debug/risc-v-gsp-rm-vm/docs/43-GMMU-PTE-DECODER.md b/debug/risc-v-gsp-rm-vm/docs/43-GMMU-PTE-DECODER.md new file mode 100644 index 0000000..ea6d3d4 --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/docs/43-GMMU-PTE-DECODER.md @@ -0,0 +1,206 @@ +# 43 — GMMU PTE/PDE decoder for TU104 (in development) + +> **Status: in development (2026-10-03, G2 work package).** This page +> documents `sim/gmmu_pte.py`: a clean-room decoder for captured Turing +> (TU104) GMMU page-table entries. It is decode-only tooling — no hardware, +> no kernel module, no mdev, no guest, no RM 610. Development-only, never +> shipped. +> +> **Rule: decode captured data only; validate against a capture before +> trusting.** Every field position below is a reverse-engineering result; +> none of it is a silicon-accuracy or timing claim (`06-ACCURACY.md` +> fidelity levels apply). + + +## 1. What this is — and is not + +`sim/gmmu_pte.py` turns a captured page-table value or blob into named +fields with exact bit positions. It is **not** a page-table walker: it never +dereferences a decoded address, never reads a target table, and never fills +a gap with a guess. Unsupported encodings and levels raise `E_UNSUPPORTED`; +set bits that no named field covers are reported (`reserved_bits` / +`reserved_mask`) and never dropped. + +The evidence base is the Turing GMMU entry-format definitions (field names, +bit ranges, aperture/KIND numeric IDs) together with the driver's own +page-table encoder/decoder behaviour, which is what proves which of the +several described encodings Turing actually uses (VER2) and how the +page-tree geometry maps to virtual-address bits. + +## 2. Encodings shipped + +| Encoding | Size | Used for | +|---|---|---| +| `ver2` | 8 bytes | VER2 leaf PTE and directory PDE — the encoding the Turing driver's page-tree path programs | +| `ver2_dual` | 16 bytes | VER2 dual PDE0 slot for mixed 64K/4K page tables: BIG half (low 64 bits) + SMALL half (high 64 bits) | + +Known-but-unsupported encodings (explicit `E_UNSUPPORTED`): the older +non-VER2 PTE/PDE encoding and the "NEW"/VER1 encodings also described by the +same register definitions. They are not shipped because the Turing driver's +page-tree path uses VER2; adding them without a capture to validate against +would be unverified surface. See §8. + +## 3. VER2 leaf PTE (8 bytes) + +| Field (our name) | Bits | Meaning | +|---|---|---| +| `valid` | 0 | entry is present | +| `aperture` / `aperture_name` | 2:1 | 0 video, 1 peer, 2 system coherent, 3 system non-coherent | +| `volatile` | 3 | cache/volatility bit: 0 = cached, 1 = volatile (the format's cache-related field) | +| `encrypted` | 4 | encrypted memory | +| `privilege` | 5 | privileged access | +| `read_only` (`writable`) | 6 | write protection | +| `atomic_disable` | 7 | atomics disabled | +| `address_sys` | 53:8 | system-aperture address bits (PA 53:12) | +| `address_vid` | 32:8 | video/peer address bits (PA 36:12) | +| `peer_id` | 35:33 | peer/fabric id (peer aperture) | +| `comptagline` | 55:36 | compression tag; overloaded as high address bits for the documented 47-bit peer case | +| `kind` / `kind_name` | 63:56 | memory kind id (see §6); unknown ids are reported, not guessed | + +## 4. VER2 directory PDE (8 bytes) + +| Field (our name) | Bits | Meaning | +|---|---|---| +| `is_pde_pointer` / `is_leaf` | 0 | 0 = pointer to a lower table, 1 = 2 MB leaf PTE stored at PDE0 | +| `aperture` / `aperture_name` | 2:1 | 0 invalid, 1 video, 2 system coherent, 3 system non-coherent | +| `volatile` | 3 | as PTE | +| `no_ats` | 5 | skip the ATS lookup for the covered range (driver sets it at depth 2) | +| `address_sys` | 53:8 | next-table address bits (system aperture) | +| `address_vid` | 32:8 | next-table address bits (video aperture) | +| `peer_id` | 35:33 | peer/fabric id | + +Note the aperture-id difference: for a leaf PTE aperture 0 is video memory; +for a directory PDE aperture 0 is invalid. + +## 5. VER2 dual PDE0 (16 bytes) + +The BIG half is the low 64 bits, the SMALL half the high 64 bits. Bit 0 +applies to the whole slot. + +| Field (our name) | Bits | Meaning | +|---|---|---| +| `is_pde_pointer` / `is_leaf` | 0 | whole-slot discriminator | +| `aperture_big` | 2:1 | 0 invalid, 1 video, 2 system coherent, 3 system non-coherent | +| `volatile_big` | 3 | as PTE | +| `no_ats` | 5 | ATS skip; lies inside the BIG address field (see §7) | +| `address_big_sys` | 53:4 | BIG target address; stores address >> 8 (PA 57:8) | +| `address_big_vid` | 32:4 | BIG target address, video aperture | +| `peer_id_big` | 35:33 | BIG peer/fabric id | +| `aperture_small` | 66:65 | as `aperture_big` | +| `volatile_small` | 67 | as PTE | +| `address_small_sys` | 117:72 | SMALL target address; stores address >> 12 (PA 57:12) | +| `address_small_vid` | 96:72 | SMALL target address, video aperture | +| `peer_id_small` | 99:97 | SMALL peer/fabric id | + +Bits 54–64, 68–71 and 118–127 are unnamed by the format definitions and are +reported as reserved when set. + +## 6. Page-tree geometry and KIND ids + +Turing's page-tree path inherits the Pascal/Volta geometry and uses the VER2 +entry encoding. Depths are root (0) to leaf (4): + +| Depth | HW level | VA bits | Index bits | Entry size | Role | +|---|---|---|---|---|---| +| 0 | PDE3 | 48:47 | 2 | 8 | pointer PDE | +| 1 | PDE2 | 46:38 | 9 | 8 | pointer PDE | +| 2 | PDE1 | 37:29 | 9 | 8 | pointer PDE (NO_ATS here) | +| 3 | PDE0 | 28:21 | 8 | 16 (dual) / 8 (2 MB leaf) | dual 64K/4K PDE, or a 2 MB leaf PTE | +| 4 | PTE | 20:12 (4 KB) / 20:16 (64 KB) | 9 (4 KB) / 5 (64 KB) | 8 | leaf PTE | + +Supported page sizes: 2 MB (depth 3), 64 KB and 4 KB (depth 4); VA width 49 +bits. A leaf entry alone does not tell 4 KB from 64 KB — the table's depth +and page-size context do, so the decoder reports `page_size` as +`4k_or_64k` when the depth is pinned to 4. + +The PTE `kind` field carries numeric ids (243 named values re-derived from +the format definitions). Common compute values: `0x00 PITCH`, +`0x06 GENERIC_MEMORY`, `0x07 INVALID`, `0x0F SMSKED_MESSAGE`, +`0xFE GENERIC_16BX2`. Unknown ids decode as `kind_name: null`, +`kind_known: false` with the raw value preserved. + +## 7. Address decode and uncertainty + +* System apertures (`system_coherent_memory`, `system_non_coherent_memory`): + the reconstructed `address` is complete (`address_complete: true`). +* Video/peer apertures: only the low address bits are unambiguous + (`address_complete: false`). `comptagline` is a compression tag in the + normal case and is documented as overloaded for 47-bit peer addresses; the + decoder therefore does not claim a complete video address. +* Dual BIG address: the field stores `address >> 8`, so `address` is + reconstructed as `field << 8`. Bit 5 of the slot is named `no_ats` and also + lies inside the BIG address field (address bit 9). The decoder reports the + flag from bit 5 and excludes that bit from the reconstructed BIG address; + the raw `address_big_sys` / `address_big_vid` fields keep the bit visible. + This assumes the target table is 4 KB aligned, which is the normal GPU + allocation granularity — if a capture shows a non-aligned target, treat + that entry as an unexplained case, not as proof of a different format. +* Reserved-bit policy: `reserved_bits` lists every set bit not covered by a + named field; `reserved_mask` is the same set as a mask. Nothing is + silently dropped. +* The VER2 PTE has no separate cache field: `volatile` (bit 3) is the only + cache-related bit the format defines, so no cache encoding is invented. +* `decode_table` is little-endian and never follows pointers; a blob whose + length is not a multiple of `entry_size` raises `E_TRUNCATED`, an empty + blob decodes to `[]`. + +## 8. Supported levels and explicit gaps + +`levels` may pin the page-tree depth (0–4) or a sequence of acceptable +depths. `None` (default) uses the full supported set and lets each entry's +bit-0 discriminator choose between the leaf-PTE and directory-PDE views, +reporting `level: null` plus `level_candidates` instead of guessing a single +depth. Any depth outside 0–4 raises `E_UNSUPPORTED`. + +Explicit gaps (never guessed): + +1. Pre-VER2 and "NEW"/VER1 encodings are not decoded (`E_UNSUPPORTED`). +2. The decoder does not walk tables, resolve virtual addresses, or infer a + 4 KB vs 64 KB leaf page size from the entry alone. +3. No capture has been validated yet: the golden vectors are constructed + field-by-field from the format definitions, not sampled from silicon. + **Validate against a real capture before trusting a decode.** +4. No timing, device-environment, isolation or security claim is made; the + simulator fidelity levels in `06-ACCURACY.md` apply. + +## 9. Usage + +Python: + +```python +from sim.gmmu_pte import decode_pte, decode_table + +pte = decode_pte(0x0600000000123456, pte_kind="ver2") +entries = decode_table(blob, base_pa=0x2000, entry_size=8, levels=4) +dual = decode_table(blob16, base_pa=0x2000, entry_size=16, kind="ver2_dual", + levels=3) +``` + +CLI: + +```bash +# one entry (default role "auto": bit 0 picks leaf vs directory) +python3 -m sim.gmmu_pte --value 0x0600000000123456 --role pte +python3 -m sim.gmmu_pte --value 0x0600000000123456 --json + +# a captured table blob, with the blob's GPU physical base address +python3 -m sim.gmmu_pte --blob table.bin --base-pa 0x2000 +python3 -m sim.gmmu_pte --blob table.bin --base-pa 0x2000 --levels 4 --json + +# a dual PDE0 slot +python3 -m sim.gmmu_pte --blob dual.bin --base-pa 0x2000 \ + --kind ver2_dual --entry-size 16 --levels 3 --json +``` + +Exit code 0 on success; 2 with an `E_*` code on stderr for unsupported +kinds/levels, truncated blobs or bad values. JSON output is sorted-key and +deterministic. + +## 10. Tests + +`tests/test_gmmu_pte.py` builds every vector field-by-field from the tables +above, asserts field round-trips, reserved-bit reporting (including the +unnamed dual bit 64 and the unnamed PDE bits 4, 6, 7, 54–63), the NO_ATS +overlap behaviour, unknown-kind reporting, unsupported kind/level/entry-size +errors, empty and truncated blobs, deterministic output, and CLI success and +error paths. 44 constructed golden entry vectors across 43 tests. diff --git a/debug/risc-v-gsp-rm-vm/docs/44-GMMU-ORACLE-AND-WORKFLOW.md b/debug/risc-v-gsp-rm-vm/docs/44-GMMU-ORACLE-AND-WORKFLOW.md new file mode 100644 index 0000000..affd350 --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/docs/44-GMMU-ORACLE-AND-WORKFLOW.md @@ -0,0 +1,254 @@ +# 44 — GMMU/FBIF oracle and workflow (in development) + +> **Status: in development (2026-10-03, G3 work package).** This page +> documents two stdlib-only harness tools: +> +> * `harness/gmmu_oracle.py` — offline comparison of a human oracle capture +> against a simulator GMMU/FBIF trace (per-register agreement, coverage, +> unmatched lists, fidelity labels); +> * `harness/gmmu_debug.py` — static↔dynamic join: a trace plus a symbol +> map becomes a per-function register-write report and a +> `greeneye_hits.tsv` the existing Ghidra annotator consumes. +> +> Development-only: these tools read files and write reports. They never +> touch a GPU, kernel module, mdev, guest or network, and they make no +> cycle/timing claim. Physical work (the oracle capture) is supervised-only +> per RUNBOOK P1/P7 and is never run by an agent. +> +> **See also:** `06-ACCURACY.md` (fidelity levels, M1–M6), +> `05-GHIDRA-BRIDGE.md` (address spaces, hit annotation), +> `17-RM-HARNESS.md` (read-only RM oracle policy), +> `25-VALUE-VALIDATION.md` (measured-value discipline), +> `36-GSP-DEVICE-MODEL.md` (the simulator device model whose traces feed +> these tools). + +## 1. What this is and why + +The device model (`sim/devices/`, docs/36) records every modelled MMIO +access with its PC, width, direction and value. Two questions follow: + +1. **Accuracy** — does the value the model serves agree with what a human + can observe on the host (read-only RM/sysfs readbacks, supervised + captures)? (`gmmu_oracle.py`) +2. **Static analysis** — which firmware function programs which + GMMU/FBIF register, in what order, and where is it in Ghidra? + (`gmmu_debug.py`) + +Both are offline. The oracle file is produced by a human; the tools ingest +it and compare semantically. Nothing here executes a physical command. + +## 2. The accuracy loop (oracle vs simulator) + +``` +human (supervised, read-only) simulator (agent-safe) + rmtool / rmtop / sysfs / debugfs greeneye-gmmu-fbif-trace/1 + -> greeneye-gmmu-oracle/1 (from sim/ devices + trace) + \ / + \ harness/gmmu_oracle.py compare / + \____________________________________/ + | + gmmu-oracle-report.{json,md} + agreement %, coverage %, unmatched lists, + fidelity label per row +``` + +Rules: + +* The oracle is **observed** (human capture). The simulator value is + **modelled** (device model / recorded trace). Every row carries that + distinction; when a value is missing on either side the row's fidelity + label says the value is **unknown** instead of guessing. +* Agreement is computed **per register on the matched intersection** — it + is never a single global "accuracy %". Coverage is reported separately. +* An oracle-only register means the simulator did not touch it under this + stimulus; a simulator-only register means no oracle counterpart exists + yet. Neither is proof of a gap. +* No timing, ordering or silicon claim. Fidelity is L0/L1 (docs/06). + +### 2.1 Oracle schema `greeneye-gmmu-oracle/1` + +An object with a `captures` list (a bare JSON list is accepted and reported +as `unlabeled-list`): + +```json +{ + "format": "greeneye-gmmu-oracle/1", + "captures": [ + {"name": "FBIF_CTL", "window": "falcon", "offset": "0x624", + "value": "0x1", "source": "rm-readback", "captured_at": "2026-10-03"} + ] +} +``` + +`name`, `window`, `offset`, `value`, `source`, `captured_at` are the +documented fields. `offset`/`value`/`addr` accept ints or `0x…` strings. +A different explicit `format` is a clean error. + +### 2.2 Simulator schema `greeneye-gmmu-fbif-trace/1` + +The producer (`sim/devices/observe.py:build_trace`, docs/36) emits a +nested document: accesses are grouped per window and per declared register, +with an `unclassified` bucket for observed offsets the profile does not +name. The `schema` key carries the format tag: + +```json +{ + "schema": "greeneye-gmmu-fbif-trace/1", + "image_sha256": "…", "profile": "tu104-570.144", + "summary": {"windows": 1, "accesses": 2}, + "windows": [ + { + "label": "falcon-observe", "base": "0x300110000", "size": "0x1000", + "mode": "observe-only", "evidence": "header", + "registers": [ + {"offset": "0x624", "name": "FBIF_CTL", "access": "rw", + "counts": {"r": 0, "w": 1, "served": 0}, + "accesses": [ + {"addr": "0x300110624", "offset": "0x624", "size": 4, "rw": "w", + "value": "0x1", "pc": "0x4002040", "vt": 123, + "window": "falcon-observe", "register": "FBIF_CTL", + "served": false, "pc_symbol": "fbif_setup"} + ]} + ], + "unclassified": {"counts": {"r": 1, "w": 0, "served": 0}, + "accesses": [{"addr": "0x300110008", "offset": "0x8", + "rw": "r", "value": null, "pc": "0x4002050"}]} + } + ] +} +``` + +`load_sim_trace` flattens this shape (and accepts a flat `events` list, a +bare list, JSONL, and aliases) so the harness survives producer naming +choices: `kind`/`rw`/`direction`/`type`/`t`; `name`/`register`/`reg`; +`window`/`block`/`device`; `offset`/`reg_offset`; `addr`/`address`/`pa`; +`value`/`data`; `pc`; `function`/`symbol`/`pc_symbol`; `handled`/`served`. +Nested documents are flattened per window/register: per-register access +order is preserved, but global interleaving across registers is not +recoverable from that grouping. The producer's top-level `summary` — +including `retained_accesses` / `dropped_accesses` — is carried into +`inputs.trace.source.summary`, so a capped (truncated) access log stays +visible downstream. A session directory is accepted too: +`session.json` pointers (`gmmu_fbif_trace`, `gmmu_fbif_trace_blob`, +`trace_blob`) or a `blobs/*.json[l]` file carrying the format tag. A raw +`read`/`write` JSONL trace without register names still parses (registers +are then reported by address). + +## 3. Static-analysis debugging loop (`gmmu_debug.py`) + +``` +greeneye-gmmu-fbif-trace/1 + symbols (greeneye-symbols/1 | addr->name map | TSV) + | + v +per-function register-write report gmmu-debug-report.{json,md} + function, register, window, offset, + sequence of written values, + first/last pc, write/read counts + | + v +greeneye_hits.tsv (addr hits symbol annotation) + | + v +analyzeHeadless ... -postScript MarkGreenEyeHits.java <greeneye_hits.tsv> +``` + +`greeneye_hits.tsv` is byte-compatible with +`greeneye/ghidra.py:export_hits` and `ghidra/MarkGreenEyeHits.java`: header +`addr\thits\tsymbol\tannotation`, hex address, one row per function, `hits` += register accesses attributed to the function. Function attribution uses +the symbol DB (containing symbol when sizes exist, the toolchain's +nearest-preceding fallback otherwise); a pc outside every symbol becomes +`sub_<pc>`. An explicit `function` field in the trace wins over resolution. + +## 4. Exact commands + +All commands run from `open-iov/debug/risc-v-gsp-rm-vm`. They are offline +and safe for an agent. + +```bash +# Accuracy loop — compare a human oracle with a simulator trace/session. +python3 harness/gmmu_oracle.py compare \ + --oracle oracle.json --sim dumps/<session-or-trace.json> \ + --out dumps/gmmu-oracle-report + +# Print (do NOT execute) the supervised, read-only capture instructions. +python3 harness/gmmu_oracle.py --print-capture-hints + +# Static↔dynamic join — report + Ghidra-ready hits TSV. +python3 harness/gmmu_debug.py report \ + --trace dumps/<session-or-trace.json> --symbols symbols.json \ + --out dumps/gmmu-debug-report + +# Synthetic end-to-end fixture (no firmware, no hardware). +python3 harness/gmmu_debug.py demo --out harness/gmmu_debug_fixture + +# Tests (synthetic only; no live oracle capture, no hardware). +python3 -m unittest tests.test_gmmu_oracle tests.test_gmmu_debug -v +``` + +`--print-capture-hints` only prints. It names the read-only RM wrapper +commands (`./rmharness/rmtool.sh gpu list/engine list/mem stats`, +`rmtop --once`), sysfs/proc/debugfs readbacks, and the optional +first-touch step, with the hard warning that physical work is +supervised-only and an agent must never run those commands. The safe +simulator observation (`python3 -m sim.gsp_boot … --record …`) is the only +capture an agent may produce. + +Generated fixture/report directories under `harness/gmmu_*` are gitignored. + +## 5. Worked example (synthetic, measured) + +`python3 harness/gmmu_debug.py demo` builds a 13-event synthetic trace with +three named functions plus one unattributed pc, runs the whole loop, and +prints a `RESULT_JSON` line. On 2026-10-03 (this host, CPython 3.12) the +loop measured **≈1 ms**, against a manual static-reading baseline frozen +in `harness/gmmu_debug.py` **before** the first measurement: + +| field | value | +|---|---| +| manual baseline (frozen) | `1200 s` — a **conservative estimate**, not measured on a human | +| threshold fraction (frozen) | `0.05` → `60 s` | +| measured (fresh run required to quote) | ≈0.001 s | +| verdict | pass | + +The baseline and fraction are constants in `harness/gmmu_debug.py`; moving +them requires a new recorded run and a note here. The demo proves the +**shape** of the loop (trace → per-function report → hits TSV → +Ghidra-ready output), never a hardware fact. + +Synthetic oracle comparison check (three oracle items, three trace events): +one agree, one mismatch, one oracle-only, one simulator-only → agreement +50 % on the two-item intersection, coverage 66.67 %. These are fixture +numbers, not an accuracy claim about any firmware. + +## 6. Limits and not-verified list + +* **No live oracle capture was taken.** The tools were exercised only with + synthetic fixtures. A real oracle requires a supervised human session + per §4 and docs/17 §6. +* **No real firmware trace was compared.** The loader is exercised against + the real `sim/devices/observe.py` producer (read-only integration test) + and against synthetic documents; no captured firmware run was compared, + and no live oracle capture was taken. +* **L0/L1 only.** The trace records execution and register accesses, not + time; no L2/L3 claim is made. Absence of a register in a trace is a fact + about that run's stimulus, not proof of dead code. +* **Agreement is per-register on the intersection**, with coverage and both + unmatched lists reported; never a single global percentage. +* **Function attribution depends on the symbol map.** Zero-size symbols use + the toolchain's nearest-preceding fallback; supply sized symbols + (`greeneye-symbols/1`) for strict boundaries. +* **Product changes are not implied.** Any emulation change still needs the + accuracy loop plus physical reproduction per docs/06 §6. +* The demo baseline is an **estimate**; no human was timed. + +## 7. Tests + +`tests/test_gmmu_oracle.py` and `tests/test_gmmu_debug.py` cover: oracle +ingest (object/list/aliases/bad format), comparison (agreement, mismatch, +oracle-only, simulator-only, name fallback, no-value, empty), session +directory ingest, report shape and sequence order, explicit-function +override, unknown-pc handling, hits-TSV compatibility with +`MarkGreenEyeHits.java`, empty traces, the frozen-threshold demo, and the +CLI error paths (exit 2, no traceback). All fixtures are synthetic; no +hardware, module, guest or network is touched. |
