diff --git a/sys/Makefile.dep b/sys/Makefile.dep index e2b0a90b22..c2773ef805 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -892,6 +892,7 @@ endif ifneq (,$(filter riotboot_flashwrite, $(USEMODULE))) USEMODULE += riotboot_slot FEATURES_REQUIRED += periph_flashpage + FEATURES_OPTIONAL += periph_flashpage_raw endif ifneq (,$(filter riotboot_slot, $(USEMODULE))) diff --git a/sys/include/riotboot/flashwrite.h b/sys/include/riotboot/flashwrite.h index 2d777f958c..4bc1d2c8b5 100644 --- a/sys/include/riotboot/flashwrite.h +++ b/sys/include/riotboot/flashwrite.h @@ -41,6 +41,15 @@ * 2. write image starting at second block * 3. write first block * + * When using periph_flashpage_raw with this module, the need to buffer a full + * flashpage page is removed, instead it must buffer two times the + * FLASHPAGE_RAW_BLOCKSIZE. One is used to buffer the current write block, + * the other buffers the first chunk (offset zero, page zero). This first + * chunk is written when finalizing the flash operation. The minimal size for + * RIOTBOOT_FLASHPAGE_BUFFER_SIZE is 4, at least the riotboot magic number must + * fit into this and FLASHPAGE_SIZE must be a multiple of + * RIOTBOOT_FLASHPAGE_BUFFER_SIZE + * * @author Kaspar Schleiser * @author Koen Zandberg * @@ -57,6 +66,36 @@ extern "C" { #include "riotboot/slot.h" #include "periph/flashpage.h" +/** + * @brief Enable/disable raw writes to flash + */ +#ifndef CONFIG_RIOTBOOT_FLASHWRITE_RAW +#define CONFIG_RIOTBOOT_FLASHWRITE_RAW IS_ACTIVE(MODULE_PERIPH_FLASHPAGE_RAW) +#endif + +/** + * @brief Intermediate buffer size for firmware image data + */ +#if CONFIG_RIOTBOOT_FLASHWRITE_RAW + +#if (FLASHPAGE_RAW_BLOCKSIZE < 4) +#define RIOTBOOT_FLASHPAGE_BUFFER_SIZE 4 +#else +#define RIOTBOOT_FLASHPAGE_BUFFER_SIZE FLASHPAGE_RAW_BLOCKSIZE +#endif + +#else /* CONFIG_RIOTBOOT_FLASHWRITE_RAW */ + +#define RIOTBOOT_FLASHPAGE_BUFFER_SIZE FLASHPAGE_SIZE + +#endif /* !CONFIG_RIOTBOOT_FLASHWRITE_RAW */ + +/** + * @brief Extra attributes required for the firmware intermediate buffer + */ +#define RIOTBOOT_FLASHPAGE_BUFFER_ATTRS \ + __attribute__((aligned(FLASHPAGE_RAW_ALIGNMENT))) + /** * @brief firmware update state structure * @@ -67,7 +106,20 @@ typedef struct { int target_slot; /**< update targets this slot */ size_t offset; /**< update is at this position */ unsigned flashpage; /**< update is at this flashpage */ - uint8_t flashpage_buf[FLASHPAGE_SIZE]; /**< flash writing buffer */ + + /** + * @brief flash writing buffer + */ + uint8_t RIOTBOOT_FLASHPAGE_BUFFER_ATTRS + flashpage_buf[RIOTBOOT_FLASHPAGE_BUFFER_SIZE]; +#if CONFIG_RIOTBOOT_FLASHWRITE_RAW || DOXYGEN + /** + * @brief Buffer for the first chunk containing the checksum when using + * FLASHWRITE_RAW + */ + uint8_t RIOTBOOT_FLASHPAGE_BUFFER_ATTRS + firstblock_buf[RIOTBOOT_FLASHPAGE_BUFFER_SIZE]; +#endif } riotboot_flashwrite_t; /** diff --git a/sys/riotboot/flashwrite.c b/sys/riotboot/flashwrite.c index c86ea04202..6949006fc6 100644 --- a/sys/riotboot/flashwrite.c +++ b/sys/riotboot/flashwrite.c @@ -44,6 +44,8 @@ int riotboot_flashwrite_init_raw(riotboot_flashwrite_t *state, int target_slot, size_t offset) { assert(offset <= FLASHPAGE_SIZE); + /* the flashpage size must be a multiple of the riotboot flashpage buffer */ + static_assert(!(FLASHPAGE_SIZE % RIOTBOOT_FLASHPAGE_BUFFER_SIZE)); LOG_INFO(LOG_PREFIX "initializing update to target slot %i\n", target_slot); @@ -54,14 +56,37 @@ int riotboot_flashwrite_init_raw(riotboot_flashwrite_t *state, int target_slot, state->target_slot = target_slot; state->flashpage = flashpage_page((void *)riotboot_slot_get_hdr(target_slot)); + if (CONFIG_RIOTBOOT_FLASHWRITE_RAW && offset) { + /* Erase the first page only if the offset (!=0) specifies that there is + * a checksum or other mechanism at the start of the page. */ + flashpage_write(state->flashpage, NULL); + } + return 0; } int riotboot_flashwrite_flush(riotboot_flashwrite_t *state) { - if (flashpage_write_and_verify(state->flashpage, state->flashpage_buf) != FLASHPAGE_OK) { - LOG_WARNING(LOG_PREFIX "error writing flashpage %u!\n", state->flashpage); - return -1; + if (CONFIG_RIOTBOOT_FLASHWRITE_RAW) { + /* Check if there is leftover data in the buffer */ + size_t flashwrite_buffer_pos = state->offset % RIOTBOOT_FLASHPAGE_BUFFER_SIZE; + if (flashwrite_buffer_pos == 0) { + return 0; + } + uint8_t* slot_start = + (uint8_t*)riotboot_slot_get_hdr(state->target_slot); + /* Get the offset of the remaining chunk */ + size_t flashpage_pos = state->offset - flashwrite_buffer_pos; + /* Write remaining chunk */ + flashpage_write_raw(slot_start + flashpage_pos, + state->flashpage_buf, + RIOTBOOT_FLASHPAGE_BUFFER_SIZE); + } + else { + if (flashpage_write_and_verify(state->flashpage, state->flashpage_buf) != FLASHPAGE_OK) { + LOG_WARNING(LOG_PREFIX "error writing flashpage %u!\n", state->flashpage); + return -1; + } } return 0; } @@ -73,22 +98,52 @@ int riotboot_flashwrite_putbytes(riotboot_flashwrite_t *state, while (len) { size_t flashpage_pos = state->offset % FLASHPAGE_SIZE; - size_t flashpage_avail = FLASHPAGE_SIZE - flashpage_pos; + size_t flashwrite_buffer_pos = state->offset % RIOTBOOT_FLASHPAGE_BUFFER_SIZE; + size_t flashpage_avail = RIOTBOOT_FLASHPAGE_BUFFER_SIZE - flashwrite_buffer_pos; size_t to_copy = min(flashpage_avail, len); - memcpy(state->flashpage_buf + flashpage_pos, bytes, to_copy); + if (CONFIG_RIOTBOOT_FLASHWRITE_RAW && flashpage_pos == 0) { + /* Erase the next page */ + state->flashpage++; + flashpage_write(state->flashpage, NULL); + } + if (CONFIG_RIOTBOOT_FLASHWRITE_RAW && + flashwrite_buffer_pos == 0) { + memset(state->flashpage_buf, 0, RIOTBOOT_FLASHPAGE_BUFFER_SIZE); + }; + + memcpy(state->flashpage_buf + flashwrite_buffer_pos, bytes, to_copy); flashpage_avail -= to_copy; + state->offset += to_copy; bytes += to_copy; len -= to_copy; if ((!flashpage_avail) || (!more)) { - if (flashpage_write_and_verify(state->flashpage, state->flashpage_buf) != FLASHPAGE_OK) { +#if CONFIG_RIOTBOOT_FLASHWRITE_RAW /* Guards access to state::firstblock_buf */ + void * addr = flashpage_addr(state->flashpage); + if (addr == riotboot_slot_get_hdr(state->target_slot) && + state->offset == RIOTBOOT_FLASHPAGE_BUFFER_SIZE) { + /* Skip flashing the first block, store it for later to flash it + * during the flashwrite_finish function */ + memcpy(state->firstblock_buf, + state->flashpage_buf, RIOTBOOT_FLASHPAGE_BUFFER_SIZE); + } + else { + flashpage_write_raw((uint8_t*)addr + flashpage_pos, + state->flashpage_buf, + RIOTBOOT_FLASHPAGE_BUFFER_SIZE); + } +#else + int res = flashpage_write_and_verify(state->flashpage, + state->flashpage_buf); + if (res != FLASHPAGE_OK) { LOG_WARNING(LOG_PREFIX "error writing flashpage %u!\n", state->flashpage); return -1; } state->flashpage++; +#endif } } @@ -104,6 +159,10 @@ int riotboot_flashwrite_finish_raw(riotboot_flashwrite_t *state, uint8_t *slot_start = (uint8_t *)riotboot_slot_get_hdr(state->target_slot); +#if CONFIG_RIOTBOOT_FLASHWRITE_RAW + memcpy(state->firstblock_buf, bytes, len); + flashpage_write_raw(slot_start, state->firstblock_buf, RIOTBOOT_FLASHPAGE_BUFFER_SIZE); +#else uint8_t *firstpage; if (len < FLASHPAGE_SIZE) { @@ -122,10 +181,12 @@ int riotboot_flashwrite_finish_raw(riotboot_flashwrite_t *state, LOG_WARNING(LOG_PREFIX "re-flashing first block failed!\n"); goto out; } - +#endif /* !CONFIG_RIOTBOOT_FLASHWRITE_RAW */ LOG_INFO(LOG_PREFIX "riotboot flashing completed successfully\n"); res = 0; +#if !CONFIG_RIOTBOOT_FLASHWRITE_RAW out: +#endif return res; }