diff options
| author | Damien George <damien@micropython.org> | 2024-03-06 19:01:46 +1100 |
|---|---|---|
| committer | Damien George <damien@micropython.org> | 2024-03-19 11:38:36 +1100 |
| commit | 9651046edd4ebf6e2141f889c6068f4ed565f13f (patch) | |
| tree | 5b294f618bc30cfa2bd8ccebbb68bbad9624a86a | |
| parent | 3c445f66369a49ba2dfa7e008d0a93c71fb1b6d3 (diff) | |
stm32/mboot: Add support for a raw filesystem.
This is enabled by default if MBOOT_FSLOAD is enabled, although a board
can explicitly disable it by `#define MBOOT_VFS_RAW (0)`.
Signed-off-by: Damien George <damien@micropython.org>
| -rwxr-xr-x | ports/stm32/mboot/Makefile | 1 | ||||
| -rw-r--r-- | ports/stm32/mboot/README.md | 16 | ||||
| -rw-r--r-- | ports/stm32/mboot/fsload.c | 59 | ||||
| -rw-r--r-- | ports/stm32/mboot/fwupdate.py | 50 | ||||
| -rw-r--r-- | ports/stm32/mboot/mboot.h | 26 | ||||
| -rw-r--r-- | ports/stm32/mboot/vfs.h | 18 | ||||
| -rw-r--r-- | ports/stm32/mboot/vfs_raw.c | 91 |
7 files changed, 232 insertions, 29 deletions
diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index 389f9f5d0..07053b329 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 73e32fced..221e3a7c3 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 abc3514ed..83905eb49 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 47ceb19ba..8578ff4fc 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 = "<BBQQL" if addr_64bit else "<BBLLL" - elems = _create_element( - _ELEM_TYPE_MOUNT, - struct.pack(mount_encoding, mount_point, fs_type, fs_base, fs_len, fs_blocksize), - ) + if fs_type == VFS_RAW: + mount_encoding = "<BBQQQQ" if addr_64bit else "<BBLLLL" + elems = _create_element( + _ELEM_TYPE_MOUNT, + struct.pack(mount_encoding, mount_point, fs_type, fs_base, fs_len, fs_base2, fs_len2), + ) + else: + mount_encoding = "<BBQQL" if addr_64bit else "<BBLLL" + elems = _create_element( + _ELEM_TYPE_MOUNT, + struct.pack(mount_encoding, mount_point, fs_type, fs_base, fs_len, fs_blocksize), + ) elems += _create_element( _ELEM_TYPE_FSLOAD, struct.pack("<B", mount_point) + bytes(filename, "ascii") ) diff --git a/ports/stm32/mboot/mboot.h b/ports/stm32/mboot/mboot.h index 2c4813332..1fabff008 100644 --- a/ports/stm32/mboot/mboot.h +++ b/ports/stm32/mboot/mboot.h @@ -92,6 +92,31 @@ #define MBOOT_LED_STATE_LED2 (0x04) #define MBOOT_LED_STATE_LED3 (0x08) +// Whether to support loading firmware from a filesystem. +#ifndef MBOOT_FSLOAD +#define MBOOT_FSLOAD (0) +#endif + +// Whether to support FAT filesystems. +#ifndef MBOOT_VFS_FAT +#define MBOOT_VFS_FAT (0) +#endif + +// Whether to support Littlefs v1 filesystems. +#ifndef MBOOT_VFS_LFS1 +#define MBOOT_VFS_LFS1 (0) +#endif + +// Whether to support Littlefs v2 filesystems. +#ifndef MBOOT_VFS_LFS2 +#define MBOOT_VFS_LFS2 (0) +#endif + +// Whether to support raw filesystems. +#ifndef MBOOT_VFS_RAW +#define MBOOT_VFS_RAW (MBOOT_FSLOAD) +#endif + // These enum values are passed as the first argument to mboot_state_change() to // notify of a change in state of the bootloader activity. This function has a // default implementation in ui.c but can be overridden by a board. Some states @@ -158,6 +183,7 @@ enum { ELEM_MOUNT_FAT = 1, ELEM_MOUNT_LFS1, ELEM_MOUNT_LFS2, + ELEM_MOUNT_RAW, }; // Configure the type used to hold an address in the mboot address space. diff --git a/ports/stm32/mboot/vfs.h b/ports/stm32/mboot/vfs.h index 53dac8800..a2e13b158 100644 --- a/ports/stm32/mboot/vfs.h +++ b/ports/stm32/mboot/vfs.h @@ -93,4 +93,22 @@ int vfs_lfs2_mount(vfs_lfs2_context_t *ctx, mboot_addr_t base_addr, mboot_addr_t #endif +#if MBOOT_VFS_RAW + +// A raw VFS contains a contiguous, single file without any metadata. + +typedef struct _vfs_raw_context_t { + mboot_addr_t seg0_base_addr; + mboot_addr_t seg0_byte_len; + mboot_addr_t seg1_base_addr; + mboot_addr_t seg1_byte_len; + mboot_addr_t file_pos; +} vfs_raw_context_t; + +extern const stream_methods_t vfs_raw_stream_methods; + +int vfs_raw_mount(vfs_raw_context_t *ctx, mboot_addr_t seg0_base_addr, mboot_addr_t seg0_byte_len, mboot_addr_t seg1_base_addr, mboot_addr_t seg1_byte_len); + +#endif + #endif // MICROPY_INCLUDED_STM32_MBOOT_VFS_H diff --git a/ports/stm32/mboot/vfs_raw.c b/ports/stm32/mboot/vfs_raw.c new file mode 100644 index 000000000..05539c695 --- /dev/null +++ b/ports/stm32/mboot/vfs_raw.c @@ -0,0 +1,91 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "mboot.h" +#include "vfs.h" + +#if MBOOT_FSLOAD && MBOOT_VFS_RAW + +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#endif + +int vfs_raw_mount(vfs_raw_context_t *ctx, mboot_addr_t seg0_base_addr, mboot_addr_t seg0_byte_len, mboot_addr_t seg1_base_addr, mboot_addr_t seg1_byte_len) { + ctx->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 |
