mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-19 11:09:42 +00:00
Merge pull request #3604 from cesanta/pico-ota-rollback
Add OTA rollback for Pico boards
This commit is contained in:
5 files changed
+154
-236
No files matched your search
+40
-208
@@ -2261,6 +2261,7 @@ bool mg_ota_flash_write(const void *buf, size_t len, struct mg_flash *flash) {
|
||||
bool mg_ota_flash_end(struct mg_flash *flash) {
|
||||
char *base = (char *) flash->start + flash->size / 2;
|
||||
bool ok = false;
|
||||
int prev_state = MG_OTA_STATE_GET();
|
||||
if (s_size) {
|
||||
size_t size = (size_t) (s_addr - base);
|
||||
uint32_t crc32 = mg_crc32(0, base, s_size);
|
||||
@@ -2289,6 +2290,7 @@ bool mg_ota_flash_end(struct mg_flash *flash) {
|
||||
s_size = 0;
|
||||
if (ok) MG_OTA_STATE_SET(MG_OTA_TESTING);
|
||||
if (ok) ok = flash->swap_fn();
|
||||
if (!ok) MG_OTA_STATE_SET(prev_state); // undo state in case of failure
|
||||
}
|
||||
MG_INFO(("Finishing OTA: %s", ok ? "ok" : "fail"));
|
||||
return ok;
|
||||
@@ -11151,11 +11153,6 @@ static bool __no_inline_not_in_flash_func(flash_erase)(void *addr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __no_inline_not_in_flash_func(mg_picosdk_swap)(void) {
|
||||
// TODO(): RP2350 might have some A/B functionality (DS 5.1)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool s_flash_irq_disabled;
|
||||
|
||||
static bool __no_inline_not_in_flash_func(mg_picosdk_write)(void *addr,
|
||||
@@ -11205,12 +11202,14 @@ static bool __no_inline_not_in_flash_func(mg_picosdk_write)(void *addr,
|
||||
static void __no_inline_not_in_flash_func(single_bank_swap)(char *p1, char *p2,
|
||||
size_t s,
|
||||
size_t ss) {
|
||||
char *tmp = mg_calloc(1, ss);
|
||||
if (tmp == NULL) return;
|
||||
char *tmp_1 = mg_calloc(1, ss); // copy from 1st partition
|
||||
if (tmp_1 == NULL) return;
|
||||
char *tmp_2 = mg_calloc(1, ss); // copy from 2nd partition
|
||||
if (tmp_2 == NULL) return;
|
||||
#if PICO_RP2040
|
||||
uint32_t xip[256 / sizeof(uint32_t)];
|
||||
void *dst = (void *) ((char *) p1 - (char *) s_mg_flash_picosdk.start);
|
||||
size_t count = MG_ROUND_UP(s, ss);
|
||||
void *dst_1 = (void *) ((char *) p1 - (char *) s_mg_flash_picosdk.start);
|
||||
void *dst_2 = (void *) ((char *) p2- (char *) s_mg_flash_picosdk.start);
|
||||
// use SDK function calls to get BootROM function pointers
|
||||
rom_connect_internal_flash_fn connect = (rom_connect_internal_flash_fn) rom_func_lookup(ROM_FUNC_CONNECT_INTERNAL_FLASH);
|
||||
rom_flash_exit_xip_fn xit = (rom_flash_exit_xip_fn) rom_func_lookup(ROM_FUNC_FLASH_EXIT_XIP);
|
||||
@@ -11221,14 +11220,19 @@ static void __no_inline_not_in_flash_func(single_bank_swap)(char *p1, char *p2,
|
||||
// 2nd bootloader (XIP) is in flash, SDK functions copy it to RAM on entry
|
||||
for (size_t i = 0; i < 256 / sizeof(uint32_t); i++)
|
||||
xip[i] = ((uint32_t *) (s_mg_flash_picosdk.start))[i];
|
||||
flash_range_erase((uint32_t) dst, count);
|
||||
// flash has been erased, no XIP to copy. Only BootROM calls possible
|
||||
for (uint32_t ofs = 0; ofs < s; ofs += ss) {
|
||||
for (size_t i = 0; i < ss; i++) tmp[i] = p2[ofs + i];
|
||||
for (size_t i = 0; i < ss; i++) {
|
||||
tmp_1[i] = p1[ofs + i];
|
||||
tmp_2[i] = p2[ofs + i];
|
||||
}
|
||||
flash_range_erase((uint32_t) dst_1 + ofs, ss);
|
||||
flash_range_erase((uint32_t) dst_2 + ofs, ss);
|
||||
__compiler_memory_barrier();
|
||||
connect();
|
||||
xit();
|
||||
program((uint32_t) dst + ofs, tmp, ss);
|
||||
program((uint32_t) dst_1 + ofs, tmp_2, ss);
|
||||
program((uint32_t) dst_2 + ofs, tmp_1, ss);
|
||||
flush();
|
||||
((void (*)(void))((intptr_t) xip + 1))(); // enter XIP again
|
||||
}
|
||||
@@ -11238,13 +11242,34 @@ static void __no_inline_not_in_flash_func(single_bank_swap)(char *p1, char *p2,
|
||||
// It might also be able to take advantage of partition swapping
|
||||
rom_reboot_fn reboot = (rom_reboot_fn) rom_func_lookup(ROM_FUNC_REBOOT);
|
||||
for (size_t ofs = 0; ofs < s; ofs += ss) {
|
||||
for (size_t i = 0; i < ss; i++) tmp[i] = p2[ofs + i];
|
||||
mg_picosdk_write(p1 + ofs, tmp, ss);
|
||||
for (size_t i = 0; i < ss; i++) {
|
||||
tmp_1[i] = p1[ofs + i];
|
||||
tmp_2[i] = p2[ofs + i];
|
||||
}
|
||||
mg_picosdk_write(p1 + ofs, tmp_2, ss);
|
||||
mg_picosdk_write(p2 + ofs, tmp_1, ss);
|
||||
}
|
||||
reboot(BOOT_TYPE_NORMAL | 0x100, 1, 0, 0); // 0x100: NO_RETURN_ON_SUCCESS
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool __no_inline_not_in_flash_func(mg_picosdk_swap)(void) {
|
||||
// TODO(): RP2350 might have some A/B functionality (DS 5.1)
|
||||
// Swap partitions. Pray power does not go away
|
||||
MG_INFO(("Swapping partitions, size %u (%u sectors)",
|
||||
s_mg_flash_picosdk.size,
|
||||
s_mg_flash_picosdk.size / s_mg_flash_picosdk.secsz));
|
||||
MG_INFO(("Do NOT power off..."));
|
||||
mg_log_level = MG_LL_NONE;
|
||||
s_flash_irq_disabled = true;
|
||||
// Runs in RAM, will reset when finished or return on failure
|
||||
single_bank_swap(
|
||||
(char *) s_mg_flash_picosdk.start,
|
||||
(char *) s_mg_flash_picosdk.start + s_mg_flash_picosdk.size / 2,
|
||||
s_mg_flash_picosdk.size / 2, s_mg_flash_picosdk.secsz);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mg_ota_begin(size_t new_firmware_size) {
|
||||
return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_picosdk);
|
||||
}
|
||||
@@ -11254,20 +11279,7 @@ bool mg_ota_write(const void *buf, size_t len) {
|
||||
}
|
||||
|
||||
bool mg_ota_end(void) {
|
||||
if (mg_ota_flash_end(&s_mg_flash_picosdk)) {
|
||||
// Swap partitions. Pray power does not go away
|
||||
MG_INFO(("Swapping partitions, size %u (%u sectors)",
|
||||
s_mg_flash_picosdk.size,
|
||||
s_mg_flash_picosdk.size / s_mg_flash_picosdk.secsz));
|
||||
MG_INFO(("Do NOT power off..."));
|
||||
mg_log_level = MG_LL_NONE;
|
||||
s_flash_irq_disabled = true;
|
||||
// Runs in RAM, will reset when finished or return on failure
|
||||
single_bank_swap(
|
||||
(char *) s_mg_flash_picosdk.start,
|
||||
(char *) s_mg_flash_picosdk.start + s_mg_flash_picosdk.size / 2,
|
||||
s_mg_flash_picosdk.size / 2, s_mg_flash_picosdk.secsz);
|
||||
}
|
||||
if (mg_ota_flash_end(&s_mg_flash_picosdk));
|
||||
return false;
|
||||
}
|
||||
struct mg_flash *mg_flash = &s_mg_flash_picosdk;
|
||||
@@ -30748,186 +30760,6 @@ struct mg_tcpip_driver mg_tcpip_driver_w5500 = {w5500_init, w5500_tx, w5500_rx,
|
||||
w5500_poll};
|
||||
#endif
|
||||
|
||||
#ifdef MG_ENABLE_LINES
|
||||
#line 1 "src/drivers/wiznet.c"
|
||||
#endif
|
||||
// Auto-detecting WIZnet SPI Ethernet driver. Supports W5500 and W5100S.
|
||||
// Detection uses the W5500 VERSIONR register (0x04); any other value
|
||||
// is treated as W5100/W5100S.
|
||||
|
||||
|
||||
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_WIZNET) && MG_ENABLE_DRIVER_WIZNET
|
||||
|
||||
static bool s_w5100; // Detected chip: false = W5500, true = W5100/W5100S
|
||||
|
||||
enum { W55_CR = 0, W55_S0 = 1, W55_TX0 = 2, W55_RX0 = 3 };
|
||||
|
||||
// W5500 SPI frame: [addr_msb][addr_lsb][(block<<3)|rw] then N data bytes.
|
||||
static void w55_txn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr,
|
||||
bool wr, void *buf, size_t len) {
|
||||
uint8_t cmd[] = {(uint8_t) (addr >> 8), (uint8_t) (addr & 255),
|
||||
(uint8_t) ((block << 3) | (wr ? 4 : 0))};
|
||||
s->begin(s->spi);
|
||||
s->txn(s->spi, cmd, NULL, sizeof(cmd));
|
||||
if (wr) s->txn(s->spi, (uint8_t *) buf, NULL, len);
|
||||
if (!wr) s->txn(s->spi, NULL, (uint8_t *) buf, len);
|
||||
s->end(s->spi);
|
||||
}
|
||||
|
||||
// W5100S SPI frame: [opcode][addr_msb][addr_lsb] then N data bytes.
|
||||
static void w51_txn(struct mg_tcpip_spi *s, uint16_t addr, bool wr, void *buf,
|
||||
size_t len) {
|
||||
uint8_t cmd[] = {(uint8_t) (wr ? 0xF0 : 0x0F), (uint8_t) (addr >> 8),
|
||||
(uint8_t) (addr & 255)};
|
||||
s->begin(s->spi);
|
||||
s->txn(s->spi, cmd, NULL, sizeof(cmd));
|
||||
if (wr) s->txn(s->spi, (uint8_t *) buf, NULL, len);
|
||||
if (!wr) s->txn(s->spi, NULL, (uint8_t *) buf, len);
|
||||
s->end(s->spi);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
static void w55_wn(struct mg_tcpip_spi *s, uint8_t bl, uint16_t a, void *b, size_t n) { w55_txn(s, bl, a, true, b, n); }
|
||||
static void w55_w1(struct mg_tcpip_spi *s, uint8_t bl, uint16_t a, uint8_t v) { w55_wn(s, bl, a, &v, 1); }
|
||||
static void w55_w2(struct mg_tcpip_spi *s, uint8_t bl, uint16_t a, uint16_t v) { uint8_t b[2] = {(uint8_t) (v >> 8), (uint8_t) (v & 255)}; w55_wn(s, bl, a, b, 2); }
|
||||
static void w55_rn(struct mg_tcpip_spi *s, uint8_t bl, uint16_t a, void *b, size_t n) { w55_txn(s, bl, a, false, b, n); }
|
||||
static uint8_t w55_r1(struct mg_tcpip_spi *s, uint8_t bl, uint16_t a) { uint8_t r = 0; w55_rn(s, bl, a, &r, 1); return r; }
|
||||
static uint16_t w55_r2(struct mg_tcpip_spi *s, uint8_t bl, uint16_t a) { uint8_t b[2] = {0, 0}; w55_rn(s, bl, a, b, 2); return (uint16_t) ((b[0] << 8) | b[1]); }
|
||||
static void w51_wn(struct mg_tcpip_spi *s, uint16_t a, void *b, size_t n) { w51_txn(s, a, true, b, n); }
|
||||
static void w51_w1(struct mg_tcpip_spi *s, uint16_t a, uint8_t v) { w51_wn(s, a, &v, 1); }
|
||||
static void w51_w2(struct mg_tcpip_spi *s, uint16_t a, uint16_t v) { uint8_t b[2] = {(uint8_t) (v >> 8), (uint8_t) (v & 255)}; w51_wn(s, a, b, 2); }
|
||||
static void w51_rn(struct mg_tcpip_spi *s, uint16_t a, void *b, size_t n) { w51_txn(s, a, false, b, n); }
|
||||
static uint8_t w51_r1(struct mg_tcpip_spi *s, uint16_t a) { uint8_t r = 0; w51_rn(s, a, &r, 1); return r; }
|
||||
static uint16_t w51_r2(struct mg_tcpip_spi *s, uint16_t a) { uint8_t b[2] = {0, 0}; w51_rn(s, a, b, 2); return (uint16_t) ((b[0] << 8) | b[1]); }
|
||||
// clang-format on
|
||||
|
||||
static bool wiznet_init(struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
|
||||
uint8_t ver;
|
||||
s->end(s->spi);
|
||||
ver = w55_r1(s, W55_CR, 0x39); // W5500 VERSIONR; 0x04 = W5500
|
||||
s_w5100 = (ver != 0x04);
|
||||
MG_DEBUG(("Wiznet chip: %s (ver %#x)", s_w5100 ? "W5100S" : "W5500", ver));
|
||||
if (s_w5100) {
|
||||
w51_w1(s, 0, 0x80); // Software reset
|
||||
mg_delayms(2); // Wait for reset (~1ms per datasheet)
|
||||
w51_w1(s, 0x72, 0x53); // PHYLCKR -> unlock PHY
|
||||
w51_w1(s, 0x46, 0); // PHYCR0 -> autonegotiation
|
||||
w51_w1(s, 0x47, 0); // PHYCR1 -> reset
|
||||
w51_w1(s, 0x72, 0x00); // PHYLCKR -> lock PHY
|
||||
w51_wn(s, 0x09, ifp->mac, 6); // SHAR -> set source MAC
|
||||
w51_w1(s, 0x1a, 6); // RMSR -> Sock0 RX buf 4KB
|
||||
w51_w1(s, 0x1b, 6); // TMSR -> Sock0 TX buf 4KB
|
||||
w51_w1(s, 0x400, 0x44); // Sock0 MR -> MACRAW + MAC filter
|
||||
w51_w1(s, 0x401, 1); // Sock0 CR -> OPEN
|
||||
return w51_r1(s, 0x403) == 0x42;
|
||||
} else {
|
||||
w55_w1(s, W55_CR, 0, 0x80); // Software reset
|
||||
mg_delayms(2); // Wait for reset (~1ms per datasheet)
|
||||
w55_w1(s, W55_CR, 0x2e, 0); // PHYCFGR -> reset
|
||||
w55_w1(s, W55_CR, 0x2e, 0xf8); // PHYCFGR -> set
|
||||
w55_w1(s, W55_S0, 0x1e, 16); // Sock0 RX buf 16KB
|
||||
w55_w1(s, W55_S0, 0x1f, 16); // Sock0 TX buf 16KB
|
||||
w55_w1(s, W55_S0, 0, 4); // Sock0 MR -> MACRAW
|
||||
w55_w1(s, W55_S0, 1, 1); // Sock0 CR -> OPEN
|
||||
return w55_r1(s, W55_S0, 3) == 0x42;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t wiznet_rx(void *buf, size_t buflen, struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
|
||||
uint16_t r = 0, n = 0, len = (uint16_t) buflen, n2;
|
||||
if (s_w5100) {
|
||||
while ((n2 = w51_r2(s, 0x426)) > n) n = n2;
|
||||
if (n > 0) {
|
||||
uint16_t ptr = w51_r2(s, 0x428);
|
||||
uint16_t rxsz = (uint16_t) ((1U << (w51_r1(s, 0x1a) & 3)) * 1024U);
|
||||
uint16_t ofs = (uint16_t) ((ptr + 2) & (rxsz - 1));
|
||||
if (n <= len + 2 && n > 1) r = (uint16_t) (n - 2);
|
||||
if (ofs + r <= rxsz) {
|
||||
w51_rn(s, (uint16_t) (0x6000 + ofs), buf, r);
|
||||
} else {
|
||||
uint16_t tail = rxsz - ofs;
|
||||
w51_rn(s, (uint16_t) (0x6000 + ofs), buf, tail);
|
||||
w51_rn(s, 0x6000, (uint8_t *) buf + tail, (size_t) (r - tail));
|
||||
}
|
||||
w51_w2(s, 0x428, (uint16_t) (ptr + n));
|
||||
w51_w1(s, 0x401, 0x40); // Sock0 CR -> RECV
|
||||
}
|
||||
} else {
|
||||
while ((n2 = w55_r2(s, W55_S0, 0x26)) > n) n = n2;
|
||||
if (n > 0) {
|
||||
uint16_t ptr = w55_r2(s, W55_S0, 0x28);
|
||||
n = w55_r2(s, W55_RX0, ptr);
|
||||
if (n <= len + 2 && n > 1) {
|
||||
r = (uint16_t) (n - 2);
|
||||
w55_rn(s, W55_RX0, (uint16_t) (ptr + 2), buf, r);
|
||||
}
|
||||
w55_w2(s, W55_S0, 0x28, (uint16_t) (ptr + n));
|
||||
w55_w1(s, W55_S0, 1, 0x40); // Sock0 CR -> RECV
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static size_t wiznet_tx(const void *buf, size_t buflen,
|
||||
struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
|
||||
uint16_t i, n = 0, len = (uint16_t) buflen;
|
||||
if (s_w5100) {
|
||||
uint16_t ptr;
|
||||
while (n < len) n = w51_r2(s, 0x420);
|
||||
ptr = w51_r2(s, 0x424);
|
||||
uint16_t txsz = (uint16_t) ((1U << (w51_r1(s, 0x1b) & 3)) * 1024U);
|
||||
uint16_t ofs = (uint16_t) (ptr & (txsz - 1));
|
||||
if (ofs + len > txsz) {
|
||||
uint16_t head = txsz - ofs;
|
||||
w51_wn(s, (uint16_t) (0x4000 + ofs), (void *) buf, head);
|
||||
w51_wn(s, 0x4000, (void *) ((uint8_t *) buf + head), len - head);
|
||||
} else {
|
||||
w51_wn(s, (uint16_t) (0x4000 + ofs), (void *) buf, len);
|
||||
}
|
||||
w51_w2(s, 0x424, (uint16_t) (ptr + len));
|
||||
w51_w1(s, 0x401, 0x20); // Sock0 CR -> SEND
|
||||
for (i = 0; i < 40; i++) {
|
||||
uint8_t ir = w51_r1(s, 0x402);
|
||||
if (ir == 0) continue;
|
||||
w51_w1(s, 0x402, ir);
|
||||
if (ir & 8) len = 0;
|
||||
if (ir & (16 | 8)) break;
|
||||
}
|
||||
} else {
|
||||
uint16_t ptr;
|
||||
while (n < len) n = w55_r2(s, W55_S0, 0x20);
|
||||
ptr = w55_r2(s, W55_S0, 0x24);
|
||||
w55_wn(s, W55_TX0, ptr, (void *) buf, len);
|
||||
w55_w2(s, W55_S0, 0x24, (uint16_t) (ptr + len));
|
||||
w55_w1(s, W55_S0, 1, 0x20); // Sock0 CR -> SEND
|
||||
for (i = 0; i < 40; i++) {
|
||||
uint8_t ir = w55_r1(s, W55_S0, 2);
|
||||
if (ir == 0) continue;
|
||||
w55_w1(s, W55_S0, 2, ir);
|
||||
if (ir & 8) len = 0;
|
||||
if (ir & (16 | 8)) break;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static bool wiznet_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||
struct mg_tcpip_spi *spi = (struct mg_tcpip_spi *) ifp->driver_data;
|
||||
if (s_w5100) {
|
||||
return s1 ? (w51_r1(spi, 0x3c) & 1) : false; // PHYSR bit 0 = LNK
|
||||
} else {
|
||||
return s1 ? (w55_r1(spi, W55_CR, 0x2e) & 1) : false; // PHYCFGR bit 0 = LNK
|
||||
}
|
||||
}
|
||||
|
||||
struct mg_tcpip_driver mg_tcpip_driver_wiznet = {wiznet_init, wiznet_tx,
|
||||
wiznet_rx, wiznet_poll};
|
||||
#endif
|
||||
|
||||
#ifdef MG_ENABLE_LINES
|
||||
#line 1 "src/drivers/xmc.c"
|
||||
#endif
|
||||
|
||||
+37
@@ -373,7 +373,44 @@ int mkdir(const char *, mode_t);
|
||||
|
||||
#if MG_OTA == MG_OTA_PICOSDK
|
||||
#include <hardware/flash.h>
|
||||
#include <hardware/watchdog.h>
|
||||
#include <hardware/structs/watchdog.h>
|
||||
#include <pico/bootrom.h>
|
||||
|
||||
#ifndef MG_OTA_WATCHDOG_MS
|
||||
#if PICO_RP2040
|
||||
#define MG_OTA_WATCHDOG_MS 8000U // max delay on RP2040 is 8.3 seconds
|
||||
#else
|
||||
#define MG_OTA_WATCHDOG_MS 10000U // max delay on RP2350 is 16 seconds
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef MG_OTA_ROLLBACK_TIMER_START
|
||||
#define MG_OTA_ROLLBACK_TIMER_START() watchdog_enable(MG_OTA_WATCHDOG_MS, false)
|
||||
#endif
|
||||
|
||||
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
|
||||
#define MG_OTA_ROLLBACK_TIMER_FEED() watchdog_update()
|
||||
#endif
|
||||
|
||||
#define MG_OTA_STATE_GET() ((uint32_t) watchdog_hw->scratch[0])
|
||||
#define MG_OTA_STATE_SET(v) \
|
||||
(watchdog_hw->scratch[0] = (uint32_t) (v), (uint32_t) watchdog_hw->scratch[0])
|
||||
|
||||
#if PICO_RP2040
|
||||
#define MG_OTA_ROLLBACK() \
|
||||
do { \
|
||||
mg_flash->swap_fn(); \
|
||||
*(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; \
|
||||
} while (0)
|
||||
#else
|
||||
#define MG_OTA_ROLLBACK() \
|
||||
do { \
|
||||
mg_flash->swap_fn(); \
|
||||
((rom_reboot_fn) rom_func_lookup(ROM_FUNC_REBOOT))( \
|
||||
BOOT_TYPE_NORMAL | 0x100, 1, 0, 0); \
|
||||
} while (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,7 +19,44 @@ int mkdir(const char *, mode_t);
|
||||
|
||||
#if MG_OTA == MG_OTA_PICOSDK
|
||||
#include <hardware/flash.h>
|
||||
#include <hardware/watchdog.h>
|
||||
#include <hardware/structs/watchdog.h>
|
||||
#include <pico/bootrom.h>
|
||||
|
||||
#ifndef MG_OTA_WATCHDOG_MS
|
||||
#if PICO_RP2040
|
||||
#define MG_OTA_WATCHDOG_MS 8000U // max delay on RP2040 is 8.3 seconds
|
||||
#else
|
||||
#define MG_OTA_WATCHDOG_MS 10000U // max delay on RP2350 is 16 seconds
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef MG_OTA_ROLLBACK_TIMER_START
|
||||
#define MG_OTA_ROLLBACK_TIMER_START() watchdog_enable(MG_OTA_WATCHDOG_MS, false)
|
||||
#endif
|
||||
|
||||
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
|
||||
#define MG_OTA_ROLLBACK_TIMER_FEED() watchdog_update()
|
||||
#endif
|
||||
|
||||
#define MG_OTA_STATE_GET() ((uint32_t) watchdog_hw->scratch[0])
|
||||
#define MG_OTA_STATE_SET(v) \
|
||||
(watchdog_hw->scratch[0] = (uint32_t) (v), (uint32_t) watchdog_hw->scratch[0])
|
||||
|
||||
#if PICO_RP2040
|
||||
#define MG_OTA_ROLLBACK() \
|
||||
do { \
|
||||
mg_flash->swap_fn(); \
|
||||
*(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; \
|
||||
} while (0)
|
||||
#else
|
||||
#define MG_OTA_ROLLBACK() \
|
||||
do { \
|
||||
mg_flash->swap_fn(); \
|
||||
((rom_reboot_fn) rom_func_lookup(ROM_FUNC_REBOOT))( \
|
||||
BOOT_TYPE_NORMAL | 0x100, 1, 0, 0); \
|
||||
} while (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -57,6 +57,7 @@ bool mg_ota_flash_write(const void *buf, size_t len, struct mg_flash *flash) {
|
||||
bool mg_ota_flash_end(struct mg_flash *flash) {
|
||||
char *base = (char *) flash->start + flash->size / 2;
|
||||
bool ok = false;
|
||||
int prev_state = MG_OTA_STATE_GET();
|
||||
if (s_size) {
|
||||
size_t size = (size_t) (s_addr - base);
|
||||
uint32_t crc32 = mg_crc32(0, base, s_size);
|
||||
@@ -85,6 +86,7 @@ bool mg_ota_flash_end(struct mg_flash *flash) {
|
||||
s_size = 0;
|
||||
if (ok) MG_OTA_STATE_SET(MG_OTA_TESTING);
|
||||
if (ok) ok = flash->swap_fn();
|
||||
if (!ok) MG_OTA_STATE_SET(prev_state); // undo state in case of failure
|
||||
}
|
||||
MG_INFO(("Finishing OTA: %s", ok ? "ok" : "fail"));
|
||||
return ok;
|
||||
|
||||
+38
-28
@@ -46,11 +46,6 @@ static bool __no_inline_not_in_flash_func(flash_erase)(void *addr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __no_inline_not_in_flash_func(mg_picosdk_swap)(void) {
|
||||
// TODO(): RP2350 might have some A/B functionality (DS 5.1)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool s_flash_irq_disabled;
|
||||
|
||||
static bool __no_inline_not_in_flash_func(mg_picosdk_write)(void *addr,
|
||||
@@ -100,12 +95,14 @@ static bool __no_inline_not_in_flash_func(mg_picosdk_write)(void *addr,
|
||||
static void __no_inline_not_in_flash_func(single_bank_swap)(char *p1, char *p2,
|
||||
size_t s,
|
||||
size_t ss) {
|
||||
char *tmp = mg_calloc(1, ss);
|
||||
if (tmp == NULL) return;
|
||||
char *tmp_1 = mg_calloc(1, ss); // copy from 1st partition
|
||||
if (tmp_1 == NULL) return;
|
||||
char *tmp_2 = mg_calloc(1, ss); // copy from 2nd partition
|
||||
if (tmp_2 == NULL) return;
|
||||
#if PICO_RP2040
|
||||
uint32_t xip[256 / sizeof(uint32_t)];
|
||||
void *dst = (void *) ((char *) p1 - (char *) s_mg_flash_picosdk.start);
|
||||
size_t count = MG_ROUND_UP(s, ss);
|
||||
void *dst_1 = (void *) ((char *) p1 - (char *) s_mg_flash_picosdk.start);
|
||||
void *dst_2 = (void *) ((char *) p2- (char *) s_mg_flash_picosdk.start);
|
||||
// use SDK function calls to get BootROM function pointers
|
||||
rom_connect_internal_flash_fn connect = (rom_connect_internal_flash_fn) rom_func_lookup(ROM_FUNC_CONNECT_INTERNAL_FLASH);
|
||||
rom_flash_exit_xip_fn xit = (rom_flash_exit_xip_fn) rom_func_lookup(ROM_FUNC_FLASH_EXIT_XIP);
|
||||
@@ -116,14 +113,19 @@ static void __no_inline_not_in_flash_func(single_bank_swap)(char *p1, char *p2,
|
||||
// 2nd bootloader (XIP) is in flash, SDK functions copy it to RAM on entry
|
||||
for (size_t i = 0; i < 256 / sizeof(uint32_t); i++)
|
||||
xip[i] = ((uint32_t *) (s_mg_flash_picosdk.start))[i];
|
||||
flash_range_erase((uint32_t) dst, count);
|
||||
// flash has been erased, no XIP to copy. Only BootROM calls possible
|
||||
for (uint32_t ofs = 0; ofs < s; ofs += ss) {
|
||||
for (size_t i = 0; i < ss; i++) tmp[i] = p2[ofs + i];
|
||||
for (size_t i = 0; i < ss; i++) {
|
||||
tmp_1[i] = p1[ofs + i];
|
||||
tmp_2[i] = p2[ofs + i];
|
||||
}
|
||||
flash_range_erase((uint32_t) dst_1 + ofs, ss);
|
||||
flash_range_erase((uint32_t) dst_2 + ofs, ss);
|
||||
__compiler_memory_barrier();
|
||||
connect();
|
||||
xit();
|
||||
program((uint32_t) dst + ofs, tmp, ss);
|
||||
program((uint32_t) dst_1 + ofs, tmp_2, ss);
|
||||
program((uint32_t) dst_2 + ofs, tmp_1, ss);
|
||||
flush();
|
||||
((void (*)(void))((intptr_t) xip + 1))(); // enter XIP again
|
||||
}
|
||||
@@ -133,13 +135,34 @@ static void __no_inline_not_in_flash_func(single_bank_swap)(char *p1, char *p2,
|
||||
// It might also be able to take advantage of partition swapping
|
||||
rom_reboot_fn reboot = (rom_reboot_fn) rom_func_lookup(ROM_FUNC_REBOOT);
|
||||
for (size_t ofs = 0; ofs < s; ofs += ss) {
|
||||
for (size_t i = 0; i < ss; i++) tmp[i] = p2[ofs + i];
|
||||
mg_picosdk_write(p1 + ofs, tmp, ss);
|
||||
for (size_t i = 0; i < ss; i++) {
|
||||
tmp_1[i] = p1[ofs + i];
|
||||
tmp_2[i] = p2[ofs + i];
|
||||
}
|
||||
mg_picosdk_write(p1 + ofs, tmp_2, ss);
|
||||
mg_picosdk_write(p2 + ofs, tmp_1, ss);
|
||||
}
|
||||
reboot(BOOT_TYPE_NORMAL | 0x100, 1, 0, 0); // 0x100: NO_RETURN_ON_SUCCESS
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool __no_inline_not_in_flash_func(mg_picosdk_swap)(void) {
|
||||
// TODO(): RP2350 might have some A/B functionality (DS 5.1)
|
||||
// Swap partitions. Pray power does not go away
|
||||
MG_INFO(("Swapping partitions, size %u (%u sectors)",
|
||||
s_mg_flash_picosdk.size,
|
||||
s_mg_flash_picosdk.size / s_mg_flash_picosdk.secsz));
|
||||
MG_INFO(("Do NOT power off..."));
|
||||
mg_log_level = MG_LL_NONE;
|
||||
s_flash_irq_disabled = true;
|
||||
// Runs in RAM, will reset when finished or return on failure
|
||||
single_bank_swap(
|
||||
(char *) s_mg_flash_picosdk.start,
|
||||
(char *) s_mg_flash_picosdk.start + s_mg_flash_picosdk.size / 2,
|
||||
s_mg_flash_picosdk.size / 2, s_mg_flash_picosdk.secsz);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mg_ota_begin(size_t new_firmware_size) {
|
||||
return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_picosdk);
|
||||
}
|
||||
@@ -149,20 +172,7 @@ bool mg_ota_write(const void *buf, size_t len) {
|
||||
}
|
||||
|
||||
bool mg_ota_end(void) {
|
||||
if (mg_ota_flash_end(&s_mg_flash_picosdk)) {
|
||||
// Swap partitions. Pray power does not go away
|
||||
MG_INFO(("Swapping partitions, size %u (%u sectors)",
|
||||
s_mg_flash_picosdk.size,
|
||||
s_mg_flash_picosdk.size / s_mg_flash_picosdk.secsz));
|
||||
MG_INFO(("Do NOT power off..."));
|
||||
mg_log_level = MG_LL_NONE;
|
||||
s_flash_irq_disabled = true;
|
||||
// Runs in RAM, will reset when finished or return on failure
|
||||
single_bank_swap(
|
||||
(char *) s_mg_flash_picosdk.start,
|
||||
(char *) s_mg_flash_picosdk.start + s_mg_flash_picosdk.size / 2,
|
||||
s_mg_flash_picosdk.size / 2, s_mg_flash_picosdk.secsz);
|
||||
}
|
||||
if (mg_ota_flash_end(&s_mg_flash_picosdk));
|
||||
return false;
|
||||
}
|
||||
struct mg_flash *mg_flash = &s_mg_flash_picosdk;
|
||||
|
||||
Reference in new issue
Block a user