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 += -DBOARD_$(BOARD)
|
||||
CFLAGS += -DAPPLICATION_ADDR=$(TEXT0_ADDR)
|
||||
CFLAGS += -DFFCONF_H=\"ports/stm32/mboot/ffconf.h\"
|
||||
|
||||
LDFLAGS = -nostdlib -L . -T stm32_generic.ld -Map=$(@:.elf=.map) --cref
|
||||
LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
|
||||
@ -70,12 +71,20 @@ else
|
||||
COPT += -Os -DNDEBUG
|
||||
endif
|
||||
|
||||
SRC_LIB = $(addprefix lib/,\
|
||||
libc/string0.c \
|
||||
)
|
||||
SRC_LIB = \
|
||||
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 = \
|
||||
main.c \
|
||||
elem.c \
|
||||
fsload.c \
|
||||
diskio.c \
|
||||
drivers/bus/softspi.c \
|
||||
drivers/bus/softqspi.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
|
||||
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
|
||||
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
|
||||
----------
|
||||
@ -57,6 +59,10 @@ How to use
|
||||
second one use the same configuration names as above but with
|
||||
`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.
|
||||
|
||||
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
|
||||
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
|
||||
-------------------------
|
||||
|
||||
|
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 "storage.h"
|
||||
#include "i2cslave.h"
|
||||
#include "mboot.h"
|
||||
|
||||
// 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
|
||||
@ -76,7 +77,7 @@
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -382,7 +383,7 @@ static const flash_layout_t flash_layout[] = {
|
||||
|
||||
#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) {
|
||||
uint32_t sector_index = 0;
|
||||
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
|
||||
+ (j + 1) * flash_layout[i].sector_size;
|
||||
if (addr < sector_start_next) {
|
||||
*sector_size = flash_layout[i].sector_size;
|
||||
return sector_index;
|
||||
}
|
||||
++sector_index;
|
||||
@ -404,13 +406,16 @@ static int flash_mass_erase(void) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int flash_page_erase(uint32_t addr) {
|
||||
uint32_t sector = flash_get_sector_index(addr);
|
||||
static int flash_page_erase(uint32_t addr, uint32_t *next_addr) {
|
||||
uint32_t sector_size = 0;
|
||||
uint32_t sector = flash_get_sector_index(addr, §or_size);
|
||||
if (sector == 0) {
|
||||
// Don't allow to erase the sector with this bootloader in it
|
||||
return -1;
|
||||
}
|
||||
|
||||
*next_addr = addr + sector_size;
|
||||
|
||||
HAL_FLASH_Unlock();
|
||||
|
||||
// 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
|
||||
|
||||
static int do_page_erase(uint32_t addr) {
|
||||
int do_page_erase(uint32_t addr, uint32_t *next_addr) {
|
||||
led_state(LED0, 1);
|
||||
|
||||
#if defined(MBOOT_SPIFLASH_ADDR)
|
||||
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,
|
||||
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 (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,
|
||||
addr - MBOOT_SPIFLASH2_ADDR, MBOOT_SPIFLASH2_ERASE_BLOCKS_PER_PAGE);
|
||||
}
|
||||
#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 (MBOOT_SPIFLASH_ADDR <= addr && addr < MBOOT_SPIFLASH_ADDR + MBOOT_SPIFLASH_BYTE_SIZE) {
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
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) {
|
||||
len = do_mass_erase();
|
||||
} 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) {
|
||||
i2c_obj.cmd_rdaddr = get_le32(buf + 1);
|
||||
len = 0;
|
||||
@ -764,7 +772,8 @@ static int dfu_process_dnload(void) {
|
||||
ret = do_mass_erase();
|
||||
} else if (dfu_state.wLength == 5) {
|
||||
// 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) {
|
||||
if (dfu_state.wLength == 5) {
|
||||
@ -1255,6 +1264,19 @@ enter_bootloader:
|
||||
mp_spiflash_init(MBOOT_SPIFLASH2_SPIFLASH);
|
||||
#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();
|
||||
|
||||
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 */
|
||||
MEMORY
|
||||
{
|
||||
FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 (can be 32K) */
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K
|
||||
FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 32K /* sector 0 (can be 32K) */
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K
|
||||
}
|
||||
|
||||
/* produce a link error if there is not this amount of RAM for these sections */
|
||||
|
Loading…
Reference in New Issue
Block a user