stm32/mboot: Add support for loading gzip'd firmware from a filesystem.
This adds support to mboot to load and program application firmware from a .dfu.gz file on the board's filesystem. See mboot/README.md for details.
This commit is contained in:
parent
4daee31706
commit
ff04b78ffd
@ -54,6 +54,7 @@ CFLAGS += -I$(BOARD_DIR)
|
|||||||
CFLAGS += -DSTM32_HAL_H='<stm32$(MCU_SERIES)xx_hal.h>'
|
CFLAGS += -DSTM32_HAL_H='<stm32$(MCU_SERIES)xx_hal.h>'
|
||||||
CFLAGS += -DBOARD_$(BOARD)
|
CFLAGS += -DBOARD_$(BOARD)
|
||||||
CFLAGS += -DAPPLICATION_ADDR=$(TEXT0_ADDR)
|
CFLAGS += -DAPPLICATION_ADDR=$(TEXT0_ADDR)
|
||||||
|
CFLAGS += -DFFCONF_H=\"ports/stm32/mboot/ffconf.h\"
|
||||||
|
|
||||||
LDFLAGS = -nostdlib -L . -T stm32_generic.ld -Map=$(@:.elf=.map) --cref
|
LDFLAGS = -nostdlib -L . -T stm32_generic.ld -Map=$(@:.elf=.map) --cref
|
||||||
LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
|
LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
|
||||||
@ -70,12 +71,20 @@ else
|
|||||||
COPT += -Os -DNDEBUG
|
COPT += -Os -DNDEBUG
|
||||||
endif
|
endif
|
||||||
|
|
||||||
SRC_LIB = $(addprefix lib/,\
|
SRC_LIB = \
|
||||||
libc/string0.c \
|
lib/libc/string0.c \
|
||||||
)
|
lib/oofatfs/ff.c \
|
||||||
|
lib/oofatfs/option/unicode.c \
|
||||||
|
extmod/uzlib/crc32.c \
|
||||||
|
extmod/uzlib/adler32.c \
|
||||||
|
extmod/uzlib/tinflate.c \
|
||||||
|
extmod/uzlib/tinfgzip.c
|
||||||
|
|
||||||
SRC_C = \
|
SRC_C = \
|
||||||
main.c \
|
main.c \
|
||||||
|
elem.c \
|
||||||
|
fsload.c \
|
||||||
|
diskio.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 \
|
||||||
|
@ -4,7 +4,9 @@ Mboot - MicroPython boot loader
|
|||||||
Mboot is a custom bootloader for STM32 MCUs, and currently supports the
|
Mboot is a custom bootloader for STM32 MCUs, and currently supports the
|
||||||
STM32F4xx and STM32F7xx families. It can provide a standard USB DFU interface
|
STM32F4xx and STM32F7xx families. It can provide a standard USB DFU interface
|
||||||
on either the FS or HS peripherals, as well as a sophisticated, custom I2C
|
on either the FS or HS peripherals, as well as a sophisticated, custom I2C
|
||||||
interface. It fits in 16k of flash space.
|
interface. It can also load and program firmware in .dfu.gz format from a
|
||||||
|
filesystem. It can fit in 16k of flash space, but all features enabled requires
|
||||||
|
32k.
|
||||||
|
|
||||||
How to use
|
How to use
|
||||||
----------
|
----------
|
||||||
@ -57,6 +59,10 @@ How to use
|
|||||||
second one use the same configuration names as above but with
|
second one use the same configuration names as above but with
|
||||||
`SPIFLASH2`, ie `MBOOT_SPIFLASH2_ADDR` etc.
|
`SPIFLASH2`, ie `MBOOT_SPIFLASH2_ADDR` etc.
|
||||||
|
|
||||||
|
To enable loading firmware from a filesystem use:
|
||||||
|
|
||||||
|
#define MBOOT_FSLOAD (1)
|
||||||
|
|
||||||
2. Build the board's main application firmware as usual.
|
2. Build the board's main application firmware as usual.
|
||||||
|
|
||||||
3. Build mboot via:
|
3. Build mboot via:
|
||||||
@ -77,6 +83,53 @@ How to use
|
|||||||
to communicate with the I2C boot loader interface. It should be run on a
|
to communicate with the I2C boot loader interface. It should be run on a
|
||||||
pyboard connected via I2C to the target board.
|
pyboard connected via I2C to the target board.
|
||||||
|
|
||||||
|
Entering Mboot from application code
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
To enter Mboot from a running application do the following:
|
||||||
|
|
||||||
|
1. Make sure I and D caches are disabled.
|
||||||
|
|
||||||
|
2. Load register r0 with the value 0x70ad0000. The lower 7 bits can be
|
||||||
|
optionally or'd with the desired I2C address.
|
||||||
|
|
||||||
|
3. Load the MSP with the value held at 0x08000000.
|
||||||
|
|
||||||
|
4. Jump to the value held at 0x08000004.
|
||||||
|
|
||||||
|
Additional data can be passed to Mboot from application code by storing this
|
||||||
|
data in a special region of RAM. This region begins at the address held at
|
||||||
|
location 0x08000000 (which will point to just after Mboot's stack). A
|
||||||
|
maximum of 1024 bytes can be stored here. To indicate to Mboot that this
|
||||||
|
region is valid load register r0 with 0x70ad0080 (instead of step 2 above),
|
||||||
|
optionally or'd with the desired I2C address.
|
||||||
|
|
||||||
|
Data in this region is a sequence of elements. Each element has the form:
|
||||||
|
|
||||||
|
<type:u8> <len:u8> <payload...>
|
||||||
|
|
||||||
|
where `type` and `len` are bytes (designated by `u8`) and `payload` is 0 or
|
||||||
|
more bytes. `len` must be the number of bytes in `payload`.
|
||||||
|
|
||||||
|
The last element in the data sequence must be the end element:
|
||||||
|
|
||||||
|
* END: type=1, len=0
|
||||||
|
|
||||||
|
Loading firmware from a filesystem
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
To get Mboot to load firmware from a filesystem and automatically program it
|
||||||
|
requires passing data elements (see above) which tell where the filesystems
|
||||||
|
are located and what filename to program. The elements to use are:
|
||||||
|
|
||||||
|
* MOUNT: type=2, len=10, payload=(<mount-point:u8> <fs-type:u8> <base-addr:u32> <byte-len:u32>)
|
||||||
|
|
||||||
|
* FSLOAD: type=3, len=1+n, payload=(<mount-point:u8> <filename...>)
|
||||||
|
|
||||||
|
`u32` means unsigned 32-bit little-endian integer.
|
||||||
|
|
||||||
|
The firmware to load must be a gzip'd DfuSe file (.dfu.gz).
|
||||||
|
|
||||||
Example: Mboot on PYBv1.x
|
Example: Mboot on PYBv1.x
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
80
ports/stm32/mboot/diskio.c
Normal file
80
ports/stm32/mboot/diskio.c
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 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 "py/mphal.h"
|
||||||
|
#include "lib/oofatfs/ff.h"
|
||||||
|
#include "lib/oofatfs/diskio.h"
|
||||||
|
#include "mboot.h"
|
||||||
|
|
||||||
|
#if MBOOT_FSLOAD
|
||||||
|
|
||||||
|
#if _MAX_SS == _MIN_SS
|
||||||
|
#define SECSIZE (_MIN_SS)
|
||||||
|
#else
|
||||||
|
#error Unsupported
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DRESULT disk_read(void *pdrv, BYTE *buf, DWORD sector, UINT count) {
|
||||||
|
fsload_bdev_t *bdev = pdrv;
|
||||||
|
|
||||||
|
if (0 <= sector && sector < bdev->byte_len / 512) {
|
||||||
|
do_read(bdev->base_addr + sector * SECSIZE, count * SECSIZE, buf);
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RES_PARERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
DRESULT disk_ioctl(void *pdrv, BYTE cmd, void *buf) {
|
||||||
|
fsload_bdev_t *bdev = pdrv;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case CTRL_SYNC:
|
||||||
|
return RES_OK;
|
||||||
|
|
||||||
|
case GET_SECTOR_COUNT:
|
||||||
|
*((DWORD*)buf) = bdev->byte_len / SECSIZE;
|
||||||
|
return RES_OK;
|
||||||
|
|
||||||
|
case GET_SECTOR_SIZE:
|
||||||
|
*((WORD*)buf) = SECSIZE;
|
||||||
|
return RES_OK;
|
||||||
|
|
||||||
|
case GET_BLOCK_SIZE:
|
||||||
|
*((DWORD*)buf) = 1; // erase block size in units of sector size
|
||||||
|
return RES_OK;
|
||||||
|
|
||||||
|
case IOCTL_INIT:
|
||||||
|
case IOCTL_STATUS:
|
||||||
|
*((DSTATUS*)buf) = STA_PROTECT;
|
||||||
|
return RES_OK;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return RES_PARERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MBOOT_FSLOAD
|
44
ports/stm32/mboot/elem.c
Normal file
44
ports/stm32/mboot/elem.c
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 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"
|
||||||
|
|
||||||
|
// Elements are of the form: (type:u8, len:u8, payload)
|
||||||
|
|
||||||
|
const uint8_t *elem_search(const uint8_t *elem, uint8_t elem_id) {
|
||||||
|
while (elem + 2 + elem[1] <= ELEM_DATA_MAX) {
|
||||||
|
if (elem[0] == elem_id) {
|
||||||
|
// Found element, return a pointer to the element data
|
||||||
|
return elem + 2;
|
||||||
|
}
|
||||||
|
if (elem[0] == ELEM_TYPE_END) {
|
||||||
|
// End of elements
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
elem += 2 + elem[1];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
63
ports/stm32/mboot/ffconf.h
Normal file
63
ports/stm32/mboot/ffconf.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _FFCONF 68020
|
||||||
|
|
||||||
|
#define _FS_READONLY 1
|
||||||
|
#define _FS_MINIMIZE 0
|
||||||
|
#define _USE_STRFUNC 0
|
||||||
|
|
||||||
|
#define _USE_FIND 0
|
||||||
|
#define _USE_MKFS 0
|
||||||
|
#define _USE_FASTSEEK 0
|
||||||
|
#define _USE_EXPAND 0
|
||||||
|
#define _USE_CHMOD 0
|
||||||
|
#define _USE_LABEL 0
|
||||||
|
#define _USE_FORWARD 0
|
||||||
|
|
||||||
|
#define _CODE_PAGE 437
|
||||||
|
#define _USE_LFN 1
|
||||||
|
#define _MAX_LFN 255
|
||||||
|
#define _LFN_UNICODE 0
|
||||||
|
#define _STRF_ENCODE 3
|
||||||
|
#define _FS_RPATH 0
|
||||||
|
|
||||||
|
#define _VOLUMES 1
|
||||||
|
#define _STR_VOLUME_ID 0
|
||||||
|
#define _MULTI_PARTITION 0
|
||||||
|
#define _MIN_SS 512
|
||||||
|
#define _MAX_SS 512
|
||||||
|
#define _USE_TRIM 0
|
||||||
|
#define _FS_NOFSINFO 0
|
||||||
|
|
||||||
|
#define _FS_TINY 1
|
||||||
|
#define _FS_EXFAT 0
|
||||||
|
#define _FS_NORTC 1
|
||||||
|
#define _NORTC_MON 1
|
||||||
|
#define _NORTC_MDAY 1
|
||||||
|
#define _NORTC_YEAR 2019
|
||||||
|
#define _FS_LOCK 0
|
||||||
|
#define _FS_REENTRANT 0
|
291
ports/stm32/mboot/fsload.c
Normal file
291
ports/stm32/mboot/fsload.c
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 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 <string.h>
|
||||||
|
|
||||||
|
#include "py/mphal.h"
|
||||||
|
#include "lib/oofatfs/ff.h"
|
||||||
|
#include "extmod/uzlib/uzlib.h"
|
||||||
|
#include "mboot.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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse DFU
|
||||||
|
uint8_t buf[512];
|
||||||
|
size_t file_offset;
|
||||||
|
|
||||||
|
// Read file header, <5sBIB
|
||||||
|
res = gz_stream_read(11, buf);
|
||||||
|
if (res != 11) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
file_offset = 11;
|
||||||
|
|
||||||
|
// Validate header, version 1
|
||||||
|
if (memcmp(buf, "DfuSe\x01", 6) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must have only 1 target
|
||||||
|
if (buf[10] != 1) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get total size
|
||||||
|
uint32_t total_size = get_le32(buf + 6);
|
||||||
|
|
||||||
|
// Read target header, <6sBi255sII
|
||||||
|
res = gz_stream_read(274, buf);
|
||||||
|
if (res != 274) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
file_offset += 274;
|
||||||
|
|
||||||
|
// Validate target header, with alt being 0
|
||||||
|
if (memcmp(buf, "Target\x00", 7) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get target size and number of elements
|
||||||
|
uint32_t target_size = get_le32(buf + 266);
|
||||||
|
uint32_t num_elems = get_le32(buf + 270);
|
||||||
|
|
||||||
|
size_t file_offset_target = file_offset;
|
||||||
|
|
||||||
|
// Parse each element
|
||||||
|
for (size_t elem = 0; elem < num_elems; ++elem) {
|
||||||
|
// Read element header, <II
|
||||||
|
res = gz_stream_read(8, buf);
|
||||||
|
if (res != 8) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
file_offset += 8;
|
||||||
|
|
||||||
|
// Get element destination address and size
|
||||||
|
uint32_t elem_addr = get_le32(buf);
|
||||||
|
uint32_t elem_size = get_le32(buf + 4);
|
||||||
|
|
||||||
|
// Erase flash before writing
|
||||||
|
if (write_to_flash) {
|
||||||
|
uint32_t addr = elem_addr;
|
||||||
|
while (addr < elem_addr + elem_size) {
|
||||||
|
res = do_page_erase(addr, &addr);
|
||||||
|
if (res != 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read element data and possibly write to flash
|
||||||
|
for (uint32_t s = elem_size; s;) {
|
||||||
|
uint32_t l = s;
|
||||||
|
if (l > sizeof(buf)) {
|
||||||
|
l = sizeof(buf);
|
||||||
|
}
|
||||||
|
res = gz_stream_read(l, buf);
|
||||||
|
if (res != l) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (write_to_flash) {
|
||||||
|
res = do_write(elem_addr, buf, l);
|
||||||
|
if (res != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
elem_addr += l;
|
||||||
|
}
|
||||||
|
s -= l;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_offset += elem_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target_size != file_offset - file_offset_target) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total_size != file_offset) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read trailing info
|
||||||
|
res = gz_stream_read(16, buf);
|
||||||
|
if (res != 16) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO validate CRC32
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsload_process_fatfs(uint32_t base_addr, uint32_t byte_len, size_t fname_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
FF_DIR dp;
|
||||||
|
res = f_opendir(&fatfs, &dp, "/");
|
||||||
|
if (res != FR_OK) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for firmware file with correct name
|
||||||
|
int r;
|
||||||
|
for (;;) {
|
||||||
|
FILINFO fno;
|
||||||
|
res = f_readdir(&dp, &fno);
|
||||||
|
char *fn = fno.fname;
|
||||||
|
if (res != FR_OK || fn[0] == 0) {
|
||||||
|
// Finished listing dir, no firmware found
|
||||||
|
r = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (memcmp(fn, fname, fname_len) == 0 && fn[fname_len] == '\0') {
|
||||||
|
// Found firmware
|
||||||
|
led_state_all(2);
|
||||||
|
r = fsload_program_file(&fatfs, fn, false);
|
||||||
|
if (r == 0) {
|
||||||
|
// Firmware is valid, program it
|
||||||
|
led_state_all(4);
|
||||||
|
r = fsload_program_file(&fatfs, fn, true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fsload_process(void) {
|
||||||
|
const uint8_t *elem = elem_search(ELEM_DATA_START, ELEM_TYPE_FSLOAD);
|
||||||
|
if (elem == NULL || elem[-1] < 2) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mount_point = elem[0];
|
||||||
|
uint8_t fname_len = elem[-1] - 1;
|
||||||
|
const char *fname = (const char*)&elem[1];
|
||||||
|
|
||||||
|
elem = ELEM_DATA_START;
|
||||||
|
for (;;) {
|
||||||
|
elem = elem_search(elem, ELEM_TYPE_MOUNT);
|
||||||
|
if (elem == NULL || elem[-1] != 10) {
|
||||||
|
// End of elements, or invalid MOUNT element
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (elem[0] == mount_point) {
|
||||||
|
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_len, fname);
|
||||||
|
// Flash LEDs based on success/failure of update
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
if (ret == 0) {
|
||||||
|
led_state_all(7);
|
||||||
|
} else {
|
||||||
|
led_state_all(1);
|
||||||
|
}
|
||||||
|
mp_hal_delay_ms(100);
|
||||||
|
led_state_all(0);
|
||||||
|
mp_hal_delay_ms(100);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
// Unknown filesystem type
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
elem += elem[-1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MBOOT_FSLOAD
|
@ -32,6 +32,7 @@
|
|||||||
#include "usbd_core.h"
|
#include "usbd_core.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "i2cslave.h"
|
#include "i2cslave.h"
|
||||||
|
#include "mboot.h"
|
||||||
|
|
||||||
// Using polling is about 10% faster than not using it (and using IRQ instead)
|
// Using polling is about 10% faster than not using it (and using IRQ instead)
|
||||||
// This DFU code with polling runs in about 70% of the time of the ST bootloader
|
// This DFU code with polling runs in about 70% of the time of the ST bootloader
|
||||||
@ -76,7 +77,7 @@
|
|||||||
|
|
||||||
static void do_reset(void);
|
static void do_reset(void);
|
||||||
|
|
||||||
static uint32_t get_le32(const uint8_t *b) {
|
uint32_t get_le32(const uint8_t *b) {
|
||||||
return b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;
|
return b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -382,7 +383,7 @@ static const flash_layout_t flash_layout[] = {
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static uint32_t flash_get_sector_index(uint32_t addr) {
|
static uint32_t flash_get_sector_index(uint32_t addr, uint32_t *sector_size) {
|
||||||
if (addr >= flash_layout[0].base_address) {
|
if (addr >= flash_layout[0].base_address) {
|
||||||
uint32_t sector_index = 0;
|
uint32_t sector_index = 0;
|
||||||
for (int i = 0; i < MP_ARRAY_SIZE(flash_layout); ++i) {
|
for (int i = 0; i < MP_ARRAY_SIZE(flash_layout); ++i) {
|
||||||
@ -390,6 +391,7 @@ static uint32_t flash_get_sector_index(uint32_t addr) {
|
|||||||
uint32_t sector_start_next = flash_layout[i].base_address
|
uint32_t sector_start_next = flash_layout[i].base_address
|
||||||
+ (j + 1) * flash_layout[i].sector_size;
|
+ (j + 1) * flash_layout[i].sector_size;
|
||||||
if (addr < sector_start_next) {
|
if (addr < sector_start_next) {
|
||||||
|
*sector_size = flash_layout[i].sector_size;
|
||||||
return sector_index;
|
return sector_index;
|
||||||
}
|
}
|
||||||
++sector_index;
|
++sector_index;
|
||||||
@ -404,13 +406,16 @@ static int flash_mass_erase(void) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int flash_page_erase(uint32_t addr) {
|
static int flash_page_erase(uint32_t addr, uint32_t *next_addr) {
|
||||||
uint32_t sector = flash_get_sector_index(addr);
|
uint32_t sector_size = 0;
|
||||||
|
uint32_t sector = flash_get_sector_index(addr, §or_size);
|
||||||
if (sector == 0) {
|
if (sector == 0) {
|
||||||
// Don't allow to erase the sector with this bootloader in it
|
// Don't allow to erase the sector with this bootloader in it
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*next_addr = addr + sector_size;
|
||||||
|
|
||||||
HAL_FLASH_Unlock();
|
HAL_FLASH_Unlock();
|
||||||
|
|
||||||
// Clear pending flags (if any)
|
// Clear pending flags (if any)
|
||||||
@ -484,11 +489,12 @@ static int spiflash_page_erase(mp_spiflash_t *spif, uint32_t addr, uint32_t n_bl
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int do_page_erase(uint32_t addr) {
|
int do_page_erase(uint32_t addr, uint32_t *next_addr) {
|
||||||
led_state(LED0, 1);
|
led_state(LED0, 1);
|
||||||
|
|
||||||
#if defined(MBOOT_SPIFLASH_ADDR)
|
#if defined(MBOOT_SPIFLASH_ADDR)
|
||||||
if (MBOOT_SPIFLASH_ADDR <= addr && addr < MBOOT_SPIFLASH_ADDR + MBOOT_SPIFLASH_BYTE_SIZE) {
|
if (MBOOT_SPIFLASH_ADDR <= addr && addr < MBOOT_SPIFLASH_ADDR + MBOOT_SPIFLASH_BYTE_SIZE) {
|
||||||
|
*next_addr = addr + MBOOT_SPIFLASH_ERASE_BLOCKS_PER_PAGE * MP_SPIFLASH_ERASE_BLOCK_SIZE;
|
||||||
return spiflash_page_erase(MBOOT_SPIFLASH_SPIFLASH,
|
return spiflash_page_erase(MBOOT_SPIFLASH_SPIFLASH,
|
||||||
addr - MBOOT_SPIFLASH_ADDR, MBOOT_SPIFLASH_ERASE_BLOCKS_PER_PAGE);
|
addr - MBOOT_SPIFLASH_ADDR, MBOOT_SPIFLASH_ERASE_BLOCKS_PER_PAGE);
|
||||||
}
|
}
|
||||||
@ -496,15 +502,16 @@ static int do_page_erase(uint32_t addr) {
|
|||||||
|
|
||||||
#if defined(MBOOT_SPIFLASH2_ADDR)
|
#if defined(MBOOT_SPIFLASH2_ADDR)
|
||||||
if (MBOOT_SPIFLASH2_ADDR <= addr && addr < MBOOT_SPIFLASH2_ADDR + MBOOT_SPIFLASH2_BYTE_SIZE) {
|
if (MBOOT_SPIFLASH2_ADDR <= addr && addr < MBOOT_SPIFLASH2_ADDR + MBOOT_SPIFLASH2_BYTE_SIZE) {
|
||||||
|
*next_addr = addr + MBOOT_SPIFLASH2_ERASE_BLOCKS_PER_PAGE * MP_SPIFLASH_ERASE_BLOCK_SIZE;
|
||||||
return spiflash_page_erase(MBOOT_SPIFLASH2_SPIFLASH,
|
return spiflash_page_erase(MBOOT_SPIFLASH2_SPIFLASH,
|
||||||
addr - MBOOT_SPIFLASH2_ADDR, MBOOT_SPIFLASH2_ERASE_BLOCKS_PER_PAGE);
|
addr - MBOOT_SPIFLASH2_ADDR, MBOOT_SPIFLASH2_ERASE_BLOCKS_PER_PAGE);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return flash_page_erase(addr);
|
return flash_page_erase(addr, next_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_read(uint32_t addr, int len, uint8_t *buf) {
|
void do_read(uint32_t addr, int len, uint8_t *buf) {
|
||||||
#if defined(MBOOT_SPIFLASH_ADDR)
|
#if defined(MBOOT_SPIFLASH_ADDR)
|
||||||
if (MBOOT_SPIFLASH_ADDR <= addr && addr < MBOOT_SPIFLASH_ADDR + MBOOT_SPIFLASH_BYTE_SIZE) {
|
if (MBOOT_SPIFLASH_ADDR <= addr && addr < MBOOT_SPIFLASH_ADDR + MBOOT_SPIFLASH_BYTE_SIZE) {
|
||||||
mp_spiflash_read(MBOOT_SPIFLASH_SPIFLASH, addr - MBOOT_SPIFLASH_ADDR, len, buf);
|
mp_spiflash_read(MBOOT_SPIFLASH_SPIFLASH, addr - MBOOT_SPIFLASH_ADDR, len, buf);
|
||||||
@ -522,7 +529,7 @@ static void do_read(uint32_t addr, int len, uint8_t *buf) {
|
|||||||
memcpy(buf, (void*)addr, len);
|
memcpy(buf, (void*)addr, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_write(uint32_t addr, const uint8_t *src8, size_t len) {
|
int do_write(uint32_t addr, const uint8_t *src8, size_t len) {
|
||||||
static uint32_t led_tog = 0;
|
static uint32_t led_tog = 0;
|
||||||
led_state(LED0, (led_tog++) & 4);
|
led_state(LED0, (led_tog++) & 4);
|
||||||
|
|
||||||
@ -634,7 +641,8 @@ void i2c_slave_process_rx_end(void) {
|
|||||||
} else if (buf[0] == I2C_CMD_MASSERASE && len == 0) {
|
} else if (buf[0] == I2C_CMD_MASSERASE && len == 0) {
|
||||||
len = do_mass_erase();
|
len = do_mass_erase();
|
||||||
} else if (buf[0] == I2C_CMD_PAGEERASE && len == 4) {
|
} else if (buf[0] == I2C_CMD_PAGEERASE && len == 4) {
|
||||||
len = do_page_erase(get_le32(buf + 1));
|
uint32_t next_addr;
|
||||||
|
len = do_page_erase(get_le32(buf + 1), &next_addr);
|
||||||
} else if (buf[0] == I2C_CMD_SETRDADDR && len == 4) {
|
} else if (buf[0] == I2C_CMD_SETRDADDR && len == 4) {
|
||||||
i2c_obj.cmd_rdaddr = get_le32(buf + 1);
|
i2c_obj.cmd_rdaddr = get_le32(buf + 1);
|
||||||
len = 0;
|
len = 0;
|
||||||
@ -764,7 +772,8 @@ static int dfu_process_dnload(void) {
|
|||||||
ret = do_mass_erase();
|
ret = do_mass_erase();
|
||||||
} else if (dfu_state.wLength == 5) {
|
} else if (dfu_state.wLength == 5) {
|
||||||
// erase page
|
// erase page
|
||||||
ret = do_page_erase(get_le32(&dfu_state.buf[1]));
|
uint32_t next_addr;
|
||||||
|
ret = do_page_erase(get_le32(&dfu_state.buf[1]), &next_addr);
|
||||||
}
|
}
|
||||||
} else if (dfu_state.wLength >= 1 && dfu_state.buf[0] == 0x21) {
|
} else if (dfu_state.wLength >= 1 && dfu_state.buf[0] == 0x21) {
|
||||||
if (dfu_state.wLength == 5) {
|
if (dfu_state.wLength == 5) {
|
||||||
@ -1255,6 +1264,19 @@ enter_bootloader:
|
|||||||
mp_spiflash_init(MBOOT_SPIFLASH2_SPIFLASH);
|
mp_spiflash_init(MBOOT_SPIFLASH2_SPIFLASH);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if MBOOT_FSLOAD
|
||||||
|
if ((initial_r0 & 0xffffff80) == 0x70ad0080) {
|
||||||
|
// Application passed through elements, validate then process them
|
||||||
|
const uint8_t *elem_end = elem_search(ELEM_DATA_START, ELEM_TYPE_END);
|
||||||
|
if (elem_end != NULL && elem_end[-1] == 0) {
|
||||||
|
fsload_process();
|
||||||
|
}
|
||||||
|
// Always reset because the application is expecting to resume
|
||||||
|
led_state_all(0);
|
||||||
|
NVIC_SystemReset();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
dfu_init();
|
dfu_init();
|
||||||
|
|
||||||
pyb_usbdd_init(&pyb_usbdd, pyb_usbdd_detect_port());
|
pyb_usbdd_init(&pyb_usbdd, pyb_usbdd_detect_port());
|
||||||
|
58
ports/stm32/mboot/mboot.h
Normal file
58
ports/stm32/mboot/mboot.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 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 <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#define ELEM_DATA_START (&_estack)
|
||||||
|
#define ELEM_DATA_MAX (ELEM_DATA_START + 1024)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ELEM_TYPE_END = 1,
|
||||||
|
ELEM_TYPE_MOUNT,
|
||||||
|
ELEM_TYPE_FSLOAD,
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
uint32_t get_le32(const uint8_t *b);
|
||||||
|
void led_state_all(unsigned int mask);
|
||||||
|
|
||||||
|
int do_page_erase(uint32_t addr, uint32_t *next_addr);
|
||||||
|
void do_read(uint32_t addr, int len, uint8_t *buf);
|
||||||
|
int do_write(uint32_t addr, const uint8_t *src8, size_t len);
|
||||||
|
|
||||||
|
const uint8_t *elem_search(const uint8_t *elem, uint8_t elem_id);
|
||||||
|
int fsload_process(void);
|
@ -5,8 +5,8 @@
|
|||||||
/* Specify the memory areas */
|
/* Specify the memory areas */
|
||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 (can be 32K) */
|
FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 32K /* sector 0 (can be 32K) */
|
||||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K
|
||||||
}
|
}
|
||||||
|
|
||||||
/* produce a link error if there is not this amount of RAM for these sections */
|
/* produce a link error if there is not this amount of RAM for these sections */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user