Skip to content
Merged
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
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ To build other targets, you can override the
|`MAPLEMINI` | LeafLabs Maple Mini board and clone derivatives | http://wiki.stm32duino.com/index.php?title=Maple_Mini |
|`STLINK` | STLink/v2 hardware clones | https://wiki.paparazziuav.org/wiki/STLink#Clones |

For each of the above targets, there are three variants that can be added to the target name:

| Target Variant | Description |
| -------------- | ----------------------------------------------------- |
|` ` | Standard bootloader, using first 8kB of flash |
|`_HIGH` | High memory bootloader for 64kB chips (experimental) |
|`_HIGH_128` | High memory bootloader for 128kB chips (experimental) |

The high memory bootloader is a variation that doesn't require the application to be at an offset, the bootloader resides in the top 6.5kB of ROM and hides its reset and stack vectors inside unused entries of the application vector table. As an example, to compile for a Bluepill board with 128kB flash, use:

make clean
make TARGET=BLUEPILL_HIGH_128


## Flash instructions
The `make flash` target will use openocd to upload the bootloader to an attached board. By default, the Makefile assumes you're using a [CMSIS-DAP](http://www.arm.com/products/processors/cortex-m/cortex-microcontroller-software-interface-standard.php) based probe, but you can override this by overriding `OOCD_INTERFACE` variable. For example:

Expand All @@ -43,9 +57,10 @@ You can also use the env variable `DEFS` to override default configuration for a

## Using the bootloader
### Building for the bootloader
The bootloader occupies the lower 8KiB of flash, so your application must offset its flash contents by 8KiB. This can be done by modifying your linker script or flags as appropriate.
The standard bootloader occupies the lower 8KiB of flash, so your application must offset its flash contents by 8KiB. This can be done by modifying your linker script or flags as appropriate.

The high memory bootloaders do not use the lower part of the flash, so you only need to make sure your application leaves 6.5kB of flash free.

See the [highboot branch](https://github.com/devanlai/dapboot/tree/highboot) for an experimental variation that doesn't require the application to be offset.

### Switching to the bootloader
The bootloader can be built to look for arbitrary patterns, but the default for the STM32F103 target looks for a magic value stored in the RTC backup registers. Writing the magic value and then resetting will run the bootloader instead of the main application.
Expand All @@ -54,6 +69,8 @@ The bootloader currently looks for `0x544F` in RTC backup register 1 and `0x4F42

You can also use a button to stay in bootloader while booting. It's configured using `HAVE_BUTTON` define. If your button has a debounce capacitor, you can use `BUTTON_SAMPLE_DELAY_CYCLES` define to specify how many cycles to wait before sampling the I/O pin, by default it is approximately 20ms in a 72Mhz MCU.

On the bluepill boards, the default is using the BOOT1 input (available on jumper in the board) to enter the bootloader.

### WebUSB
This bootloader implements the draft [WebUSB](https://wicg.github.io/webusb/) specification, which allows web pages to access the bootloader (after presenting the user with a device picker dialog).

Expand Down
44 changes: 43 additions & 1 deletion release.Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@ BUILD_DIR ?= ./build

all: dapboot-bluepill.bin \
dapboot-maplemini.bin \
dapboot-stlink.bin
dapboot-stlink.bin \
dapboot-bluepill-high.bin \
dapboot-maplemini-high.bin \
dapboot-stlink-high.bin \
dapboot-bluepill-high-128.bin \
dapboot-maplemini-high-128.bin \
dapboot-stlink-high-128.bin

clean:
$(Q)$(RM) $(BUILD_DIR)/*.bin
Expand Down Expand Up @@ -59,3 +65,39 @@ dapboot-maplemini.bin: | $(BUILD_DIR)
$(Q)$(MAKE) TARGET=MAPLEMINI -C src/ clean
$(Q)$(MAKE) TARGET=MAPLEMINI -C src/
$(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@)

dapboot-bluepill-high.bin: | $(BUILD_DIR)
@printf " BUILD $(@)\n"
$(Q)$(MAKE) TARGET=BLUEPILL_HIGH -C src/ clean
$(Q)$(MAKE) TARGET=BLUEPILL_HIGH -C src/
$(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@)

dapboot-stlink-high.bin: | $(BUILD_DIR)
@printf " BUILD $(@)\n"
$(Q)$(MAKE) TARGET=STLINK_HIGH -C src/ clean
$(Q)$(MAKE) TARGET=STLINK_HIGH -C src/
$(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@)

dapboot-maplemini-high.bin: | $(BUILD_DIR)
@printf " BUILD $(@)\n"
$(Q)$(MAKE) TARGET=MAPLEMINI_HIGH -C src/ clean
$(Q)$(MAKE) TARGET=MAPLEMINI_HIGH -C src/
$(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@)

dapboot-bluepill-high-128.bin: | $(BUILD_DIR)
@printf " BUILD $(@)\n"
$(Q)$(MAKE) TARGET=BLUEPILL_HIGH_128 -C src/ clean
$(Q)$(MAKE) TARGET=BLUEPILL_HIGH_128 -C src/
$(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@)

dapboot-stlink-high-128.bin: | $(BUILD_DIR)
@printf " BUILD $(@)\n"
$(Q)$(MAKE) TARGET=STLINK_HIGH_128 -C src/ clean
$(Q)$(MAKE) TARGET=STLINK_HIGH_128 -C src/
$(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@)

dapboot-maplemini-high-128.bin: | $(BUILD_DIR)
@printf " BUILD $(@)\n"
$(Q)$(MAKE) TARGET=MAPLEMINI_HIGH_128 -C src/ clean
$(Q)$(MAKE) TARGET=MAPLEMINI_HIGH_128 -C src/
$(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@)
15 changes: 9 additions & 6 deletions src/dapboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@
#include "winusb.h"
#include "config.h"

/* Used only in the high memory bootloader */
extern volatile const vector_table_t vector_table;

static inline void __set_MSP(uint32_t topOfMainStack) {
asm("msr msp, %0" : : "r" (topOfMainStack));
}

bool validate_application(void) {
if ((*(volatile uint32_t *)APP_BASE_ADDRESS & 0x2FFE0000) == 0x20000000) {
if (((uint32_t)(APP_INITIAL_STACK) & 0x2FFE0000) == 0x20000000) {
return true;
}
return false;
Expand All @@ -41,19 +44,19 @@ bool validate_application(void) {
static void jump_to_application(void) __attribute__ ((noreturn));

static void jump_to_application(void) {
vector_table_t* app_vector_table = (vector_table_t*)APP_BASE_ADDRESS;


/* Use the application's vector table */
target_relocate_vector_table();
if (APP_RELOCATE_VECTORS)
target_relocate_vector_table();

/* Do any necessary early setup for the application */
target_pre_main();

/* Initialize the application's stack pointer */
__set_MSP((uint32_t)(app_vector_table->initial_sp_value));
__set_MSP((uint32_t)(APP_INITIAL_STACK));

/* Jump to the application entry point */
app_vector_table->reset();
APP_ENTRY_POINT();

while (1);
}
Expand Down
18 changes: 18 additions & 0 deletions src/dapboot.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,22 @@

extern bool validate_application(void);

#ifdef BOOTLOADER_HIGH
/* Definitions for high memory bootloader */
#define APP_INITIAL_STACK vector_table.reserved_x001c[0]
#define APP_ENTRY_POINT vector_table.reserved_x001c[1]
#define APP_RELOCATE_VECTORS 0
#define DFU_PATCH_VECTORS 1
#define BOOTLOADER_OFFSET 0

#else
/* Definitions for low memory (standard) bootloader */
#define APP_INITIAL_STACK ((vector_table_t*)APP_BASE_ADDRESS)->initial_sp_value
#define APP_ENTRY_POINT ((vector_table_t*)APP_BASE_ADDRESS)->reset
#define APP_RELOCATE_VECTORS 1
#define DFU_PATCH_VECTORS 0
#define BOOTLOADER_OFFSET 0x00002000

#endif

#endif
37 changes: 36 additions & 1 deletion src/dfu.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <stdlib.h>
#include <string.h>

#include <libopencm3/cm3/vector.h>
#include <libopencm3/usb/usbd.h>
#include <libopencm3/usb/dfu.h>

Expand Down Expand Up @@ -94,6 +95,24 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data
(void)usbd_dev;
(void)req;


if (DFU_PATCH_VECTORS && current_dfu_offset == 0) {
if (dfu_download_size < offsetof(vector_table_t, reserved_x001c[1])) {
/* Can't handle splitting the vector table right now */
dfu_set_status(DFU_STATUS_ERR_VENDOR);
} else {
vector_table_t* app_vector_table = (vector_table_t*)dfu_download_buffer;
/* Stash the application's initial stack value and reset
pointer in unused vector table entries */
app_vector_table->reserved_x001c[0] = (vector_table_entry_t)(app_vector_table->initial_sp_value);
app_vector_table->reserved_x001c[1] = app_vector_table->reset;
/* Overwrite the stack and reset pointer to run the
bootloader instead */
app_vector_table->initial_sp_value = &_stack;
app_vector_table->reset = reset_handler;
}
}

const uint16_t* data = (uint16_t*)dfu_download_buffer;
uint16_t* dest = (uint16_t*)(APP_BASE_ADDRESS + current_dfu_offset);

Expand Down Expand Up @@ -234,7 +253,6 @@ static int dfu_control_class_request(usbd_device *usbd_dev,
__attribute__ ((fallthrough));
}
case STATE_DFU_UPLOAD_IDLE: {
*buf = (uint8_t*)(APP_BASE_ADDRESS + current_dfu_offset);
uint16_t len_to_copy = req->wLength;
size_t max_firmware_size = target_get_max_firmware_size();
if (current_dfu_offset + req->wLength > max_firmware_size) {
Expand All @@ -244,6 +262,23 @@ static int dfu_control_class_request(usbd_device *usbd_dev,
dfu_set_state(STATE_DFU_UPLOAD_IDLE);
}
*len = len_to_copy;
if (DFU_PATCH_VECTORS && current_dfu_offset < sizeof(vector_table_t)) {
/* Copy the flash memory to the download buffer, to
undo the vector modifications. */
memcpy(dfu_download_buffer, (const void*)(APP_BASE_ADDRESS),
sizeof(dfu_download_buffer));
vector_table_t* app_vector_table = (vector_table_t*)dfu_download_buffer;
/* Put the original stack pointer and reset vectors
back */
app_vector_table->initial_sp_value = (unsigned int*)(app_vector_table->reserved_x001c[0]);
app_vector_table->reset = app_vector_table->reserved_x001c[1];
app_vector_table->reserved_x001c[0] = 0;
app_vector_table->reserved_x001c[1] = 0;
/* Return the correct pointer */
*buf = dfu_download_buffer + current_dfu_offset;
} else {
*buf = (uint8_t*)(APP_BASE_ADDRESS + current_dfu_offset);
}
current_dfu_offset += len_to_copy;
break;
}
Expand Down
1 change: 1 addition & 0 deletions src/rules.mk
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ CPPFLAGS += -I$(INCLUDE_DIR) $(DEFS)
# Linker flags

LDFLAGS += -flto -Os -g --static -nostartfiles
LDFLAGS += -specs=nano.specs
LDFLAGS += -L$(LIB_DIR)
LDFLAGS += -T$(LDSCRIPT)
LDFLAGS += -Wl,-Map=$(*).map
Expand Down
2 changes: 1 addition & 1 deletion src/stm32f103/bluepill/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#define CONFIG_H_INCLUDED

#ifndef APP_BASE_ADDRESS
#define APP_BASE_ADDRESS 0x08002000
#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET)
#endif
#ifndef FLASH_SIZE_OVERRIDE
#define FLASH_SIZE_OVERRIDE 0x20000
Expand Down
2 changes: 1 addition & 1 deletion src/stm32f103/generic/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#define CONFIG_H_INCLUDED

#ifndef APP_BASE_ADDRESS
#define APP_BASE_ADDRESS 0x08002000
#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET)
#endif
#ifndef FLASH_SIZE_OVERRIDE
#define FLASH_SIZE_OVERRIDE 0x20000
Expand Down
2 changes: 1 addition & 1 deletion src/stm32f103/maplemini/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#define CONFIG_H_INCLUDED

#ifndef APP_BASE_ADDRESS
#define APP_BASE_ADDRESS 0x08002000
#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET)
#endif
#ifndef FLASH_PAGE_SIZE
#define FLASH_PAGE_SIZE 1024
Expand Down
2 changes: 1 addition & 1 deletion src/stm32f103/stlink/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#define CONFIG_H_INCLUDED

#ifndef APP_BASE_ADDRESS
#define APP_BASE_ADDRESS 0x08002000
#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET)
#endif
#ifndef FLASH_SIZE_OVERRIDE
#define FLASH_SIZE_OVERRIDE 0x20000
Expand Down
109 changes: 109 additions & 0 deletions src/stm32f103/stm32f1.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/

/* Generic linker script for STM32 targets using libopencm3. */

/* Memory regions must be defined in the ld script which includes this one. */

/* Enforce emmition of the vector table. */
EXTERN (vector_table)

/* Define the entry point of the output file. */
ENTRY(reset_handler)

/* Define sections. */
SECTIONS
{
.vectors : {
*(.vectors) /* Vector table */
} >vectors

.text : {
*(.text*) /* Program code */
. = ALIGN(4);
*(.rodata*) /* Read-only data */
. = ALIGN(4);
} >rom

/* C++ Static constructors/destructors, also used for __attribute__
* ((constructor)) and the likes */
.preinit_array : {
. = ALIGN(4);
__preinit_array_start = .;
KEEP (*(.preinit_array))
__preinit_array_end = .;
} >rom
.init_array : {
. = ALIGN(4);
__init_array_start = .;
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
__init_array_end = .;
} >rom
.fini_array : {
. = ALIGN(4);
__fini_array_start = .;
KEEP (*(.fini_array))
KEEP (*(SORT(.fini_array.*)))
__fini_array_end = .;
} >rom

/*
* Another section used by C++ stuff, appears when using newlib with
* 64bit (long long) printf support
*/
.ARM.extab : {
*(.ARM.extab*)
} >rom
.ARM.exidx : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} >rom

. = ALIGN(4);
_etext = .;

.data : {
_data = .;
*(.data*) /* Read-write initialized data */
. = ALIGN(4);
_edata = .;
} >ram AT >rom
_data_loadaddr = LOADADDR(.data);

.bss : {
*(.bss*) /* Read-write zero initialized data */
*(COMMON)
. = ALIGN(4);
_ebss = .;
} >ram

/*
* The .eh_frame section appears to be used for C++ exception handling.
* You may need to fix this if you're using C++.
*/
/DISCARD/ : { *(.eh_frame) }

. = ALIGN(4);
end = .;
}

PROVIDE(_stack = ORIGIN(ram) + LENGTH(ram));

Loading