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>
pull/14042/head
Damien George 2024-03-06 19:01:46 +11:00
rodzic 3c445f6636
commit 9651046edd
7 zmienionych plików z 232 dodań i 29 usunięć

Wyświetl plik

@ -120,6 +120,7 @@ SRC_C += \
ui.c \ ui.c \
vfs_fat.c \ vfs_fat.c \
vfs_lfs.c \ vfs_lfs.c \
vfs_raw.c \
drivers/bus/softspi.c \ drivers/bus/softspi.c \
drivers/bus/softqspi.c \ drivers/bus/softqspi.c \
drivers/memory/spiflash.c \ drivers/memory/spiflash.c \

Wyświetl plik

@ -71,13 +71,23 @@ How to use
#define MBOOT_FSLOAD (1) #define MBOOT_FSLOAD (1)
and then enable one or more of the following depending on what filesystem 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 support is required in Mboot:
quite compact, but littlefs supports both read and write so is rather
large):
#define MBOOT_VFS_FAT (1) #define MBOOT_VFS_FAT (1)
#define MBOOT_VFS_LFS1 (1) #define MBOOT_VFS_LFS1 (1)
#define MBOOT_VFS_LFS2 (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. 2. Build the board's main application firmware as usual.

Wyświetl plik

@ -39,7 +39,7 @@
#if MBOOT_FSLOAD #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 #error Must enable at least one VFS component
#endif #endif
@ -234,28 +234,51 @@ int fsload_process(void) {
// End of elements. // End of elements.
return -MBOOT_ERRNO_FSLOAD_NO_MOUNT; 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 base_addr;
mboot_addr_t byte_len; mboot_addr_t byte_len;
uint32_t block_size = MBOOT_FSLOAD_DEFAULT_BLOCK_SIZE; mboot_addr_t arg2 = 0;
if (elem[-1] == 10 || elem[-1] == 14) { 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. // 32-bit base and length given, extract them.
base_addr = get_le32(&elem[2]); base_addr = get_le32(&elem[2]);
byte_len = get_le32(&elem[6]); byte_len = get_le32(&elem[6]);
if (elem[-1] == 14) { if (elem_len >= 14) {
// Block size given, extract it. // Argument 2 given, extract it.
block_size = get_le32(&elem[10]); arg2 = get_le32(&elem[10]);
if (elem_len == 18) {
// Argument 3 given, extract it.
arg3 = get_le32(&elem[14]);
}
} }
#if MBOOT_ADDRESS_SPACE_64BIT #if MBOOT_ADDRESS_SPACE_64BIT
} else if (elem[-1] == 22) { } else if (elem_len == 22 || elem_len == 34) {
// 64-bit base and length given, and block size, so extract them. // 64-bit base and length given, so extract them.
base_addr = get_le64(&elem[2]); base_addr = get_le64(&elem[2]);
byte_len = get_le64(&elem[10]); 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 #endif
} else { } else {
// Invalid MOUNT element. // Invalid MOUNT element.
return -MBOOT_ERRNO_FSLOAD_INVALID_MOUNT; return -MBOOT_ERRNO_FSLOAD_INVALID_MOUNT;
} }
if (elem[0] == mount_point) { if (elem[0] == mount_point) {
int ret; int ret;
union { union {
@ -268,27 +291,43 @@ int fsload_process(void) {
#if MBOOT_VFS_LFS2 #if MBOOT_VFS_LFS2
vfs_lfs2_context_t lfs2; vfs_lfs2_context_t lfs2;
#endif #endif
#if MBOOT_VFS_RAW
vfs_raw_context_t raw;
#endif
} ctx; } ctx;
const stream_methods_t *methods; const stream_methods_t *methods;
#if MBOOT_VFS_FAT #if MBOOT_VFS_FAT
if (elem[1] == ELEM_MOUNT_FAT) { if (elem[1] == ELEM_MOUNT_FAT) {
(void)block_size;
ret = vfs_fat_mount(&ctx.fat, base_addr, byte_len); ret = vfs_fat_mount(&ctx.fat, base_addr, byte_len);
methods = &vfs_fat_stream_methods; methods = &vfs_fat_stream_methods;
} else } else
#endif #endif
#if MBOOT_VFS_LFS1 #if MBOOT_VFS_LFS1
if (elem[1] == ELEM_MOUNT_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); ret = vfs_lfs1_mount(&ctx.lfs1, base_addr, byte_len, block_size);
methods = &vfs_lfs1_stream_methods; methods = &vfs_lfs1_stream_methods;
} else } else
#endif #endif
#if MBOOT_VFS_LFS2 #if MBOOT_VFS_LFS2
if (elem[1] == ELEM_MOUNT_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); ret = vfs_lfs2_mount(&ctx.lfs2, base_addr, byte_len, block_size);
methods = &vfs_lfs2_stream_methods; methods = &vfs_lfs2_stream_methods;
} else } else
#endif #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 // Unknown filesystem type
return -MBOOT_ERRNO_FSLOAD_INVALID_MOUNT; return -MBOOT_ERRNO_FSLOAD_INVALID_MOUNT;

Wyświetl plik

@ -9,6 +9,7 @@ import deflate, machine, stm
VFS_FAT = 1 VFS_FAT = 1
VFS_LFS1 = 2 VFS_LFS1 = 2
VFS_LFS2 = 3 VFS_LFS2 = 3
VFS_RAW = 4
# Constants for creating mboot elements. # Constants for creating mboot elements.
_ELEM_TYPE_END = const(1) _ELEM_TYPE_END = const(1)
@ -226,8 +227,18 @@ def _create_element(kind, body):
def update_app_elements( 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,
): ):
if fs_type != VFS_RAW:
# Check firmware is of .dfu or .dfu.gz type # Check firmware is of .dfu or .dfu.gz type
try: try:
with open(filename, "rb") as f: with open(filename, "rb") as f:
@ -243,6 +254,13 @@ def update_app_elements(
raise Exception("littlefs requires fs_blocksize parameter") raise Exception("littlefs requires fs_blocksize parameter")
mount_point = 1 mount_point = 1
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" mount_encoding = "<BBQQL" if addr_64bit else "<BBLLL"
elems = _create_element( elems = _create_element(
_ELEM_TYPE_MOUNT, _ELEM_TYPE_MOUNT,

Wyświetl plik

@ -92,6 +92,31 @@
#define MBOOT_LED_STATE_LED2 (0x04) #define MBOOT_LED_STATE_LED2 (0x04)
#define MBOOT_LED_STATE_LED3 (0x08) #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 // 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 // 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 // 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_FAT = 1,
ELEM_MOUNT_LFS1, ELEM_MOUNT_LFS1,
ELEM_MOUNT_LFS2, ELEM_MOUNT_LFS2,
ELEM_MOUNT_RAW,
}; };
// Configure the type used to hold an address in the mboot address space. // Configure the type used to hold an address in the mboot address space.

Wyświetl plik

@ -93,4 +93,22 @@ int vfs_lfs2_mount(vfs_lfs2_context_t *ctx, mboot_addr_t base_addr, mboot_addr_t
#endif #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 #endif // MICROPY_INCLUDED_STM32_MBOOT_VFS_H

Wyświetl plik

@ -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