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>
This commit is contained in:
parent
3c445f6636
commit
9651046edd
@ -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 \
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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")
|
||||
)
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
91
ports/stm32/mboot/vfs_raw.c
Normal file
91
ports/stm32/mboot/vfs_raw.c
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user