1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-25 14:33:52 +01:00

Merge pull request #7209 from kYc0o/mcuboot_nrf52dk

Add MCUBoot linking options for RIOT images
This commit is contained in:
Francisco Acosta 2017-07-17 15:12:57 +02:00 committed by GitHub
commit 1523186f99
19 changed files with 710 additions and 15 deletions

View File

@ -1,7 +1,7 @@
# include Makefile.local if it exists
-include Makefile.local
all:
all: link
# set undefined variables
RIOTBASE ?= $(dir $(lastword $(MAKEFILE_LIST)))
@ -279,7 +279,7 @@ endif
BASELIBS += $(BINDIR)/${APPLICATION}.a
BASELIBS += $(APPDEPS)
.PHONY: all clean flash term doc debug debug-server reset objdump help info-modules
.PHONY: all link clean flash term doc debug debug-server reset objdump help info-modules
.PHONY: ..in-docker-container
ELFFILE ?= $(BINDIR)/$(APPLICATION).elf
@ -293,17 +293,19 @@ LINKFLAGPREFIX ?= -Wl,
DIRS += $(EXTERNAL_MODULE_DIRS)
_LINK = $(if $(CPPMIX),$(CXX),$(LINK)) $(UNDEF) $(LINKFLAGPREFIX)--start-group $(BASELIBS) -lm $(LINKFLAGPREFIX)--end-group $(LINKFLAGPREFIX)-Map=$(BINDIR)/$(APPLICATION).map $(LINKFLAGPREFIX)--cref $(LINKFLAGS)
ifeq ($(BUILD_IN_DOCKER),1)
all: ..in-docker-container
link: ..in-docker-container
else
## make script for your application. Build RIOT-base here!
all: ..compiler-check ..build-message $(RIOTBUILD_CONFIG_HEADER_C) $(USEPKG:%=${BINDIR}/%.a) $(APPDEPS)
link: ..compiler-check ..build-message $(RIOTBUILD_CONFIG_HEADER_C) $(USEPKG:%=${BINDIR}/%.a) $(APPDEPS)
$(Q)DIRS="$(DIRS)" "$(MAKE)" -C $(APPDIR) -f $(RIOTMAKE)/application.inc.mk
ifeq (,$(RIOTNOLINK))
ifeq ($(BUILDOSXNATIVE),1)
$(Q)$(if $(CPPMIX),$(CXX),$(LINK)) $(UNDEF) -o $(ELFFILE) $$(find $(BASELIBS) -size +8c) $(LINKFLAGS) $(LINKFLAGPREFIX)-no_pie
else
$(Q)$(if $(CPPMIX),$(CXX),$(LINK)) $(UNDEF) -o $(ELFFILE) $(LINKFLAGPREFIX)--start-group $(BASELIBS) -lm $(LINKFLAGPREFIX)--end-group $(LINKFLAGPREFIX)-Map=$(BINDIR)/$(APPLICATION).map $(LINKFLAGPREFIX)--cref $(LINKFLAGS)
$(Q)$(_LINK) -o $(ELFFILE)
endif
$(Q)$(SIZE) $(ELFFILE)
$(Q)$(OBJCOPY) $(OFLAGS) $(ELFFILE) $(HEXFILE)
@ -567,3 +569,6 @@ endif
CFLAGS := $(patsubst -D%,,$(CFLAGS))
CFLAGS := $(patsubst -U%,,$(CFLAGS))
CFLAGS += -include '$(RIOTBUILD_CONFIG_HEADER_C)'
# include multislot support
include $(RIOTMAKE)/multislot.mk

View File

@ -12,7 +12,7 @@ export JLINK_DEVICE := nrf52
# special options when using SoftDevice
ifneq (,$(filter nordic_softdevice_ble,$(USEPKG)))
export JLINK_PRE_FLASH := erase\nloadfile $(BINDIR)/softdevice.hex
export JLINK_FLASH_ADDR := 0x1f000
export FLASH_ADDR := 0x1f000
export LINKER_SCRIPT ?= $(RIOTCPU)/$(CPU)/ldscripts/$(CPU_MODEL)_sd.ld
endif
include $(RIOTMAKE)/tools/jlink.inc.mk

View File

@ -12,7 +12,7 @@ export JLINK_DEVICE := nrf52
# special options when using SoftDevice
ifneq (,$(filter nordic_softdevice_ble,$(USEPKG)))
export JLINK_PRE_FLASH := erase\nloadfile $(BINDIR)/softdevice.hex
export JLINK_FLASH_ADDR := 0x1f000
export FLASH_ADDR := 0x1f000
export LINKER_SCRIPT ?= $(RIOTCPU)/$(CPU)/ldscripts/$(CPU_MODEL)_sd.ld
endif
include $(RIOTMAKE)/tools/jlink.inc.mk

View File

@ -3,3 +3,5 @@ INCLUDES += -I$(RIOTCPU)/cortexm_common/include
INCLUDES += -I$(RIOTCPU)/cortexm_common/include/vendor
USEMODULE += cortexm_common_periph
export IMAGE_HDR_SIZE ?= 512

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) 2017 Kaspar Schleiser
*
* 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.
*/
/**
* @addtogroup cpu_cortex_common
* @{
*
* @file
* @brief Offset definitions for cortex MCUs
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
*
* @}
*/
boot_offset = DEFINED( offset ) ? offset : 0x0 ;
rom_length = DEFINED( length ) ? length : rom_length ;

View File

@ -1,5 +1,11 @@
export CPU_ARCH = cortex-m4f
export CPU_FAM = nrf52
# Export internal ROM alignment and slot sizes for bootloader support
export IMAGE_ALIGN = 8
export SLOT0_SIZE = 0x8000
export SLOT1_SIZE = 0x3C000
export SLOT2_SIZE = 0x3C000
include $(RIOTCPU)/nrf5x_common/Makefile.include
include $(RIOTMAKE)/arch/cortexm.inc.mk

View File

@ -18,10 +18,14 @@
* @}
*/
rom_length = 512K;
INCLUDE multislot.ld
MEMORY
{
rom (rx) : ORIGIN = 0x00000000, LENGTH = 512K
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
rom (rx) : ORIGIN = 0x00000000 + boot_offset, LENGTH = rom_length
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
}
INCLUDE cortexm_base.ld

View File

@ -12,7 +12,7 @@
# JLINK_SERIAL: Device serial used by JLink
# JLINK_IF: Interface used by JLink, default: "SWD"
# JLINK_SPEED: Interface clock speed to use (in kHz), default "2000"
# JLINK_FLASH_ADDR: Starting address of the target's flash memory, default: "0"
# FLASH_ADDR: Starting address of the target's flash memory, default: "0"
# JLINK_PRE_FLASH: Additional JLink commands to execute before flashing
# JLINK_POST_FLASH: Additional JLink commands to execute after flashing
#
@ -57,8 +57,6 @@ _JLINK=JLinkExe
_JLINK_SERVER=JLinkGDBServer
_JLINK_IF=SWD
_JLINK_SPEED=2000
# default starting address of the devices flash memory
_FLASH_ADDR=0
#
# a couple of tests for certain configuration options
@ -86,8 +84,9 @@ test_config() {
echo "Error: No target device defined in JLINK_DEVICE env var"
exit 1
fi
if [ -z "${JLINK_FLASH_ADDR}" ]; then
JLINK_FLASH_ADDR=${_FLASH_ADDR}
if [ -z "${FLASH_ADDR}" ]; then
echo "Error: No flash address defined in FLASH_ADDR env var"
exit 1
fi
}
@ -142,7 +141,7 @@ do_flash() {
if [ ! -z "${JLINK_PRE_FLASH}" ]; then
printf "${JLINK_PRE_FLASH}\n" >> ${BINDIR}/burn.seg
fi
echo "loadbin ${HEXFILE} ${JLINK_FLASH_ADDR}" >> ${BINDIR}/burn.seg
echo "loadbin ${HEXFILE} ${FLASH_ADDR}" >> ${BINDIR}/burn.seg
if [ ! -z "${JLINK_POST_FLASH}" ]; then
printf "${JLINK_POST_FLASH}\n" >> ${BINDIR}/burn.seg
fi
@ -211,6 +210,7 @@ shift # pop $1 from $@
case "${ACTION}" in
flash)
echo "### Flashing Target ###"
echo "### Flashing at address ${FLASH_ADDR} ###"
do_flash "$@"
;;
debug)

89
dist/tools/mcuboot/imgtool.md vendored Normal file
View File

@ -0,0 +1,89 @@
## Image tool
The Python program `imgtool.py` can be used to perform the
operations that are necessary to manage keys and sign images.
This program is written for Python3, and has several dependencies on
Python libraries. These can be installed using 'pip3' manually:
pip3 install --user pycrypto
pip3 install --user pyasn1
pip3 install --user ecdsa
or, on Ubuntu, using the package manager:
sudo apt-get install python3-crypto python3-pyasn1 python3-ecdsa
## Managing keys
This tool currently supports rsa-2048 and ecdsa-p256 keys. You can
generate a keypair for one of these types using the 'keygen' command:
./imgtool.py keygen -k filename.pem -t rsa-2048
or use ecdsa-p256 for the type. The key type used should match what
mcuboot is configured to verify.
This key file is what is used to sign images, this file should be
protected, and not widely distributed.
## Incorporating the public key into the code
There is a development key distributed with mcuboot that can be used
for testing. Since this private key is widely distributed, it should
never be used for production. Once you have generated a production
key, as described above, you should replace the public key in the
bootloader with the generated one.
For Zephyr, the keys live in the file `boot/zephyr/keys.c`. For
mynewt, follow the instructions in `doc/signed_images.md` to generate
the key file.
./imgtool.py getpub -k filename.pem
will extract the public key from the given private key file, and
output it as a C data structure. You can replace or insert this code
into the key file.
## Signing images
Image signing takes a binary image intended for Slot 0 and adds a
header and trailer that the bootloader is expecting:
usage: imgtool.py sign [-h] -k filename --align ALIGN -v VERSION -H
HEADER_SIZE [--pad PAD] [--rsa-pkcs1-15]
infile outfile
positional arguments:
infile
outfile
optional arguments:
-h, --help show this help message and exit
-k filename, --key filename
--align ALIGN
-v VERSION, --version VERSION
-H HEADER_SIZE, --header-size HEADER_SIZE
--included-header Image has gap for header
--pad PAD Pad image to this many bytes, adding trailer magic
--rsa-pkcs1-15 Use old PKCS#1 v1.5 signature algorithm
The main arguments given are the key file generated above, a version
field to place in the header (1.2.3 for example), the alignment of the
flash device in question, and the header size.
The header size depends on the operating system and the particular
flash device. For Zephyr, it will be configured as part of the build,
and will be a small power of two. By default, the header will be
prepended to the image. If `--included-header` is given, the image
must start with header-size bytes of zeros, and the header will be
overwritten over these bytes.
The optional --pad argument will place a trailer on the image that
indicates that the image should be considered an upgrade. Writing
this image in slot 1 will then cause the bootloader to upgrade to it.
Lastly, the --rsa-pkcs1-15 will cause the tool to use the older,
deprecated pkcs#1 v1.5 signing algorithm when using RSA. This can be
enabled in the bootloader as well, and may be needed if you are using
an older version of the bootloader.

100
dist/tools/mcuboot/imgtool.py vendored Executable file
View File

@ -0,0 +1,100 @@
#! /usr/bin/env python3
import argparse
from imgtool import keys
from imgtool import image
from imgtool import version
import sys
def gen_rsa2048(args):
keys.RSA2048.generate().export_private(args.key)
def gen_ecdsa_p256(args):
keys.ecdsa256p1.generate().export_private(args.key)
def gen_ecdsa_p224(args):
print("TODO: p-224 not yet implemented")
keygens = {
'rsa-2048': gen_rsa2048,
'ecdsa-p256': gen_ecdsa_p256,
'ecdsa-p224': gen_ecdsa_p224, }
def do_keygen(args):
if args.type not in keygens:
msg = "Unexpected key type: {}".format(args.type)
raise argparse.ArgumentTypeError(msg)
keygens[args.type](args)
def do_getpub(args):
key = keys.load(args.key)
key.emit_c()
def do_sign(args):
align = args.align
if args.rsa_pkcs1_15:
keys.sign_rsa_pss = False
img = image.Image.load(args.infile, version=args.version,
header_size=args.header_size,
included_header=args.included_header,
pad=args.pad)
key = keys.load(args.key) if args.key else None
img.sign(key)
if args.pad:
img.pad_to(args.pad, args.align)
img.save(args.outfile)
subcmds = {
'keygen': do_keygen,
'getpub': do_getpub,
'sign': do_sign, }
def alignment_value(text):
value = int(text)
if value not in [1, 2, 4, 8]:
msg = "{} must be one of 1, 2, 4 or 8".format(value)
raise argparse.ArgumentTypeError(msg)
return value
def intparse(text):
"""Parse a command line argument as an integer.
Accepts 0x and other prefixes to allow other bases to be used."""
return int(text, 0)
def args():
parser = argparse.ArgumentParser()
subs = parser.add_subparsers(help='subcommand help', dest='subcmd')
keygenp = subs.add_parser('keygen', help='Generate pub/private keypair')
keygenp.add_argument('-k', '--key', metavar='filename', required=True)
keygenp.add_argument('-t', '--type', metavar='type',
choices=['rsa-2048', 'ecdsa-p224', 'ecdsa-p256'],
required=True)
getpub = subs.add_parser('getpub', help='Get public key from keypair')
getpub.add_argument('-k', '--key', metavar='filename', required=True)
sign = subs.add_parser('sign', help='Sign an image with a private key')
sign.add_argument('-k', '--key', metavar='filename')
sign.add_argument("--align", type=alignment_value, required=True)
sign.add_argument("-v", "--version", type=version.decode_version, required=True)
sign.add_argument("-H", "--header-size", type=intparse, required=True)
sign.add_argument("--included-header", default=False, action='store_true',
help='Image has gap for header')
sign.add_argument("--pad", type=intparse,
help='Pad image to this many bytes, adding trailer magic')
sign.add_argument("--rsa-pkcs1-15", help='Use old PKCS#1 v1.5 signature algorithm',
default=False, action='store_true')
sign.add_argument("infile")
sign.add_argument("outfile")
args = parser.parse_args()
if args.subcmd is None:
print('Must specify a subcommand', file=sys.stderr)
sys.exit(1)
subcmds[args.subcmd](args)
if __name__ == '__main__':
args()

View File

172
dist/tools/mcuboot/imgtool/image.py vendored Normal file
View File

@ -0,0 +1,172 @@
"""
Image signing and management.
"""
from . import version as versmod
import hashlib
import struct
IMAGE_MAGIC = 0x96f3b83c
IMAGE_HEADER_SIZE = 32
# Image header flags.
IMAGE_F = {
'PIC': 0x0000001,
'SHA256': 0x0000002,
'PKCS15_RSA2048_SHA256': 0x0000004,
'ECDSA224_SHA256': 0x0000008,
'NON_BOOTABLE': 0x0000010,
'ECDSA256_SHA256': 0x0000020,
'PKCS1_PSS_RSA2048_SHA256': 0x0000040, }
TLV_VALUES = {
'SHA256': 1,
'RSA2048': 2,
'ECDSA224': 3,
'ECDSA256': 4, }
TLV_HEADER_SIZE = 4
# Sizes of the image trailer, depending on image alignment.
trailer_sizes = {
1: 402,
2: 788,
4: 1560,
8: 3104, }
boot_magic = bytes([
0x77, 0xc2, 0x95, 0xf3,
0x60, 0xd2, 0xef, 0x7f,
0x35, 0x52, 0x50, 0x0f,
0x2c, 0xb6, 0x79, 0x80, ])
class TLV():
def __init__(self):
self.buf = bytearray()
def add(self, kind, payload):
"""Add a TLV record. Kind should be a string found in TLV_VALUES above."""
buf = struct.pack('<BBH', TLV_VALUES[kind], 0, len(payload))
self.buf += buf
self.buf += payload
def get(self):
return bytes(self.buf)
class Image():
@classmethod
def load(cls, path, included_header=False, **kwargs):
"""Load an image from a given file"""
with open(path, 'rb') as f:
payload = f.read()
obj = cls(**kwargs)
obj.payload = payload
# Add the image header if needed.
if not included_header and obj.header_size > 0:
obj.payload = (b'\000' * obj.header_size) + obj.payload
obj.check()
return obj
def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE, pad=0):
self.version = version or versmod.decode_version("0")
self.header_size = header_size or IMAGE_HEADER_SIZE
self.pad = pad
def __repr__(self):
return "<Image version={}, header_size={}, pad={}, payloadlen=0x{:x}>".format(
self.version,
self.header_size,
self.pad,
len(self.payload))
def save(self, path):
with open(path, 'wb') as f:
f.write(self.payload)
def check(self):
"""Perform some sanity checking of the image."""
# If there is a header requested, make sure that the image
# starts with all zeros.
if self.header_size > 0:
if any(v != 0 for v in self.payload[0:self.header_size]):
raise Exception("Padding requested, but image does not start with zeros")
def sign(self, key):
self.add_header(key)
tlv = TLV()
# Note that ecdsa wants to do the hashing itself, which means
# we get to hash it twice.
sha = hashlib.sha256()
sha.update(self.payload)
digest = sha.digest()
tlv.add('SHA256', digest)
if key is not None:
sig = key.sign(self.payload)
tlv.add(key.sig_tlv(), sig)
self.payload += tlv.get()
def add_header(self, key):
"""Install the image header.
The key is needed to know the type of signature, and
approximate the size of the signature."""
flags = 0
tlvsz = 0
if key is not None:
flags |= IMAGE_F[key.sig_type()]
tlvsz += TLV_HEADER_SIZE + key.sig_len()
flags |= IMAGE_F['SHA256']
tlvsz += 4 + hashlib.sha256().digest_size
fmt = ('<' +
# type ImageHdr struct {
'I' + # Magic uint32
'H' + # TlvSz uint16
'B' + # KeyId uint8
'B' + # Pad1 uint8
'H' + # HdrSz uint16
'H' + # Pad2 uint16
'I' + # ImgSz uint32
'I' + # Flags uint32
'BBHI' + # Vers ImageVersion
'I' # Pad3 uint32
) # }
assert struct.calcsize(fmt) == IMAGE_HEADER_SIZE
header = struct.pack(fmt,
IMAGE_MAGIC,
tlvsz, # TlvSz
0, # KeyId (TODO: allow other ids)
0, # Pad1
self.header_size,
0, # Pad2
len(self.payload) - self.header_size, # ImageSz
flags, # Flags
self.version.major,
self.version.minor or 0,
self.version.revision or 0,
self.version.build or 0,
0) # Pad3
self.payload = bytearray(self.payload)
self.payload[:len(header)] = header
def pad_to(self, size, align):
"""Pad the image to the given size, with the given flash alignment."""
tsize = trailer_sizes[align]
padding = size - (len(self.payload) + tsize)
if padding < 0:
msg = "Image size (0x{:x}) + trailer (0x{:x}) exceeds requested size 0x{:x}".format(
len(self.payload), tsize, size)
raise Exception(msg)
pbytes = b'\xff' * padding
pbytes += boot_magic
pbytes += b'\xff' * (tsize - len(boot_magic))
self.payload += pbytes

137
dist/tools/mcuboot/imgtool/keys.py vendored Normal file
View File

@ -0,0 +1,137 @@
"""
Cryptographic key management for imgtool.
"""
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5, PKCS1_PSS
from ecdsa import SigningKey, NIST256p, util
import hashlib
from pyasn1.type import namedtype, univ
from pyasn1.codec.der.encoder import encode
# By default, we use RSA-PSS (PKCS 2.1). That can be overridden on
# the command line to support the older (less secure) PKCS1.5
sign_rsa_pss = True
AUTOGEN_MESSAGE = "/* Autogenerated by imgtool.py, do not edit. */"
class RSAPublicKey(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('modulus', univ.Integer()),
namedtype.NamedType('publicExponent', univ.Integer()))
class RSA2048():
def __init__(self, key):
"""Construct an RSA2048 key with the given key data"""
self.key = key
@staticmethod
def generate():
return RSA2048(RSA.generate(2048))
def export_private(self, path):
with open(path, 'wb') as f:
f.write(self.key.exportKey('PEM'))
def emit_c(self):
node = RSAPublicKey()
node['modulus'] = self.key.n
node['publicExponent'] = self.key.e
print(AUTOGEN_MESSAGE)
print("const unsigned char rsa_pub_key[] = {", end='')
encoded = bytearray(encode(node))
for count, b in enumerate(encoded):
if count % 8 == 0:
print("\n\t", end='')
else:
print(" ", end='')
print("0x{:02x},".format(b), end='')
print("\n};")
print("const unsigned int rsa_pub_key_len = {};".format(len(encoded)))
def sig_type(self):
"""Return the type of this signature (as a string)"""
if sign_rsa_pss:
return "PKCS1_PSS_RSA2048_SHA256"
else:
return "PKCS15_RSA2048_SHA256"
def sig_len(self):
return 256
def sig_tlv(self):
return "RSA2048"
def sign(self, payload):
sha = SHA256.new(payload)
if sign_rsa_pss:
signer = PKCS1_PSS.new(self.key)
else:
signer = PKCS1_v1_5.new(self.key)
signature = signer.sign(sha)
assert len(signature) == self.sig_len()
return signature
class ECDSA256P1():
def __init__(self, key):
"""Construct an ECDSA P-256 private key"""
self.key = key
@staticmethod
def generate():
return ECDSA256P1(SigningKey.generate(curve=NIST256p))
def export_private(self, path):
with open(path, 'wb') as f:
f.write(key.to_pem())
def emit_c(self):
vk = self.key.get_verifying_key()
print(AUTOGEN_MESSAGE)
print("const unsigned char ecdsa_pub_key[] = {", end='')
encoded = bytes(vk.to_der())
for count, b in enumerate(encoded):
if count % 8 == 0:
print("\n\t", end='')
else:
print(" ", end='')
print("0x{:02x},".format(b), end='')
print("\n};")
print("const unsigned int ecdsa_pub_key_len = {};".format(len(encoded)))
def sign(self, payload):
# To make this fixed length, possibly pad with zeros.
sig = self.key.sign(payload, hashfunc=hashlib.sha256, sigencode=util.sigencode_der)
sig += b'\000' * (self.sig_len() - len(sig))
return sig
def sig_len(self):
# The DER encoding depends on the high bit, and can be
# anywhere from 70 to 72 bytes. Because we have to fill in
# the length field before computing the signature, however,
# we'll give the largest, and the sig checking code will allow
# for it to be up to two bytes larger than the actual
# signature.
return 72
def sig_type(self):
"""Return the type of this signature (as a string)"""
return "ECDSA256_SHA256"
def sig_tlv(self):
return "ECDSA256"
def load(path):
with open(path, 'rb') as f:
pem = f.read()
try:
key = RSA.importKey(pem)
if key.n.bit_length() != 2048:
raise Exception("Unsupported RSA bit length, only 2048 supported")
return RSA2048(key)
except ValueError:
key = SigningKey.from_pem(pem)
if key.curve.name != 'NIST256p':
raise Exception("Unsupported ECDSA curve")
return ECDSA256P1(key)

33
dist/tools/mcuboot/imgtool/version.py vendored Normal file
View File

@ -0,0 +1,33 @@
"""
Semi Semantic Versioning
Implements a subset of semantic versioning that is supportable by the image header.
"""
import argparse
from collections import namedtuple
import re
SemiSemVersion = namedtuple('SemiSemVersion', ['major', 'minor', 'revision', 'build'])
version_re = re.compile(r"""^([1-9]\d*|0)(\.([1-9]\d*|0)(\.([1-9]\d*|0)(\+([1-9]\d*|0))?)?)?$""")
def decode_version(text):
"""Decode the version string, which should be of the form maj.min.rev+build"""
m = version_re.match(text)
# print("decode:", text, m.groups())
if m:
result = SemiSemVersion(
int(m.group(1)) if m.group(1) else 0,
int(m.group(3)) if m.group(3) else 0,
int(m.group(5)) if m.group(5) else 0,
int(m.group(7)) if m.group(7) else 0)
return result
else:
msg = "Invalid version number, should be maj.min.rev+build with later parts optional"
raise argparse.ArgumentTypeError(msg)
if __name__ == '__main__':
print(decode_version("1.2"))
print(decode_version("1.0"))
print(decode_version("0.0.2+75"))
print(decode_version("0.0.0+00"))

54
makefiles/multislot.mk Normal file
View File

@ -0,0 +1,54 @@
ifdef SLOT0_SIZE
IMGTOOL ?= $(RIOTBASE)/dist/tools/mcuboot/imgtool.py
override IMGTOOL := $(abspath $(IMGTOOL))
BINFILE ?= $(BINDIR)/$(APPLICATION).bin
SIGN_BINFILE = $(BINDIR)/signed-$(APPLICATION).bin
MCUBOOT_KEYFILE ?= $(BINDIR)/key.pem
OFLAGS = -O binary
MCUBOOT_BIN ?= $(BINDIR)/mcuboot.bin
MCUBOOT_BIN_URL ?= http://download.riot-os.org/mynewt.mcuboot.bin
MCUBOOT_BIN_MD5 ?= 0c71a0589bd3709fc2d90f07a0035ce7
create-key: $(MCUBOOT_KEYFILE)
ifeq ($(BINDIR)/key.pem,$(MCUBOOT_KEYFILE))
$(MCUBOOT_KEYFILE):
$(Q)mkdir -p $(BINDIR)
$(Q)$(IMGTOOL) keygen -k $@ -t rsa-2048
endif
mcuboot: create-key link
@$(COLOR_ECHO)
@$(COLOR_ECHO) '${COLOR_PURPLE}Re-linking for MCUBoot at $(SLOT0_SIZE)...${COLOR_RESET}'
@$(COLOR_ECHO)
$(Q)$(_LINK) $(LINKFLAGPREFIX)--defsym=offset="$$(($(SLOT0_SIZE) + $(IMAGE_HDR_SIZE)))" \
$(LINKFLAGPREFIX)--defsym=length="$$(($(SLOT1_SIZE) - $(IMAGE_HDR_SIZE)))" \
$(LINKFLAGPREFIX)--defsym=image_header="$(IMAGE_HDR_SIZE)" -o $(ELFFILE) && \
$(OBJCOPY) $(OFLAGS) $(ELFFILE) $(BINFILE) && \
$(IMGTOOL) sign --key $(MCUBOOT_KEYFILE) --version $(IMAGE_VERSION) --align \
$(IMAGE_ALIGN) -H $(IMAGE_HDR_SIZE) $(BINFILE) $(SIGN_BINFILE)
@$(COLOR_ECHO)
@$(COLOR_ECHO) '${COLOR_PURPLE}Signed with $(MCUBOOT_KEYFILE) for version $(IMAGE_VERSION)\
${COLOR_RESET}'
@$(COLOR_ECHO)
$(MCUBOOT_BIN):
$(Q)$(DLCACHE) $(MCUBOOT_BIN_URL) $(MCUBOOT_BIN_MD5) $@
.PHONY: flash-bootloader flash-mcuboot
flash-bootloader: HEXFILE = $(MCUBOOT_BIN)
flash-bootloader: $(MCUBOOT_BIN) $(FLASHDEPS)
FLASH_ADDR=0x0 $(FLASHER) $(FFLAGS)
flash-mcuboot: HEXFILE = $(SIGN_BINFILE)
flash-mcuboot: mcuboot $(FLASHDEPS) flash-bootloader
FLASH_ADDR=$(SLOT0_SIZE) $(FLASHER) $(FFLAGS)
else
mcuboot:
$(Q)echo "error: mcuboot not supported on board $(BOARD)!"
$(Q)false
endif # SLOT0_SIZE

View File

@ -1,3 +1,6 @@
# set default values for selected global variables
FLASH_ADDR ?= 0x0
export Q # Used in front of Makefile lines to suppress the printing of the command if user did not opt-in to see them.
export QQ # as Q, but be more quiet
export QUIET # The parameter to use whether to show verbose makefile commands or not.
@ -53,6 +56,7 @@ export WERROR # Treat all compiler warnings as errors if set to 1
export GITCACHE # path to git-cache executable
export FLASHER # The command to call on "make flash".
export FFLAGS # The parameters to supply to FLASHER.
export FLASH_ADDR # Define an offset to flash code into ROM memory.
export TERMPROG # The command to call on "make term".
export TERMFLAGS # Additional parameters to supply to TERMPROG.
export PORT # The port to connect the TERMPROG to.

14
tests/mcuboot/Makefile Normal file
View File

@ -0,0 +1,14 @@
APPLICATION = hello_mcuboot
BOARD ?= nrf52dk
include ../Makefile.tests_common
BOARD_WHITELIST := nrf52dk
export IMAGE_VERSION = 1.1.1+1
# this test is supposed to always build the mcuboot image
all: mcuboot
include $(RIOTBASE)/Makefile.include

20
tests/mcuboot/README.md Normal file
View File

@ -0,0 +1,20 @@
# MCUBoot test application
This test is intended to compile a hello-world program taking into account
the existence of the MCUBoot bootloader at the first 32K in the ROM.
For this first support, a pre-compiled mynewt MCUBoot binary is downloaded at
compile time.
The goal is to produce an ELF file which is linked to be flashed at a
`BOOTLOADER_OFFSET` offset rather than the beginning of ROM. MCUBoot also
expects an image padded with some specific headers containing the version
information, and TLVs with hash and signing information. This is done through
the imgtool.py application, which is executed automatically by the build
system.
This test can be called using `make mcuboot` to produce such ELF file,
which can also be flashed using `make flash-mcuboot`.This command also flashes
the pre-compiled bootloader.
It's also possible to build and flash MCUBoot by following the instructions on
the MCUBoot repository either using mynewt or zephyr operating systems.

33
tests/mcuboot/main.c Normal file
View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2017 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.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief MCUBoot compile test application
*
* @author Francisco Acosta <francisco.acosta@inria.fr>
*
* @}
*/
#include <stdio.h>
#include "cpu.h"
int main(void)
{
puts("Hello MCUBoot!");
printf("You are running RIOT on a(n) %s board.\n", RIOT_BOARD);
printf("This board features a(n) %s MCU.\n", RIOT_MCU);
printf("The startup address is: %p\n", (void*)SCB->VTOR);
return 0;
}