From 390f32922d81b56495198d3d93b7258f450c03b6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 23 Jun 2020 22:57:16 +1000 Subject: [PATCH] stm32/mboot: Decouple stream, filesystem and top-level loading code. This commit factors the code for files and streaming to separate source files (vfs_fat.c and gzstream.c respectively) and introduces an abstract gzstream interface to make it easier to plug in different filesystems. Signed-off-by: Damien George --- ports/stm32/mboot/Makefile | 3 +- ports/stm32/mboot/fsload.c | 110 +++++----------------- ports/stm32/mboot/gzstream.c | 94 ++++++++++++++++++ ports/stm32/mboot/gzstream.h | 45 +++++++++ ports/stm32/mboot/mboot.h | 5 - ports/stm32/mboot/vfs.h | 43 +++++++++ ports/stm32/mboot/{diskio.c => vfs_fat.c} | 54 +++++++++-- 7 files changed, 256 insertions(+), 98 deletions(-) create mode 100644 ports/stm32/mboot/gzstream.c create mode 100644 ports/stm32/mboot/gzstream.h create mode 100644 ports/stm32/mboot/vfs.h rename ports/stm32/mboot/{diskio.c => vfs_fat.c} (60%) diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index a72b752ac9..9ab186412e 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -102,7 +102,8 @@ SRC_C = \ main.c \ elem.c \ fsload.c \ - diskio.c \ + gzstream.c \ + vfs_fat.c \ drivers/bus/softspi.c \ drivers/bus/softqspi.c \ drivers/memory/spiflash.c \ diff --git a/ports/stm32/mboot/fsload.c b/ports/stm32/mboot/fsload.c index 12f39c65b3..dc40a28ea0 100644 --- a/ports/stm32/mboot/fsload.c +++ b/ports/stm32/mboot/fsload.c @@ -27,81 +27,18 @@ #include #include "py/mphal.h" -#include "lib/oofatfs/ff.h" -#include "extmod/uzlib/uzlib.h" #include "mboot.h" +#include "vfs.h" #if MBOOT_FSLOAD -#define DICT_SIZE (1 << 15) - -typedef struct _gz_stream_t { - FIL fp; - TINF_DATA tinf; - uint8_t buf[512]; - uint8_t dict[DICT_SIZE]; -} gz_stream_t; - -static gz_stream_t gz_stream SECTION_NOZERO_BSS; - -static int gz_stream_read_src(TINF_DATA *tinf) { - UINT n; - FRESULT res = f_read(&gz_stream.fp, gz_stream.buf, sizeof(gz_stream.buf), &n); - if (res != FR_OK) { - return -1; - } - if (n == 0) { - return -1; - } - tinf->source = gz_stream.buf + 1; - tinf->source_limit = gz_stream.buf + n; - return gz_stream.buf[0]; -} - -static int gz_stream_open(FATFS *fatfs, const char *filename) { - FRESULT res = f_open(fatfs, &gz_stream.fp, filename, FA_READ); - if (res != FR_OK) { - return -1; - } - memset(&gz_stream.tinf, 0, sizeof(gz_stream.tinf)); - gz_stream.tinf.readSource = gz_stream_read_src; - - int st = uzlib_gzip_parse_header(&gz_stream.tinf); - if (st != TINF_OK) { - f_close(&gz_stream.fp); - return -1; - } - - uzlib_uncompress_init(&gz_stream.tinf, gz_stream.dict, DICT_SIZE); - - return 0; -} - -static int gz_stream_read(size_t len, uint8_t *buf) { - gz_stream.tinf.dest = buf; - gz_stream.tinf.dest_limit = buf + len; - int st = uzlib_uncompress_chksum(&gz_stream.tinf); - if (st == TINF_DONE) { - return 0; - } - if (st < 0) { - return st; - } - return gz_stream.tinf.dest - buf; -} - -static int fsload_program_file(FATFS *fatfs, const char *filename, bool write_to_flash) { - int res = gz_stream_open(fatfs, filename); - if (res != 0) { - return res; - } - +static int fsload_program_file(bool write_to_flash) { // Parse DFU uint8_t buf[512]; size_t file_offset; // Read file header, <5sBIB - res = gz_stream_read(11, buf); + int res = gz_stream_read(11, buf); if (res != 11) { return -1; } @@ -204,26 +141,23 @@ static int fsload_program_file(FATFS *fatfs, const char *filename, bool write_to return 0; } -static int fsload_process_fatfs(uint32_t base_addr, uint32_t byte_len, const char *fname) { - fsload_bdev_t bdev = {base_addr, byte_len}; - FATFS fatfs; - fatfs.drv = &bdev; - FRESULT res = f_mount(&fatfs); - if (res != FR_OK) { - return -1; +static int fsload_validate_and_program_file(void *stream, const stream_methods_t *meth, const char *fname) { + // First pass verifies the file, second pass programs it + for (unsigned int pass = 0; pass <= 1; ++pass) { + led_state_all(pass == 0 ? 2 : 4); + int res = meth->open(stream, fname); + if (res == 0) { + res = gz_stream_init(stream, meth->read); + if (res == 0) { + res = fsload_program_file(pass == 0 ? false : true); + } + } + meth->close(stream); + if (res != 0) { + return res; + } } - - // Validate firmware - led_state_all(2); - int r = fsload_program_file(&fatfs, fname, false); - - if (r == 0) { - // Firmware is valid, program it - led_state_all(4); - r = fsload_program_file(&fatfs, fname, true); - } - - return r; + return 0; } int fsload_process(void) { @@ -250,7 +184,11 @@ int fsload_process(void) { uint32_t base_addr = get_le32(&elem[2]); uint32_t byte_len = get_le32(&elem[6]); if (elem[1] == ELEM_MOUNT_FAT) { - int ret = fsload_process_fatfs(base_addr, byte_len, fname); + vfs_fat_context_t ctx; + int ret = vfs_fat_mount(&ctx, base_addr, byte_len); + if (ret == 0) { + ret = fsload_validate_and_program_file(&ctx, &vfs_fat_stream_methods, fname); + } // Flash LEDs based on success/failure of update for (int i = 0; i < 4; ++i) { if (ret == 0) { diff --git a/ports/stm32/mboot/gzstream.c b/ports/stm32/mboot/gzstream.c new file mode 100644 index 0000000000..20ba33045f --- /dev/null +++ b/ports/stm32/mboot/gzstream.c @@ -0,0 +1,94 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2020 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 + +#include "py/mphal.h" +#include "extmod/uzlib/uzlib.h" +#include "gzstream.h" +#include "mboot.h" + +#if MBOOT_FSLOAD + +#define DICT_SIZE (1 << 15) + +typedef struct _gz_stream_t { + void *stream_data; + stream_read_t stream_read; + TINF_DATA tinf; + uint8_t buf[512]; + uint8_t dict[DICT_SIZE]; +} gz_stream_t; + +static gz_stream_t gz_stream SECTION_NOZERO_BSS; + +static int gz_stream_read_src(TINF_DATA *tinf) { + int n = gz_stream.stream_read(gz_stream.stream_data, gz_stream.buf, sizeof(gz_stream.buf)); + if (n < 0) { + // Stream error + return -1; + } + if (n == 0) { + // No data / EOF + return -1; + } + + tinf->source = gz_stream.buf + 1; + tinf->source_limit = gz_stream.buf + n; + return gz_stream.buf[0]; +} + +int gz_stream_init(void *stream_data, stream_read_t stream_read) { + gz_stream.stream_data = stream_data; + gz_stream.stream_read = stream_read; + + memset(&gz_stream.tinf, 0, sizeof(gz_stream.tinf)); + gz_stream.tinf.readSource = gz_stream_read_src; + + int st = uzlib_gzip_parse_header(&gz_stream.tinf); + if (st != TINF_OK) { + return -1; + } + + uzlib_uncompress_init(&gz_stream.tinf, gz_stream.dict, DICT_SIZE); + + return 0; +} + +int gz_stream_read(size_t len, uint8_t *buf) { + gz_stream.tinf.dest = buf; + gz_stream.tinf.dest_limit = buf + len; + int st = uzlib_uncompress_chksum(&gz_stream.tinf); + if (st == TINF_DONE) { + return 0; + } + if (st < 0) { + return st; + } + return gz_stream.tinf.dest - buf; +} + +#endif // MBOOT_FSLOAD diff --git a/ports/stm32/mboot/gzstream.h b/ports/stm32/mboot/gzstream.h new file mode 100644 index 0000000000..ec11ba79bb --- /dev/null +++ b/ports/stm32/mboot/gzstream.h @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2020 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. + */ +#ifndef MICROPY_INCLUDED_STM32_MBOOT_GZSTREAM_H +#define MICROPY_INCLUDED_STM32_MBOOT_GZSTREAM_H + +#include +#include + +typedef int (*stream_open_t)(void *stream, const char *fname); +typedef void (*stream_close_t)(void *stream); +typedef int (*stream_read_t)(void *stream, uint8_t *buf, size_t len); + +typedef struct _stream_methods_t { + stream_open_t open; + stream_close_t close; + stream_read_t read; +} stream_methods_t; + +int gz_stream_init(void *stream_data, stream_read_t stream_read); +int gz_stream_read(size_t len, uint8_t *buf); + +#endif // MICROPY_INCLUDED_STM32_MBOOT_GZSTREAM_H diff --git a/ports/stm32/mboot/mboot.h b/ports/stm32/mboot/mboot.h index 7dc1ada0c3..ef9af7854b 100644 --- a/ports/stm32/mboot/mboot.h +++ b/ports/stm32/mboot/mboot.h @@ -44,11 +44,6 @@ enum { ELEM_MOUNT_FAT = 1, }; -typedef struct _fsload_bdev_t { - uint32_t base_addr; - uint32_t byte_len; -} fsload_bdev_t; - extern uint8_t _estack[ELEM_DATA_SIZE]; uint32_t get_le32(const uint8_t *b); diff --git a/ports/stm32/mboot/vfs.h b/ports/stm32/mboot/vfs.h new file mode 100644 index 0000000000..9ee1c5ff3c --- /dev/null +++ b/ports/stm32/mboot/vfs.h @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2020 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. + */ +#ifndef MICROPY_INCLUDED_STM32_MBOOT_VFS_H +#define MICROPY_INCLUDED_STM32_MBOOT_VFS_H + +#include "lib/oofatfs/ff.h" +#include "gzstream.h" + +typedef struct _vfs_fat_context_t { + uint32_t bdev_base_addr; + uint32_t bdev_byte_len; + FATFS fatfs; + FIL fp; +} vfs_fat_context_t; + +extern const stream_methods_t vfs_fat_stream_methods; + +int vfs_fat_mount(vfs_fat_context_t *ctx, uint32_t base_addr, uint32_t byte_len); + +#endif // MICROPY_INCLUDED_STM32_MBOOT_VFS_H diff --git a/ports/stm32/mboot/diskio.c b/ports/stm32/mboot/vfs_fat.c similarity index 60% rename from ports/stm32/mboot/diskio.c rename to ports/stm32/mboot/vfs_fat.c index 5f68f26a8e..20994c8b4c 100644 --- a/ports/stm32/mboot/diskio.c +++ b/ports/stm32/mboot/vfs_fat.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2019 Damien P. George + * Copyright (c) 2019-2020 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 @@ -28,6 +28,7 @@ #include "lib/oofatfs/ff.h" #include "lib/oofatfs/diskio.h" #include "mboot.h" +#include "vfs.h" #if MBOOT_FSLOAD @@ -38,10 +39,10 @@ #endif DRESULT disk_read(void *pdrv, BYTE *buf, DWORD sector, UINT count) { - fsload_bdev_t *bdev = pdrv; + vfs_fat_context_t *ctx = pdrv; - if (0 <= sector && sector < bdev->byte_len / 512) { - do_read(bdev->base_addr + sector * SECSIZE, count * SECSIZE, buf); + if (0 <= sector && sector < ctx->bdev_byte_len / 512) { + do_read(ctx->bdev_base_addr + sector * SECSIZE, count * SECSIZE, buf); return RES_OK; } @@ -49,14 +50,14 @@ DRESULT disk_read(void *pdrv, BYTE *buf, DWORD sector, UINT count) { } DRESULT disk_ioctl(void *pdrv, BYTE cmd, void *buf) { - fsload_bdev_t *bdev = pdrv; + vfs_fat_context_t *ctx = pdrv; switch (cmd) { case CTRL_SYNC: return RES_OK; case GET_SECTOR_COUNT: - *((DWORD*)buf) = bdev->byte_len / SECSIZE; + *((DWORD*)buf) = ctx->bdev_byte_len / SECSIZE; return RES_OK; case GET_SECTOR_SIZE: @@ -77,4 +78,45 @@ DRESULT disk_ioctl(void *pdrv, BYTE cmd, void *buf) { } } +int vfs_fat_mount(vfs_fat_context_t *ctx, uint32_t base_addr, uint32_t byte_len) { + ctx->bdev_base_addr = base_addr; + ctx->bdev_byte_len = byte_len; + ctx->fatfs.drv = ctx; + FRESULT res = f_mount(&ctx->fatfs); + if (res != FR_OK) { + return -1; + } + return 0; +} + +static int vfs_fat_stream_open(void *stream_in, const char *fname) { + vfs_fat_context_t *stream = stream_in; + FRESULT res = f_open(&stream->fatfs, &stream->fp, fname, FA_READ); + if (res != FR_OK) { + return -1; + } + return 0; +} + +static void vfs_fat_stream_close(void *stream_in) { + vfs_fat_context_t *stream = stream_in; + f_close(&stream->fp); +} + +static int vfs_fat_stream_read(void *stream_in, uint8_t *buf, size_t len) { + vfs_fat_context_t *stream = stream_in; + UINT n; + FRESULT res = f_read(&stream->fp, buf, len, &n); + if (res != FR_OK) { + return -1; + } + return n; +} + +const stream_methods_t vfs_fat_stream_methods = { + vfs_fat_stream_open, + vfs_fat_stream_close, + vfs_fat_stream_read, +}; + #endif // MBOOT_FSLOAD