diff --git a/Makefile.dep b/Makefile.dep index 9037185244a4..364eeb333f6f 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -611,6 +611,12 @@ ifneq (,$(filter vfs,$(USEMODULE))) endif endif +ifneq (,$(filter fw_slots,$(USEMODULE))) + FEATURES_REQUIRED += periph_flashpage + FEATURES_REQUIRED += periph_slots + USEMODULE += hashes +endif + # include package dependencies -include $(USEPKG:%=$(RIOTPKG)/%/Makefile.dep) diff --git a/Makefile.include b/Makefile.include index 77f021992dd9..ad99725dd907 100644 --- a/Makefile.include +++ b/Makefile.include @@ -11,6 +11,7 @@ RIOTBOARD ?= $(RIOTBASE)/boards RIOTPKG ?= $(RIOTBASE)/pkg RIOTPROJECT ?= $(shell git rev-parse --show-toplevel 2>/dev/null || pwd) GITCACHE ?= $(RIOTBASE)/dist/tools/git/git-cache +FW_METADATA ?= $(RIOTBASE)/dist/tools/firmware_metadata APPDIR ?= $(CURDIR) BINDIRBASE ?= $(APPDIR)/bin BINDIR ?= $(BINDIRBASE)/$(BOARD) @@ -26,6 +27,7 @@ override RIOTBOARD := $(abspath $(RIOTBOARD)) override RIOTPKG := $(abspath $(RIOTPKG)) override RIOTPROJECT := $(abspath $(RIOTPROJECT)) override GITCACHE := $(abspath $(GITCACHE)) +override FW_METADATA := $(abspath $(FW_METADATA)) override APPDIR := $(abspath $(APPDIR)) override BINDIRBASE := $(abspath $(BINDIRBASE)) override BINDIR := $(abspath $(BINDIR)) @@ -261,6 +263,10 @@ BASELIBS += $(APPDEPS) ELFFILE ?= $(BINDIR)/$(APPLICATION).elf HEXFILE ?= $(ELFFILE:.elf=.hex) +ifeq ($(FW_SLOTS),1) +BINFILE = $(ELFFILE:.elf=.bin) +endif + # variables used to compile and link c++ CPPMIX ?= $(if $(wildcard *.cpp),1,) @@ -280,6 +286,12 @@ 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) +endif +ifeq ($(FW_SLOTS),1) + $(STRIP) --strip-unneeded --strip-debug $(ELFFILE) + $(OBJCOPY) -O binary $(ELFFILE) $(BINFILE) + $(FW_METADATA)/bin/./generate-metadata $(BINFILE) $(VERSION) $(UUID) + srec_cat $(BINFILE) -binary -offset $(FW_METADATA_SPACE) firmware-metadata.bin -binary -o $(BINDIR)/slot-image-$(UUID)-$(VERSION).bin -binary endif $(Q)$(SIZE) $(ELFFILE) $(Q)$(OBJCOPY) $(OFLAGS) $(ELFFILE) $(HEXFILE) @@ -294,6 +306,12 @@ endif # BUILD_IN_DOCKER ..build-message: @$(COLOR_ECHO) '${COLOR_GREEN}Building application "$(APPLICATION)" for "$(BOARD)" with MCU "$(MCU)".${COLOR_RESET}' +ifeq ($(FW_SLOTS),1) + @$(COLOR_ECHO) '${COLOR_RED}This is a build for FW slot $(FW_SLOT).${COLOR_RESET}' +endif +ifeq ($(BOOTLOADER),1) + @$(COLOR_ECHO) '${COLOR_RED}This is a Bootloader build.${COLOR_RESET}' +endif @$(COLOR_ECHO) # add extra include paths for packages in $(USEMODULE) diff --git a/cpu/Makefile.include.cortexm_common b/cpu/Makefile.include.cortexm_common index e46b85157638..d21eba4be54a 100644 --- a/cpu/Makefile.include.cortexm_common +++ b/cpu/Makefile.include.cortexm_common @@ -15,6 +15,24 @@ export CFLAGS_OPT ?= -Os export CFLAGS += $(CFLAGS_CPU) $(CFLAGS_LINK) $(CFLAGS_DBG) $(CFLAGS_OPT) export ASFLAGS += $(CFLAGS_CPU) $(CFLAGS_DBG) + +# If the FW_SLOTS flag is set, we will generate a linker script at compile-time +# reflecting the FW_IMAGE_OFFSET and FW_IMAGE_LENGTH values defined in the +# target project's Makefile. +ifeq ($(FW_SLOTS),1) +ifeq ($(FW_SLOT),1) +LINKER_SCRIPT = $(CPU_MODEL)_slot1.ld +endif +ifeq ($(FW_SLOT),2) +LINKER_SCRIPT = $(CPU_MODEL)_slot2.ld +endif +endif + +# If we compile a bootloader, set the correct linker script +ifeq ($(BOOTLOADER),1) +export LINKER_SCRIPT = $(CPU_MODEL)-bootloader.ld +endif + export LINKFLAGS += -L$(RIOTCPU)/$(CPU)/ldscripts -L$(RIOTCPU)/cortexm_common/ldscripts export LINKER_SCRIPT ?= $(CPU_MODEL).ld export LINKFLAGS += -T$(LINKER_SCRIPT) -Wl,--fatal-warnings diff --git a/cpu/Makefile.include.gnu b/cpu/Makefile.include.gnu index ea61b4cc7628..88cca20e604c 100644 --- a/cpu/Makefile.include.gnu +++ b/cpu/Makefile.include.gnu @@ -16,3 +16,4 @@ export OBJCOPY = true endif export OBJDUMP = $(PREFIX)objdump export DBG = $(GDBPREFIX)gdb +export STRIP = $(PREFIX)strip diff --git a/cpu/stm32f1/Makefile.features b/cpu/stm32f1/Makefile.features index 7a418ea511cb..71946471e0f1 100644 --- a/cpu/stm32f1/Makefile.features +++ b/cpu/stm32f1/Makefile.features @@ -1 +1,2 @@ FEATURES_PROVIDED += periph_pm +FEATURES_PROVIDED += periph_slots \ No newline at end of file diff --git a/cpu/stm32f1/cpu.c b/cpu/stm32f1/cpu.c index c2d465a64054..c531c4518e50 100644 --- a/cpu/stm32f1/cpu.c +++ b/cpu/stm32f1/cpu.c @@ -96,14 +96,19 @@ #endif #endif +#ifndef FW_SLOTS static void clk_init(void); +#endif void cpu_init(void) { /* initialize the Cortex-M core */ cortexm_init(); + /* initialize system clocks */ +#ifndef FW_SLOTS clk_init(); +#endif /* trigger static peripheral initialization */ periph_init(); } @@ -111,6 +116,7 @@ void cpu_init(void) /** * @brief Configure the clock system of the stm32f1 */ +#ifndef FW_SLOTS static void clk_init(void) { /* Reset the RCC clock configuration to the default reset state(for debug purpose) */ @@ -162,3 +168,4 @@ static void clk_init(void) while ((RCC->CR & RCC_CR_HSIRDY) != 0) {} #endif } +#endif diff --git a/cpu/stm32f1/include/cpu_conf.h b/cpu/stm32f1/include/cpu_conf.h index fed1ee08e916..8e0797facd96 100644 --- a/cpu/stm32f1/include/cpu_conf.h +++ b/cpu/stm32f1/include/cpu_conf.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 INRIA + * Copyright (C) 2013, 2016 Inria * Copyright (C) 2014 Freie Universität Berlin * * This file is subject to the terms and conditions of the GNU Lesser General @@ -18,6 +18,7 @@ * * @author Alaeddine Weslati * @author Hauke Petersen + * @author Francisco Acosta */ #ifndef CPU_CONF_H @@ -35,13 +36,123 @@ extern "C" { #endif +/** + * @brief Flash page configuration + * @{ + */ +#define FLASHPAGE_SIZE (2048U) + +#if defined(CPU_MODEL_STM32F103CB) || defined(CPU_MODEL_STM32F103RB) +#define FLASHPAGE_NUMOF (64U) +#elif defined(CPU_MODEL_STM32F103RE) +#define FLASHPAGE_NUMOF (256U) +#endif +/** @} */ + +/** + * @brief Offset to reset handler on VTOR + * @{ + */ +#define VTOR_RESET_HANDLER 0x4 /** One pointer after the beginning */ +/** @} */ + +#if defined(CPU_MODEL_STM32F103RE) +/* + * @brief Flash partitioning for FW slots + * @{ + */ + +#ifndef FW_METADATA_SPACE +#define FW_METADATA_SPACE (0x100) +#endif + +#define MAX_FW_SLOTS (2) +#define FW_SLOT_PAGES (120) +#define BOOTLOADER_SPACE (0x4000) +#define FW_SLOT_SIZE FLASHPAGE_SIZE * FW_SLOT_PAGES +#define FW_SLOT_1 FLASH_BASE + BOOTLOADER_SPACE +#define FW_SLOT_1_END FW_SLOT_1 + FW_SLOT_SIZE +#define FW_SLOT_1_PAGE (8) +#define FW_SLOT_2 FW_SLOT_1_END +#define FW_SLOT_2_END FW_SLOT_2 + FW_SLOT_SIZE +#define FW_SLOT_2_PAGE (128) + +#ifdef FW_SLOTS + #if FW_SLOT == 1 + #define CURRENT_FIRMWARE_ADDR FW_SLOT_1 + #define CURRENT_FIRMWARE_PAGE FW_SLOT_1_PAGE + #define CURRENT_FIRMWARE_END FW_SLOT_1_END + #endif + + #if FW_SLOT == 2 + #define CURRENT_FIRMWARE_ADDR FW_SLOT_2 + #define CURRENT_FIRMWARE_PAGE FW_SLOT_2_PAGE + #define CURRENT_FIRMWARE_END FW_SLOT_2_END + #endif + +#endif /* FW_SLOTS */ + +/** @} */ + +/** + * @brief Get FW internal address for a given slot + * + * @param[in] slot FW slot + * + * @return FW slot address + */ +static inline uint32_t get_slot_address(uint8_t slot) +{ + switch (slot) { + case 1: + return FW_SLOT_1; + break; + + case 2: + return FW_SLOT_2; + break; + } + + return 0; +} + +/** + * @brief Get internal page for a given slot + * + * @param[in] slot FW slot + * + * @return FW slot page + */ +static inline uint32_t get_slot_page(uint8_t slot) +{ + switch (slot) { + case 1: + return FW_SLOT_1_PAGE; + break; + + case 2: + return FW_SLOT_2_PAGE; + break; + } + + return 0; +} + +#endif /* defined(CPU_MODEL_STM32F103RE) */ +/** @} */ + /** * @brief ARM Cortex-M specific CPU configuration * @{ */ -#define CPU_DEFAULT_IRQ_PRIO (1U) -#define CPU_IRQ_NUMOF (60U) -#define CPU_FLASH_BASE FLASH_BASE +#define CPU_DEFAULT_IRQ_PRIO (1U) +#define CPU_IRQ_NUMOF (60U) + +#ifdef FW_SLOTS +#define CPU_FLASH_BASE (CURRENT_FIRMWARE_ADDR + FW_METADATA_SPACE) +#else +#define CPU_FLASH_BASE FLASH_BASE +#endif /** @} */ /** diff --git a/cpu/stm32f1/ldscripts/stm32f103re-bootloader.ld b/cpu/stm32f1/ldscripts/stm32f103re-bootloader.ld new file mode 100644 index 000000000000..fcffe7e5a93f --- /dev/null +++ b/cpu/stm32f1/ldscripts/stm32f103re-bootloader.ld @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 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. + */ + +/** + * @addtogroup cpu_stm32f1 + * @{ + * + * @file + * @brief Memory definitions for the STM32F103RE + * + * @author Hauke Petersen + * + * @} + */ + +MEMORY +{ + rom (rx) : ORIGIN = 0x08000000, LENGTH = 16K + romslot1 (rx) : ORIGIN = 0x08004000, LENGTH = 0x3C100 + romslot2 (rx) : ORIGIN = 0x08040000, LENGTH = 0x3C100 + ram (xrw) : ORIGIN = 0x20000000, LENGTH = 64K + cpuid (r) : ORIGIN = 0x1ffff7e8, LENGTH = 12 +} + +_cpuid_address = ORIGIN(cpuid); + +INCLUDE cortexm_base.ld + +SECTIONS +{ + . = ALIGN(0x1000); + .slot.1 : + { + KEEP(*(.slot.1.*)) + } > romslot1 + + . = ALIGN(0x1000); + .slot.2 : + { + KEEP(*(.slot.2.*)) + } > romslot2 +} diff --git a/cpu/stm32f1/ldscripts/stm32f103re_slot1.ld b/cpu/stm32f1/ldscripts/stm32f103re_slot1.ld new file mode 100644 index 000000000000..b3c457fba4a2 --- /dev/null +++ b/cpu/stm32f1/ldscripts/stm32f103re_slot1.ld @@ -0,0 +1,30 @@ +/* + * 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. + */ + +/** + * @addtogroup cpu_stm32f1 + * @{ + * + * @file + * @brief Memory definitions for the STM32F103RE Slot 0 + * + * @author Francisco Acosta + * + * @} + */ + +MEMORY +{ + rom (rx) : ORIGIN = 0x08004100, LENGTH = 0x3C000 + ram (xrw) : ORIGIN = 0x20000000, LENGTH = 64K + cpuid (r) : ORIGIN = 0x1ffff7e8, LENGTH = 12 +} + +_cpuid_address = ORIGIN(cpuid); + +INCLUDE cortexm_base.ld diff --git a/cpu/stm32f1/ldscripts/stm32f103re_slot2.ld b/cpu/stm32f1/ldscripts/stm32f103re_slot2.ld new file mode 100644 index 000000000000..4ea73fb00007 --- /dev/null +++ b/cpu/stm32f1/ldscripts/stm32f103re_slot2.ld @@ -0,0 +1,30 @@ +/* + * 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. + */ + +/** + * @addtogroup cpu_stm32f1 + * @{ + * + * @file + * @brief Memory definitions for the STM32F103RE Slot 1 + * + * @author Francisco Acosta + * + * @} + */ + +MEMORY +{ + rom (rx) : ORIGIN = 0x08040100, LENGTH = 0x3C000 + ram (xrw) : ORIGIN = 0x20000000, LENGTH = 64K + cpuid (r) : ORIGIN = 0x1ffff7e8, LENGTH = 12 +} + +_cpuid_address = ORIGIN(cpuid); + +INCLUDE cortexm_base.ld diff --git a/dist/tools/firmware_metadata/.gitignore b/dist/tools/firmware_metadata/.gitignore new file mode 100644 index 000000000000..54b488b3e122 --- /dev/null +++ b/dist/tools/firmware_metadata/.gitignore @@ -0,0 +1,3 @@ +/generate-metadata +/git-fetch-tweetnacl +tweetnacl \ No newline at end of file diff --git a/dist/tools/firmware_metadata/Makefile b/dist/tools/firmware_metadata/Makefile new file mode 100644 index 000000000000..969aaa9cd094 --- /dev/null +++ b/dist/tools/firmware_metadata/Makefile @@ -0,0 +1,19 @@ +RIOTBASE := ../../.. +RIOT_INCLUDE = $(RIOTBASE)/sys/include +SHA256_DIR := $(RIOTBASE)/sys/hashes +SHA256_INCLUDE := $(RIOT_INCLUDE)/hashes +METADATA_SRC := generate-metadata.c $(SHA256_DIR)/sha256.c +METADATA_HDR := $(RIOT_INCLUDE)/fw_slots.h $(RIOT_INCLUDE)/hashes/sha256.h + +CFLAGS += -g -O3 -Wall -Wextra -pedantic -std=c99 + +all: bin bin/generate-metadata + +bin: + mkdir bin + +bin/generate-metadata: + $(CC) $(CFLAGS) -I$(RIOT_INCLUDE) $(METADATA_SRC) -o $@ + +clean: + rm -rf bin/generate-metadata \ No newline at end of file diff --git a/dist/tools/firmware_metadata/README.md b/dist/tools/firmware_metadata/README.md new file mode 100644 index 000000000000..7e6830b0d475 --- /dev/null +++ b/dist/tools/firmware_metadata/README.md @@ -0,0 +1,33 @@ +# Metadata generator for firmware verification +This program will generate a binary file containing a metadata structure as +follows: + +```c +typedef struct FW_metadata { + uint8_t hash[SHA256_DIGEST_LENGTH]; /* SHA256 Hash of firmware image */ + uint8_t shash[SIGN_LEN]; /* Signed SHA256 */ + uint32_t size; /* Size of firmware image */ + uint32_t uuid; /* Integer representing unique firmware ID */ + uint16_t version; /* Integer representing firmware version */ +} FW_metadata_t; +``` + +This structure will be filled with the data obtained from the firmware. + +## Usage +To use, you should call `generate-metadata` with the following arguments: + +```console +./generate-metadata +``` + +Where: + +_:_ The firmware in binary format + +_:_ The firmware version in 16-bit HEX + +__ Unique ID for the application in 32-bit HEX + +The results will be printed if the operation is successful, and a binary +called "firmware-metadata.bin" will be created. diff --git a/dist/tools/firmware_metadata/generate-metadata.c b/dist/tools/firmware_metadata/generate-metadata.c new file mode 100644 index 000000000000..776d7fcbbba5 --- /dev/null +++ b/dist/tools/firmware_metadata/generate-metadata.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2016, Mark Solters . + * 2016, Francisco Acosta + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/** + * @ingroup FW + * @file + * @brief Meta-data generation for FW images + * + * @author Mark Solters + * @author Francisco Acosta + */ + +#include +#include +#include +#include + +#include "fw_slots.h" +#include "hashes/sha256.h" + +/* Input firmware .bin file */ +FILE *firmware_bin; + +/* Output metadata .bin file */ +FILE *metadata_bin; + +uint32_t firmware_size = 0; + +int main(int argc, char *argv[]) +{ + FW_metadata_t metadata; + sha256_context_t firmware_sha256; + uint8_t output_buffer[sizeof(FW_metadata_t)]; + int bytes_read = 0; + uint8_t firmware_buffer[1024]; + + (void)argc; + + if (!argv[1]) { + printf("Please provide a .bin file to perform SHA256 on as the first argument.\n"); + return -1; + } + + if (!argv[2]) { + printf("Please provide a 16-bit hex firmware version integer as the second argument.\n"); + return -1; + } + + if (!argv[3]) { + printf("Please provide a 32-bit hex UUID integer as the third argument.\n"); + return -1; + } + + /* (1) Open the firmware .bin file */ + firmware_bin = fopen(argv[1], "r"); + + sha256_init(&firmware_sha256); + + while((bytes_read = fread(firmware_buffer, 1, sizeof(firmware_buffer), firmware_bin))) { + sha256_update(&firmware_sha256, firmware_buffer, bytes_read); + firmware_size += bytes_read; + } + sha256_final(&firmware_sha256, metadata.hash); + + printf("Firmware bytes read: %u\n", firmware_size); + + /* Close the .bin file. */ + fclose(firmware_bin); + + /* + * TODO Sign hash + */ + for (unsigned long i = 0; i < sizeof(metadata.shash); i++) { + metadata.shash[i] = 0; + } + + /* Generate FW image metadata */ + + metadata.size = firmware_size; + sscanf(argv[2], "%xu", (unsigned int *)&(metadata.version)); + sscanf(argv[3], "%xu", &(metadata.uuid)); + memcpy(output_buffer, (uint8_t*)&metadata, sizeof(FW_metadata_t)); + + printf("Firmware Size: %d\n", metadata.size); + printf("Firmware Version: %#x\n", metadata.version); + printf("Firmware UUID: %#x\n", metadata.uuid); + printf("Firmware HASH: "); + for (unsigned long i = 0; i < sizeof(metadata.hash); i++) { + printf("%02x ", metadata.hash[i]); + } + printf("\n"); + printf("Firmware signed HASH: "); + for (unsigned long i = 0; i < sizeof(metadata.shash); i++) { + printf("%02x ", metadata.shash[i]); + } + printf("\n"); + + /* Open the output firmware .bin file */ + metadata_bin = fopen("firmware-metadata.bin", "w"); + + /* Write the metadata */ + printf("Metadata size: %lu\n", sizeof(FW_metadata_t)); + fwrite(output_buffer, sizeof(output_buffer), 1, metadata_bin); + + /* 0xff spacing until firmware binary starts */ + uint8_t blank_buffer[256 - sizeof(FW_metadata_t)]; + + for (unsigned long b = 0; b < sizeof(blank_buffer); b++) { + blank_buffer[b] = 0xff; + } + + fwrite(blank_buffer, sizeof(blank_buffer), 1, metadata_bin); + + /* Close the metadata file */ + fclose(metadata_bin); + + return 0; +} diff --git a/examples/bootloader/Makefile b/examples/bootloader/Makefile new file mode 100644 index 000000000000..5d6aff4cb29a --- /dev/null +++ b/examples/bootloader/Makefile @@ -0,0 +1,24 @@ +# name of your application +APPLICATION = bootloader + +# If no BOARD is found in the environment, use this default: +BOARD ?= iotlab-m3 + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +#CFLAGS += -DDEVELHELP + +# Mark this example as the bootloader +BOOTLOADER = 1 + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +# Use fw_slots module to manage images on ROM +USEMODULE += fw_slots + +include $(RIOTBASE)/Makefile.include diff --git a/examples/bootloader/README.md b/examples/bootloader/README.md new file mode 100644 index 000000000000..30404ce4c546 --- /dev/null +++ b/examples/bootloader/README.md @@ -0,0 +1 @@ +TODO \ No newline at end of file diff --git a/examples/bootloader/main.c b/examples/bootloader/main.c new file mode 100644 index 000000000000..aa264455301c --- /dev/null +++ b/examples/bootloader/main.c @@ -0,0 +1,55 @@ +/* + * Copyright (C)2016 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 examples + * @{ + * + * @file + * @brief Default bootloader application to manage FW slots + * + * @author Francisco Acosta + * + * @} + */ + +#include +#include +#include + +#include "fw_slots.h" + +int main(void) +{ + uint8_t boot_slot = 0; + + (void) puts("Welcome to RIOT bootloader!\n"); + (void) puts("Trying to boot the newest firmware version\n"); + + boot_slot = fw_slots_find_newest_int_image(); + + if (boot_slot > 0) { + if (fw_slots_verify_int_slot(boot_slot) == 0) { + uint32_t address; + printf("Image on slot %d verified! Booting...\n", boot_slot); + address = fw_slots_get_slot_address(boot_slot); + fw_slots_jump_to_image(address); + } else { + printf("Slot %u inconsistent!\n", boot_slot); + } + } else { + (void) puts("No bootable slot found!\n"); + } + + /* + * TODO Add serial bootloader + */ + + /* Should not happen */ + return 0; +} diff --git a/examples/firmware_swapping/.gitignore b/examples/firmware_swapping/.gitignore new file mode 100644 index 000000000000..eee4db0f36a1 --- /dev/null +++ b/examples/firmware_swapping/.gitignore @@ -0,0 +1 @@ +*.hex diff --git a/examples/firmware_swapping/Makefile b/examples/firmware_swapping/Makefile new file mode 100644 index 000000000000..b0f8a706c5da --- /dev/null +++ b/examples/firmware_swapping/Makefile @@ -0,0 +1,127 @@ +# If no BOARD is found in the environment, use this default: +export BOARD ?= iotlab-m3 + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + + +FIRMWARE ?= gnrc_networking + +# Define the parameters for the FW slot 1 +VERSION_IMAGE_1 = 0x1 +UUID_IMAGE_1 = 0xabcd1234 + +# Define the parameters for the FW slot 1 +VERSION_IMAGE_2 = 0x2 +UUID_IMAGE_2 = 0xabcd1234 + + + +# # # # # # # # # # # # # +# General configuration # +# # # # # # # # # # # # # + +# Activate FW slots +FW_SLOTS = 1 + +FW_METADATA_SPACE = 0x100 # 76 bytes meta-data, 256 byte aligned +FIRMWARE_IMAGE_OFFSET_1 = 0x08004000 # Start at page 8 +FIRMWARE_IMAGE_OFFSET_2 = 0x08040000 # Start at page 128 + +# OBJCOPY config (specific for M3 nodes should use +OBJCOPY ?= arm-none-eabi-objcopy +OBJCOPYFLAGS = --output-target elf32-littlearm +OBJCOPYFLAGS += --binary-architecture=arm + +# Configured output by root Makefile.include +# $* will be replaced by the slot number +FIRMWARE_PATH = ../$(FIRMWARE) +FIRMWARE_BIN_PATH = $(FIRMWARE_PATH)/bin/$(BOARD) +FIRMWARE_SLOT_BIN=$(FIRMWARE_BIN_PATH)/slot-image-$(UUID_IMAGE_$*)-$(VERSION_IMAGE_$*).bin + + +.PHONY: all generate-metadata bootloader-$(FIRMWARE).elf + +all: bootloader-$(FIRMWARE).elf + +SLOTS = $(FIRMWARE)-slot-1.o $(FIRMWARE)-slot-2.o + +bootloader-$(FIRMWARE).elf: $(FIRMWARE)-slot-1.o $(FIRMWARE)-slot-2.o + CFLAGS+=-DFW_METADATA_SPACE=$(FW_METADATA_SPACE) \ + BASELIBS+=" $(CURDIR)/$(FIRMWARE)-slot-1.o" \ + BASELIBS+=" $(CURDIR)/$(FIRMWARE)-slot-2.o" \ + ELFFILE=$(CURDIR)/$@ \ + make -C ../bootloader clean all + + +$(SLOTS): $(FIRMWARE)-slot-%.o: generate-metadata + CFLAGS="-DFW_SLOT=$* \ + -DVERSION=$(VERSION_IMAGE_$*) \ + -DUUID=$(UUID_IMAGE_$*) \ + -DFW_SLOTS=$(FW_SLOTS)" \ + FW_SLOTS=$(FW_SLOTS) FW_SLOT=$* \ + FW_METADATA_SPACE=$(FW_METADATA_SPACE) \ + VERSION=$(VERSION_IMAGE_$*) UUID=$(UUID_IMAGE_$*) \ + make -C $(FIRMWARE_PATH) clean all + @ + $(OBJCOPY) $(OBJCOPYFLAGS) \ + --change-section-vma .data=$(FIRMWARE_IMAGE_OFFSET_$*)\ + --change-section-lma .data=$(FIRMWARE_IMAGE_OFFSET_$*)\ + --rename-section .data=.slot.$*.text \ + --input-target binary $(FIRMWARE_SLOT_BIN) \ + $@ + + +# +# Version without using the magic with $* to get slot number +# +# FIRMWARE_SLOT_1 = slot-image-$(UUID_IMAGE_1)-$(VERSION_IMAGE_1).bin +# $(FIRMWARE)-slot-1.o: +# CFLAGS="-DFW_SLOT=1 -DVERSION=$(VERSION_IMAGE_1) -DUUID=$(UUID_IMAGE_1) \ +# -DFW_SLOTS=$(FW_SLOTS)" \ +# FW_SLOTS=$(FW_SLOTS) FW_SLOT=1 \ +# FW_METADATA_SPACE=$(FW_METADATA_SPACE) \ +# VERSION=$(VERSION_IMAGE_1) UUID=$(UUID_IMAGE_1) \ +# make -C $(FIRMWARE_PATH) clean all +# @ +# cp $(FIRMWARE_PATH)/bin/$(BOARD)/$(FIRMWARE_SLOT_1) $(FIRMWARE_SLOT_1) +# @ +# $(OBJCOPY) $(OBJCOPYFLAGS) \ +# --change-section-vma .data=$(FIRMWARE_IMAGE_OFFSET_1)\ +# --change-section-lma .data=$(FIRMWARE_IMAGE_OFFSET_1)\ +# --rename-section .data=.slot.1.text \ +# --input-target binary $(FIRMWARE_SLOT_1) \ +# $@ +# +# FIRMWARE_SLOT_2 = slot-image-$(UUID_IMAGE_2)-$(VERSION_IMAGE_2).bin +# $(FIRMWARE)-slot-2.o: +# CFLAGS="-DFW_SLOT=2 -DVERSION=$(VERSION_IMAGE_2) -DUUID=$(UUID_IMAGE_2) \ +# -DFW_SLOTS=$(FW_SLOTS)" \ +# FW_SLOTS=$(FW_SLOTS) FW_SLOT=2 \ +# FW_METADATA_SPACE=$(FW_METADATA_SPACE) \ +# VERSION=$(VERSION_IMAGE_2) UUID=$(UUID_IMAGE_2) \ +# make -C $(FIRMWARE_PATH) clean all +# @ +# cp $(FIRMWARE_PATH)/bin/$(BOARD)/$(FIRMWARE_SLOT_2) $(FIRMWARE_SLOT_2) +# @ +# $(OBJCOPY) $(OBJCOPYFLAGS) \ +# --change-section-vma .data=$(FIRMWARE_IMAGE_OFFSET_2)\ +# --change-section-lma .data=$(FIRMWARE_IMAGE_OFFSET_2)\ +# --rename-section .data=.slot.2.text \ +# --input-target binary $(FIRMWARE_SLOT_2) \ +# $@ + + +GENERATE_METADA_DIR = ../../dist/tools/firmware_metadata +generate-metadata: $(GENERATE_METADA_DIR)/bin/generate-metadata + +$(GENERATE_METADA_DIR)/bin/generate-metadata: + make -C $(GENERATE_METADA_DIR) + +clean: + @rm -f *.hex *.bin *.elf *.o + +flash: + OPENOCD_CONFIG=../../boards/$(BOARD)/dist/openocd.cfg \ + HEXFILE=firmware-slots.hex \ + ../../dist/tools/openocd/openocd.sh flash diff --git a/sys/Makefile b/sys/Makefile index d25fb28e334c..6b5eba345524 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -120,6 +120,10 @@ ifneq (,$(filter devfs,$(USEMODULE))) DIRS += fs/devfs endif +ifneq (,$(filter fw_slots,$(USEMODULE))) + DIRS += fw_slots +endif + DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE}))) include $(RIOTBASE)/Makefile.base diff --git a/sys/fw_slots/Makefile b/sys/fw_slots/Makefile new file mode 100644 index 000000000000..9c9ae9884ad6 --- /dev/null +++ b/sys/fw_slots/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base \ No newline at end of file diff --git a/sys/fw_slots/fw_slots.c b/sys/fw_slots/fw_slots.c new file mode 100644 index 000000000000..c2882675f319 --- /dev/null +++ b/sys/fw_slots/fw_slots.c @@ -0,0 +1,477 @@ +/* + * Copyright (c) 2016, Mark Solters . + * 2016, Francisco Acosta + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/** + * @file + * @author Mark Solters + * @author Francisco Acosta + * + */ + +#include +#include + +#include "fw_slots.h" +#include "cpu_conf.h" +#include "irq.h" +#include "periph/flashpage.h" +#include "hashes/sha256.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define HASH_BUF (1024) + +static uint8_t firmware_buffer[HASH_BUF]; + +/** + * @brief Read internal flash to a buffer at specific address. + * + * @param[in] address - Address to be read. + * @param[in] count - count in bytes. + * + * @param[out] data_buffer - The buffer filled with the read information. + * + */ +static void int_flash_read(uint8_t *data_buffer, uint32_t address, uint32_t count) +{ + uint8_t *read_addres = (uint8_t*)address; + while (count--) { + *data_buffer++ = *read_addres++; + } +} + +int fw_slots_validate_int_slot(uint8_t fw_slot) +{ + /* + * TODO + */ + + return 0; +} + +uint32_t fw_slots_get_slot_address(uint8_t fw_slot) +{ + return get_slot_address(fw_slot); +} + +uint32_t fw_slots_get_slot_page(uint8_t fw_slot) +{ + return get_slot_page(fw_slot); +} + +void fw_slots_print_metadata(FW_metadata_t *metadata) +{ + printf("Firmware Size: %ld\n", metadata->size); + printf("Firmware Version: %#x\n", metadata->version); + printf("Firmware UUID: %#lx\n", metadata->uuid); + printf("Firmware HASH: "); + for (unsigned long i = 0; i < sizeof(metadata->hash); i++) { + printf("%02x ", metadata->hash[i]); + } + printf("\n"); + printf("Firmware signed HASH: "); + for (unsigned long i = 0; i < sizeof(metadata->shash); i++) { + printf("%02x ", metadata->shash[i]); + } + printf("\n"); +} + +int fw_slots_get_int_metadata(uint8_t fw_slot_page, FW_metadata_t *fw_metadata) +{ + uint32_t fw_address; + + fw_address = fw_slot_page * FLASHPAGE_SIZE + CPU_FLASH_BASE; + + DEBUG("[fw_slots] Getting internal metadata on page %d at address %#lx\n", + fw_slot_page, fw_address); + int_flash_read((uint8_t*)fw_metadata, fw_address, sizeof(FW_metadata_t)); + + return 0; +} + +int fw_slots_get_slot_metadata(uint8_t fw_slot, FW_metadata_t *fw_slot_metadata) +{ + /* + * TODO + */ + + return 0; +} + +int fw_slots_get_int_slot_metadata(uint8_t fw_slot, FW_metadata_t *fw_slot_metadata) +{ + uint32_t page; + + DEBUG("[fw_slots] Getting internal FW slot %d metadata\n", fw_slot); + if (fw_slot > MAX_FW_SLOTS || fw_slot == 0) { + printf("[fw_slots] FW slot not valid, should be <= %d and > 0\n", + MAX_FW_SLOTS); + return -1; + } + + page = fw_slots_get_slot_page(fw_slot); + + return fw_slots_get_int_metadata(page, fw_slot_metadata); +} + +int fw_slots_overwrite_int_slot_metadata(uint8_t fw_slot, FW_metadata_t *fw_slot_metadata) +{ + /* + * TODO + */ + return 0; +} + + + +int fw_slots_overwrite_slot_metadata(uint8_t fw_slot, FW_metadata_t *fw_slot_metadata) +{ + /* + * TODO + */ + return 0; +} + +int fw_slots_backup_golden_image(void) +{ + /* + * TODO + */ + return 0; +} + +int fw_slots_verify_int_slot(uint8_t fw_slot) +{ + FW_metadata_t fw_metadata; + uint32_t fw_image_address; + uint32_t address; + uint16_t rest; + sha256_context_t sha256_ctx; + uint8_t hash[SHA256_DIGEST_LENGTH]; + int parts = 0, i = 0; + + /* Determine the external flash address corresponding to the FW slot */ + if (fw_slot > MAX_FW_SLOTS || fw_slot == 0) { + printf("[fw_slots] FW slot not valid, should be <= %d and > 0\n", + MAX_FW_SLOTS); + return -1; + } + + /* Read the metadata of the corresponding FW slot */ + fw_image_address = fw_slots_get_slot_address(fw_slot); + + if (fw_slots_get_int_slot_metadata(fw_slot, &fw_metadata) == 0) { + fw_slots_print_metadata(&fw_metadata); + } else { + printf("[fw_slots] ERROR cannot get slot metadata.\n"); + } + + printf("Verifying slot %d at 0x%lx \n", fw_slot, fw_image_address); + + address = fw_image_address; + address += FW_METADATA_SPACE; + sha256_init(&sha256_ctx); + + parts = fw_metadata.size / sizeof(firmware_buffer); + rest = fw_metadata.size % sizeof(firmware_buffer); + + while (parts) { + int_flash_read(firmware_buffer, address, sizeof(firmware_buffer)); + sha256_update(&sha256_ctx, firmware_buffer, sizeof(firmware_buffer)); + address += sizeof(firmware_buffer); + parts--; + } + + int_flash_read(firmware_buffer, address, rest); + sha256_update(&sha256_ctx, firmware_buffer, rest); + sha256_final(&sha256_ctx, hash); + + for (i = 0; i < sizeof(hash); i++) { + if (hash[i] != fw_metadata.hash[i]) { + printf("[fw_slots] hash verification failed!\n"); + return -1; + } + } + + return 0; +} + +int fw_slots_validate_metadata(FW_metadata_t *metadata) +{ + /* Is the FW slot erased? + * First, we check to see if every byte in the metadata is 0xFF. + * If this is the case, this metadata is "erased" and therefore we assume + * the FW slot to be empty. + */ + int erased = 1; + uint8_t *metadata_ptr = (uint8_t*)metadata; + int b = FW_METADATA_LENGTH; + + while (b--) { + if (*metadata_ptr++ != 0xff) { + /* We encountered a non-erased byte. + * There's some non-trivial data here. + */ + erased = 0; + break; + } + } + + /* If the FW slot is erased, it's not valid! No more work to do here. */ + if (erased) { + return -1; + } + + /* If we get this far, all metadata bytes were cleared (0xff) */ + return 0; +} + +int fw_slots_find_matching_int_slot(uint16_t version) +{ + int matching_slot = -1; /* Assume there is no matching FW slot. */ + + /* Iterate through each of the FW slots. */ + for (int slot = 1; slot <= MAX_FW_SLOTS; slot++) { + + /* Get the metadata of the current FW slot. */ + FW_metadata_t fw_slot_metadata; + if(fw_slots_get_int_slot_metadata(slot, &fw_slot_metadata) == 0) { + fw_slots_print_metadata(&fw_slot_metadata); + } else { + printf("[fw_slots] ERROR cannot get slot metadata.\n"); + } + + /* Is this slot empty? If yes, skip. */ + if (fw_slots_validate_metadata(&fw_slot_metadata) == false) { + continue; + } + + /* Does this slot's FW version match our search parameter? */ + if (fw_slot_metadata.version == version) { + matching_slot = slot; + break; + } + } + + if (matching_slot == -1) { + printf("[fw_slots] No FW slot matches Firmware v%i\n", version); + } else { + printf("[fw_slots] FW slot #%i matches Firmware v%i\n", matching_slot, + version); + } + + return matching_slot; +} + +int fw_slots_find_empty_int_slot(void) +{ + /* Iterate through each of the MAX_FW_SLOTS internal slots. */ + for (int slot = 1; slot <= MAX_FW_SLOTS; slot++) { + + /* Get the metadata of the current FW slot. */ + FW_metadata_t fw_slot_metadata; + + if(fw_slots_get_int_slot_metadata(slot, &fw_slot_metadata) == 0) { + fw_slots_print_metadata(&fw_slot_metadata); + } else { + printf("[fw_slots] ERROR cannot get slot metadata.\n"); + } + + /* Is this slot invalid? If yes, let's treat it as empty. */ + if (fw_slots_validate_metadata(&fw_slot_metadata) == false) { + return slot; + } + } + + printf("[fw_slots] Could not find any empty FW slots!" + "\nSearching for oldest FW slot...\n"); + /* + * If execution goes this far, no empty slot was found. Now, we look for + * the oldest FW slot instead. + */ + return fw_slots_find_oldest_int_image(); +} + +int fw_slots_find_oldest_int_image(void) +{ + /* The oldest firmware should be the v0 */ + int oldest_fw_slot = 1; + uint16_t oldest_firmware_version = 0; + + /* Iterate through each of the MAX_FW_SLOTS internal slots. */ + for (int slot = 1; slot <= MAX_FW_SLOTS; slot++) { + /* Get the metadata of the current FW slot. */ + FW_metadata_t fw_slot_metadata; + + if(fw_slots_get_int_slot_metadata(slot, &fw_slot_metadata) == 0) { + fw_slots_print_metadata(&fw_slot_metadata); + } else { + printf("[fw_slots] ERROR cannot get slot metadata.\n"); + } + + /* Is this slot populated? If not, skip. */ + if (fw_slots_validate_metadata(&fw_slot_metadata) == false) { + continue; + } + + /* Is this the oldest image we've found thus far? */ + if (oldest_firmware_version) { + if (fw_slot_metadata.version < oldest_firmware_version) { + oldest_fw_slot = slot; + oldest_firmware_version = fw_slot_metadata.version; + } + } else { + oldest_fw_slot = slot; + oldest_firmware_version = fw_slot_metadata.version; + } + } + + printf("[fw_slots] Oldest FW slot: #%d; Firmware v%d\n", oldest_fw_slot, + oldest_firmware_version); + + return oldest_fw_slot; +} + +int fw_slots_find_newest_int_image(void) +{ + /* At first, we only assume knowledge of version v0 */ + int newest_fw_slot = 0; + uint16_t newest_firmware_version = 0; + + /* Iterate through each of the MAX_FW_SLOTS. */ + for (int slot = 1; slot <= MAX_FW_SLOTS ; slot++) { + /* Get the metadata of the current FW slot. */ + FW_metadata_t fw_slot_metadata; + + if(fw_slots_get_int_slot_metadata(slot, &fw_slot_metadata) == 0) { + fw_slots_print_metadata(&fw_slot_metadata); + } else { + printf("[fw_slots] ERROR cannot get slot metadata.\n"); + } + + /* Is this slot populated? If not, skip. */ + if (fw_slots_validate_metadata(&fw_slot_metadata) == -1) { + continue; + } + + /* Is this the newest non-Golden Image image we've found thus far? */ + if ( fw_slot_metadata.version > newest_firmware_version ) { + newest_fw_slot = slot; + newest_firmware_version = fw_slot_metadata.version; + } + } + + printf("Newest FW slot: #%d; Firmware v%d\n", newest_fw_slot, + newest_firmware_version); + + return newest_fw_slot; +} + +int fw_slots_erase_int_image(uint8_t fw_slot) +{ + /* Get page address of the fw_slot in internal flash */ + uint32_t fw_image_base_address; + /* Get the page where the fw_slot is located */ + uint8_t slot_page; + + if (fw_slot > MAX_FW_SLOTS || fw_slot == 0) { + printf("[fw_slots] FW slot not valid, should be <= %d and > 0\n", + MAX_FW_SLOTS); + return -1; + } + + fw_image_base_address = fw_slots_get_slot_address(fw_slot); + + printf("[fw_slots] Erasing FW slot %u [%#lx, %#lx]...\n", fw_slot, + fw_image_base_address, + fw_image_base_address + (FW_SLOT_PAGES * FLASHPAGE_SIZE) - 1); + + slot_page = fw_slots_get_slot_page(fw_slot); + + /* Erase each page in the FW internal slot! */ + for (int page = slot_page; page < slot_page + FW_SLOT_PAGES; page++) { + DEBUG("[fw_slots] Erasing page %d\n", page); + flashpage_write(page, NULL); + } + + printf("[fw_slots] Erase successful\n"); + + return 0; +} + +int fw_slots_update_firmware(uint8_t fw_slot) +{ + /* + * TODO + */ + return 0; +} + +int fw_slots_store_fw_data( uint32_t ext_address, uint8_t *data, size_t data_length ) +{ + /* + * TODO + */ + return 0; +} + +/* + * _estack pointer needed to reset PSP position + */ +extern uint32_t _estack; + +void fw_slots_jump_to_image(uint32_t destination_address) +{ + if (destination_address) { + /* + * Only add the metadata length offset if destination_address is NOT 0! + * (Jumping to 0x0 is used to reboot the device) + */ + destination_address += FW_METADATA_SPACE; + } + + /* Disable IRQ */ + (void)irq_disable(); + + /* Move PSP to the end of the stack */ + __set_PSP((uint32_t)&_estack); + + /* Move to the second pointer on VTOR (reset_handler_default) */ + destination_address += VTOR_RESET_HANDLER; + + /* Load the destination address */ + __asm("LDR R0, [%[dest]]"::[dest]"r"(destination_address)); + /* Make sure the Thumb State bit is set. */ + __asm("ORR R0, #1"); + /* Branch execution */ + __asm("BX R0"); +} diff --git a/sys/include/fw_slots.h b/sys/include/fw_slots.h new file mode 100644 index 000000000000..4f37022f048e --- /dev/null +++ b/sys/include/fw_slots.h @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2016, Mark Solters + * 2016, Francisco Acosta + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/** + * @defgroup sys_fw_slots Firmware slots management + * @ingroup sys + * @brief Slots management for several FW in ROM and ext Flash + * @{ + * + * @file + * @brief FW Image R/W and Verification + * + * @author Mark Solters + * @author Francisco Acosta + */ + +#ifndef FW_SLOTS_H +#define FW_SLOTS_H + +#include "hashes/sha256.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief FW_METADATA_LENGTH: + * This is just the size of the FW_metadata_t struct, which is 4-byte + * aligned. We use 76 bytes currently, so this struct will be 76 bytes. + */ +#define FW_METADATA_LENGTH sizeof(FW_metadata_t) + +/** + * @brief SIGN_LEN: + * Provisional length for signed hash + */ +#define SIGN_LEN (SHA256_DIGEST_LENGTH) + +/** + * @brief Structure to store firmware metadata + * @{ + */ +typedef struct FW_metadata { + uint8_t hash[SHA256_DIGEST_LENGTH]; /**< SHA256 Hash of firmware image */ + uint8_t shash[SIGN_LEN]; /**< Signed SHA256 */ + uint32_t size; /**< Size of firmware image */ + uint32_t uuid; /**< Integer representing unique firmware ID */ + uint16_t version; /**< Integer representing firmware version */ +} FW_metadata_t; +/** @} */ + +/** + * @brief Print formatted FW image metadata to STDIO. + * + * @param[in] metadata Metadata struct to fill with firmware metadata + * + */ +void fw_slots_print_metadata(FW_metadata_t *metadata); + +/** + * @brief Validate internal FW slot as a secure firmware + * + * @param[in] fw_slot The FW slot to be validated. + * + * @return 0 on success or error code + */ +int fw_slots_validate_int_slot(uint8_t fw_slot); + +/** + * @brief Get the internal metadata belonging to an FW slot in internal + * flash, using the flash page. + * + * @param[in] fw_slot_page The FW slot page to be read for metadata. + * + * @param[in] *fw_metadata Pointer to the FW_metadata_t struct where + * the metadata is to be written. + * + * @return 0 on success or error code + */ +int fw_slots_get_int_metadata(uint8_t fw_slot_page, + FW_metadata_t *fw_metadata); + +/** + * @brief Get the metadata belonging to an FW slot in external flash. + * + * @param[in] fw_slot The FW slot to be read for metadata. + * + * @param[in] *fw_slot_metadata Pointer to the FW_metadata_t struct where + * the metadata is to be written. + * + * @return 0 on success or error code + */ +int fw_slots_get_slot_metadata(uint8_t fw_slot, FW_metadata_t *fw_slot_metadata); + +/** + * @brief Get the metadata belonging to an FW slot in internal flash. + * + * @param[in] fw_slot The FW slot to be read for metadata. + * + * @param[in] *fw_slot_metadata Pointer to the FW_metadata_t struct where + * the metadata is to be written. + * + * @return 0 on success or error code + */ +int fw_slots_get_int_slot_metadata(uint8_t fw_slot, + FW_metadata_t *fw_slot_metadata); + +/** + * @brief Get the address corresponding to a given slot + * + * @param[in] fw_slot The FW slot to get the address. + * + * + * @return 0 on success or error code + */ +uint32_t fw_slots_get_slot_address(uint8_t fw_slot); + +/** + * @brief Get the page corresponding to a given slot + * + * @param[in] fw_slot The FW slot to get the page. + * + * + * @return 0 on success or error code + */ +uint32_t fw_slots_get_slot_page(uint8_t fw_slot); + +/** + * @brief Write new metadata to a specific FW slot in internal flash. + * + * @param fw_slot The FW slot to be modified. + * + * @param *fw_slot_metadata Pointer to the new FW_metadata_t data. + * + * @return 0 on success or error code + */ +int fw_slots_overwrite_int_slot_metadata(uint8_t fw_slot, + FW_metadata_t *fw_slot_metadata); + +/** + * @brief Write new metadata to a specific FW slot in external flash. + * + * @param fw_slot The FW slot to be modified. + * + * @param *fw_slot_metadata Pointer to the new FW_metadata_t data. + * + * @return 0 on success or error code + */ +int fw_slots_overwrite_slot_metadata(uint8_t fw_slot, + FW_metadata_t *fw_slot_metadata); + +/** + * @brief Copy the current firmware into FW slot 0 as the "Golden Image" + * + * @return 0 for success or error code + */ +int fw_slots_backup_golden_image(void); + +/** + * @brief Given an FW slot, verify the firmware content against the metadata. + * + * @param[in] fw_slot FW slot index to verify. (1-N) + * + * @return 0 for success or error code + */ +int fw_slots_verify_int_slot(uint8_t fw_slot); + +/** + * @brief Returns true only if the metadata provided indicates the FW slot + * is populated and valid. + * + * @param[in] *metadata FW metadata to be validated + * + * @return 0 if the FW slot is populated and valid. Otherwise, -1. + */ +int fw_slots_validate_metadata(FW_metadata_t *metadata); + +/** + * @brief Find a FW slot containing firmware matching the supplied + * firmware version number. Will only find the first matching + * slot. + * + * @param[in] version FW slot version. + * + * @return The FW slot index of the matching FW slot. Return -1 in the event + * of no match. + */ +int fw_slots_find_matching_int_slot(uint16_t version); + +/** + * @brief Find the first empty FW slot. Failing this, find the slot with the + * most out-of-date firmware version. + * + * @return The FW slot index of the empty/oldest FW slot. This will never be + * 0 because the Golden Image should never be erased. + */ +int fw_slots_find_empty_int_slot(void); + +/** + * @brief Find the FW slot containing the most out-of-date firmware version. + * FW slots are in internal flash. + * + * @return The FW slot index of the oldest firmware version. + */ +int fw_slots_find_oldest_int_image(void); + +/** + * @brief Find the FW slot containing the most recent firmware version. + * FW slots are in internal flash. + * + * @return The FW slot index of the newest firmware version. + */ +int fw_slots_find_newest_int_image(void); + +/** + * @brief Clear an FW slot in internal flash. + * + * @param[in] fw_slot The FW slot index of the firmware image to be erased. + * + * @return -1 or error code + */ +int fw_slots_erase_int_image(uint8_t fw_slot); + +/** + * @brief Overwrite firmware located in internal flash with the firmware + * stored in an external flash FW slot. + * + * @param[in] fw_slot The FW slot index of the firmware image to be copied. + * 0 = "Golden Image" backup, aka factory restore + * 1, 2, 3, n = FW Download slots + * + * @return -1 or error code + */ +int fw_slots_update_firmware(uint8_t fw_slot); + +/** + * @brief Store firmware data in external flash at the specified + * address. + * + * @param[in] ext_address External flash address to begin writing data. + * + * @param[in] data Pointer to the data buffer to be written. + * + * @param[in] data_length Length of the buffer + * + * @return -1 or error code + */ +int fw_slots_store_fw_data(uint32_t ext_address, uint8_t *data, size_t data_length); + +/** + * @brief Begin executing another firmware binary located in internal flash. + * + * @param[in] destination_address Internal flash address of the vector table + * for the firmware binary that is to be booted + * into. Since this FW lib prepends metadata + * to each binary, the true VTOR start address + * will be FW_METADATA_SPACE bytes past this + * address. + * + */ +void fw_slots_jump_to_image(uint32_t destination_address); + +#ifdef __cplusplus +} +#endif + +#endif /* FW_SLOTS_H */ +/** @} */