-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
362 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
#include "flash.h" | ||
#include "log.h" | ||
#include "ota.h" | ||
|
||
#if MG_OTA == MG_OTA_PICOSDK | ||
|
||
// Both RP2040 and RP2350 have no flash, low-level flash access support in | ||
// bootrom, and high-level support in Pico-SDK (2.0+ for the RP2350) | ||
// - The RP2350 in RISC-V mode is not yet (fully) supported (nor tested) | ||
|
||
static bool mg_picosdk_write(void *, const void *, size_t); | ||
static bool mg_picosdk_swap(void); | ||
|
||
static struct mg_flash s_mg_flash_picosdk = { | ||
(void *) 0x10000000, // Start, not used here; functions handle offset | ||
#ifdef PICO_FLASH_SIZE_BYTES | ||
PICO_FLASH_SIZE_BYTES, // Size, from board definitions | ||
#else | ||
0x200000, // Size, guess... is 2M enough ? | ||
#endif | ||
FLASH_SECTOR_SIZE, // Sector size, from hardware_flash | ||
FLASH_PAGE_SIZE, // Align, from hardware_flash | ||
mg_picosdk_write, mg_picosdk_swap, | ||
}; | ||
|
||
#define MG_MODULO2(x, m) ((x) & ((m) -1)) | ||
|
||
static bool __no_inline_not_in_flash_func(flash_sector_start)( | ||
volatile uint32_t *dst) { | ||
char *base = (char *) s_mg_flash_picosdk.start, | ||
*end = base + s_mg_flash_picosdk.size; | ||
volatile char *p = (char *) dst; | ||
return p >= base && p < end && | ||
MG_MODULO2(p - base, s_mg_flash_picosdk.secsz) == 0; | ||
} | ||
|
||
static bool __no_inline_not_in_flash_func(flash_erase)(void *addr) { | ||
if (flash_sector_start(addr) == false) { | ||
MG_ERROR(("%p is not on a sector boundary", addr)); | ||
return false; | ||
} | ||
void *dst = (void *) ((char *) addr - (char *) s_mg_flash_picosdk.start); | ||
flash_range_erase((uint32_t) dst, s_mg_flash_picosdk.secsz); | ||
MG_DEBUG(("Sector starting at %p erasure", 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, | ||
const void *buf, | ||
size_t len) { | ||
if ((len % s_mg_flash_picosdk.align) != 0) { | ||
MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_picosdk.align)); | ||
return false; | ||
} | ||
if ((((size_t) addr - (size_t) s_mg_flash_picosdk.start) % | ||
s_mg_flash_picosdk.align) != 0) { | ||
MG_ERROR(("%p is not on a page boundary", addr)); | ||
return false; | ||
} | ||
|
||
uint32_t *dst = (uint32_t *) addr; | ||
uint32_t *src = (uint32_t *) buf; | ||
uint32_t *end = (uint32_t *) ((char *) buf + len); | ||
|
||
#ifndef __riscv | ||
MG_ARM_DISABLE_IRQ(); | ||
#else | ||
asm volatile("csrrc zero, mstatus, %0" : : "i"(1 << 3) : "memory"); | ||
#endif | ||
while (src < end) { | ||
uint32_t dst_ofs = (uint32_t) dst - (uint32_t) s_mg_flash_picosdk.start; | ||
if (flash_sector_start(dst) && flash_erase(dst) == false) break; | ||
// flash_range_program() runs in RAM and handles writing up to | ||
// FLASH_PAGE_SIZE bytes. Source must not be in flash | ||
flash_range_program((uint32_t) dst_ofs, (uint8_t *) src, | ||
s_mg_flash_picosdk.align); | ||
src = (uint32_t *) ((char *) src + s_mg_flash_picosdk.align); | ||
dst = (uint32_t *) ((char *) dst + s_mg_flash_picosdk.align); | ||
} | ||
if (!s_flash_irq_disabled) { | ||
#ifndef __riscv | ||
MG_ARM_ENABLE_IRQ(); | ||
#else | ||
asm volatile("csrrs mstatus, %0" : : "i"(1 << 3) : "memory"); | ||
#endif | ||
} | ||
MG_DEBUG(("Flash write %lu bytes @ %p.", len, dst)); | ||
return true; | ||
} | ||
|
||
// just overwrite instead of swap | ||
static void __no_inline_not_in_flash_func(single_bank_swap)(char *p1, char *p2, | ||
size_t s, | ||
size_t ss) { | ||
char *tmp = malloc(ss); | ||
if (tmp == 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); | ||
// use SDK function calls to get BootROM function pointers | ||
rom_connect_internal_flash_fn connect = (rom_connect_internal_flash_fn) rom_func_lookup_inline(ROM_FUNC_CONNECT_INTERNAL_FLASH); | ||
rom_flash_exit_xip_fn xit = (rom_flash_exit_xip_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_EXIT_XIP); | ||
rom_flash_range_program_fn program = (rom_flash_range_program_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_RANGE_PROGRAM); | ||
rom_flash_flush_cache_fn flush = (rom_flash_flush_cache_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE); | ||
// no stdlib calls here. | ||
MG_ARM_DISABLE_IRQ(); | ||
// 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]; | ||
__compiler_memory_barrier(); | ||
connect(); | ||
xit(); | ||
program((uint32_t) dst + ofs, tmp, ss); | ||
flush(); | ||
((void (*)(void))((intptr_t) xip + 1))(); // enter XIP again | ||
} | ||
*(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; // AIRCR = SYSRESETREQ | ||
#else | ||
// RP2350 has bootram and copies second bootloader there, SDK uses that copy, | ||
// It might also be able to take advantage of partition swapping | ||
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); | ||
} | ||
#ifndef __riscv | ||
*(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; // AIRCR = SYSRESETREQ | ||
#else | ||
// TODO(): find a way to do a system reset, like block resets and watchdog | ||
#endif | ||
#endif | ||
} | ||
|
||
bool mg_ota_begin(size_t new_firmware_size) { | ||
return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_picosdk); | ||
} | ||
|
||
bool mg_ota_write(const void *buf, size_t len) { | ||
return mg_ota_flash_write(buf, len, &s_mg_flash_picosdk); | ||
} | ||
|
||
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); | ||
} | ||
return false; | ||
} | ||
#endif |