mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-17 04:52:59 +01:00
b/c/particle-mesh: Support particle bootloader
Closes: https://github.com/RIOT-OS/RIOT/issues/12320
This commit is contained in:
parent
c3b70d5a65
commit
7582be6d5e
@ -9,18 +9,36 @@ PORT_DARWIN ?= $(firstword $(sort $(wildcard /dev/tty.SLAB_USBtoUART*)))
|
|||||||
# add the common header files to the include path
|
# add the common header files to the include path
|
||||||
INCLUDES += -I$(RIOTBOARD)/common/particle-mesh/include
|
INCLUDES += -I$(RIOTBOARD)/common/particle-mesh/include
|
||||||
|
|
||||||
# This board uses a DAP-Link programmer
|
ifeq (1,$(PARTICLE_MONOFIRMWARE))
|
||||||
# Flashing support is provided through pyocd (default) and openocd.
|
CFLAGS += -DPARTICLE_MONOFIRMWARE
|
||||||
# For openocd, a version built against the development branch and containing
|
ROM_OFFSET = 0x30000
|
||||||
# the support for nrf52 cpu is required.
|
FW_ROM_LEN = 0xc4000
|
||||||
PROGRAMMER ?= pyocd
|
FLASHFILE = $(BINFILE)-checksummed
|
||||||
ifeq (pyocd,$(PROGRAMMER))
|
# Setting DFU_ARGS won't work as the implied --reset causes errors.
|
||||||
# The board is not recognized automatically by pyocd, so the CPU target
|
FFLAGS = -d 0x2B04:0xD00E -a 0 -s 0x30000:leave -D $(FLASHFILE)
|
||||||
# option is passed explicitly
|
PROGRAMMER = dfu-util
|
||||||
FLASH_TARGET_TYPE ?= -t nrf52840
|
include $(RIOTMAKE)/tools/dfu.inc.mk
|
||||||
include $(RIOTMAKE)/tools/pyocd.inc.mk
|
else
|
||||||
else ifeq (openocd,$(PROGRAMMER))
|
# This board uses a DAP-Link programmer
|
||||||
DEBUG_ADAPTER ?= dap
|
# Flashing support is provided through pyocd (default) and openocd.
|
||||||
|
# For openocd, a version built against the development branch and containing
|
||||||
|
# the support for nrf52 cpu is required.
|
||||||
|
PROGRAMMER ?= pyocd
|
||||||
|
ifeq (pyocd,$(PROGRAMMER))
|
||||||
|
# The board is not recognized automatically by pyocd, so the CPU target
|
||||||
|
# option is passed explicitly
|
||||||
|
FLASH_TARGET_TYPE ?= -t nrf52840
|
||||||
|
include $(RIOTMAKE)/tools/pyocd.inc.mk
|
||||||
|
else ifeq (openocd,$(PROGRAMMER))
|
||||||
|
DEBUG_ADAPTER ?= dap
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
MONOFIRMWARETOOL = ${RIOTBOARD}/common/particle-mesh/monofirmware-tool.py
|
||||||
|
|
||||||
|
# Rule (variations) for building a monofirmware populated with the right
|
||||||
|
# (lengths and) checksum in it; only used iuf PARTICLE_MONOFIRMWARE=1
|
||||||
|
%.bin-checksummed: %.bin %.elf ${MONOFIRMWARETOOL}
|
||||||
|
${MONOFIRMWARETOOL} $*.elf $< $@
|
||||||
|
|
||||||
include $(RIOTBOARD)/common/nrf52/Makefile.include
|
include $(RIOTBOARD)/common/nrf52/Makefile.include
|
||||||
|
@ -45,6 +45,31 @@ void board_nrfantenna_select(enum board_nrfantenna_selection choice)
|
|||||||
|
|
||||||
void board_init(void)
|
void board_init(void)
|
||||||
{
|
{
|
||||||
|
#ifdef PARTICLE_MONOFIRMWARE
|
||||||
|
/* For comparison with MicroPython's hook into this bootloader, they'd set
|
||||||
|
* SCB->VTOR here. That is necessary because while the particle bootloader
|
||||||
|
* largely mimics the ARM bootup by requiring a VTOR at the start of the
|
||||||
|
* writable firmware, it does not set the VTOR to the loaded data.
|
||||||
|
*
|
||||||
|
* That step is not executed *right* here as cpu_init will do it a few
|
||||||
|
* lines down anyway. */
|
||||||
|
|
||||||
|
/* Force keeping the metadata -- the __attribute__((used)) in their macro
|
||||||
|
* expansions and the KEEP on the section in the linker only almost do
|
||||||
|
* that: at least *something* from the object file needs to be referenced
|
||||||
|
* to pull them all in. */
|
||||||
|
extern uint32_t particle_monofirmware_padding;
|
||||||
|
uint32_t x;
|
||||||
|
x = (uint32_t)&particle_monofirmware_padding;
|
||||||
|
(void)x;
|
||||||
|
|
||||||
|
/* Clear out POWER_CLOCK and GPIOTE interrupts set by the bootloader. (If
|
||||||
|
* actual RIOT code enables them, it'll do so after the board_init call).
|
||||||
|
* */
|
||||||
|
NVIC_DisableIRQ(0);
|
||||||
|
NVIC_DisableIRQ(6);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* initialize the boards LEDs */
|
/* initialize the boards LEDs */
|
||||||
gpio_init(LED0_PIN, GPIO_OUT);
|
gpio_init(LED0_PIN, GPIO_OUT);
|
||||||
gpio_set(LED0_PIN);
|
gpio_set(LED0_PIN);
|
||||||
|
84
boards/common/particle-mesh/bootloader.c
Normal file
84
boards/common/particle-mesh/bootloader.c
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014-2016 Freie Universität Berlin
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @author Christian Amsüss <chrysn@fsfe.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef PARTICLE_MONOFIRMWARE
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "vectors_cortexm.h"
|
||||||
|
|
||||||
|
extern const void *_isr_vectors;
|
||||||
|
|
||||||
|
/* As described in Particle's dynalib/inc/module_info.h (there under LGPL-3
|
||||||
|
* terms; detailed license compatibility discussion skipped for not crossing
|
||||||
|
* any threshold of originality) */
|
||||||
|
typedef struct module_info_t {
|
||||||
|
const void* module_start_address;
|
||||||
|
const void* module_end_address;
|
||||||
|
uint8_t reserved;
|
||||||
|
uint8_t flags;
|
||||||
|
uint16_t module_version;
|
||||||
|
uint16_t platform_id;
|
||||||
|
uint8_t module_function;
|
||||||
|
uint8_t module_index;
|
||||||
|
uint32_t dependency; /* was module_dependency_t; kept in here to ensure it's blank */
|
||||||
|
uint32_t dependency2; /* was module_dependency_t */
|
||||||
|
} module_info_t;
|
||||||
|
|
||||||
|
/* 64 words is the distance between the known ISR table and the fixed-position
|
||||||
|
* module_info at 0x200. If the ISR length is changed for any reason, this must
|
||||||
|
* be adapted until particle_monofirmware_module_info is precisely at 0x30200.
|
||||||
|
* (monofirmware-tool.py will complain if it isn't).
|
||||||
|
*
|
||||||
|
* This is not `static` as *something* from this module needs to be visible to
|
||||||
|
* the outside for the __attribute__((used)) and the KEEP on the section in the
|
||||||
|
* linker script to work; see also the reference to it in board.c. */
|
||||||
|
ISR_VECTOR(50) const uint32_t particle_monofirmware_padding[64] = {0, };
|
||||||
|
|
||||||
|
#ifdef PARTICLE_MONOFIRMWARE_CHECKSUMLIMIT
|
||||||
|
/* Watch the ISR vector sequence here: This is placed *after* the module_info
|
||||||
|
* in the flash (52 > 51), but placed before it in the code to be referencable
|
||||||
|
* from there.
|
||||||
|
*
|
||||||
|
* The actual value is replaced by monofirmware-tool.py.
|
||||||
|
* */
|
||||||
|
ISR_VECTOR(52) static const uint8_t particle_monofirmware_checksum[4] = {0, 0, 0, 0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ISR_VECTOR(51) static const struct module_info_t particle_monofirmware_module_info = {
|
||||||
|
.module_start_address = &_isr_vectors,
|
||||||
|
#ifdef PARTICLE_MONOFIRMWARE_CHECKSUMLIMIT
|
||||||
|
.module_end_address = &particle_monofirmware_checksum,
|
||||||
|
/* else, it is set by the monofirmware-tool.
|
||||||
|
*
|
||||||
|
* For that (more common) case where we check the full firmware, we would
|
||||||
|
* want to set set
|
||||||
|
*
|
||||||
|
* .module_end_address = &_etext + (&_erelocate - &_srelocate),
|
||||||
|
*
|
||||||
|
* but that can not be determined at compile time. Instead, this will be
|
||||||
|
* populated by monofirmware-tool.py, which needs to write in here anyway
|
||||||
|
* for the checksum. */
|
||||||
|
#endif
|
||||||
|
.module_version = 0x138, /* module version that micropython uses */
|
||||||
|
|
||||||
|
.platform_id = PARTICLE_PLATFORM_ID,
|
||||||
|
|
||||||
|
.module_index = 3, /* MOD_FUNC_MONO_FIRMWARE from dynalib/inc/module_info.h */
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
typedef int dont_be_pedantic;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @} */
|
@ -35,6 +35,38 @@ To flash the board with OpenOCD, use the `PROGRAMMER` variable:
|
|||||||
PROGRAMMER=openocd make BOARD=<board name> -C examples/hello-world flash
|
PROGRAMMER=openocd make BOARD=<board name> -C examples/hello-world flash
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Alternative flashing procedure: Particle bootloader and DFU-Util
|
||||||
|
|
||||||
|
As an alternative to using the Particle Mesh debugger,
|
||||||
|
the USB DFU mode bootloader shipped with Particle Mesh devices can be used.
|
||||||
|
That mode can be entered by keeping the SETUP/MODE button pressed after power-up or reset
|
||||||
|
until the RGB LED **blinks yellow fast**.
|
||||||
|
|
||||||
|
In this mode, the bootloader and Soft Device are kept in place,
|
||||||
|
reducing the available flash memory to 784kB,
|
||||||
|
but in turn, devices can easily be switched back to using Particle software.
|
||||||
|
|
||||||
|
In order to use this mode, set `PARTICLE_MONOFIRMWARE=1` wherever you set the `BOARD`.
|
||||||
|
This will trigger the use of the appropriate linker script,
|
||||||
|
and switch in [`dfu-util`](http://dfu-util.sourceforge.net/) as the default programmer,
|
||||||
|
which needs to be installed.
|
||||||
|
<!-- Using PARTICLE_MONOFIRMWARE=1 images with an external programmer is untested but might just work once checksums are static. -->
|
||||||
|
|
||||||
|
On Linux hosts, devices for the DFU mode are frequently only accessible to root,
|
||||||
|
causing error messages like
|
||||||
|
|
||||||
|
dfu-util: Cannot open DFU device 2b04:d00e
|
||||||
|
dfu-util: No DFU capable USB device available
|
||||||
|
|
||||||
|
To make Particle devices writable for all users,
|
||||||
|
follow [Particle's instructions](https://docs.particle.io/support/particle-tools-faq/installing-dfu-util/)
|
||||||
|
on setting up dfu-util.
|
||||||
|
|
||||||
|
The Particle bootloader checks the firmware's checksum at startup time.
|
||||||
|
As these checks would be frustrated by flash writes inside RIOT (typically using the @ref drivers_periph_flashpage),
|
||||||
|
the CFLAG @ref PARTICLE_MONOFIRMWARE_CHECKSUMLIMIT "-DPARTICLE_MONOFIRMWARE_CHECKSUMLIMIT" can be set.
|
||||||
|
Then, the checksum is only calculated over the memory region that contains the interrupt vector and the bootloader manifest.
|
||||||
|
|
||||||
### Reset the board
|
### Reset the board
|
||||||
|
|
||||||
The on-board reset button doesn't work, so to trigger a reset of the board, use
|
The on-board reset button doesn't work, so to trigger a reset of the board, use
|
||||||
|
@ -26,6 +26,61 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @name Bootloader configuration options
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @brief Build a firmware suitable for the Particle bootloader
|
||||||
|
*
|
||||||
|
* If this is defined, additional metadata about the firmware is included in
|
||||||
|
* the firmware (called the module_info in Particle), and additional code is
|
||||||
|
* inserted in board setup.
|
||||||
|
*
|
||||||
|
* Do not define this manually; instead, set `PARTICLE_MONOFIRMWARE=1` as an
|
||||||
|
* variable in the build scripts like `BOARD` is defined, and the particle
|
||||||
|
* common make file defines it and configures suitable postprocessing of the
|
||||||
|
* binary.
|
||||||
|
*
|
||||||
|
* @see @ref boards_common_particle-mesh
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifdef DOXYGEN
|
||||||
|
#define PARTICLE_MONOFIRMWARE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @brief Limit Particle bootloader checksumming to the binary start
|
||||||
|
*
|
||||||
|
* If this define is set in the Makefile, the binary size announced to the
|
||||||
|
* bootloader is limited to the reset vector and the firmware metadata, and
|
||||||
|
* only that part is checksummed.
|
||||||
|
*
|
||||||
|
* This is useful when @ref drivers_periph_flashpage is used, as otherwise the
|
||||||
|
* firmware's writes on itself would invalidate its checksum.
|
||||||
|
*
|
||||||
|
* @see @ref boards_common_particle-mesh
|
||||||
|
*/
|
||||||
|
#ifdef DOXYGEN
|
||||||
|
#define PARTICLE_MONOFIRMWARE_CHECKSUMLIMIT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @brief Platform ID of the board for the Particle bootloader
|
||||||
|
*
|
||||||
|
* This is set by the individual board's build configuration, and gets used
|
||||||
|
* when building with @ref PARTICLE_MONOFIRMWARE; then, it is put into the
|
||||||
|
* module information for the board bootloader to verify that the firmware was
|
||||||
|
* built for the right device.
|
||||||
|
*
|
||||||
|
* The individual values are documented in the Particle DeviceOS source code in
|
||||||
|
* `build/platform-id.mk`.
|
||||||
|
*/
|
||||||
|
#ifdef DOXYGEN
|
||||||
|
#define PARTICLE_PLATFORM_ID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name LED pin configuration
|
* @name LED pin configuration
|
||||||
* @{
|
* @{
|
||||||
|
88
boards/common/particle-mesh/monofirmware-tool.py
Executable file
88
boards/common/particle-mesh/monofirmware-tool.py
Executable file
@ -0,0 +1,88 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""Verify that a pair of .bin/.elf has the particle_monofirmware_module_info at
|
||||||
|
the right place. If there is a particle_monofirmware_checksum symbol, verify
|
||||||
|
that the module_info says the firmware ends at it, and populate the
|
||||||
|
checksum. Otherwise, append the checksum and set the end-of-firmware field
|
||||||
|
accordingly."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import zlib
|
||||||
|
import subprocess
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
expected_romstart = 0x30000
|
||||||
|
expected_moduleinfo_position = expected_romstart + 0x200
|
||||||
|
expected_moduleinfo_size = 0x18
|
||||||
|
# offsetof(module_end_address, module_info_t)
|
||||||
|
end_field_offset = 4
|
||||||
|
end_field_start = expected_moduleinfo_position - expected_romstart + end_field_offset
|
||||||
|
end_field_end = end_field_start + 4
|
||||||
|
|
||||||
|
|
||||||
|
def check(condition, message):
|
||||||
|
if not condition:
|
||||||
|
print(message, file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
p = argparse.ArgumentParser(description=__doc__)
|
||||||
|
p.add_argument("elf_in", help="Program in ELF form")
|
||||||
|
p.add_argument("bin_in", help="objdump'd ROM binary of elf_in")
|
||||||
|
p.add_argument("bin_out", help="File to write the modified bin to")
|
||||||
|
args = p.parse_args()
|
||||||
|
|
||||||
|
symbols = subprocess.check_output([
|
||||||
|
os.environ.get('NM', 'nm'),
|
||||||
|
"--format=posix",
|
||||||
|
"--print-size",
|
||||||
|
"--defined-only",
|
||||||
|
args.elf_in,
|
||||||
|
])
|
||||||
|
symbols = [line.split(' ') for line in symbols.decode('ascii').split('\n') if line]
|
||||||
|
position = {s[0]: int(s[2], 16) for s in symbols}
|
||||||
|
size = {s[0]: int(s[3], 16) for s in symbols if s[3]}
|
||||||
|
symtype = {s[0]: s[1] for s in symbols}
|
||||||
|
|
||||||
|
romstart = min(pos for (name, pos) in position.items() if symtype[name] not in '?A')
|
||||||
|
|
||||||
|
check(romstart == expected_romstart, "Defined symbols do not start at %#x" % expected_romstart)
|
||||||
|
|
||||||
|
check(position.get('particle_monofirmware_module_info') == expected_moduleinfo_position,
|
||||||
|
"""Struct particle_monofirmware_module_info not found at %#x but on %#x,
|
||||||
|
adjust the size of particle_monofirmware_padding to match""" %
|
||||||
|
(expected_moduleinfo_position, position.get('particle_monofirmware_module_info')))
|
||||||
|
check(size.get('particle_monofirmware_module_info') == expected_moduleinfo_size,
|
||||||
|
"Struct particle_monofirmware_module_info not of expected size %d" % expected_moduleinfo_size)
|
||||||
|
|
||||||
|
binary = bytearray(open(args.bin_in, 'rb').read())
|
||||||
|
|
||||||
|
indicated_start = int.from_bytes(binary[expected_moduleinfo_position - romstart:][:4], 'little')
|
||||||
|
check(romstart == indicated_start,
|
||||||
|
"particle_monofirmware_module_info.module_start does not point to the module start")
|
||||||
|
|
||||||
|
if 'particle_monofirmware_checksum' in position:
|
||||||
|
print("monifirmware-tool: Checksum symbol found, populating it.")
|
||||||
|
check(size.get('particle_monofirmware_checksum') == 4, "Checksum not of expected size 4")
|
||||||
|
|
||||||
|
checksum_until = position['particle_monofirmware_checksum'] - romstart
|
||||||
|
|
||||||
|
indicated_end = int.from_bytes(binary[end_field_start:end_field_end], 'little')
|
||||||
|
check(indicated_end == position['particle_monofirmware_checksum'],
|
||||||
|
"particle_monofirmware_module_info.module_end does not point to particle_monofirmware_checksum")
|
||||||
|
else:
|
||||||
|
print("monifirmware-tool: No checksum symbol found, appending the checksum to the binary.")
|
||||||
|
checksum_until = len(binary)
|
||||||
|
|
||||||
|
# Populating the length: When the whole binary (including the rom
|
||||||
|
# initialization block) is checksummed, the end address can't be populated
|
||||||
|
# from C because that would require evaluating `&_etext + (&_erelocate -
|
||||||
|
# &_srelocate)` in advance
|
||||||
|
binary[end_field_start:end_field_end] = (checksum_until + romstart).to_bytes(4, 'little')
|
||||||
|
|
||||||
|
checksum = zlib.crc32(binary[:checksum_until]).to_bytes(4, 'big')
|
||||||
|
binary[checksum_until:checksum_until + 4] = checksum
|
||||||
|
|
||||||
|
with open(args.bin_out, "wb") as o:
|
||||||
|
o.write(binary)
|
@ -1 +1,2 @@
|
|||||||
|
CFLAGS += "-DPARTICLE_PLATFORM_ID=12"
|
||||||
include $(RIOTBOARD)/common/particle-mesh/Makefile.include
|
include $(RIOTBOARD)/common/particle-mesh/Makefile.include
|
||||||
|
@ -1 +1,2 @@
|
|||||||
|
CFLAGS += "-DPARTICLE_PLATFORM_ID=13"
|
||||||
include $(RIOTBOARD)/common/particle-mesh/Makefile.include
|
include $(RIOTBOARD)/common/particle-mesh/Makefile.include
|
||||||
|
@ -1 +1,2 @@
|
|||||||
|
CFLAGS += "-DPARTICLE_PLATFORM_ID=14"
|
||||||
include $(RIOTBOARD)/common/particle-mesh/Makefile.include
|
include $(RIOTBOARD)/common/particle-mesh/Makefile.include
|
||||||
|
Loading…
Reference in New Issue
Block a user