Skip to content

Commit

Permalink
stm32/mboot: Leave bootloader from thread mode, not from IRQ.
Browse files Browse the repository at this point in the history
Leaving the bootloader from an IRQ (eg USB or I2C IRQ) will not work if
MBOOT_LEAVE_BOOTLOADER_VIA_RESET is disabled, ie if mboot jumps directly to
the application.  This is because the CPU will still be in IRQ state when
the application starts and IRQs of lower priority will be blocked.

Fix this by setting a flag when the bootloader should finish, and exit the
bootloader always from the main (top level) thread.

This also improves the USB behaviour of mboot: it no longer abruptly
disconnects when the manifest command is sent.

Signed-off-by: Damien George <damien@micropython.org>
  • Loading branch information
dpgeorge committed May 20, 2021
1 parent 748339b commit ea81bcf
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 17 deletions.
2 changes: 2 additions & 0 deletions ports/stm32/mboot/dfu.h
Expand Up @@ -26,6 +26,7 @@
#ifndef MICROPY_INCLUDED_STM32_MBOOT_DFU_H
#define MICROPY_INCLUDED_STM32_MBOOT_DFU_H

#include <stdbool.h>
#include <stdint.h>

// DFU spec: https://www.usb.org/sites/default/files/DFU_1.1.pdf
Expand Down Expand Up @@ -106,6 +107,7 @@ typedef struct _dfu_state_t {
dfu_cmd_t cmd;
dfu_status_t status;
uint8_t error;
bool leave_dfu;
uint16_t wBlockNum;
uint16_t wLength;
uint32_t addr;
Expand Down
33 changes: 16 additions & 17 deletions ports/stm32/mboot/main.c
Expand Up @@ -106,8 +106,6 @@ static volatile uint32_t systick_ms;
// Global dfu state
dfu_context_t dfu_context SECTION_NOZERO_BSS;

static void do_reset(void);

uint32_t get_le32(const uint8_t *b) {
return b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;
}
Expand Down Expand Up @@ -771,7 +769,7 @@ void i2c_slave_process_rx_end(i2c_slave_t *i2c) {
memcpy(buf + 12 + sizeof(MICROPY_HW_MCU_NAME), MICROPY_HW_BOARD_NAME, sizeof(MICROPY_HW_BOARD_NAME) - 1);
len = 12 + sizeof(MICROPY_HW_MCU_NAME) + sizeof(MICROPY_HW_BOARD_NAME) - 1;
} else if (buf[0] == I2C_CMD_RESET && len == 0) {
do_reset();
dfu_context.leave_dfu = true;
} else if (buf[0] == I2C_CMD_GETLAYOUT && len == 0) {
len = strlen(FLASH_LAYOUT_STR);
memcpy(buf, FLASH_LAYOUT_STR, len);
Expand Down Expand Up @@ -864,6 +862,7 @@ static void dfu_init(void) {
dfu_context.cmd = DFU_CMD_NONE;
dfu_context.status = DFU_STATUS_OK;
dfu_context.error = 0;
dfu_context.leave_dfu = false;
dfu_context.addr = 0x08000000;
}

Expand Down Expand Up @@ -931,7 +930,8 @@ static void dfu_handle_rx(int cmd, int arg, int len, const void *buf) {

static void dfu_process(void) {
if (dfu_context.state == DFU_STATE_MANIFEST) {
do_reset();
// Set a flag to leave DFU mode from the main thread (here we are in an IRQ handler).
dfu_context.leave_dfu = true;
}

if (dfu_context.state == DFU_STATE_BUSY) {
Expand Down Expand Up @@ -1412,17 +1412,6 @@ static void leave_bootloader(void) {
NVIC_SystemReset();
}

static void do_reset(void) {
led_state_all(0);
mp_hal_delay_ms(50);
pyb_usbdd_shutdown();
#if defined(MBOOT_I2C_SCL)
i2c_slave_shutdown(MBOOT_I2Cx, I2Cx_EV_IRQn);
#endif
mp_hal_delay_ms(50);
leave_bootloader();
}

extern PCD_HandleTypeDef pcd_fs_handle;
extern PCD_HandleTypeDef pcd_hs_handle;

Expand Down Expand Up @@ -1561,7 +1550,7 @@ void stm32_main(int initial_r0) {
#if MBOOT_USB_RESET_ON_DISCONNECT
bool has_connected = false;
#endif
for (;;) {
while (!dfu_context.leave_dfu) {
#if USE_USB_POLLING
#if MBOOT_USB_AUTODETECT_PORT || MICROPY_HW_USB_MAIN_DEV == USB_PHY_FS_ID
if (USB_OTG_FS->GINTSTS & USB_OTG_FS->GINTMSK) {
Expand All @@ -1585,10 +1574,20 @@ void stm32_main(int initial_r0) {
has_connected = true;
}
if (has_connected && pyb_usbdd.hUSBDDevice.dev_state == USBD_STATE_SUSPENDED) {
do_reset();
break;
}
#endif
}

// Shutdown and leave the bootloader.
led_state_all(0);
mp_hal_delay_ms(50);
pyb_usbdd_shutdown();
#if defined(MBOOT_I2C_SCL)
i2c_slave_shutdown(MBOOT_I2Cx, I2Cx_EV_IRQn);
#endif
mp_hal_delay_ms(50);
leave_bootloader();
}

void NMI_Handler(void) {
Expand Down

0 comments on commit ea81bcf

Please sign in to comment.