diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index 389f9f5d0f..07053b3293 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -120,6 +120,7 @@ SRC_C += \ ui.c \ vfs_fat.c \ vfs_lfs.c \ + vfs_raw.c \ drivers/bus/softspi.c \ drivers/bus/softqspi.c \ drivers/memory/spiflash.c \ diff --git a/ports/stm32/mboot/README.md b/ports/stm32/mboot/README.md index 73e32fcedc..221e3a7c3a 100644 --- a/ports/stm32/mboot/README.md +++ b/ports/stm32/mboot/README.md @@ -71,13 +71,23 @@ How to use #define MBOOT_FSLOAD (1) and then enable one or more of the following depending on what filesystem - support is required in Mboot (note that the FAT driver is read-only and - quite compact, but littlefs supports both read and write so is rather - large): + support is required in Mboot: #define MBOOT_VFS_FAT (1) #define MBOOT_VFS_LFS1 (1) #define MBOOT_VFS_LFS2 (1) + #define MBOOT_VFS_RAW (1) + + Note that the FAT and LFS2 drivers are read-only and quite compact, but + LFS1 supports both read and write so is rather large. + + The raw filesystem type is enabled by default and is a flat section of + storage containing a single file without any metadata. The raw filesystem + can either be one regoin, or split over two separate, contiguous regions. + The latter is useful for wear levelling: given a chunk of flash, write the + firmware starting at a random block within that chunk and wrap around to + the beginning of the chunk when the end is reached. Then use a split + raw filesystem to inform mboot of this wrapping. 2. Build the board's main application firmware as usual. diff --git a/ports/stm32/mboot/fsload.c b/ports/stm32/mboot/fsload.c index abc3514ed1..83905eb491 100644 --- a/ports/stm32/mboot/fsload.c +++ b/ports/stm32/mboot/fsload.c @@ -39,7 +39,7 @@ #if MBOOT_FSLOAD -#if !(MBOOT_VFS_FAT || MBOOT_VFS_LFS1 || MBOOT_VFS_LFS2) +#if !(MBOOT_VFS_FAT || MBOOT_VFS_LFS1 || MBOOT_VFS_LFS2 || MBOOT_VFS_RAW) #error Must enable at least one VFS component #endif @@ -234,28 +234,51 @@ int fsload_process(void) { // End of elements. return -MBOOT_ERRNO_FSLOAD_NO_MOUNT; } + + // Extract element arguments based on the element length: + // - 10 bytes: mount_point fs_type uint32_t uint32_t + // - 14 bytes: mount_point fs_type uint32_t uint32_t uint32_t + // - 18 bytes: mount_point fs_type uint32_t uint32_t uint32_t uint32_t + // - 22 bytes: mount_point fs_type uint64_t uint64_t uint32_t + // - 34 bytes: mount_point fs_type uint64_t uint64_t uint64_t uint64_t mboot_addr_t base_addr; mboot_addr_t byte_len; - uint32_t block_size = MBOOT_FSLOAD_DEFAULT_BLOCK_SIZE; - if (elem[-1] == 10 || elem[-1] == 14) { + mboot_addr_t arg2 = 0; + mboot_addr_t arg3 = 0; + (void)arg2; + (void)arg3; + uint8_t elem_len = elem[-1]; + if (elem_len == 10 || elem_len == 14 || elem_len == 18) { // 32-bit base and length given, extract them. base_addr = get_le32(&elem[2]); byte_len = get_le32(&elem[6]); - if (elem[-1] == 14) { - // Block size given, extract it. - block_size = get_le32(&elem[10]); + if (elem_len >= 14) { + // Argument 2 given, extract it. + arg2 = get_le32(&elem[10]); + if (elem_len == 18) { + // Argument 3 given, extract it. + arg3 = get_le32(&elem[14]); + } } #if MBOOT_ADDRESS_SPACE_64BIT - } else if (elem[-1] == 22) { - // 64-bit base and length given, and block size, so extract them. + } else if (elem_len == 22 || elem_len == 34) { + // 64-bit base and length given, so extract them. base_addr = get_le64(&elem[2]); byte_len = get_le64(&elem[10]); - block_size = get_le32(&elem[18]); + if (elem_len == 22) { + // 32-bit argument 2 given, extract it. + arg2 = get_le32(&elem[18]); + } else { + // 64-bit argument 2 and 3 given, extract them. + arg2 = get_le64(&elem[18]); + arg3 = get_le64(&elem[26]); + } #endif } else { // Invalid MOUNT element. return -MBOOT_ERRNO_FSLOAD_INVALID_MOUNT; } + if (elem[0] == mount_point) { int ret; union { @@ -268,27 +291,43 @@ int fsload_process(void) { #if MBOOT_VFS_LFS2 vfs_lfs2_context_t lfs2; #endif + #if MBOOT_VFS_RAW + vfs_raw_context_t raw; + #endif } ctx; const stream_methods_t *methods; #if MBOOT_VFS_FAT if (elem[1] == ELEM_MOUNT_FAT) { - (void)block_size; ret = vfs_fat_mount(&ctx.fat, base_addr, byte_len); methods = &vfs_fat_stream_methods; } else #endif #if MBOOT_VFS_LFS1 if (elem[1] == ELEM_MOUNT_LFS1) { + uint32_t block_size = arg2; + if (block_size == 0) { + block_size = MBOOT_FSLOAD_DEFAULT_BLOCK_SIZE; + } ret = vfs_lfs1_mount(&ctx.lfs1, base_addr, byte_len, block_size); methods = &vfs_lfs1_stream_methods; } else #endif #if MBOOT_VFS_LFS2 if (elem[1] == ELEM_MOUNT_LFS2) { + uint32_t block_size = arg2; + if (block_size == 0) { + block_size = MBOOT_FSLOAD_DEFAULT_BLOCK_SIZE; + } ret = vfs_lfs2_mount(&ctx.lfs2, base_addr, byte_len, block_size); methods = &vfs_lfs2_stream_methods; } else #endif + #if MBOOT_VFS_RAW + if (elem[1] == ELEM_MOUNT_RAW) { + ret = vfs_raw_mount(&ctx.raw, base_addr, byte_len, arg2, arg3); + methods = &vfs_raw_stream_methods; + } else + #endif { // Unknown filesystem type return -MBOOT_ERRNO_FSLOAD_INVALID_MOUNT; diff --git a/ports/stm32/mboot/fwupdate.py b/ports/stm32/mboot/fwupdate.py index 47ceb19ba5..8578ff4fc9 100644 --- a/ports/stm32/mboot/fwupdate.py +++ b/ports/stm32/mboot/fwupdate.py @@ -9,6 +9,7 @@ import deflate, machine, stm VFS_FAT = 1 VFS_LFS1 = 2 VFS_LFS2 = 3 +VFS_RAW = 4 # Constants for creating mboot elements. _ELEM_TYPE_END = const(1) @@ -226,28 +227,45 @@ def _create_element(kind, body): def update_app_elements( - filename, fs_base, fs_len, fs_type=VFS_FAT, fs_blocksize=0, status_addr=None, addr_64bit=False + filename, + fs_base, + fs_len, + fs_type=VFS_FAT, + fs_blocksize=0, + status_addr=None, + addr_64bit=False, + *, + fs_base2=0, + fs_len2=0, ): - # Check firmware is of .dfu or .dfu.gz type - try: - with open(filename, "rb") as f: - hdr = deflate.DeflateIO(f, deflate.GZIP).read(6) - except Exception: - with open(filename, "rb") as f: - hdr = f.read(6) - if hdr != b"DfuSe\x01": - print("Firmware must be a .dfu(.gz) file.") - return () + if fs_type != VFS_RAW: + # Check firmware is of .dfu or .dfu.gz type + try: + with open(filename, "rb") as f: + hdr = deflate.DeflateIO(f, deflate.GZIP).read(6) + except Exception: + with open(filename, "rb") as f: + hdr = f.read(6) + if hdr != b"DfuSe\x01": + print("Firmware must be a .dfu(.gz) file.") + return () if fs_type in (VFS_LFS1, VFS_LFS2) and not fs_blocksize: raise Exception("littlefs requires fs_blocksize parameter") mount_point = 1 - mount_encoding = "seg0_base_addr = seg0_base_addr; + ctx->seg0_byte_len = seg0_byte_len; + ctx->seg1_base_addr = seg1_base_addr; + ctx->seg1_byte_len = seg1_byte_len; + return 0; +} + +static int vfs_raw_stream_open(void *stream_in, const char *fname) { + vfs_raw_context_t *stream = stream_in; + (void)fname; + stream->file_pos = 0; + return 0; +} + +static void vfs_raw_stream_close(void *stream_in) { + (void)stream_in; +} + +static int vfs_raw_stream_read(void *stream_in, uint8_t *buf, size_t len) { + vfs_raw_context_t *stream = stream_in; + size_t orig_len = len; + while (len) { + mboot_addr_t addr; + mboot_addr_t remain; + if (stream->file_pos < stream->seg0_byte_len) { + // Reading from segment 0. + mboot_addr_t seg0_pos = stream->file_pos; + addr = stream->seg0_base_addr + seg0_pos; + remain = stream->seg0_byte_len - seg0_pos; + } else { + // Reading from segment 1. + mboot_addr_t seg1_pos = stream->file_pos - stream->seg0_byte_len; + addr = stream->seg1_base_addr + seg1_pos; + remain = stream->seg1_byte_len - seg1_pos; + if (!remain) { + // At the end of segment 1. + break; + } + } + size_t l = MIN(len, remain); + hw_read(addr, l, buf); + stream->file_pos += l; + buf += l; + len -= l; + } + return orig_len - len; +} + +const stream_methods_t vfs_raw_stream_methods = { + vfs_raw_stream_open, + vfs_raw_stream_close, + vfs_raw_stream_read, +}; + +#endif // MBOOT_FSLOAD && MBOOT_VFS_RAW