mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-09-14 02:17:13 +00:00
Give BWM OTA begin enough time to erase, and stop retuning UART for the transfer
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+36
-12
@@ -24,9 +24,30 @@ static uint16_t wifi_crc16(const uint8_t *d, size_t n, uint16_t crc) {
|
||||
// Runs only during setup, so it fully owns the RX stream here.
|
||||
typedef enum { W_H1, W_H2, W_CL, W_CH, W_LL, W_LH, W_PL, W_KL, W_KH } wstate_t;
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
// Drop whatever is already sitting in the UART RX ring so a late ACK from a
|
||||
// timed-out command cannot be consumed as the next command's response (same
|
||||
// cmd code, e.g. a slow OTA_BEGIN ack arriving after we already gave up).
|
||||
static void bwm_cmd_drain_rx(void) {
|
||||
uint8_t buf[64];
|
||||
uint32_t t0 = GetTickCount();
|
||||
while (bwm_uart_rx_available()) {
|
||||
uint16_t avail = bwm_uart_rx_available();
|
||||
(void)bwm_uart_read(buf, (uint32_t)MIN(avail, (uint16_t)sizeof(buf)));
|
||||
if (GetTickCountDelta(t0) > 50) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int bwm_cmd(uint16_t cmd, const uint8_t *req, uint16_t req_len,
|
||||
uint8_t *resp, uint16_t *resp_len, uint32_t timeout_ms) {
|
||||
|
||||
bwm_cmd_drain_rx();
|
||||
|
||||
// ---- build + send HOST_CMD frame ----
|
||||
uint8_t frame[8 + 256];
|
||||
if (req_len > sizeof(frame) - 8) {
|
||||
@@ -63,6 +84,7 @@ int bwm_cmd(uint16_t cmd, const uint8_t *req, uint16_t req_len,
|
||||
uint8_t buf[64];
|
||||
uint16_t avail = bwm_uart_rx_available();
|
||||
if (avail == 0) {
|
||||
SpinDelay(1);
|
||||
continue;
|
||||
}
|
||||
uint32_t n = bwm_uart_read(buf, (uint32_t)MIN(avail, (uint16_t)sizeof(buf)));
|
||||
@@ -304,16 +326,20 @@ int bwm_esp_ota_begin(uint32_t total_size) {
|
||||
uint8_t off = 0;
|
||||
(void)bwm_cmd(BWM_CMD_LOG_FORWARD_ENABLE, &off, 1, NULL, NULL, 500);
|
||||
|
||||
// Drop the AT32<->ESP link to a slow, forgiving baud for the transfer. An OTA
|
||||
// is one-shot so speed is irrelevant, and 921600 is marginal against the
|
||||
// flash-write / BLE contention that drops the odd frame -> random timeouts.
|
||||
(void)bwm_fwd_negotiate_baud(BWM_OTA_BAUD);
|
||||
// Stop BLE so NimBLE is not hitting flash (NVS / auto-suspend) while we
|
||||
// erase and program the OTA slot. WiFi stays as-is: tearing it down here
|
||||
// is slow and the USB OTA path does not need it off.
|
||||
(void)bwm_cmd(BWM_CMD_STOP_BLE_SPP, NULL, 0, NULL, NULL, 2000);
|
||||
|
||||
// Do NOT retune the UART here. Version already proved the current baud
|
||||
// works; dropping 921600 -> 460800/460000 can leave the ESP on one rate
|
||||
// and the AT32 on the other, after which every OTA_BEGIN times out.
|
||||
|
||||
uint8_t p[4] = {
|
||||
(uint8_t)(total_size & 0xFF), (uint8_t)((total_size >> 8) & 0xFF),
|
||||
(uint8_t)((total_size >> 16) & 0xFF), (uint8_t)((total_size >> 24) & 0xFF)
|
||||
};
|
||||
return bwm_cmd(BWM_CMD_OTA_BEGIN, p, sizeof(p), NULL, NULL, 15000);
|
||||
return bwm_cmd(BWM_CMD_OTA_BEGIN, p, sizeof(p), NULL, NULL, BWM_OTA_BEGIN_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
int bwm_esp_ota_write(const uint8_t *data, uint16_t len) {
|
||||
@@ -322,25 +348,23 @@ int bwm_esp_ota_write(const uint8_t *data, uint16_t len) {
|
||||
// fail the OTA_END size check) - recovery is to restart the whole OTA, which
|
||||
// the client does. Keep a generous timeout so a slow flash write is not
|
||||
// mistaken for a drop.
|
||||
return bwm_cmd(BWM_CMD_OTA_WRITE, data, len, NULL, NULL, 15000);
|
||||
return bwm_cmd(BWM_CMD_OTA_WRITE, data, len, NULL, NULL, BWM_OTA_WRITE_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
int bwm_esp_ota_end(void) {
|
||||
// OTA_END goes out at the slow OTA baud (both ends still there); only after it
|
||||
// do we restore the fast link and log forwarding (both set in _begin).
|
||||
int r = bwm_cmd(BWM_CMD_OTA_END, NULL, 0, NULL, NULL, 20000);
|
||||
(void)bwm_fwd_negotiate_baud(BWM_UART_BAUD_TARGET);
|
||||
uint8_t on = 1;
|
||||
(void)bwm_cmd(BWM_CMD_LOG_FORWARD_ENABLE, &on, 1, NULL, NULL, 500);
|
||||
(void)bwm_cmd(BWM_CMD_START_BLE_SPP, NULL, 0, NULL, NULL, 2000);
|
||||
return r;
|
||||
}
|
||||
|
||||
int bwm_esp_ota_abort(void) {
|
||||
// OTA gave up mid-transfer: restore the fast link + logs so the module is
|
||||
// usable again. The ESP's incomplete OTA state is discarded by the next BEGIN.
|
||||
(void)bwm_fwd_negotiate_baud(BWM_UART_BAUD_TARGET);
|
||||
// Restore logs + BLE. Leave the UART baud alone (see begin). The ESP's
|
||||
// incomplete OTA state is discarded by the next BEGIN.
|
||||
uint8_t on = 1;
|
||||
(void)bwm_cmd(BWM_CMD_LOG_FORWARD_ENABLE, &on, 1, NULL, NULL, 500);
|
||||
(void)bwm_cmd(BWM_CMD_START_BLE_SPP, NULL, 0, NULL, NULL, 2000);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
+9
-3
@@ -75,11 +75,17 @@ int bwm_wifi_forward_status(uint8_t *state, uint32_t *ip_out);
|
||||
// After a working OTA_END, the ESP has marked the new partition bootable but
|
||||
// does not reboot on its own - REBOOT must be sent explicitly (DEV.md 12.8).
|
||||
#define BWM_CMD_REBOOT 1803
|
||||
// Run the OTA at a slow, reliable baud (restored to the fast rate at end/abort).
|
||||
#ifndef BWM_OTA_BAUD
|
||||
#define BWM_OTA_BAUD 460000
|
||||
// BEGIN erases (or finishes aborting) an OTA slot. A 1.8 MB erase on ESP32-C2
|
||||
// commonly takes 20-40 s, so this must be well above the old 15 s race.
|
||||
#ifndef BWM_OTA_BEGIN_TIMEOUT_MS
|
||||
#define BWM_OTA_BEGIN_TIMEOUT_MS 60000
|
||||
#endif
|
||||
#ifndef BWM_OTA_WRITE_TIMEOUT_MS
|
||||
#define BWM_OTA_WRITE_TIMEOUT_MS 20000
|
||||
#endif
|
||||
#define BWM_CMD_GET_VERSION_INFO 1000 // resp: running firmware version string
|
||||
#define BWM_CMD_STOP_BLE_SPP 4022 // no payload: stop BLE during OTA (flash contention)
|
||||
#define BWM_CMD_START_BLE_SPP 4021 // no payload: restore BLE after OTA
|
||||
int bwm_esp_get_version(uint8_t *buf, uint16_t *buflen);
|
||||
int bwm_esp_ota_begin(uint32_t total_size);
|
||||
int bwm_esp_ota_write(const uint8_t *data, uint16_t len);
|
||||
|
||||
+27
-8
@@ -2310,15 +2310,23 @@ static void progressbar(long sent, long total, int style) {
|
||||
static int bwm_ota_once(const uint8_t *fw, size_t fwlen, uint32_t write_delay_ms) {
|
||||
PacketResponseNG resp;
|
||||
|
||||
// BEGIN: tell the BWM how many bytes are coming (it erases the target partition)
|
||||
// BEGIN: tell the BWM how many bytes are coming. The ESP erases the idle
|
||||
// OTA slot here; that can take 20-40 s on a 4 MB ESP32-C2, so wait longer
|
||||
// than the device-side 60 s timeout plus USB round-trip.
|
||||
uint8_t beg[5] = { BWM_OTA_ACTION_BEGIN,
|
||||
(uint8_t)(fwlen & 0xFF), (uint8_t)((fwlen >> 8) & 0xFF),
|
||||
(uint8_t)((fwlen >> 16) & 0xFF), (uint8_t)((fwlen >> 24) & 0xFF) };
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_PM5_BWM_ESP_OTA, beg, sizeof(beg));
|
||||
if ((WaitForResponseTimeout(CMD_PM5_BWM_ESP_OTA, &resp, 20000) == false) || (resp.status != PM3_SUCCESS)) {
|
||||
PrintAndLogEx(FAILED, "OTA begin failed (is a responsive BWM fitted?)");
|
||||
return PM3_EFAILED;
|
||||
if (WaitForResponseTimeout(CMD_PM5_BWM_ESP_OTA, &resp, 75000) == false) {
|
||||
PrintAndLogEx(FAILED, "OTA begin timed out (ESP is likely still erasing the OTA slot)");
|
||||
PrintAndLogEx(HINT, "Wait a few seconds and retry; do not power-cycle mid-erase.");
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
if (resp.status != PM3_SUCCESS) {
|
||||
PrintAndLogEx(FAILED, "OTA begin failed (status %d)%s", resp.status,
|
||||
(resp.status == PM3_ETIMEOUT) ? " - UART timeout waiting for ESP" : "");
|
||||
return resp.status;
|
||||
}
|
||||
PrintAndLogEx(INFO, "Uploading " _YELLOW_("%zu") " bytes of ESP firmware over the BWM link...", fwlen);
|
||||
|
||||
@@ -2336,11 +2344,13 @@ static int bwm_ota_once(const uint8_t *fw, size_t fwlen, uint32_t write_delay_ms
|
||||
memcpy(buf + 1, fw + sent, n);
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_PM5_BWM_ESP_OTA, buf, (uint16_t)(n + 1));
|
||||
bool got = WaitForResponseTimeout(CMD_PM5_BWM_ESP_OTA, &resp, 15000);
|
||||
bool got = WaitForResponseTimeout(CMD_PM5_BWM_ESP_OTA, &resp, 25000);
|
||||
if (!got || resp.status != PM3_SUCCESS) {
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
if (!got) {
|
||||
PrintAndLogEx(WARNING, "OTA write stalled at offset %zu (no response)", sent);
|
||||
} else if (resp.status == PM3_ETIMEOUT) {
|
||||
PrintAndLogEx(WARNING, "OTA write timed out at offset %zu (ESP did not ACK that chunk)", sent);
|
||||
} else {
|
||||
PrintAndLogEx(WARNING, "OTA write rejected at offset %zu (status %d)", sent, resp.status);
|
||||
}
|
||||
@@ -2376,7 +2386,7 @@ static int bwm_ota_once(const uint8_t *fw, size_t fwlen, uint32_t write_delay_ms
|
||||
// That is a genuine failure (boot partition NOT switched) - restart, and
|
||||
// hint at pacing, which is the usual cure.
|
||||
PrintAndLogEx(WARNING, "OTA finalize rejected (status %d) - data was lost in transit", resp.status);
|
||||
PrintAndLogEx(HINT, "Try a per-write delay: " _YELLOW_("hw bwm upgrade -f <fw> --delay 10"));
|
||||
PrintAndLogEx(HINT, "Try a per-write delay: " _YELLOW_("hw bwm upgrade -f <fw> --delay 20"));
|
||||
return PM3_EFAILED;
|
||||
}
|
||||
// No answer at all. Over BLE the END auto-reboot drops the link before the ack
|
||||
@@ -2410,14 +2420,14 @@ static int CmdBWMUpgrade(const char *Cmd) {
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str1("f", "file", "<fn>", "ESP32 firmware image (.bin)"),
|
||||
arg_int0(NULL, "delay", "<ms>", "per-chunk delay to pace the slow AT32<->ESP UART (default 10)"),
|
||||
arg_int0(NULL, "delay", "<ms>", "per-chunk delay to pace the slow AT32<->ESP UART (default 20)"),
|
||||
arg_param_end,
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
int fnlen = 0;
|
||||
char fn[FILE_PATH_SIZE] = {0};
|
||||
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)fn, sizeof(fn), &fnlen);
|
||||
uint32_t write_delay_ms = (uint32_t)arg_get_int_def(ctx, 2, 10);
|
||||
uint32_t write_delay_ms = (uint32_t)arg_get_int_def(ctx, 2, 20);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
if (fnlen == 0) {
|
||||
@@ -2468,7 +2478,16 @@ static int CmdBWMUpgrade(const char *Cmd) {
|
||||
// retry (offset-idempotent ESP write) lands.
|
||||
for (int attempt = 1; attempt <= max_attempts; attempt++) {
|
||||
if (attempt > 1) {
|
||||
// Abort the in-flight ESP OTA (if any) and give a slow erase a chance
|
||||
// to finish before we BEGIN again. Otherwise attempt N's BEGIN races
|
||||
// attempt N-1's still-running erase and times out.
|
||||
PrintAndLogEx(INFO, "restarting OTA from the beginning (attempt " _YELLOW_("%d") "/%d)", attempt, max_attempts);
|
||||
uint8_t ab[1] = { BWM_OTA_ACTION_ABORT };
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_PM5_BWM_ESP_OTA, ab, sizeof(ab));
|
||||
PacketResponseNG abortr;
|
||||
(void)WaitForResponseTimeout(CMD_PM5_BWM_ESP_OTA, &abortr, 8000);
|
||||
msleep(3000);
|
||||
}
|
||||
int res = bwm_ota_once(fw, fwlen, write_delay_ms);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user