Import upstream version v2.0-rc7

Date:      Tue Apr 04 16:36:57 2019 -0400
parents
*.swp
*.swo
*~
.vscode
toboot/.obj
toboot/toboot.bin
toboot/toboot.elf
toboot/toboot.ihex
booster/.obj
booster/booster.bin
booster/booster.elf
booster/booster.ihex
booster/make-booster
tests/secure-erase/pass-1/.obj/
tests/secure-erase/pass-1/secure-erase-pass1.bin
tests/secure-erase/pass-1/secure-erase-pass1.elf
tests/secure-erase/pass-1/secure-erase-pass1.ihex
tests/secure-erase/pass-2/.obj/
tests/secure-erase/pass-2/secure-erase-pass2.bin
tests/secure-erase/pass-2/secure-erase-pass2.elf
tests/secure-erase/pass-2/secure-erase-pass2.ihex
\ No newline at end of file
Toboot API v1.0
===============
Toboot allows for some interaction between your application and the Tomu itself.
Version
-------
This document describes v1.0 of the API, which is the default API used if your program doesn't declare compatibility with another API version.
Watchdog Timer
--------------
Toboot sets the watchdog timer. You must disable it, or start feeding it right away. A quick hack to disable the Watchdog timer is to write:
````c++
*(uint32_t *)0x40088000UL = 0;
````
Application Entrypoint
----------------------
Your application's stack pointer is expected to live at offset 0x4000, and the entrypoint is expected to be at offset 0x4004. This is the same as the stock Silabs bootloader, and follows standard ARM Cortex M0 behavior.
Toboot is guaranteed to exist within the first 0x2000 bytes, and in fact future versions may even be under 0x1000 bytes. This means that you can have code and data within the 0x2000-0x4000 region. You can even use it as storage if you like.
By adopting this convention, we ensure compatibility with legacy Toboot code and bootloaders.
Boot Token
----------
The first 8 bytes of memory (offset 0x20000000 - 0x20000008) are reserved for a Toboot token. The boot token has the following structure:
````c++
struct boot_token {
// Set this to 0x74624346 and reboot to enter bootloader
uint32_t magic;
// Set this to 0 when your program starts.
uint8_t boot_count;
// The bootloader should set this to 0x23 for Tomu.
uint8_t board_model;
// Unused.
uint16_t reserved;
};
````
The value board\_model is defined to be 0x23. The boot\_count value describes the number of times the board has rebooted. If the board fails to reboot three times in a row, it will enter Toboot automatically.
The *magic* value allows you to enter Toboot programmatically. Set this value to 0x74624346 and reboot.
Toboot Configuration Values
---------------------------
The configuration values for Toboot V1.0 are stored at offset 0x94 from the start of the binary image. Normally this is where external interrupt vector 21 lives, however the highest interurpt vector on the EFM32HG is 20. Therefore, Toboot V1.0 uses this word as the Toboot Configuration Flag to configure how Toboot behaves. It is a 32-bit word with the following format:
````c++
0xrrrrLLLL
````
*rrrr* are reserved values. Set these to 0.
If *LLLL* is set to 0x70b0, then users cannot enter Toboot by shorting out the pads. Use this to lock out firmware uploads, except programmatically.
Offset 0x98 is the Toboot App Flag. This configures how your app is loaded. It contains the following format:
````c++
0xrrSSKKKK
````
*rr* values are reserved. Set these to 0.
The *KKKK* values are the key. They must be set to 0x6fb0, otherwise the App Configuration Flag is ignored.
Set *SS* to the starting page number to load your program at. Page sizes are 1024 bytes. To maintain compatibility with earlier programs, you may want to set this value to 0x00106fb0, which will explicitly cause programs to be loaded at offset 0x4000. Be very careful with this value, because you can use it to overwrite Toboot. This approach is deprecated in favor of using something like Booster to update Toboot itself.
\ No newline at end of file
# Toboot API V2.0
Toboot allows for some interaction between your program and the running bootloader. This document describes V2.0 of the API, which is defined by a magic value at the start of the header.
## Program Header
Your application binary should have a program header located 0x94 bytes from the start of the binary image. This places it directly after the interrupt vector table on the EFM32. The header should have the following structure, which is described in detail below:
````c++
struct toboot_configuration {
uint32_t magic; // Set to 0x907070b2
uint8_t start; // The starting page for your program
uint8_t config; // Configuration flags. Defined below.
uint16_t reserved_gen; // Reserved value. Toboot will set this to the generational counter.
uint32_t lock_entry; // Set to 0x18349420 to prevent the user from entering Toboot manually.
uint32_t erase_mask_lo; // A bitmask of sectors to erase when updating the program
uint32_t erase_mask_hi; // A bitmask of sectors to erase when updating the program
uint32_t reserved_hash; // A hash of the header. Calculated by Toboot.
};
````
### Toboot Magic
The first block of your program should contain the value 0x907070b2 at offset 0x94. This tells the Toboot loader to use V2.0 for loading.
### Start Sector
The starting sector indicates where in RAM the first block will be loaded. This value should be located somewhere after Toboot. It is illegal to load the program into any region populated by Toboot itself -- to perform an update, use a separate program to do the updating, such as [booster](./booster).
Sectors are 1024-bytes large. To maintain compatibility with AN0042 programs, load your program at offset 0x4000 and set this to `16`.
### Config Flags
Configuration flags are populated in the `config` field of the toboot struct. The following flags are valid:
````
/* Toboot V1.0 leaves IRQs enabled, mimicking the behavior of
* AN0042. Toboot V2.0 makes this configurable by adding a
* configuration flag.
*/
#define TOBOOT_CONFIG_FLAG_ENABLE_IRQ_MASK 0x01
#define TOBOOT_CONFIG_FLAG_ENABLE_IRQ_SHIFT 0
#define TOBOOT_CONFIG_FLAG_ENABLE_IRQ (1 << 0)
#define TOBOOT_CONFIG_FLAG_DISABLE_IRQ (0 << 0)
/* When running a normal program, you won't want Toboot to run.
* However, when developing new software it is handy to have
* Toboot run at poweron, instead of jumping straight to your
* program. Set this flag to enter your program whenever the
* system has just powered on.
*/
#define TOBOOT_CONFIG_FLAG_AUTORUN_MASK 0x02
#define TOBOOT_CONFIG_FLAG_AUTORUN_SHIFT 1
#define TOBOOT_CONFIG_FLAG_AUTORUN (1 << 1)
#define TOBOOT_CONFIG_FLAG_AUTORUN_DISABLED (0 << 1)
````
Other configuration values are reserved and should not be used.
### Lock Entry
Ordinarily, users can force entry into Toboot by shorting out the pads
when applying power. You may want to disable this behavor for some reason.
Be very careful when doing this, because it can cause a device to become unusable.
To prevent users from entering Toboot by shorting out pads when applying power, set the `lock_entry` field to `0x18349420`.
It is still possible to enter Toboot due to a failed boot, or by setting the magic value. This also does not disable SWD support.
### Erase Mask
The two bitmasks *erase_mask_lo* and *erase_mask_hi* define sectors that must be erased prior to programming. If a bit is '1', then that sector will be erased even if it does not need programming.
If a bit is 0, then the specified sector will only be erased if it needs programming.
To erase all of flash (except Toboot), set this value to 0xffffffff 0xffffffff. To erase only the minimum number of sectors, set this value to 0x00000000 0x00000000.
You should use this to ensure that sensitive data stores are erased before replacing firmware. For example, secret keys.
### Generational Counter (*internal use*)
Toboot uses this value to determine which program in flash is the newest. This field is automatically populated when your program is loaded, so you can leave this value unset. The first program ever programmed will have a generation of "1", and every subsequent program will have a generation of 1 more.
Toboot will launch the program that has the highest generational counter located within a valid Toboot header with a hash that matches.
For example, if offset 0x2000 contains a valid header and has a generational counter of 2, and offset 0x3000 has a valid header and has a generational counter of 3, then Toboot will run the program at offset 0x3000.
This generational counter is computed automatically by looking at all programs on the system and adding 1 to the result. This result is stored in the `reserved_gen` field.
### Hash (*internal use*)
Toboot computes a hash of the header and stores the result in this field. The current hash algorithm is xxHash. A program must have a valid Magic and a valid hash to be considered. Programs with an invalid hash are not considered.
This field is overwritten by Toboot with the computed hash when the program is first written.
## Watchdog Timer
Toboot sets the watchdog timer. You must disable it, or start feeding it right away. A quick hack to disable the Watchdog timer is to write:
````c++
*(uint32_t *)0x40088000UL = 0;
````
## Application Entrypoint
Your application's stack pointer is expected to live at offset 0x0 from the start of your program, and the entrypoint is expected to be at offset 0x4. This is optionally followed by the standard interrupt vector table.
## Communicating with Toboot
The first 8 bytes of memory (offset 0x20000000 - 0x20000008) are reserved for a Toboot token. The boot token has the following structure:
````c++
struct boot_token {
// Set this to 0x74624346 and reboot to enter bootloader
uint32_t magic;
// Set this to 0 when your program starts.
uint8_t boot_count;
// The bootloader should set this to 0x23 for Tomu.
uint8_t board_model;
// Unused.
uint16_t reserved;
};
````
The value `board_model` is defined to be 0x23. The `boot_count` value describes the number of times the board has rebooted. If the board fails to reboot three times in a row, it will enter Toboot automatically.
The `magic` value allows you to force entry into Toboot programmatically. Set this value to 0x74624346 and reboot. This can be used as part of a "perform firmware upgrade" process.
## Version Differences
There are several differences between V2.0 of the API and V1.0. Notable differences include:
* Full program header, rather than reusing unused interrupt vectors
* Bitfield indicating sectors to erase when replacing a program
* Generational counter, allowing program to be placed anywhere in RAM
* Hashed header
Toboot API V1.0 is deprecated and should not be used for future programs.
\ No newline at end of file
This diff is collapsed.
# Bootloader for [Tomu board](http://tomu.im)
This repo contains Toboot and associated support files for the [EFM32HG Tomu board](https://github.com/im-tomu/tomu-hardware).
Toboot is a DFU-based bootloader for Tomu that supports easy and driver-free firmware updates.
## Using Toboot
When you insert Tomu, Toboot runs by default. Some programs (such as the u2f code) set the `TOBOOT_CONFIG_FLAG_AUTORUN` flag, and start running immediately. To enter Toboot on these devices, short out the two outer pins with tweezers as you insert Tomu:
![Force Entry](media/toboot-force.jpg "Force entry into Toboot")
When Toboot runs, the lights will flash like this:
![Toboot Pattern](media/toboot-mode.gif?raw=true "Toboot Pattern")
You should install dfu-util. Linux users can find it in your package manager. Mac users can use Homebrew. The Windows binary is provided in the [bin/](./bin) directory. Chrome users can use a [web version](https://devanlai.github.io/webdfu/dfu-util/) (but Linux users should make sure the udev permissions are set up correctly first.)
You can show a list of detected devices with `dfu-util --list`. You can load a new program image with `dfu-util --download`.
## Toboot API
While Toboot supports jumping straight to code, it is also possible to take advantage of more advanced features such as secure sectors and automatic program booting. This requires setting up a configuration struct located at the correct offset.
More information on the Toboot API is available in [API.md](API.md).
## Watchdog Timer
**Toboot Sets the Watchdog Timer**. Your program **will** reboot if the watchdog timer isn't cleared within a few tens of milliseconds. This is to ensure the code returns to the bootloader if you accidentally do something like flash an MP3 file, or try to program the .ihex version.
A quick-and-dirty way to do this is to put the following at the start of your program:
````c++
*(uint32_t *)0x40088000UL = 0;
````
Of course, it's better to actually use a Watchdog driver and keep the watchdog fed normally. But this will at least get you going.
## Entering Toboot
**By default, Toboot will always run when a board is powered on**. To automatically run your program at poweron, create a valid Toboot V2.0 header and set `TOBOOT_CONFIG_FLAG_AUTORUN`. This was done to make it easy to develop software, because all you need to do to load new firmware is to unplug Tomu and plug it back in.
There are several reasons why a user might end up in Toboot:
1. The config value doesn't have `TOBOOT_CONFIG_FLAG_AUTORUN` set, and the board has just been powered on.
1. There is no main application loaded. This can happen if you've erased the flash, or if you've loaded an invalid binary. The program's start address must be in flash, and the stack pointer must be in RAM.
1. The board has failed to finish booting three times in a row. This can happen if you've loaded an invalid program, or if you haven't cleared the watchdog timer.
1. The magic value `0x74624346` is stored in the boot token area, at RAM address 0x20000000.
1. The user shorts the two outer pads together when they apply power AND the program has NOT set TOBOOT_LOCKOUT_MAGIC.
## Installing or Upgrading Toboot
To install Toboot, use the `boosted` files in the [prebuilt/](./prebuilt) directory:
* **Toboot**: Use dfu-util to load [prebuilt/toboot-boosted.dfu](./prebuilt/toboot-boosted.dfu) using:
`dfu-util -D prebuilt/toboot-boosted.dfu`
* **AN0042**: Use the serial bootloader to load [prebuilt/toboot-boosted.bin](./prebuilt/toboot-boosted.bin)
Toboot is unable to reflash itself. This is to prevent partial updates from corrupting the firmware. Instead, a support program is appended to the start of Toboot, and the entire thing is uploaded as one chunk.
## Building Toboot
Toboot is designed to be simple to build. Ensure you have an ARM toolchain installed such as the [official one from ARM](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm), as well as `make`. Then simply build:
````sh
cd toboot/
make
````
## Creating Updates
Toboot is not allowed to overwrite intself, to prevent partial updates from making a board unusable.
The `Booster` program is used to update or install Toboot. The source code is located in the [booster/](./booster) directory. Use `make-booster` to wrap toboot.bin in a booster app, and flash the resulting image using dfu-util:
````sh
cd ../booster/
make
gcc make-booster.c -o make-booster
./make-booster ../toboot/toboot.bin toboot-booster.bin
cp toboot-booster.bin toboot-booster.dfu
dfu-suffix --pid 0x70b1 --vid 0x1209 --add toboot-booster.dfu
````
You can then flash the resulting `toboot-booster.dfu` using dfu-util, or using the legacy serial uploader to flash `toboot-booster.bin`:
`dfu-util -d 1209:70b1 -D toboot-booster.dfu`
## Flashing onto a new Tomu
Brand-new Tomus will not have Toboot installed. Instead, they might have the SiLabs `AN0042` bootloader.
The recommend way to load the bootloader onto a Tomu board is using a Raspberry Pi with
[OpenOCD](http://openocd.org/). Instructions for doing this can be found in the
[openocd](openocd) directory. You need OpenOCD **version 0.10.0 or later** to
have EFM32HG support.
Tomu can be powered using the 3.3V pin, so you can create a sort of "programming wand" by bringing 3.3V, GND, SCK, and SIO out to a 0.1" header, running openocd in a loop, and touching the programming pins on the side of a Tomu board. The process only takes a few seconds, so contact doesn't have to be great.
## Legacy Bootloader
SiLabs AN0042 was the original bootloader. It requires an IAR compiler to build, as well as custom drivers/software on the host device. This bootloader is available in the 'an0042' branch, and has been removed from the master branch. It is here for historical interest, and for compatibility with stock EFM32HG utilities.
# Binaries for Windows
These binaries are precompiled versions of dfu-util, mirrored from
[dfu-util.sourceforge.net](http://dfu-util.sourceforge.net/releases/dfu-util-0.8-binaries/win32-mingw32/).
How to use tomu with Black Magic Probe
======================================
Introduction
------------
The [Black Magic Probe](https://github.com/blacksphere/blackmagic/wiki)/BMP is a debugger for various ARM devices which includes the EFM32 series.
You can buy a [full model](https://1bitsquared.com/products/black-magic-probe) but the interresting part is that it's open source. Therefore, it is possible for flash the firmware on various equipement, for example stlink-like probes.
Here we use the [bluepill hardware](http://wiki.stm32duino.com/index.php?title=Blue_Pill). It's a cheap microcontroller which can be transformed in a good BMP.
You can find easily on internet how to transform a normal bluepill into a BMP, it is not covered here. We used [this repository](https://github.com/UweBonnes/blackmagic/tree/bluepill) on the branch bluepill. Please keep in mind that the pins are different than the master branch. If you use the master branch, change accordingly.
Requirements
------------
Hardware:
- Bluepill with BMP firmware on it
- 1 tomu
- Cables
Software
- Regular toolchain for gcc/gdb arm
Connecting the BMP and tomu
---------------------------
Connect as following:
- PB9 is SWDIO
- PB8 is SWCLK
- 3V3 is VCC
- GND is GND
You can refer to the following schematics to see how the connection is done (original bluepill pintout is from http://reblag.dk/stm32/).
![Connection](connect.png)
It can be easier for debugging to temporary solder connections on the tomu debug pads. You can see it here in action
![Soldering](connect2.png)
Once connected, you can debug the tomu as follow.
Debugging
---------
```bash
arm-none-eabi-gdb --ex 'target extended-remote /dev/ttyACM0'
```
Once in gdb, you can scan to find the ID of tomu (should be 1):
```
(gdb) monitor swdp_scan
Target voltage: Not Detected
Available Targets:
No. Att Driver
1 EFM32 Happy Gecko
```
Attach to the EFM32
```
(gdb) attach 1
Attaching to Remote target
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0x000003bc in ?? ()
```
From there you can use regular gdb commands, such as `info all` etc. to get the status of the program.
To start a compiled sample, do the following:
```
(gdb) file blinky.elf
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from blinky.elf...done.
(gdb) load blinky.elf
Loading section .text, size 0x1904 lma 0x0
Loading section .ARM.exidx, size 0x8 lma 0x1904
Loading section .data, size 0x440 lma 0x190c
Start address 0x534, load size 7500
Transfer rate: 12 KB/sec, 750 bytes/write.
```
You can then `continue` and have source debugging.
For example,
```
(gdb) frame
#0 0x000003b8 in SpinDelay (millis=500) at main.c:46
46 while (uptime_millis < sleep_until);
```
Benefits
--------
With this method, you can debug even without bootloader the different programs on tomu.
Also, you can use the following macro in your programs:
```c
#define DEBUG_BREAK() __asm__("BKPT #0");
```
It will break properly inside the debugger.
TODO
----
Document the various interactions with the differents bootloaders.
Try and document if the serial output is working.
\ No newline at end of file
PACKAGE = booster
ADD_CFLAGS =
ADD_LFLAGS =
TRGT ?= arm-none-eabi-
CC = $(TRGT)gcc
CXX = $(TRGT)g++
OBJCOPY = $(TRGT)objcopy
LDSCRIPT = tomu.ld
DBG_CFLAGS = -ggdb -g -DDEBUG -Wall
DBG_LFLAGS = -ggdb -g -Wall
CFLAGS = $(ADD_CFLAGS) \
-Wall -Wextra \
-mcpu=cortex-m0plus -mfloat-abi=soft -mthumb \
-ffunction-sections -fdata-sections -fno-common \
-fomit-frame-pointer -Os
CXXFLAGS = $(CFLAGS) -std=c++11 -fno-rtti -fno-exceptions
LFLAGS = $(ADD_LFLAGS) $(CFLAGS) \
-nostartfiles \
-Wl,--gc-sections \
-Wl,--no-warn-mismatch,--script=$(LDSCRIPT),--build-id=none
OBJ_DIR = .obj
CSOURCES = main.c
CPPSOURCES = $(wildcard *.cpp)
ASOURCES = $(wildcard *.S)
COBJS = $(addprefix $(OBJ_DIR)/, $(notdir $(CSOURCES:.c=.o)))
CXXOBJS = $(addprefix $(OBJ_DIR)/, $(notdir $(CPPSOURCES:.cpp=.o)))
AOBJS = $(addprefix $(OBJ_DIR)/, $(notdir $(ASOURCES:.S=.o)))
OBJECTS = $(COBJS) $(CXXOBJS) $(AOBJS)
VPATH = .
QUIET = @
ALL = all
TARGET = $(PACKAGE).elf
CLEAN = clean
$(ALL): $(TARGET)
$(OBJECTS): | $(OBJ_DIR)
$(TARGET): $(OBJECTS) $(LDSCRIPT)
$(QUIET) echo " LD $@"
$(QUIET) $(CXX) $(OBJECTS) $(LFLAGS) -o $@
$(QUIET) echo " OBJCOPY $(PACKAGE).bin"
$(QUIET) $(OBJCOPY) -O binary $@ $(PACKAGE).bin
$(QUIET) echo " IHEX $(PACKAGE).ihex"
$(QUIET) $(OBJCOPY) -O ihex $@ $(PACKAGE).ihex
$(DEBUG): CFLAGS += $(DBG_CFLAGS)
$(DEBUG): LFLAGS += $(DBG_LFLAGS)
CFLAGS += $(DBG_CFLAGS)
LFLAGS += $(DBG_LFLAGS)
$(DEBUG): $(TARGET)
$(OBJ_DIR):
$(QUIET) mkdir $(OBJ_DIR)
$(COBJS) : $(OBJ_DIR)/%.o : %.c Makefile
$(QUIET) echo " CC $< $(notdir $@)"
$(QUIET) $(CC) -c $< $(CFLAGS) -o $@ -MMD
$(OBJ_DIR)/%.o: %.cpp
$(QUIET) echo " CXX $< $(notdir $@)"
$(QUIET) $(CXX) -c $< $(CXXFLAGS) -o $@ -MMD
$(OBJ_DIR)/%.o: %.S
$(QUIET) echo " AS $< $(notdir $@)"
$(QUIET) $(CC) -x assembler-with-cpp -c $< $(CFLAGS) -o $@ -MMD
.PHONY: $(CLEAN)
$(CLEAN):
$(QUIET) rm -f $(wildcard $(OBJ_DIR)/*.d)
$(QUIET) rm -f $(wildcard $(OBJ_DIR)/*.o)
$(QUIET) rm -f $(TARGET) $(PACKAGE).bin $(PACKAGE).symbol
include $(wildcard $(OBJ_DIR)/*.d)
Booster: the Toboot Updater
===========================
Toboot cannot update itself, because a partial flash would result in an unbootable system.
`Booster` is used to guide the installation of Toboot. By appending a new version of Toboot to the end of Booster, we can create a program to update Toboot itself.
Usage
-----
First, compile booster. Then, append the application header, and finally append the application itself. This can be accomplished with `make-booster`.
````sh
cd toboot/
make
cd ../booster/
make
gcc make-booster.c -o make-booster
./make-booster ../toboot/toboot.bin toboot-booster.bin
````
The resulting `toboot-booster.bin` can be flashed with Toboot itself, or can be loaded using the legacy serial bootloader.
Design
------
Booster uses xxHash to verify the application is loaded correctly. It also needs to know how many bytes to load. To do this, it looks at the `booster_data` struct, which has the following format:
````c++
struct booster_data
{
uint32_t payload_size; // Number of bytes to flash
uint32_t xxhash32; // A hash of the entire program to be loaded
uint32_t payload[0]; // The contents of the firmware to build
};
````
The `payload_size` value indicates the number of bytes to write. Ideally it's a multiple of four.
The `xxhash32` is the result of a 32-bit xxHash operation. The seed is defined in booster.h as `BOOSTER_SEED`, and is `0x68d9d190L`.
Finally, `payload` is an array of uint32 values.
The `struct booster_data` object is placed directly following the program image. The `make-booster` program copies the contents of `booster.bin` to a new file, then appends `struct booster_data` to the end. That way, all `booster` has to do is refer to the contents of the struct in order to program the data.
As a happy coincidence, if `struct booster_data` is not populated (i.e. if you just flash the raw `booster.bin` to a device), then `xxhash32` will not be valid and `booster.bin` will simply reset the device.
\ No newline at end of file
#ifndef BOOSTER_H_
#define BOOSTER_H_
#include <stdint.h>
#include <stdbool.h>
#define XXH_NO_LONG_LONG
#define XXH_FORCE_ALIGN_CHECK 0
#define XXH_FORCE_NATIVE_FORMAT 0
#define XXH_PRIVATE_API
#include "xxhash.h"
#define BOOSTER_SEED 0x68d9d190L
struct booster_data
{
uint32_t payload_size;
uint32_t xxhash;
uint32_t payload[0];
};
#endif /* BOOSTER_H_ */
\ No newline at end of file
#include "booster.h"
#include "../toboot/mcu.h"
#include "../toboot/toboot-api.h"
// Defined in the linker
extern struct booster_data booster_data;
// Configure Toboot by identifying this as a V2 header.
// Place the header at page 16, to maintain compatibility with
// the serial bootloader and legacy programs.
__attribute__((used, section(".toboot_header"))) struct toboot_configuration toboot_configuration = {
.magic = TOBOOT_V2_MAGIC,
.start = 16,
.config = TOBOOT_CONFIG_FLAG_ENABLE_IRQ,
};
__attribute__((section(".toboot_runtime"))) extern struct toboot_runtime toboot_runtime;
// Returns whether the flash controller is busy
static bool ftfl_busy()
{
return (MSC->STATUS & MSC_STATUS_BUSY);
}
// Wait for the flash memory controller to finish any pending operation.
static void ftfl_busy_wait()
{
while (ftfl_busy())
;
}
// Erase the sector that contains the specified address.
static void ftfl_begin_erase_sector(uint32_t address)
{
// Erase the page at the specified address.
MSC->WRITECTRL |= MSC_WRITECTRL_WREN;
ftfl_busy_wait();
MSC->ADDRB = address;
MSC->WRITECMD = MSC_WRITECMD_LADDRIM;
MSC->WRITECMD = MSC_WRITECMD_ERASEPAGE;
}
// Program the specified word at the given address.
// Assumes the page has already been erased.
void ftfl_begin_program_word(uint32_t address, uint32_t value)
{
// Write the buffer word to the currently selected address.
// Note that after this is done, the address is incremented by 4.
ftfl_busy_wait();
MSC->ADDRB = address;
ftfl_busy_wait();
MSC->WRITECTRL |= MSC_WRITECTRL_WREN;
MSC->WDATA = value;
MSC->WRITECMD = MSC_WRITECMD_WRITEONCE;
}
uint32_t bytes_left;
uint32_t target_addr;
uint32_t *current_ptr;
uint32_t page_offset;
// Erase the Toboot configuration, which will prevent us from
// overwriting Toboot again.
// Run this from RAM to ensure we don't hardfault.
// Reboot after we're done.
__attribute__((section(".data"), noreturn)) static void finish_flashing(void)
{
uint32_t vector_addr = (uint32_t)&toboot_configuration;
// Jump back into the bootloader when we reboot
toboot_runtime.magic = TOBOOT_FORCE_ENTRY_MAGIC;
while (MSC->STATUS & MSC_STATUS_BUSY)
;
MSC->WRITECTRL |= MSC_WRITECTRL_WREN;
MSC->ADDRB = vector_addr;
MSC->WRITECMD = MSC_WRITECMD_LADDRIM;
MSC->WRITECMD = MSC_WRITECMD_ERASEPAGE;
while (MSC->STATUS & MSC_STATUS_BUSY)
;
NVIC_SystemReset();
while (1);
}
void Reset_Handler(void)
{
// If the booster data is too big, just let the watchdog timer reboot us,
// since the program is invalid.
if (booster_data.payload_size > 65536)
{
NVIC_SystemReset();
}
// Ensure the hash matches what's expected.
if (XXH32(booster_data.payload, booster_data.payload_size, BOOSTER_SEED) != booster_data.xxhash)
{
NVIC_SystemReset();
}
// Reset the boot counter, to prevent boot loops
toboot_runtime.boot_count = 0;
// Disable the watchdog timer
WDOG->CTRL = 0;
// Ensure interrupts are disabled, because this updater runs without them.
__disable_irq();
// Enable flash memory clocks
CMU->OSCENCMD = CMU_OSCENCMD_AUXHFRCOEN;