summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien George <damien@micropython.org>2024-03-06 19:01:46 +1100
committerDamien George <damien@micropython.org>2024-03-19 11:38:36 +1100
commit9651046edd4ebf6e2141f889c6068f4ed565f13f (patch)
tree5b294f618bc30cfa2bd8ccebbb68bbad9624a86a
parent3c445f66369a49ba2dfa7e008d0a93c71fb1b6d3 (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-xports/stm32/mboot/Makefile1
-rw-r--r--ports/stm32/mboot/README.md16
-rw-r--r--ports/stm32/mboot/fsload.c59
-rw-r--r--ports/stm32/mboot/fwupdate.py50
-rw-r--r--ports/stm32/mboot/mboot.h26
-rw-r--r--ports/stm32/mboot/vfs.h18
-rw-r--r--ports/stm32/mboot/vfs_raw.c91
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