stm32: Enter bootloader via a system reset.

Entering a bootloader (ST system bootloader, or custom mboot) from software
by directly branching to it is not reliable, and the reliability of it
working can depend on the peripherals that were enabled by the application
code.  It's also not possible to branch to a bootloader if the WDT is
enabled (unless the bootloader has specific provisions to feed the WDT).

This patch changes the way a bootloader is entered from software by first
doing a complete system reset, then branching to the desired bootloader
early on in the start-up process.  The top two words of RAM (of the stack)
are reserved to store flags indicating that the bootloader should be
entered after a reset.
This commit is contained in:
Damien George 2019-06-22 21:26:03 +10:00
parent 205c6d0dc9
commit 04c7cdb668
27 changed files with 91 additions and 51 deletions

View File

@ -92,6 +92,7 @@ CFLAGS += -fsingle-precision-constant -Wdouble-promotion
endif endif
LDFLAGS = -nostdlib -L $(LD_DIR) $(addprefix -T,$(LD_FILES)) -Map=$(@:.elf=.map) --cref LDFLAGS = -nostdlib -L $(LD_DIR) $(addprefix -T,$(LD_FILES)) -Map=$(@:.elf=.map) --cref
LDFLAGS += --defsym=_estack_reserve=8
LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
# Remove uncalled code from the final image. # Remove uncalled code from the final image.

View File

@ -31,7 +31,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 16K; _sstack = _estack - 16K;
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -30,7 +30,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 24K; _sstack = _estack - 24K;
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -29,7 +29,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 32K; /* tunable */ _sstack = _estack - 32K; /* tunable */
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -16,7 +16,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte above last byte /* Define the stack. The stack is full descending so begins just above last byte above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 6K; /* tunable */ _sstack = _estack - 6K; /* tunable */
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -18,7 +18,7 @@ _minimum_heap_size = 16K; /* tunable */
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 16K; _sstack = _estack - 16K;
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -18,7 +18,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 16K; /* tunable */ _sstack = _estack - 16K; /* tunable */
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -19,7 +19,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 16K; /* tunable */ _sstack = _estack - 16K; /* tunable */
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -18,7 +18,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 16K; /* tunable */ _sstack = _estack - 16K; /* tunable */
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -21,7 +21,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 16K; /* tunable */ _sstack = _estack - 16K; /* tunable */
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -21,7 +21,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 16K; /* tunable */ _sstack = _estack - 16K; /* tunable */
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -19,7 +19,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 16K; /* tunable */ _sstack = _estack - 16K; /* tunable */
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -20,7 +20,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 16K; /* tunable */ _sstack = _estack - 16K; /* tunable */
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -17,7 +17,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 32K; /* tunable */ _sstack = _estack - 32K; /* tunable */
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -19,7 +19,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 16K; /* tunable */ _sstack = _estack - 16K; /* tunable */
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -20,7 +20,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 32K; /* tunable */ _sstack = _estack - 32K; /* tunable */
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -19,7 +19,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 32K; /* tunable */ _sstack = _estack - 32K; /* tunable */
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -19,7 +19,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 16K; /* tunable */ _sstack = _estack - 16K; /* tunable */
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -17,7 +17,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 6K; /* tunable */ _sstack = _estack - 6K; /* tunable */
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -20,7 +20,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 16K; /* tunable */ _sstack = _estack - 16K; /* tunable */
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -20,7 +20,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
_sstack = _estack - 16K; /* tunable */ _sstack = _estack - 16K; /* tunable */
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -20,7 +20,7 @@ _minimum_heap_size = 16K;
/* Define the stack. The stack is full descending so begins just above last byte /* Define the stack. The stack is full descending so begins just above last byte
of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
_estack = ORIGIN(RAM) + LENGTH(RAM) + LENGTH(SRAM2); _estack = ORIGIN(RAM) + LENGTH(RAM) + LENGTH(SRAM2) - _estack_reserve;
_sstack = _estack - 206K; /* tunable */ _sstack = _estack - 206K; /* tunable */
/* RAM extents for the garbage collector */ /* RAM extents for the garbage collector */

View File

@ -45,6 +45,7 @@
#include "systick.h" #include "systick.h"
#include "pendsv.h" #include "pendsv.h"
#include "powerctrl.h"
#include "pybthread.h" #include "pybthread.h"
#include "gccollect.h" #include "gccollect.h"
#include "factoryreset.h" #include "factoryreset.h"
@ -368,6 +369,9 @@ STATIC uint update_reset_mode(uint reset_mode) {
#endif #endif
void stm32_main(uint32_t reset_mode) { void stm32_main(uint32_t reset_mode) {
// Check if bootloader should be entered instead of main application
powerctrl_check_enter_bootloader();
// Enable caches and prefetch buffers // Enable caches and prefetch buffers
#if defined(STM32F4) #if defined(STM32F4)

View File

@ -237,7 +237,7 @@ MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id);
// Resets the pyboard in a manner similar to pushing the external RESET button. // Resets the pyboard in a manner similar to pushing the external RESET button.
STATIC mp_obj_t machine_reset(void) { STATIC mp_obj_t machine_reset(void) {
NVIC_SystemReset(); powerctrl_mcu_reset();
return mp_const_none; return mp_const_none;
} }
MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_obj, machine_reset); MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_obj, machine_reset);
@ -248,15 +248,6 @@ STATIC mp_obj_t machine_soft_reset(void) {
} }
MP_DEFINE_CONST_FUN_OBJ_0(machine_soft_reset_obj, machine_soft_reset); MP_DEFINE_CONST_FUN_OBJ_0(machine_soft_reset_obj, machine_soft_reset);
__attribute__((naked)) void branch_to_bootloader(uint32_t r0, uint32_t addr) {
__asm volatile (
"ldr r2, [r1, #0]\n" // get address of stack pointer
"msr msp, r2\n" // get stack pointer
"ldr r2, [r1, #4]\n" // get address of destination
"bx r2\n" // branch to bootloader
);
}
// Activate the bootloader without BOOT* pins. // Activate the bootloader without BOOT* pins.
STATIC NORETURN mp_obj_t machine_bootloader(size_t n_args, const mp_obj_t *args) { STATIC NORETURN mp_obj_t machine_bootloader(size_t n_args, const mp_obj_t *args) {
#if MICROPY_HW_ENABLE_USB #if MICROPY_HW_ENABLE_USB
@ -266,24 +257,10 @@ STATIC NORETURN mp_obj_t machine_bootloader(size_t n_args, const mp_obj_t *args)
storage_flush(); storage_flush();
#endif #endif
#if __DCACHE_PRESENT == 1
// Flush and disable caches before turning off peripherals (eg SDRAM)
SCB_DisableICache();
SCB_DisableDCache();
#endif
HAL_RCC_DeInit();
HAL_DeInit();
#if (__MPU_PRESENT == 1)
// MPU must be disabled for bootloader to function correctly
HAL_MPU_Disable();
#endif
#if MICROPY_HW_USES_BOOTLOADER #if MICROPY_HW_USES_BOOTLOADER
if (n_args == 0 || !mp_obj_is_true(args[0])) { if (n_args == 0 || !mp_obj_is_true(args[0])) {
// By default, with no args given, we enter the custom bootloader (mboot) // By default, with no args given, we enter the custom bootloader (mboot)
branch_to_bootloader(0x70ad0000, 0x08000000); powerctrl_enter_bootloader(0x70ad0000, 0x08000000);
} }
if (n_args == 1 && mp_obj_is_str_or_bytes(args[0])) { if (n_args == 1 && mp_obj_is_str_or_bytes(args[0])) {
@ -292,15 +269,14 @@ STATIC NORETURN mp_obj_t machine_bootloader(size_t n_args, const mp_obj_t *args)
const char *data = mp_obj_str_get_data(args[0], &len); const char *data = mp_obj_str_get_data(args[0], &len);
void *mboot_region = (void*)*((volatile uint32_t*)0x08000000); void *mboot_region = (void*)*((volatile uint32_t*)0x08000000);
memmove(mboot_region, data, len); memmove(mboot_region, data, len);
branch_to_bootloader(0x70ad0080, 0x08000000); powerctrl_enter_bootloader(0x70ad0080, 0x08000000);
} }
#endif #endif
#if defined(STM32F7) || defined(STM32H7) #if defined(STM32F7) || defined(STM32H7)
branch_to_bootloader(0, 0x1ff00000); powerctrl_enter_bootloader(0, 0x1ff00000);
#else #else
__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH(); powerctrl_enter_bootloader(0, 0x00000000);
branch_to_bootloader(0, 0x00000000);
#endif #endif
while (1); while (1);

View File

@ -30,6 +30,60 @@
#include "rtc.h" #include "rtc.h"
#include "genhdr/pllfreqtable.h" #include "genhdr/pllfreqtable.h"
#if defined(STM32H7)
#define RCC_SR RSR
#define RCC_SR_SFTRSTF RCC_RSR_SFTRSTF
#define RCC_SR_RMVF RCC_RSR_RMVF
#else
#define RCC_SR CSR
#define RCC_SR_SFTRSTF RCC_CSR_SFTRSTF
#define RCC_SR_RMVF RCC_CSR_RMVF
#endif
// Location in RAM of bootloader state (just after the top of the stack)
extern uint32_t _estack[];
#define BL_STATE ((uint32_t*)&_estack)
NORETURN void powerctrl_mcu_reset(void) {
BL_STATE[1] = 1; // invalidate bootloader address
#if __DCACHE_PRESENT == 1
SCB_CleanDCache();
#endif
NVIC_SystemReset();
}
NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) {
BL_STATE[0] = r0;
BL_STATE[1] = bl_addr;
#if __DCACHE_PRESENT == 1
SCB_CleanDCache();
#endif
NVIC_SystemReset();
}
static __attribute__((naked)) void branch_to_bootloader(uint32_t r0, uint32_t bl_addr) {
__asm volatile (
"ldr r2, [r1, #0]\n" // get address of stack pointer
"msr msp, r2\n" // get stack pointer
"ldr r2, [r1, #4]\n" // get address of destination
"bx r2\n" // branch to bootloader
);
}
void powerctrl_check_enter_bootloader(void) {
uint32_t bl_addr = BL_STATE[1];
BL_STATE[1] = 1; // invalidate bootloader address
if ((bl_addr & 0xfff) == 0 && (RCC->RCC_SR & RCC_SR_SFTRSTF)) {
// Reset by NVIC_SystemReset with bootloader data set -> branch to bootloader
RCC->RCC_SR = RCC_SR_RMVF;
#if defined(STM32F0) || defined(STM32F4) || defined(STM32L4)
__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
#endif
uint32_t r0 = BL_STATE[0];
branch_to_bootloader(r0, bl_addr);
}
}
#if !defined(STM32F0) #if !defined(STM32F0)
// Assumes that PLL is used as the SYSCLK source // Assumes that PLL is used as the SYSCLK source

View File

@ -28,6 +28,10 @@
#include <stdint.h> #include <stdint.h>
NORETURN void powerctrl_mcu_reset(void);
NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr);
void powerctrl_check_enter_bootloader(void);
int powerctrl_rcc_clock_config_pll(RCC_ClkInitTypeDef *rcc_init, uint32_t sysclk_mhz, bool need_pllsai); int powerctrl_rcc_clock_config_pll(RCC_ClkInitTypeDef *rcc_init, uint32_t sysclk_mhz, bool need_pllsai);
int powerctrl_set_sysclk(uint32_t sysclk, uint32_t ahb, uint32_t apb1, uint32_t apb2); int powerctrl_set_sysclk(uint32_t sysclk, uint32_t ahb, uint32_t apb1, uint32_t apb2);
void powerctrl_enter_stop_mode(void); void powerctrl_enter_stop_mode(void);

View File

@ -72,6 +72,7 @@
#include "stm32_it.h" #include "stm32_it.h"
#include "pendsv.h" #include "pendsv.h"
#include "irq.h" #include "irq.h"
#include "powerctrl.h"
#include "pybthread.h" #include "pybthread.h"
#include "gccollect.h" #include "gccollect.h"
#include "extint.h" #include "extint.h"
@ -144,7 +145,7 @@ int pyb_hard_fault_debug = 0;
void HardFault_C_Handler(ExceptionRegisters_t *regs) { void HardFault_C_Handler(ExceptionRegisters_t *regs) {
if (!pyb_hard_fault_debug) { if (!pyb_hard_fault_debug) {
NVIC_SystemReset(); powerctrl_mcu_reset();
} }
#if MICROPY_HW_ENABLE_USB #if MICROPY_HW_ENABLE_USB