| Mode | Name | Size | |
|---|---|---|---|
| -rw-r--r-- | README.md | 6025 | logstatsplainblame |
| d--------- | _obsolete | 175 | logstatsplain |
| d--------- | build | 429 | logstatsplain |
| -rw-r--r-- | ctl2.py | 865 | logstatsplainblame |
| -rw-r--r-- | e4_flash.py | 10905 | logstatsplainblame |
| -rw-r--r-- | eb_hammer_test.py | 2139 | logstatsplainblame |
| -rw-r--r-- | flash.py | 2952 | logstatsplainblame |
| -rw-r--r-- | flash_custom.py | 2952 | logstatsplainblame |
| -rw-r--r-- | ftdi_debug.py | 4071 | logstatsplainblame |
| -rw-r--r-- | fw.bin | 98006 | logstatsplainblame |
| -rw-r--r-- | fw_handmade_CLEAN.bin | 9143 | logstatsplainblame |
| -rw-r--r-- | fw_handmade_HAMMER.bin | 9645 | logstatsplainblame |
| -rw-r--r-- | fw_tinygrad.bin | 98016 | logstatsplainblame |
| -rw-r--r-- | handmade_gcp_hammer_0xEB.patch | 5461 | logstatsplainblame |
| -rw-r--r-- | host_hammer.py | 4583 | logstatsplainblame |
| -rw-r--r-- | mech2.py | 1475 | logstatsplainblame |
| -rw-r--r-- | race_experiment.py | 11492 | logstatsplainblame |
Bridge GCP cmd-buffer TOCTOU hammer (ASM2464PD, opcode 0xEB)
Custom bridge firmware that rapidly flips a single 32-bit PCIe target between two
values, to race the PSP GCP TA's triple-read of cmdBuf[0x818] (the double-fetch
in fw/FINDING-GCP-FE09-CONFIGINIT-HOST-OFFSET-WRITE.md, Add.70). The bridge is
the concurrent writer the host USB path can't be (submit-then-poll is too slow).
What the bridge actually runs (IMPORTANT)
The box's bridge runs the handmade firmware from tinygrad/asm2464pd-firmware
built at HEAD ed4e39b7 (clean) → USB id 3801:0001 "custom ed4e39b7-CLEAN",
~9 KB. tinygrad's usb.py asserts the product string starts with "custom", and
ops_amd.py accepts VID 0x3801/0xADD1.
The repo's prebuilt fw.bin / fw_tinygrad.bin (98 KB, product "USB 3.2 …") are a
DIFFERENT, protocol-incompatible variant — flashing them bricks the GPU link
(first E4 control-read fails with a USB pipe error). Do not flash those.
fw_handmade_CLEAN.bin— exact restore baseline (ed4e39b7-CLEAN, sha21d1fce4…)fw_handmade_HAMMER.bin— the hammer build currently flashed (ed4e39b7-DIRTY, shaf2cbf85b…)handmade_gcp_hammer_0xEB.patch— the source change (apply tohandmade/src/main.c)eb_hammer_test.py— on-box functional test (verified PASS)_obsolete/— the old binary-patch approach (built against the wrong 98 KB image)
The handler (integrated in handmade/src/main.c, not binary-patched)
New vendor control command 0xEB, reusing the firmware's own posted-MWr TLP
primitive (REG_PCIE_* @ 0xB210–0xB296; STATUS=0x07; TRIGGER=0x0F, fire-and-forget).
It rings the PSP ring doorbell itself (one MemWr), then immediately flips the
target between A and B — the single 8051 can't both service a host submit and hammer,
so the final doorbell must come from the bridge.
Config lives in a pinned firmware global hammer_cfg (XDATA, 32 B), poked by the
host via the 0xE5 XDATA-write path (no timing constraint). Read its address from
the build map (_hammer_cfg, currently 0x0009 — in the firmware's global RAM;
note 0x6000 is NOT backed by RAM). The 0xEB trigger carries only the 4-byte
arming magic.
hammer_cfg (all little-endian):
[0:8] doorbell PCIe addr (0 => skip) [8:12] doorbell value
[12:20] hammer target PCIe addr [20:24] value A
[24:28] value B [28:30] iterations (<=0x8000)
[30] fmt_type (0 => 0x60 MWr64) [31] byte_en (0 => 0x0F)
trigger: control_write(0xEB, data=b'HMR\x01')
Byte maps (match the proven host path): value DATA_0 = LE LSB (natural store);
addr ADDR_0<-byte3 .. ADDR_3<-byte0, HIGH_0<-byte7 .. HIGH_3<-byte4. The doorbell
reg {reg_pref}_67 (regMP0_SMN_C2PMSG) resolves to PCIe addr 0x1005820c.
Safety gates: distinct opcode; arming magic; doorbell skipped if addr==0; iter
clamp; always ACKs with a ZLP. Host side: host_hammer.py (arm/fire/capture_doorbell).
Build / flash / restore
# build (from a clean clone of tinygrad/asm2464pd-firmware @ ed4e39b7)
cd handmade && patch -p1 < .../handmade_gcp_hammer_0xEB.patch # or edit main.c
make wrapped # -> build/firmware_wrapped.bin ("custom ed4e39b7-DIRTY")
# flash (on the box; FTDI + bridge both on 192.168.1.200)
python ftdi_debug.py --bootloader --no-read # -> ROM bootloader 174c:2463
# wait until `lsusb | grep 174c:246` shows the bootloader, THEN:
PYTHONPATH=/root/psp/tinygrad python flash.py firmware_wrapped.bin
python ftdi_debug.py --reset --no-read
# restore the clean firmware the same way with fw_handmade_CLEAN.bin
Recovery (PROVEN on this board)
The FTDI (ftdi://ftdi:230x/1, already EEPROM-provisioned CBUS=GPIO) can force the
ASM2464's mask-ROM bootloader via --bootloader, independent of flash contents, so
a bad image is always reflashable. Demonstrated repeatedly: running fw → bootloader
(174c:2463) → reflash → boot. The FT230X has no MPSSE, so the ONLY out-of-band
path is this strap; if it ever fails, recovery needs a SPI programmer on the chip.
Status
- [x] handler integrated in source, asm-verified, flashed (
ed4e39b7-DIRTY) - [x] box fully functional on it: LINK UP, GPU init, vram
0x5ff000000 - [x] doorbell + hammer handler built, asm-verified (FMT once, doorbell gated,
target-addr set, A/B loop), flashed; config RAM at
0x0009round-trips - [x] exercised on HW (
mech2.py): doorbell writes the configured value to its address (Test A:0xD00DB011); hammer runs the A/B loop (Test B:0xBBBBBBBB, last-write wins); arming-magic gate no-ops; identical behaviour to the provenpcie_mem_write - [~] VRAM read-back cannot verify distinct-address writes:
pcie_mem_readover the USB tunnel is cache-confounded (stale write-latch without flush, 0 withflush_hdp) — a read-tool limit, not a handler bug (positive control withpcie_mem_writefails identically). Distinct-address correctness must be settled by the PSP-side oracle in the chain. - [x] race RUN (
race_experiment.py, on-boxhw/race.py): fault-free observability test against the live state-3 chain. Positive controls green (signal0/256; overlap plumbing completes). 24/24 the TA read the STAGED0x818, never the hammered value (stage 0x1000 flip->2 => {0:12};stage 2 flip->0x1000 => {256:12}). => the cmd buffer is cached/snapshotted at dispatch; the double-fetch TOCTOU is NOT exploitable by a concurrent PCIe writer in this ordering. See FINDING Add.72. - [~] bridge-issued doorbell is DEAD: a bridge single-TLP MWr to the wptr reg
0x1005820cdoes not trigger PSP processing (host streaming write does). This blocks the one untested ordering (hammer live before dispatch snapshot). - [ ] win condition (NOT achieved): a chosen 32-bit value at a far-OOB target with normal status. The write ceiling stands; the double-fetch does not lift it here.
