stm32/main: Catch and report corrupted lfs filesystem at startup.

On stm32, the startup code attempts to mount the configured filesystem.  If
there is an existing littlefs filesystem that's suitable corrupted it's
possible for the reported blocksize to be incorrect here:

     uint32_t block_size = lfs2_fromle32(superblock->block_size);

This `block_size` (which is read from the filesystem iteself) is used to
create the len argument passed to `pyb_flash_make_new()`.  In that function
the len arg is validated to be a mutliple of the underlying hardware block
size, as well as not bigger than the physical flash.  Any failure is raised
as a ValueError.  This exception is not caught currently in main, it flows
up to the high level assert / startup failure.

As this occurs before `boot.py` is run, the users (potentially frozen)
application code doesn't have any opportunity to detect and handle the
issue.

This commit adds a helper function which attempts to create a block device,
and on error returns `None` instead of raising an exception.  Using this in
main means that a potentially corrupt filesystem will simply remain
unmounted, and the application can handle the issue safely.

The fix here also handles the case where the littlefs filesystem is valid
but the autodetection code (which detects the filesystem size) does not
work correctly.  In that case it will retry mounting the filesystem using
the whole size of the block device.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
This commit is contained in:
Andrew Leech
2024-01-24 09:08:29 +11:00
committed by Damien George
parent f96417dbf2
commit b33b9f8121
3 changed files with 41 additions and 22 deletions

View File

@@ -185,8 +185,15 @@ MP_NOINLINE static bool init_flash_fs(uint reset_mode) {
if (len != -1) {
// Detected a littlefs filesystem so create correct block device for it
mp_obj_t args[] = { MP_OBJ_NEW_QSTR(MP_QSTR_len), MP_OBJ_NEW_SMALL_INT(len) };
bdev = MP_OBJ_TYPE_GET_SLOT(&pyb_flash_type, make_new)(&pyb_flash_type, 0, 1, args);
mp_obj_t lfs_bdev = pyb_flash_new_obj(0, len);
if (lfs_bdev == mp_const_none) {
// Invalid len detected, filesystem header block likely corrupted.
// Create bdev with default size to attempt to mount the whole flash or
// let user attempt recovery if desired.
bdev = pyb_flash_new_obj(0, -1);
} else {
bdev = lfs_bdev;
}
}
#endif

View File

@@ -273,6 +273,29 @@ const pyb_flash_obj_t pyb_flash_obj = {
0, // actual size handled in ioctl, MP_BLOCKDEV_IOCTL_BLOCK_COUNT case
};
mp_obj_t pyb_flash_new_obj(mp_int_t start, mp_int_t len) {
uint32_t bl_len = (storage_get_block_count() - FLASH_PART1_START_BLOCK) * FLASH_BLOCK_SIZE;
if (start == -1) {
start = 0;
} else if (!(0 <= start && start < bl_len && start % MICROPY_HW_BDEV_BLOCKSIZE_EXT == 0)) {
return mp_const_none;
}
if (len == -1) {
len = bl_len - start;
} else if (!(0 < len && start + len <= bl_len && len % MICROPY_HW_BDEV_BLOCKSIZE_EXT == 0)) {
return mp_const_none;
}
pyb_flash_obj_t *self = mp_obj_malloc(pyb_flash_obj_t, &pyb_flash_type);
self->use_native_block_size = false;
self->start = start;
self->len = len;
return MP_OBJ_FROM_PTR(self);
}
static void pyb_flash_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
pyb_flash_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (self == &pyb_flash_obj) {
@@ -296,30 +319,15 @@ static mp_obj_t pyb_flash_make_new(const mp_obj_type_t *type, size_t n_args, siz
// Default singleton object that accesses entire flash, including virtual partition table
return MP_OBJ_FROM_PTR(&pyb_flash_obj);
}
pyb_flash_obj_t *self = mp_obj_malloc(pyb_flash_obj_t, &pyb_flash_type);
self->use_native_block_size = false;
uint32_t bl_len = (storage_get_block_count() - FLASH_PART1_START_BLOCK) * FLASH_BLOCK_SIZE;
mp_int_t start = args[ARG_start].u_int;
if (start == -1) {
start = 0;
} else if (!(0 <= start && start < bl_len && start % MICROPY_HW_BDEV_BLOCKSIZE_EXT == 0)) {
mp_raise_ValueError(NULL);
}
mp_int_t len = args[ARG_len].u_int;
if (len == -1) {
len = bl_len - start;
} else if (!(0 < len && start + len <= bl_len && len % MICROPY_HW_BDEV_BLOCKSIZE_EXT == 0)) {
mp_obj_t self = pyb_flash_new_obj(start, len);
if (self == mp_const_none) {
// Invalid start or end arg
mp_raise_ValueError(NULL);
}
self->start = start;
self->len = len;
return MP_OBJ_FROM_PTR(self);
return self;
}
static mp_obj_t pyb_flash_readblocks(size_t n_args, const mp_obj_t *args) {

View File

@@ -77,4 +77,8 @@ extern const struct _pyb_flash_obj_t pyb_flash_obj;
struct _fs_user_mount_t;
void pyb_flash_init_vfs(struct _fs_user_mount_t *vfs);
#if !BUILDING_MBOOT
mp_obj_t pyb_flash_new_obj(mp_int_t start, mp_int_t len);
#endif
#endif // MICROPY_INCLUDED_STM32_STORAGE_H