Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions boards.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6146,6 +6146,7 @@ GenF4.menu.upload_method.dfuMethod=STM32CubeProgrammer (DFU)
GenF4.menu.upload_method.dfuMethod.upload.protocol=dfu
GenF4.menu.upload_method.dfuMethod.upload.options=-v {upload.vid} -p {upload.pid} -a {upload.address} -s {upload.start}
GenF4.menu.upload_method.dfuMethod.upload.tool=stm32CubeProg
GenF4.menu.upload_method.dfuMethod.build.bootloader_flags=-DBL_SYSTEM

GenF4.menu.upload_method.bmpMethod=BMP (Black Magic Probe)
GenF4.menu.upload_method.bmpMethod.upload.protocol=gdb_bmp
Expand Down
24 changes: 23 additions & 1 deletion cores/arduino/hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,26 @@ static void __empty_dtr_toggling(uint8_t *buf, uint32_t *len)
(void)len;
}
void dtr_togglingHook(uint8_t *buf, uint32_t *len) __attribute__((weak, alias("__empty_dtr_toggling")));
#endif
#endif

#if defined(USBCON) && defined(USBD_USE_CDC)
/**
* Empty cdc_1200bps_touch() hook.
*
* Called when the host closes the CDC port while the line coding is set to
* 1200 bps, which is the Arduino convention for asking the board to reboot
* into its bootloader.
*
* Its defined as a weak symbol and it can be redefined to implement the
* bootloader entry a given board needs.
*
* It is called from the USB control transfer callback, so it runs in handler
* mode. Branching to a bootloader from here leaves the core inside an
* exception that never returns; schedule the work and perform it from thread
* mode or after a reset instead.
*/
static void __empty_1200bps_touch(void)
{
}
void cdc_1200bps_touchHook(void) __attribute__((weak, alias("__empty_1200bps_touch")));
#endif /* USBCON && USBD_USE_CDC */
5 changes: 5 additions & 0 deletions cores/arduino/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,16 @@

#define ARDUINO_MAIN
#include "Arduino.h"
#include "bootloader.h"

// Force init to be called *first*, i.e. before static object allocation.
// Otherwise, statically allocated objects that need HAL may fail.
__attribute__((constructor(101))) void premain()
{
#if defined(BL_SYSTEM)
/* Before any clock or cache setup below, so the ROM gets a quiet part. */
jumpToSystemBootloaderIfRequested();
#endif

// Required by FreeRTOS, see http://www.freertos.org/RTOS-Cortex-M3-M4.html
#ifdef NVIC_PRIORITYGROUP_4
Expand Down
7 changes: 7 additions & 0 deletions libraries/SrcWrapper/inc/bootloader.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#ifndef _BOOTLOADER_H_
#define _BOOTLOADER_H_

#include <stdint.h>

/* Ensure DTR_TOGGLING_SEQ enabled */
#if defined(BL_LEGACY_LEAF) || defined(BL_HID)
#ifndef DTR_TOGGLING_SEQ
Expand All @@ -12,6 +14,11 @@
extern "C" {
#endif /* __cplusplus */

#if defined(BL_SYSTEM)
uint32_t systemBootloaderAddress(void);
void jumpToSystemBootloaderIfRequested(void);
#endif /* BL_SYSTEM */

#ifdef __cplusplus
}
#endif /* __cplusplus */
Expand Down
80 changes: 80 additions & 0 deletions libraries/SrcWrapper/src/stm32/bootloader.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "bootloader.h"

#include <stdbool.h>

#include "stm32_def.h"
#include "backup.h"

Expand Down Expand Up @@ -37,3 +39,81 @@ void dtr_togglingHook(uint8_t *buf, uint32_t *len)
}
}
#endif /* BL_HID */

#if defined(BL_SYSTEM)
/* Request to enter the ST system memory bootloader. It lives in .noinit so it
* survives the reset that carries it, and holds an arbitrary value after a
* power cycle, which is why it is only trusted after a software reset. A
* backup register would not do, some series have none. Low half counts
* attempts. */
static uint32_t bootloaderRequest __attribute__((section(".noinit")));

#define BOOTLOADER_REQUEST_MAGIC 0x5A5A0000
#define BOOTLOADER_REQUEST_MASK 0xFFFF0000

/* The ROM clocks USB from the HSE but does not know which crystal is fitted,
* so it measures one against the HSI and resets the part when that measurement
* misses, see AN2606 "HSE detected" -> no -> "Generate System reset". On a
* 12 MHz STM32F405 a single attempt reached DFU 5 times in 12, so the request
* survives the reset and is tried again. */
#define BOOTLOADER_MAX_ATTEMPTS 16

/* Where the bootloader's vector table lives, a stack pointer to load followed
* by an address to jump to. Remapping system memory to 0 keeps this free of
* per-series addresses. WEAK so a board can point it elsewhere. */
WEAK uint32_t systemBootloaderAddress(void)
{
#ifdef __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH
/* Remap system Flash memory at address 0x00000000 */
__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
return 0;
#else
#error "System memory address unknown for this series"
#endif
}

/* Called from premain(), before init() runs HAL_Init(), so the ROM gets the
* part close to reset state. It cannot be done from cdc_1200bps_touchHook():
* that runs in handler mode and the ROM would never leave the exception. */
WEAK void jumpToSystemBootloaderIfRequested(void)
{
uint32_t request = bootloaderRequest;
uint32_t attempts = request & ~BOOTLOADER_REQUEST_MASK;
bool requested = __HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST)
&& ((request & BOOTLOADER_REQUEST_MASK) == BOOTLOADER_REQUEST_MAGIC);

/* Clear it in every other case, so a power cycle always leaves the
* bootloader and a completed session does not re-enter it. */
bootloaderRequest = 0;

if (!requested || (attempts >= BOOTLOADER_MAX_ATTEMPTS)) {
return;
}
bootloaderRequest = BOOTLOADER_REQUEST_MAGIC | (attempts + 1);
__HAL_RCC_CLEAR_RESET_FLAGS();

uint32_t sys = systemBootloaderAddress();

/* Assembly to prevent modifying the stack pointer after loading it, and to
* ensure a jump rather than a call. */
asm volatile(
"ldr r0, [%[sys], #0] \n\t" // get address of stack pointer
"msr msp, r0 \n\t" // set stack pointer
"ldr r0, [%[sys], #4] \n\t" // get address of reset handler
"dsb \n\t" // data sync barrier
"isb \n\t" // instruction sync barrier
"bx r0 \n\t" // branch to bootloader
: : [sys] "l"(sys) : "r0"
);

__builtin_unreachable();
}

/* Host closed the CDC port at 1200 bps. Record the request and reset; a reset
* is safe from the control transfer callback where a branch is not. */
void cdc_1200bps_touchHook(void)
{
bootloaderRequest = BOOTLOADER_REQUEST_MAGIC;
NVIC_SystemReset();
}
#endif /* BL_SYSTEM */
7 changes: 7 additions & 0 deletions libraries/USBDevice/src/cdc/usbd_cdc_if.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ __IO bool rtsState = false;
__IO bool receivePended = true;
static uint32_t transmitStart = 0;

extern void cdc_1200bps_touchHook(void);

#ifdef DTR_TOGGLING_SEQ
/* DTR toggling sequence management */
extern void dtr_togglingHook(uint8_t *buf, uint32_t *len);
Expand Down Expand Up @@ -195,6 +197,11 @@ static int8_t USBD_CDC_Control(uint8_t cmd, uint8_t *pbuf, uint16_t length)
transmitStart = 0;
}
rtsState = (((USBD_SetupReqTypedef *)pbuf)->wValue & CLS_RTS);
/* Host closing the port at 1200 bps is the Arduino convention for
requesting a reboot into the bootloader. */
if (!dtrState && linecoding.bitrate == 1200) {
cdc_1200bps_touchHook();
}
#ifdef DTR_TOGGLING_SEQ
dtr_toggling++; /* Count DTR toggling */
#endif
Expand Down