mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
riotboot: add riot-based minimal bootloader
riotboot is introduced here and makes use of riotboot_hdr, which indentifies the images encapsulated as slots. The slot size and offset is configurable, which makes slots extendable if needed, e.g. 2 or more slots can be transparently added. Co-authored-by: Kaspar Schleiser <kaspar@schleiser.de> Co-authored-by: Gaëtan Harter <gaetan.harter@fu-berlin.de>
This commit is contained in:
parent
a638f31bce
commit
281d8084bf
@ -434,6 +434,11 @@ else
|
||||
_LINK = $(if $(CPPMIX),$(LINKXX),$(LINK)) $(UNDEF) $(LINKFLAGPREFIX)--start-group $(BASELIBS) -lm $(LINKFLAGPREFIX)--end-group $(LINKFLAGS) $(LINKFLAGPREFIX)-Map=$(BINDIR)/$(APPLICATION).map
|
||||
endif # BUILDOSXNATIVE
|
||||
|
||||
# include bootloaders support. When trying to overwrite one variable
|
||||
# like HEXFILE, the value will already have been evaluated when declaring
|
||||
# the link target. Therefore, it must be placed before `link`.
|
||||
include $(RIOTMAKE)/boot/riotboot.mk
|
||||
|
||||
ifeq ($(BUILD_IN_DOCKER),1)
|
||||
link: ..in-docker-container
|
||||
else
|
||||
|
25
bootloaders/riotboot/Makefile
Normal file
25
bootloaders/riotboot/Makefile
Normal file
@ -0,0 +1,25 @@
|
||||
# Default RIOT bootloader
|
||||
APPLICATION = riotboot
|
||||
|
||||
# Default testing board
|
||||
BOARD ?= samr21-xpro
|
||||
|
||||
# Select the boards with riotboot feature
|
||||
FEATURES_REQUIRED += riotboot
|
||||
|
||||
# Disable unused modules
|
||||
CFLAGS += -DNDEBUG -DLOG_LEVEL=LOG_NONE
|
||||
DISABLE_MODULE += auto_init
|
||||
|
||||
# Include riotboot flash partition functionality
|
||||
USEMODULE += riotboot_slot
|
||||
|
||||
# RIOT codebase
|
||||
RIOTBASE ?= $(CURDIR)/../../
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
||||
# limit riotboot bootloader size
|
||||
# TODO: Manage to set this variable for boards which already embed a
|
||||
# bootloader, currently it will be overwritten
|
||||
ROM_LEN := $(RIOTBOOT_LEN)
|
75
bootloaders/riotboot/README.md
Normal file
75
bootloaders/riotboot/README.md
Normal file
@ -0,0 +1,75 @@
|
||||
# Overview
|
||||
This folder contains a simple bootloader called "riotboot".
|
||||
A header with metadata of length `RIOTBOOT_HDR_LEN` precedes
|
||||
the RIOT firmware. The header contains "RIOT" as a magic
|
||||
number to recognise a RIOT firmware image, a checksum, and
|
||||
the version of the RIOT firmware `APP_VER`.
|
||||
This bootloader verifies the checksum of the header which is located
|
||||
at an offset (`ROM_OFFSET`) with respect to the `ROM_START_ADDR`
|
||||
defined by the CPU, just after the space allocated for riotboot.
|
||||
|
||||
riotboot consists of:
|
||||
|
||||
- This application which serves as minimal bootloader,
|
||||
- the module "riotboot_hdr" used to recognise RIOT firmware which riotboot
|
||||
can boot,
|
||||
- the module "riotboot_slot" used to manage the partitions (slots) with a
|
||||
RIOT header attached to them,
|
||||
- a tool in dist/tools/riotboot_gen_hdr for header generation,
|
||||
- several make targets to glue everything together.
|
||||
|
||||
## Concept
|
||||
`riotboot` expects the flash to be formatted in slots: at CPU_FLASH_BASE
|
||||
address resides the bootloader, which is followed by a slot 0 with a
|
||||
RIOT firmware.
|
||||
|
||||
A RIOT firmware in a single slot is composed by:
|
||||
|
||||
```
|
||||
|------------------------------- FLASH -------------------------------------|
|
||||
|----- RIOTBOOT_LEN ----|----------- RIOTBOOT_SLOT_SIZE (slot 0) -----------|
|
||||
|----- RIOTBOOT_HDR_LEN ------|
|
||||
---------------------------------------------------------------------------
|
||||
| riotboot | riotboot_hdr_t + filler (0) | RIOT firmware |
|
||||
---------------------------------------------------------------------------
|
||||
```
|
||||
|
||||
Please note that `RIOTBOOT_HDR_LEN` depends on the architecture of the
|
||||
MCU, since it needs to be aligned to 256B. This is fixed regardless of
|
||||
`sizeof(riotboot_hdr_t)`
|
||||
|
||||
The bootloader will, on reset, verify the checksum of the first slot header,
|
||||
then boot it. If the slot doesn't have a valid checksum, no image will be
|
||||
booted and the bootloader will enter `while(1);` endless loop.
|
||||
|
||||
# Requirements
|
||||
A board capable to use riotboot must meet the following requirements:
|
||||
|
||||
- Embed a Cortex-M0+/3/4/7 processor
|
||||
- Provide the variables `ROM_START_ADDR` and `ROM_LEN`
|
||||
- Use cpu/cortexm_common/ldscripts/cortexm.ld ld script
|
||||
- Pass the cortexm_common_ldscript test in tests/
|
||||
- Being able to execute startup code at least twice (`board_init()`)
|
||||
- Declare `FEATURES_PROVIDED += riotboot` to pull the rigth dependencies
|
||||
- Being able to flash binary files, if integration with the build
|
||||
system is required for flashing
|
||||
|
||||
The above requirements are usually met if the board succeeds to execute
|
||||
the riotboot test in tests/.
|
||||
|
||||
# Usage
|
||||
Just compile your application using the target `riotboot`. The header
|
||||
is generated automatically according to your `APP_VER`, which can be
|
||||
optionally set (0 by default) in your makefile.
|
||||
|
||||
## Flashing
|
||||
The image can be flashed using `riotboot/flash` which also flashes
|
||||
the bootloader.
|
||||
|
||||
e.g. `BOARD=samr21-xpro FEATURES_REQUIRED+=riotboot APP_VER=$(date +%s) make -C examples/hello-world riotboot/flash`
|
||||
|
||||
The command compiles both the hello-world example and riotboot,
|
||||
generates the header and attaches it at the beginning of the example
|
||||
binary.
|
||||
|
||||
A comprehensive test is available at tests/riotboot.
|
46
bootloaders/riotboot/main.c
Normal file
46
bootloaders/riotboot/main.c
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* Inria
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup bootloaders RIOT compatible bootloaders
|
||||
* @ingroup bootloaders
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Minimal riot-based bootloader
|
||||
*
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* @author Francisco Acosta <francisco.acosta@inria.fr>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "panic.h"
|
||||
#include "riotboot/slot.h"
|
||||
|
||||
void kernel_init(void)
|
||||
{
|
||||
/* bootloader boots only slot 0 if it is valid */
|
||||
unsigned slot = 0;
|
||||
|
||||
if (riotboot_slot_validate(slot) == 0) {
|
||||
riotboot_slot_jump(slot);
|
||||
}
|
||||
|
||||
/* serious trouble! */
|
||||
while (1) {}
|
||||
}
|
||||
|
||||
NORETURN void core_panic(core_panic_t crash_code, const char *message)
|
||||
{
|
||||
(void)crash_code;
|
||||
(void)message;
|
||||
while (1) {}
|
||||
}
|
122
makefiles/boot/riotboot.mk
Normal file
122
makefiles/boot/riotboot.mk
Normal file
@ -0,0 +1,122 @@
|
||||
ifneq (,$(filter riotboot,$(FEATURES_USED)))
|
||||
|
||||
.PHONY: riotboot/flash riotboot/flash-bootloader riotboot/flash-slot0 riotboot/bootloader/%
|
||||
|
||||
RIOTBOOT_DIR = $(RIOTBASE)/bootloaders/riotboot
|
||||
RIOTBOOT ?= $(RIOTBOOT_DIR)/bin/$(BOARD)/riotboot.elf
|
||||
CFLAGS += -I$(BINDIR)/riotbuild
|
||||
|
||||
HEADER_TOOL_DIR = $(RIOTBASE)/dist/tools/riotboot_gen_hdr
|
||||
HEADER_TOOL ?= $(HEADER_TOOL_DIR)/bin/genhdr
|
||||
BINDIR_APP = $(BINDIR)/$(APPLICATION)
|
||||
|
||||
# Indicate the reserved space for a header, 256B by default
|
||||
# Notice that it must be 256B aligned. This is restricted by
|
||||
# the Cortex-M0+/3/4/7 architecture
|
||||
RIOTBOOT_HDR_LEN ?= 0x100
|
||||
|
||||
# Export variables for 'riotboot_slot'
|
||||
export SLOT0_LEN
|
||||
|
||||
# By default, slot 0 is found just after RIOTBOOT_LEN. It might
|
||||
# be overridden to add more offset as needed.
|
||||
export SLOT0_OFFSET ?= $(RIOTBOOT_LEN)
|
||||
|
||||
# Mandatory APP_VER, set to 0 by default
|
||||
APP_VER ?= 0
|
||||
|
||||
# Final target for slot 0 with riot_hdr
|
||||
SLOT0_RIOT_BIN = $(BINDIR_APP)-slot0.riot.bin
|
||||
|
||||
# For slot generation only link is needed
|
||||
$(BINDIR_APP)-%.elf: link
|
||||
$(Q)$(_LINK) -o $@
|
||||
|
||||
# Slot 0 firmware offset, after header
|
||||
SLOT0_IMAGE_OFFSET := $$(($(RIOTBOOT_LEN) + $(RIOTBOOT_HDR_LEN)))
|
||||
|
||||
# Link slots ELF *after* riot_hdr and limit the ROM to the slots length
|
||||
$(BINDIR_APP)-slot0.elf: FW_ROM_LEN=$$((SLOT0_LEN - $(RIOTBOOT_HDR_LEN)))
|
||||
$(BINDIR_APP)-slot0.elf: ROM_OFFSET=$(SLOT0_IMAGE_OFFSET)
|
||||
|
||||
# Create binary target with RIOT header
|
||||
$(SLOT0_RIOT_BIN): %.riot.bin: %.hdr %.bin
|
||||
@echo "creating $@..."
|
||||
$(Q)cat $^ > $@
|
||||
|
||||
# Compile header tool if it doesn't exist, force its compilation in case
|
||||
# some files changed
|
||||
$(HEADER_TOOL): FORCE
|
||||
@echo "compiling $@..."
|
||||
$(Q)/usr/bin/env -i \
|
||||
QUIET=$(QUIET) \
|
||||
PATH=$(PATH) \
|
||||
$(MAKE) --no-print-directory -C $(HEADER_TOOL_DIR) all
|
||||
|
||||
# Generate RIOT header and keep the original binary file
|
||||
# It must be always regenerated in case of any changes, so FORCE
|
||||
.PRECIOUS: %.bin
|
||||
%.hdr: $(HEADER_TOOL) %.bin FORCE
|
||||
$(Q)$(HEADER_TOOL) generate $< $(APP_VER) $$(($(ROM_START_ADDR)+$(OFFSET))) $(RIOTBOOT_HDR_LEN) - > $@
|
||||
|
||||
$(BINDIR_APP)-slot0.hdr: OFFSET=$(SLOT0_IMAGE_OFFSET)
|
||||
|
||||
# Generic target to create a binary file from the image with header
|
||||
riotboot: $(SLOT0_RIOT_BIN)
|
||||
|
||||
# riotboot bootloader compile target
|
||||
riotboot/flash-bootloader: riotboot/bootloader/flash
|
||||
riotboot/bootloader/%:
|
||||
$(Q)/usr/bin/env -i \
|
||||
QUIET=$(QUIET)\
|
||||
PATH=$(PATH) BOARD=$(BOARD) \
|
||||
$(MAKE) --no-print-directory -C $(RIOTBOOT_DIR) $*
|
||||
|
||||
# Generate a binary file from the bootloader which fills all the
|
||||
# allocated space. This is used afterwards to create a combined
|
||||
# binary with both bootloader and RIOT firmware with header
|
||||
BOOTLOADER_BIN = $(RIOTBOOT_DIR)/bin/$(BOARD)
|
||||
$(BOOTLOADER_BIN)/riotboot.extended.bin: $(BOOTLOADER_BIN)/riotboot.bin
|
||||
$(Q)cp $^ $@.tmp
|
||||
$(Q)truncate -s $$(($(RIOTBOOT_LEN))) $@.tmp
|
||||
$(Q)mv $@.tmp $@
|
||||
|
||||
# Only call sub make if not already in riotboot
|
||||
ifneq ($(BOOTLOADER_BIN)/riotboot.bin,$(BINFILE))
|
||||
$(BOOTLOADER_BIN)/riotboot.bin: riotboot/bootloader/binfile
|
||||
endif
|
||||
|
||||
# Create combined binary booloader + RIOT firmware with header
|
||||
RIOTBOOT_COMBINED_BIN = $(BINDIR_APP)-slot0-combined.bin
|
||||
riotboot/combined-slot0: $(RIOTBOOT_COMBINED_BIN)
|
||||
$(RIOTBOOT_COMBINED_BIN): $(BOOTLOADER_BIN)/riotboot.extended.bin $(SLOT0_RIOT_BIN)
|
||||
$(Q)cat $^ > $@
|
||||
|
||||
# Flashing rule for edbg to flash combined binaries
|
||||
riotboot/flash-combined-slot0: HEXFILE=$(RIOTBOOT_COMBINED_BIN)
|
||||
# Flashing rule for openocd to flash combined binaries
|
||||
riotboot/flash-combined-slot0: ELFFILE=$(RIOTBOOT_COMBINED_BIN)
|
||||
riotboot/flash-combined-slot0: $(RIOTBOOT_COMBINED_BIN) $(FLASHDEPS)
|
||||
$(FLASHER) $(FFLAGS)
|
||||
|
||||
# Flashing rule for slot 0
|
||||
riotboot/flash-slot0: export IMAGE_OFFSET=$(SLOT0_OFFSET)
|
||||
# Flashing rule for edbg to flash only slot 0
|
||||
riotboot/flash-slot0: HEXFILE=$(SLOT0_RIOT_BIN)
|
||||
# openocd
|
||||
riotboot/flash-slot0: ELFFILE=$(SLOT0_RIOT_BIN)
|
||||
riotboot/flash-slot0: $(SLOT0_RIOT_BIN) $(FLASHDEPS)
|
||||
$(FLASHER) $(FFLAGS)
|
||||
|
||||
# Targets to generate only slot 0 binary
|
||||
riotboot/slot0: $(SLOT0_RIOT_BIN)
|
||||
|
||||
# Default flashing rule for bootloader + slot 0
|
||||
riotboot/flash: riotboot/flash-slot0 riotboot/flash-bootloader
|
||||
|
||||
else
|
||||
riotboot:
|
||||
$(Q)echo "error: riotboot feature not selected! (try FEATURES_REQUIRED += riotboot)"
|
||||
$(Q)false
|
||||
|
||||
endif # (,$(filter riotboot,$(FEATURES_USED)))
|
Loading…
Reference in New Issue
Block a user