diff --git a/.github/workflows/imgtool.yaml b/.github/workflows/imgtool.yaml new file mode 100644 index 0000000000..a2a31f46e2 --- /dev/null +++ b/.github/workflows/imgtool.yaml @@ -0,0 +1,24 @@ +on: + push: + branches: + - master + +name: imgtool + +jobs: + environment: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Install packages + run: | + export PATH="$HOME/.local/bin:$PATH" + ./ci/imgtool_install.sh + - name: Publish + env: + TWINE_TOKEN: ${{ secrets.TWINE_TOKEN }} + run: | + export PATH="$HOME/.local/bin:$PATH" + ./ci/imgtool_run.sh diff --git a/.github/workflows/mynewt.yaml b/.github/workflows/mynewt.yaml index 576c1d3800..b4f5e0712d 100644 --- a/.github/workflows/mynewt.yaml +++ b/.github/workflows/mynewt.yaml @@ -1,9 +1,14 @@ # For development, trigger this on any push. -on: [pull_request] +on: + push: + branches: + - master + pull_request: + +name: Mynewt jobs: environment: - name: Mynewt build runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -16,6 +21,7 @@ jobs: free pwd - name: Signed commit check + if: ${{ github.event_name == 'pull_request' }} run: | ./ci/check-signed-off-by.sh - name: Mynewt install diff --git a/.github/workflows/sim.yaml b/.github/workflows/sim.yaml index 2fa0f1c466..8623568506 100644 --- a/.github/workflows/sim.yaml +++ b/.github/workflows/sim.yaml @@ -1,5 +1,11 @@ # For development, trigger this on any push. -on: [pull_request] +on: + push: + branches: + - master + pull_request: + +name: Sim jobs: environment: @@ -20,7 +26,6 @@ jobs: - "sig-rsa validate-primary-slot overwrite-only large-write" - "sig-ecdsa enc-ec256 validate-primary-slot" - "sig-rsa validate-primary-slot overwrite-only downgrade-prevention" - name: Sim runs-on: ubuntu-latest env: MULTI_FEATURES: ${{ matrix.features }} @@ -36,6 +41,7 @@ jobs: free pwd - name: Signed commit check + if: ${{ github.event_name == 'pull_request' }} run: | ./ci/check-signed-off-by.sh - name: Install stable Rust diff --git a/.mbedignore b/.mbedignore new file mode 100644 index 0000000000..bd68385665 --- /dev/null +++ b/.mbedignore @@ -0,0 +1,19 @@ +boot/boot_serial/* +boot/mynewt/* +boot/zephyr/* +boot/cypress/* +ci/* +docs/* +ptest/* +samples/* +scripts/* +sim/* +testplan/* +ext/cddl_gen/* +ext/fiat/* +ext/mbedtls/* +ext/mbedtls-asn1/* +ext/nrf/* +ext/tinycrypt/tests/* +ext/tinycrypt/* +ext/tinycrypt-sha512/* \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 95ffb4d953..4d2254bd3b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,58 +1,59 @@ -# Travis configuration. Build the simulator and run its tests. +# Travis configuration. Run FI hardening tests. -addons: - apt: - packages: - - "python3-pip" +language: minimal -language: rust - -cache: - directories: - - $HOME/TOOLCHAIN - - cargo +services: + - docker matrix: include: - # Runs each value defined in $SINGLE_FEATURES by itself in the order - # the were defined. - # - os: linux - # env: SINGLE_FEATURES="sig-ecdsa sig-ed25519 enc-kw bootstrap" TEST=sim - # - os: linux - # env: SINGLE_FEATURES="none sig-rsa sig-rsa3072 overwrite-only validate-primary-slot swap-move" TEST=sim - # - os: linux - # env: SINGLE_FEATURES="enc-rsa enc-ec256 enc-x25519" TEST=sim - - # Values defined in $MULTI_FEATURES consist of any number of features - # to be enabled at the same time. The list of multi-values should be - # separated by ',' and each list of values is run sequentially in the - # defined order. - # - os: linux - # env: MULTI_FEATURES="sig-rsa overwrite-only large-write,sig-ecdsa overwrite-only large-write,multiimage overwrite-only large-write" TEST=sim - # - os: linux - # env: MULTI_FEATURES="sig-rsa validate-primary-slot,sig-ecdsa validate-primary-slot,sig-rsa multiimage validate-primary-slot" TEST=sim - # - os: linux - # env: MULTI_FEATURES="enc-kw overwrite-only large-write,enc-rsa overwrite-only large-write" TEST=sim - # - os: linux - # env: MULTI_FEATURES="sig-rsa enc-rsa validate-primary-slot,swap-move enc-rsa sig-rsa validate-primary-slot" TEST=sim - # - os: linux - # env: MULTI_FEATURES="sig-rsa enc-kw validate-primary-slot bootstrap,sig-ed25519 enc-x25519 validate-primary-slot" TEST=sim - # - os: linux - # env: MULTI_FEATURES="sig-ecdsa enc-kw validate-primary-slot" TEST=sim - # - os: linux - # env: MULTI_FEATURES="sig-rsa validate-primary-slot overwrite-only large-write,sig-ecdsa enc-ec256 validate-primary-slot" TEST=sim - # - os: linux - # env: MULTI_FEATURES="sig-rsa validate-primary-slot overwrite-only downgrade-prevention" TEST=sim - - # - os: linux - # language: go - # env: TEST=mynewt - # go: - # - "1.12" + - os: linux + language: minimal + env: BUILD_TYPE=RELEASE SKIP_SIZE=2,4,6,8,10 TEST=fih-tests DAMAGE_TYPE=SIGNATURE + + - os: linux + language: minimal + env: BUILD_TYPE=RELEASE SKIP_SIZE=2,4,6,8,10 FIH_LEVEL=LOW TEST=fih-tests DAMAGE_TYPE=SIGNATURE + + - os: linux + language: minimal + env: BUILD_TYPE=RELEASE SKIP_SIZE=2,4,6,8,10 FIH_LEVEL=MEDIUM TEST=fih-tests DAMAGE_TYPE=SIGNATURE + + - os: linux + language: minimal + env: BUILD_TYPE=MINSIZEREL SKIP_SIZE=2,4,6 TEST=fih-tests DAMAGE_TYPE=SIGNATURE + + - os: linux + language: minimal + env: BUILD_TYPE=MINSIZEREL SKIP_SIZE=2,4,6 FIH_LEVEL=LOW TEST=fih-tests DAMAGE_TYPE=SIGNATURE + + - os: linux + language: minimal + env: BUILD_TYPE=MINSIZEREL SKIP_SIZE=2,4,6 FIH_LEVEL=MEDIUM TEST=fih-tests DAMAGE_TYPE=SIGNATURE + + - os: linux + language: minimal + env: BUILD_TYPE=MINSIZEREL SKIP_SIZE=8,10 TEST=fih-tests DAMAGE_TYPE=SIGNATURE - os: linux - language: python - env: TEST=imgtool + language: minimal + env: BUILD_TYPE=MINSIZEREL SKIP_SIZE=8,10 FIH_LEVEL=LOW TEST=fih-tests DAMAGE_TYPE=SIGNATURE + + - os: linux + language: minimal + env: BUILD_TYPE=MINSIZEREL SKIP_SIZE=8,10 FIH_LEVEL=MEDIUM TEST=fih-tests DAMAGE_TYPE=SIGNATURE + + ## Corrupt image hash is not tested as it is in the unprotected TLV section + ## and is easy to calculate a valid hash for a changed image + #- os: linux + # language: minimal + # env: BUILD_TYPE=MINSIZEREL SKIP_SIZE=2,4,6 TEST=fih-tests DAMAGE_TYPE=IMAGE_HASH + + ## Max profile is not tested as it requires HW entropy source which is not + ## present in the QEMU system being used for the tests. + #- os: linux + # language: minimal + # env: FIH_LEVEL=MAX TEST=fih-tests before_install: - | diff --git a/README.md b/README.md index af977dd60a..94bda52472 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,22 @@ # [mcuboot](http://mcuboot.com/) +[![Package on PyPI](https://img.shields.io/pypi/v/imgtool.svg)][pypi] [![Coverity Scan Build Status](https://scan.coverity.com/projects/12307/badge.svg)][coverity] -[![Build/Test](https://img.shields.io/travis/JuulLabs-OSS/mcuboot/master.svg?label=travis-ci)][travis] +[![Build Status (Sim)](https://github.com/mcu-tools/mcuboot/workflows/Sim/badge.svg)][sim] +[![Build Status (Mynewt)](https://github.com/mcu-tools/mcuboot/workflows/Mynewt/badge.svg)][mynewt] +[![Publishing Status (imgtool)](https://github.com/mcu-tools/mcuboot/workflows/imgtool/badge.svg)][imgtool] +[![Build Status (Travis CI)](https://img.shields.io/travis/mcu-tools/mcuboot/master.svg?label=travis-ci)][travis] +[![Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)][license] +[pypi]: https://pypi.org/project/imgtool/ [coverity]: https://scan.coverity.com/projects/mcuboot -[travis]: https://travis-ci.org/JuulLabs-OSS/mcuboot +[sim]: https://github.com/mcu-tools/mcuboot/actions?query=workflow:Sim +[mynewt]: https://github.com/mcu-tools/mcuboot/actions?query=workflow:Mynewt +[imgtool]: https://github.com/mcu-tools/mcuboot/actions?query=workflow:imgtool +[travis]: https://travis-ci.org/mcu-tools/mcuboot +[license]: https://github.com/mcu-tools/mcuboot/blob/master/LICENSE -This is mcuboot version 1.6.0 +This is mcuboot version 1.7.0-rc2 MCUboot is a secure bootloader for 32-bit MCUs. The goal of MCUboot is to define a common infrastructure for the bootloader, system flash layout on @@ -25,6 +35,7 @@ Instructions for different operating systems can be found here: - [Zephyr](docs/readme-zephyr.md) - [Mynewt](docs/readme-mynewt.md) - [RIOT](docs/readme-riot.md) +- [Mbed-OS](docs/readme-mbed.md) - [Simulator](sim/README.rst) ## Roadmap @@ -32,7 +43,7 @@ Instructions for different operating systems can be found here: The issues being planned and worked on are tracked using GitHub issues. To participate please visit: -[MCUBoot GitHub Issues](https://github.com/JuulLabs-OSS/mcuboot/issues) +[MCUBoot GitHub Issues](https://github.com/mcu-tools/mcuboot/issues) ~~Issues were previously tracked on [MCUboot JIRA](https://runtimeco.atlassian.net/projects/MCUB/summary) , but it is now deprecated.~~ diff --git a/boot/bootutil/include/bootutil/image.h b/boot/bootutil/include/bootutil/image.h index 5de8192086..90543b7ebb 100644 --- a/boot/bootutil/include/bootutil/image.h +++ b/boot/bootutil/include/bootutil/image.h @@ -89,6 +89,16 @@ struct flash_area; #define IMAGE_TLV_DEPENDENCY 0x40 /* Image depends on other image */ #define IMAGE_TLV_SEC_CNT 0x50 /* security counter */ #define IMAGE_TLV_BOOT_RECORD 0x60 /* measured boot record */ + /* + * vendor reserved TLVs at xxA0-xxFF, + * where xx denotes the upper byte + * range. Examples: + * 0x00a0 - 0x00ff + * 0x01a0 - 0x01ff + * 0x02a0 - 0x02ff + * ... + * 0xffa0 - 0xfffe + */ #define IMAGE_TLV_ANY 0xffff /* Used to iterate over all TLV */ struct image_version { diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c index 431f4dd03b..b81a7cea31 100644 --- a/boot/bootutil/src/loader.c +++ b/boot/bootutil/src/loader.c @@ -2047,6 +2047,75 @@ boot_get_slot_usage(struct boot_loader_state *state, uint8_t slot_usage[], return image_cnt; } +#ifdef MCUBOOT_DIRECT_XIP_REVERT +/** + * Checks whether the image in the given slot was previously selected to run. + * Erases the image if it was selected but its execution failed, otherwise marks + * it as selected if it has not been before. + * + * @param state Image metadata from the image trailer. This function fills + * this struct with the data read from the image trailer. + * @param slot Image slot number. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_select_or_erase(struct boot_swap_state *state, uint32_t slot) +{ + const struct flash_area *fap; + int fa_id; + int rc; + + fa_id = flash_area_id_from_image_slot(slot); + rc = flash_area_open(fa_id, &fap); + assert(rc == 0); + + memset(state, 0, sizeof(struct boot_swap_state)); + rc = boot_read_swap_state(fap, state); + assert(rc == 0); + + if (state->magic != BOOT_MAGIC_GOOD || + (state->copy_done == BOOT_FLAG_SET && + state->image_ok != BOOT_FLAG_SET)) { + /* + * A reboot happened without the image being confirmed at + * runtime or its trailer is corrupted/invalid. Erase the image + * to prevent it from being selected again on the next reboot. + */ + BOOT_LOG_DBG("Erasing faulty image in the %s slot.", + (slot == BOOT_PRIMARY_SLOT) ? "primary" : "secondary"); + rc = flash_area_erase(fap, 0, fap->fa_size); + assert(rc == 0); + + flash_area_close(fap); + rc = -1; + } else { + if (state->copy_done != BOOT_FLAG_SET) { + if (state->copy_done == BOOT_FLAG_BAD) { + BOOT_LOG_DBG("The copy_done flag had an unexpected value. Its " + "value was neither 'set' nor 'unset', but 'bad'."); + } + /* + * Set the copy_done flag, indicating that the image has been + * selected to boot. It can be set in advance, before even + * validating the image, because in case the validation fails, the + * entire image slot will be erased (including the trailer). + */ + rc = boot_write_copy_done(fap); + if (rc != 0) { + BOOT_LOG_WRN("Failed to set copy_done flag of the image in " + "the %s slot.", (slot == BOOT_PRIMARY_SLOT) ? + "primary" : "secondary"); + rc = 0; + } + } + flash_area_close(fap); + } + + return rc; +} +#endif /* MCUBOOT_DIRECT_XIP_REVERT */ + #ifdef MCUBOOT_RAM_LOAD #if !defined(IMAGE_EXECUTABLE_RAM_START) || !defined(IMAGE_EXECUTABLE_RAM_SIZE) @@ -2207,6 +2276,9 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) uint32_t img_sz; uint32_t img_loaded = 0; #endif /* MCUBOOT_RAM_LOAD */ +#ifdef MCUBOOT_DIRECT_XIP_REVERT + struct boot_swap_state slot_state; +#endif /* MCUBOOT_DIRECT_XIP_REVERT */ fih_int fih_rc = FIH_FAILURE; memset(state, 0, sizeof(struct boot_loader_state)); @@ -2234,7 +2306,6 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) /* Select the newest and valid image. */ for (i = 0; i < img_cnt; i++) { selected_slot = 0; - selected_image_header = NULL; /* Iterate over all the slots that are in use (contain an image) * and select the one that holds the newest image. @@ -2257,6 +2328,17 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) selected_image_header = hdr; } } + +#ifdef MCUBOOT_DIRECT_XIP_REVERT + rc = boot_select_or_erase(&slot_state, selected_slot); + if (rc != 0) { + /* The selected image slot has been erased. */ + slot_usage[selected_slot] = 0; + selected_image_header = NULL; + continue; + } +#endif /* MCUBOOT_DIRECT_XIP_REVERT */ + #ifdef MCUBOOT_RAM_LOAD /* Image is first loaded to RAM and authenticated there in order to * prevent TOCTOU attack during image copy. This could be applied @@ -2268,6 +2350,8 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) &img_sz); if (rc != 0 ) { /* Image loading failed try the next one. */ + slot_usage[selected_slot] = 0; + selected_image_header = NULL; continue; } else { img_loaded = 1; @@ -2294,6 +2378,7 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) * and start over. */ slot_usage[selected_slot] = 0; + selected_image_header = NULL; } if (fih_not_eq(fih_rc, FIH_SUCCESS) || (selected_image_header == NULL)) { @@ -2305,13 +2390,24 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) /* Update the stored security counter with the newer (active) image's * security counter value. */ - rc = boot_update_security_counter(0, selected_slot, - selected_image_header); - if (rc != 0) { - BOOT_LOG_ERR("Security counter update failed after image " - "validation."); - goto out; +#ifdef MCUBOOT_DIRECT_XIP_REVERT + /* When the 'revert' mechanism is enabled in direct-xip mode, the + * security counter can be increased only after reboot, if the image + * has been confirmed at runtime (the image_ok flag has been set). + * This way a 'revert' can be performed when it's necessary. + */ + if (slot_state.image_ok == BOOT_FLAG_SET) { +#endif + rc = boot_update_security_counter(0, selected_slot, + selected_image_header); + if (rc != 0) { + BOOT_LOG_ERR("Security counter update failed after image " + "validation."); + goto out; + } +#ifdef MCUBOOT_DIRECT_XIP_REVERT } +#endif #endif /* MCUBOOT_HW_ROLLBACK_PROT */ #ifdef MCUBOOT_MEASURED_BOOT diff --git a/boot/cypress/README.md b/boot/cypress/README.md index 9dc7defb0f..9e140038ad 100644 --- a/boot/cypress/README.md +++ b/boot/cypress/README.md @@ -43,7 +43,7 @@ Those are represented as submodules. To retrieve source code with subsequent submodules pull: - git clone --recursive https://github.com/JuulLabs-OSS/mcuboot.git + git clone --recursive https://github.com/mcu-tools/mcuboot.git Submodules can also be updated and initialized separately: diff --git a/boot/mbed/app_enc_keys.c b/boot/mbed/app_enc_keys.c new file mode 100644 index 0000000000..cf4bd40640 --- /dev/null +++ b/boot/mbed/app_enc_keys.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2020 Embedded Planet + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +#include +#include + +#if defined(MCUBOOT_SIGN_RSA) +#define HAVE_KEYS +extern const unsigned char rsa_pub_key[]; +extern unsigned int rsa_pub_key_len; +#elif defined(MCUBOOT_SIGN_EC256) +#define HAVE_KEYS +extern const unsigned char ecdsa_pub_key[]; +extern unsigned int ecdsa_pub_key_len; +#elif defined(MCUBOOT_SIGN_ED25519) +#define HAVE_KEYS +extern const unsigned char ed25519_pub_key[]; +extern unsigned int ed25519_pub_key_len; +#else +#error "No public key available for given signing algorithm." +#endif + +/* + * Note: Keys for both signing and encryption must be provided by the application. + * mcuboot's imgtool utility can be used to generate these keys and convert them into compatible C code. + * See imgtool's documentation, specifically the section: "Incorporating the public key into the code" which can be found here: + * https://github.com/JuulLabs-OSS/mcuboot/blob/master/docs/imgtool.md#incorporating-the-public-key-into-the-code + */ +#if defined(HAVE_KEYS) +const struct bootutil_key bootutil_keys[] = { + { +#if defined(MCUBOOT_SIGN_RSA) + .key = rsa_pub_key, + .len = &rsa_pub_key_len, +#elif defined(MCUBOOT_SIGN_EC256) + .key = ecdsa_pub_key, + .len = &ecdsa_pub_key_len, +#elif defined(MCUBOOT_SIGN_ED25519) + .key = ed25519_pub_key, + .len = &ed25519_pub_key_len, +#endif + }, +}; +const int bootutil_key_cnt = 1; + +#if defined(MCUBOOT_ENCRYPT_RSA) + +extern const unsigned char enc_priv_key[]; +extern const unsigned int enc_priv_key_len; + +const struct bootutil_key bootutil_enc_key = { + .key = enc_priv_key, + .len = &enc_priv_key_len, +}; +#elif defined(MCUBOOT_ENCRYPT_KW) +#error "Encrypted images with AES-KW is not implemented yet." +#endif + +#endif diff --git a/boot/mbed/include/flash_map_backend/flash_map_backend.h b/boot/mbed/include/flash_map_backend/flash_map_backend.h new file mode 100644 index 0000000000..8057baf893 --- /dev/null +++ b/boot/mbed/include/flash_map_backend/flash_map_backend.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * Copyright (c) 2020 Embedded Planet + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the Licens + */ + +#ifndef H_UTIL_FLASH_MAP_ +#define H_UTIL_FLASH_MAP_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + * Provides abstraction of flash regions for type of use. + * I.e. dude where's my image? + * + * System will contain a map which contains flash areas. Every + * region will contain flash identifier, offset within flash and length. + * + * 1. This system map could be in a file within filesystem (Initializer + * must know/figure out where the filesystem is at). + * 2. Map could be at fixed location for project (compiled to code) + * 3. Map could be at specific place in flash (put in place at mfg time). + * + * Note that the map you use must be valid for BSP it's for, + * match the linker scripts when platform executes from flash, + * and match the target offset specified in download script. + */ +#include + +/** + * @brief Structure describing an area on a flash device. + * + * Multiple flash devices may be available in the system, each of + * which may have its own areas. For this reason, flash areas track + * which flash device they are part of. + */ +struct flash_area { + /** + * This flash area's ID; unique in the system. + */ + uint8_t fa_id; + + /** + * ID of the flash device this area is a part of. + */ + uint8_t fa_device_id; + + uint16_t pad16; + + /** + * This area's offset, relative to the beginning of its flash + * device's storage. + */ + uint32_t fa_off; + + /** + * This area's size, in bytes. + */ + uint32_t fa_size; +}; + +/** + * @brief Structure describing a sector within a flash area. + * + * Each sector has an offset relative to the start of its flash area + * (NOT relative to the start of its flash device), and a size. A + * flash area may contain sectors with different sizes. + */ +struct flash_sector { + /** + * Offset of this sector, from the start of its flash area (not device). + */ + uint32_t fs_off; + + /** + * Size of this sector, in bytes. + */ + uint32_t fs_size; +}; + +/* + * Start using flash area. + */ +int flash_area_open(uint8_t id, const struct flash_area ** fapp); + +void flash_area_close(const struct flash_area * fap); + +/* + * Read/write/erase. Offset is relative from beginning of flash area. + */ +int flash_area_read(const struct flash_area * fap, uint32_t off, void *dst, + uint32_t len); +int flash_area_write(const struct flash_area * fap, uint32_t off, const void *src, + uint32_t len); +int flash_area_erase(const struct flash_area * fap, uint32_t off, uint32_t len); + +/* + * Alignment restriction for flash writes. + */ +uint8_t flash_area_align(const struct flash_area * fap); + +/* + * What is value is read from erased flash bytes. + */ +uint8_t flash_area_erased_val(const struct flash_area * fap); + +/* + * Given flash area ID, return info about sectors within the area. + */ +int flash_area_get_sectors(int fa_id, uint32_t *count, + struct flash_sector *sectors); + + +int flash_area_id_from_image_slot(int slot); +int flash_area_id_from_multi_image_slot(int image_index, int slot); + + +int flash_area_id_to_image_slot(int area_id); +/** + * Converts the specified flash area ID and image index (in multi-image setup) + * to an image slot index. + * + * Returns image slot index (0 or 1), or -1 if ID doesn't correspond to an image + * slot. + */ +int flash_area_id_to_multi_image_slot(int image_index, int area_id); + +#ifdef __cplusplus +} +#endif + +#endif /* H_UTIL_FLASH_MAP_ */ diff --git a/boot/mbed/include/flash_map_backend/secondary_bd.h b/boot/mbed/include/flash_map_backend/secondary_bd.h new file mode 100644 index 0000000000..c14d3cec69 --- /dev/null +++ b/boot/mbed/include/flash_map_backend/secondary_bd.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 Embedded Planet + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the Licens + * + * Created on: Jul 30, 2020 + * Author: gdbeckstein + */ + +#ifndef MCUBOOT_BOOT_MBED_INCLUDE_FLASH_MAP_BACKEND_SECONDARY_BD_H_ +#define MCUBOOT_BOOT_MBED_INCLUDE_FLASH_MAP_BACKEND_SECONDARY_BD_H_ + +#include "blockdevice/BlockDevice.h" + +/** + * This is implemented as a weak function and may be redefined + * by the application. The default case is to return the + * BlockDevice object returned by BlockDevice::get_default_instance(); + * + * @retval secondary_bd Secondary BlockDevice where update candidates are stored + */ +mbed::BlockDevice* get_secondary_bd(void); + +#endif /* MCUBOOT_BOOT_MBED_INCLUDE_FLASH_MAP_BACKEND_SECONDARY_BD_H_ */ diff --git a/boot/mbed/include/mcuboot_config/mcuboot_assert.h b/boot/mbed/include/mcuboot_config/mcuboot_assert.h new file mode 100644 index 0000000000..8c8d52d522 --- /dev/null +++ b/boot/mbed/include/mcuboot_config/mcuboot_assert.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2018 Open Source Foundries Limited + * + * Copyright (c) 2020 Embedded Planet + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the Licens + */ + +#include "platform/mbed_assert.h" diff --git a/boot/mbed/include/mcuboot_config/mcuboot_config.h b/boot/mbed/include/mcuboot_config/mcuboot_config.h new file mode 100644 index 0000000000..29dbf7560d --- /dev/null +++ b/boot/mbed/include/mcuboot_config/mcuboot_config.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018 Open Source Foundries Limited + * Copyright (c) 2019-2020 Arm Limited + * Copyright (c) 2019-2020 Linaro Limited + * Copyright (c) 2020 Embedded Planet + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __MCUBOOT_CONFIG_H__ +#define __MCUBOOT_CONFIG_H__ + +/* + * For available configurations and their explanations, + * see mbed_lib.json. + */ + +#define SIGNATURE_TYPE_RSA 0 +#define SIGNATURE_TYPE_EC256 1 +#define SIGNATURE_TYPE_ED25519 2 + +/* + * Signature algorithm + */ +#if (MCUBOOT_SIGNATURE_ALGORITHM == SIGNATURE_TYPE_RSA) +#define MCUBOOT_SIGN_RSA +# if (MCUBOOT_RSA_SIGNATURE_LENGTH != 2048 && \ + MCUBOOT_RSA_SIGNATURE_LENGTH != 3072) +# error "Invalid RSA key size (must be 2048 or 3072)" +# else +# define MCUBOOT_SIGN_RSA_LEN MCUBOOT_RSA_SIGNATURE_LENGTH +# endif +#elif (MCUBOOT_SIGNATURE_ALGORITHM == SIGNATURE_TYPE_EC256) +#define MCUBOOT_SIGN_EC256 +#elif (MCUBOOT_SIGNATURE_ALGORITHM == SIGNATURE_TYPE_ED25519) +#define MCUBOOT_SIGN_ED25519 +#endif + +/* + * Crypto backend + */ +#define MBEDTLS 0 +#define TINYCRYPT 1 + +#if (MCUBOOT_CRYPTO_BACKEND == MBEDTLS) +#define MCUBOOT_USE_MBED_TLS +#elif (MCUBOOT_CRYPTO_BACKEND == TINYCRYPT) +/** + * XXX TinyCrypt is currently not supported by Mbed-OS due to build conflicts. + * See https://github.com/AGlass0fMilk/mbed-mcuboot-blinky/issues/2 + */ +#error TinyCrypt is currently not supported by Mbed-OS due to build conflicts. +#define MCUBOOT_USE_TINYCRYPT +#endif + +/* + * Only one image (two slots) supported for now + */ +#define MCUBOOT_IMAGE_NUMBER 1 + +/* + * Encrypted Images + */ +#if defined(MCUBOOT_ENCRYPT_RSA) || defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519) +#define MCUBOOT_ENC_IMAGES +#endif + +/* + * Enabling this option uses newer flash map APIs. This saves RAM and + * avoids deprecated API usage. + */ +#define MCUBOOT_USE_FLASH_AREA_GET_SECTORS + +/* + * No watchdog integration for now + */ +#define MCUBOOT_WATCHDOG_FEED() \ + do { \ + } while (0) + + +#endif /* __MCUBOOT_CONFIG_H__ */ diff --git a/boot/mbed/include/mcuboot_config/mcuboot_logging.h b/boot/mbed/include/mcuboot_config/mcuboot_logging.h new file mode 100644 index 0000000000..ef82ec93fe --- /dev/null +++ b/boot/mbed/include/mcuboot_config/mcuboot_logging.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * Copyright (c) 2020 Cypress Semiconductor Corporation + * Copyright (c) 2020 Embedded Planet + * Copyright (c) 2020 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the Licens. + */ + +#ifndef __MCUBOOT_LOGGING_H__ +#define __MCUBOOT_LOGGING_H__ + +#define MCUBOOT_LOG_LEVEL_OFF 0 +#define MCUBOOT_LOG_LEVEL_ERROR 1 +#define MCUBOOT_LOG_LEVEL_WARNING 2 +#define MCUBOOT_LOG_LEVEL_INFO 3 +#define MCUBOOT_LOG_LEVEL_DEBUG 4 + +/* + * The compiled log level determines the maximum level that can be + * printed. + */ +#ifndef MCUBOOT_LOG_LEVEL +#define MCUBOOT_LOG_LEVEL MCUBOOT_LOG_LEVEL_OFF +#endif + +#if MCUBOOT_LOG_LEVEL == MCUBOOT_LOG_LEVEL_OFF +#define MBED_CONF_MBED_TRACE_ENABLE 0 +#else +#define MBED_CONF_MBED_TRACE_ENABLE 1 +#define MCUBOOT_HAVE_LOGGING +#endif + +#if MCUBOOT_LOG_LEVEL == MCUBOOT_LOG_LEVEL_ERROR +#define MBED_TRACE_MAX_LEVEL TRACE_LEVEL_ERROR +#elif MCUBOOT_LOG_LEVEL == MCUBOOT_LOG_LEVEL_WARNING +#define MBED_TRACE_MAX_LEVEL TRACE_LEVEL_WARN +#elif MCUBOOT_LOG_LEVEL == MCUBOOT_LOG_LEVEL_INFO +#define MBED_TRACE_MAX_LEVEL TRACE_LEVEL_INFO +#elif MCUBOOT_LOG_LEVEL == MCUBOOT_LOG_LEVEL_DEBUG +#define MBED_TRACE_MAX_LEVEL TRACE_LEVEL_DEBUG +#endif + +#define TRACE_GROUP "MCUb" +#include "mbed_trace.h" + +#define MCUBOOT_LOG_MODULE_DECLARE(domain) /* ignore */ +#define MCUBOOT_LOG_MODULE_REGISTER(domain) /* ignore */ + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_ERROR +#define MCUBOOT_LOG_ERR tr_error +#else +#define MCUBOOT_LOG_ERR(...) IGNORE(__VA_ARGS__) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_WARNING +#define MCUBOOT_LOG_WRN tr_warn +#else +#define MCUBOOT_LOG_WRN(...) IGNORE(__VA_ARGS__) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_INFO +#define MCUBOOT_LOG_INF tr_info +#else +#define MCUBOOT_LOG_INF(...) IGNORE(__VA_ARGS__) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_DEBUG +#define MCUBOOT_LOG_DBG tr_debug +#else +#define MCUBOOT_LOG_DBG(...) IGNORE(__VA_ARGS__) +#endif + +#endif /* __MCUBOOT_LOGGING_H__ */ diff --git a/boot/mbed/include/os/os_malloc.h b/boot/mbed/include/os/os_malloc.h new file mode 100644 index 0000000000..3aa6f1ec34 --- /dev/null +++ b/boot/mbed/include/os/os_malloc.h @@ -0,0 +1 @@ +/** Not required for Mbed -- malloc calls are retargeted by the platform */ diff --git a/boot/mbed/include/sysflash/sysflash.h b/boot/mbed/include/sysflash/sysflash.h new file mode 100644 index 0000000000..e19945b3f3 --- /dev/null +++ b/boot/mbed/include/sysflash/sysflash.h @@ -0,0 +1,14 @@ +/* Manual version of auto-generated version. */ + +#ifndef __SYSFLASH_H__ +#define __SYSFLASH_H__ + +#define PRIMARY_ID 0 +#define SECONDARY_ID 1 +#define SCRATCH_ID 2 + +#define FLASH_AREA_IMAGE_PRIMARY(x) PRIMARY_ID +#define FLASH_AREA_IMAGE_SECONDARY(x) SECONDARY_ID +#define FLASH_AREA_IMAGE_SCRATCH SCRATCH_ID + +#endif /* __SYSFLASH_H__ */ diff --git a/boot/mbed/mbed_lib.json b/boot/mbed/mbed_lib.json new file mode 100644 index 0000000000..7c9ccba740 --- /dev/null +++ b/boot/mbed/mbed_lib.json @@ -0,0 +1,165 @@ +{ + "name": "mcuboot", + "config": { + "bootloader-build": { + "help": "Build the bootloader, in addition to the MCUboot library.", + "macro_name": "MCUBOOT_BOOTLOADER_BUILD", + "accepted_values": [true, false], + "value": true + }, + "primary-slot-address": { + "help": "Start address of the primary (bootable) image slot. Target-dependent, please set on a per-target basis.", + "macro_name": "MCUBOOT_PRIMARY_SLOT_START_ADDR", + "required": true + }, + "slot-size": { + "help": "Size of the primary (bootable) image slot, in bytes. Target-dependent, please set on a per-target basis.", + "macro_name": "MCUBOOT_SLOT_SIZE", + "required": true + }, + "scratch-address": { + "help": "Start address of the scratch area. If needed, please set on a per-target basis.", + "macro_name": "MCUBOOT_SCRATCH_START_ADDR" + }, + "scratch-size": { + "help": "Size of the scratch area, in bytes. If needed, please set on a per-target basis.", + "macro_name": "MCUBOOT_SCRATCH_SIZE" + }, + "header-size": { + "help": "Header size, in bytes, prepended to the bootable application image. Should be one or multiple times the sector size.", + "macro_name": "MCUBOOT_HEADER_SIZE", + "required": true, + "value": 4096 + }, + "validate-primary-slot": { + "help": "Always check the signature of the image in the primary slot before booting, even if no upgrade was performed. This is recommended if the boot time penalty is acceptable.", + "macro_name": "MCUBOOT_VALIDATE_PRIMARY_SLOT", + "accepted_values": [true, null], + "value": true + }, + "signature-algorithm": { + "help": "The algorithm used for digital signing.", + "macro_name": "MCUBOOT_SIGNATURE_ALGORITHM", + "required": true, + "accepted_values": ["SIGNATURE_TYPE_RSA", "SIGNATURE_TYPE_EC256", "SIGNATURE_TYPE_ED25519"], + "value": "SIGNATURE_TYPE_RSA" + }, + "rsa-signature-length": { + "help": "If RSA is used for signature algorithm, this specifies the length.", + "macro_name": "MCUBOOT_RSA_SIGNATURE_LENGTH", + "required": true, + "accepted_values": [2048, 3072], + "value": 2048 + }, + "crypto-backend": { + "help": "The crypto library backend. NOTE: TinyCrypt is currently only supported with GCC for Mbed-OS builds.", + "macro_name": "MCUBOOT_CRYPTO_BACKEND", + "required": true, + "accepted_values": ["MBEDTLS", "TINYCRYPT"], + "value": "MBEDTLS" + }, + "overwrite-only": { + "help": "The default is to support A/B image swapping with rollback. A simpler code path, which only supports overwriting the existing image with the update image, is also available. (null to disable)", + "macro_name": "MCUBOOT_OVERWRITE_ONLY", + "accepted_values": [true, null], + "value": null + }, + "overwrite-only-fast": { + "help": "Only erase and overwrite those primary slot sectors needed to install the new image, rather than the entire image slot.", + "macro_name": "MCUBOOT_OVERWRITE_ONLY_FAST", + "accepted_values": [true, null], + "value": null + }, + "log-level": { + "help": "Verbosity of MCUboot logging.", + "macro_name": "MCUBOOT_LOG_LEVEL", + "accepted_values": ["MCUBOOT_LOG_LEVEL_OFF", "MCUBOOT_LOG_LEVEL_ERROR", "MCUBOOT_LOG_LEVEL_WARN", "MCUBOOT_LOG_LEVEL_INFO", "MCUBOOT_LOG_LEVEL_DEBUG"], + "value": "MCUBOOT_LOG_LEVEL_OFF" + }, + "log-bootloader-only": { + "help": "Exclude non-bootloader logs from Mbed OS (e.g. underlying storage).", + "macro_name": "MCUBOOT_LOG_BOOTLOADER_ONLY", + "accepted_values": [true, false], + "value": true + }, + "max-img-sectors": { + "help": "Maximum number of flash sectors per image slot. Target-dependent, please set on a per-target basis.", + "macro_name": "MCUBOOT_MAX_IMG_SECTORS", + "required": true + }, + "read-granularity": { + "help": "Granularity of read operations, in bytes. Enables a workaround if your block device does not support reading a single byte at a time. If this is used, it should be at least the value of your specific ->get_read_size() result.", + "macro_name": "MCUBOOT_READ_GRANULARITY", + "value": null + }, + "hardware-key": { + "help": "Use hardware key (NOT TESTED)", + "macro_name": "MCUBOOT_HW_KEY", + "accepted_values": [true, null], + "value": null + }, + "boot-swap-move": { + "help": "Boot swap using move (NOT TESTED)", + "macro_name": "MCUBOOT_SWAP_USING_MOVE", + "accepted_values": [true, null], + "value": null + }, + "updateable-image-number": { + "help": "Updateable image number (NOT TESTED)", + "macro_name": "MCUBOOT_IMAGE_NUMBER" + }, + "MCUBOOT_SWAP_SAVE_ENCTLV": { + "help": "Swap save enctlv (NOT TESTED)", + "macro_name": "MCUBOOT_IMAGE_NUMBER", + "value": null + }, + "encrypt-rsa": { + "help": "Encrypt images using RSA (NOT TESTED)", + "macro_name": "MCUBOOT_ENCRYPT_RSA", + "accepted_values": [true, null], + "value": null + }, + "encrypt-ec256": { + "help": "Encrypt images using EC256 (NOT TESTED)", + "macro_name": "MCUBOOT_ENCRYPT_EC256", + "accepted_values": [true, null], + "value": null + }, + "encrypt-x25519": { + "help": "Encrypt images using X25519 (NOT TESTED)", + "macro_name": "MCUBOOT_ENCRYPT_X25519", + "accepted_values": [true, null], + "value": null + }, + "bootstrap": { + "help": "Bootstrap (NOT TESTED)", + "macro_name": "MCUBOOT_BOOTSTRAP", + "value": null + }, + "use-bench": { + "help": "Use bench (NOT TESTED)", + "macro_name": "MCUBOOT_USE_BENCH", + "value": null + }, + "downgrade-prevention": { + "help": "Prevent downgrades (NOT TESTED)", + "macro_name": "MCUBOOT_DOWNGRADE_PREVENTION", + "value": null + }, + "hw-rollback-protection": { + "help": "Hardware rollback protection (NOT TESTED)", + "macro_name": "MCUBOOT_HW_ROLLBACK_PROT", + "value": null + }, + "measured-boot": { + "help": "Measured boot (NOT TESTED)", + "macro_name": "MCUBOOT_MEASURED_BOOT", + "value": null + }, + "share-data": { + "help": "Share data (NOT TESTED)", + "macro_name": "MCUBOOT_DATA_SHARING", + "value": null + } + } +} diff --git a/boot/mbed/mcuboot_main.cpp b/boot/mbed/mcuboot_main.cpp new file mode 100644 index 0000000000..c5f413789c --- /dev/null +++ b/boot/mbed/mcuboot_main.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020 Embedded Planet + * Copyright (c) 2020 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +#if MCUBOOT_BOOTLOADER_BUILD + +#include +#include "bootutil/bootutil.h" +#include "bootutil/image.h" +#include "hal/serial_api.h" +#include "mbed_application.h" + +#if (MCUBOOT_CRYPTO_BACKEND == MBEDTLS) +#include "mbedtls/platform.h" +#elif (MCUBOOT_CRYPTO_BACKEND == TINYCRYPT) +#include "tinycrypt/ecc.h" +#endif + +#define MBED_TRACE_MAX_LEVEL TRACE_LEVEL_DEBUG +#define TRACE_GROUP "BL" +#include "mbed-trace/mbed_trace.h" + +#if (MCUBOOT_CRYPTO_BACKEND == TINYCRYPT) +/* XXX add this global definition for linking only + * TinyCrypt is used for signature verification and ECIES using secp256r1 and AES encryption; + * RNG is not required. So here we provide a stub. + * See https://github.com/mcu-tools/mcuboot/pull/791#discussion_r514480098 + */ + +extern "C" { +int default_CSPRNG(uint8_t *dest, unsigned int size) { return 0; } +} +#endif + +int main() +{ + int rc; + + mbed_trace_init(); +#if MCUBOOT_LOG_BOOTLOADER_ONLY + mbed_trace_include_filters_set("MCUb,BL"); +#endif + + tr_info("Starting MCUboot"); + +#if (MCUBOOT_CRYPTO_BACKEND == MBEDTLS) + // Initialize mbedtls crypto for use by MCUboot + mbedtls_platform_context unused_ctx; + rc = mbedtls_platform_setup(&unused_ctx); + if(rc != 0) { + tr_error("Failed to setup Mbed TLS, error: %d", rc); + exit(rc); + } +#elif (MCUBOOT_CRYPTO_BACKEND == TINYCRYPT) + uECC_set_rng(0); +#endif + + struct boot_rsp rsp; + rc = boot_go(&rsp); + if(rc != 0) { + tr_error("Failed to locate firmware image, error: %d", rc); + exit(rc); + } + + uint32_t address = rsp.br_image_off + rsp.br_hdr->ih_hdr_size; + + // Workaround: The extra \n ensures the last trace gets flushed + // before mbed_start_application() destroys the stack and jumps + // to the application + tr_info("Booting firmware image at 0x%x\n", address); + + // Run the application in the primary slot + // Add header size offset to calculate the actual start address of application + mbed_start_application(address); +} + +#endif // MCUBOOT_BOOTLOADER_BUILD diff --git a/boot/mbed/src/flash_map_backend.cpp b/boot/mbed/src/flash_map_backend.cpp new file mode 100644 index 0000000000..a28817aac6 --- /dev/null +++ b/boot/mbed/src/flash_map_backend.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2020 Embedded Planet + * Copyright (c) 2020 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +#include +#include +#include "flash_map_backend/flash_map_backend.h" +#include "flash_map_backend/secondary_bd.h" +#include "sysflash/sysflash.h" + +#include "blockdevice/BlockDevice.h" +#include "FlashIAP/FlashIAPBlockDevice.h" + +#include "mcuboot_config/mcuboot_logging.h" + +#define FLASH_DEVICE_INTERNAL_FLASH 0 +#define FLASH_AREAS 3 + +/** Application defined secondary block device */ +mbed::BlockDevice* mcuboot_secondary_bd = get_secondary_bd(); + +/** Internal application block device */ +static FlashIAPBlockDevice mcuboot_primary_bd(MCUBOOT_PRIMARY_SLOT_START_ADDR, MCUBOOT_SLOT_SIZE); + +#ifndef MCUBOOT_OVERWRITE_ONLY +/** Scratch space is at the end of internal flash, after the main application */ +static FlashIAPBlockDevice mcuboot_scratch_bd(MCUBOOT_SCRATCH_START_ADDR, MCUBOOT_SCRATCH_SIZE); +#endif + +static mbed::BlockDevice* flash_map_bd[FLASH_AREAS] = { + (mbed::BlockDevice*) &mcuboot_primary_bd, /** Primary (loadable) image area */ + mcuboot_secondary_bd, /** Secondary (update candidate) image area */ +#ifndef MCUBOOT_OVERWRITE_ONLY + (mbed::BlockDevice*) &mcuboot_scratch_bd /** Scratch space for swapping images */ +#else + nullptr +#endif +}; + +static struct flash_area flash_areas[FLASH_AREAS]; + +int flash_area_open(uint8_t id, const struct flash_area** fapp) { + + *fapp = &flash_areas[id]; + struct flash_area* fap = (struct flash_area*)*fapp; + + // The offset of the slot is from the beginning of the flash device. + switch (id) { + case PRIMARY_ID: + fap->fa_off = MCUBOOT_PRIMARY_SLOT_START_ADDR; + break; + case SECONDARY_ID: + // The offset of the secondary slot is not currently used. + fap->fa_off = 0; + break; +#ifndef MCUBOOT_OVERWRITE_ONLY + case SCRATCH_ID: + fap->fa_off = MCUBOOT_SCRATCH_START_ADDR; + break; +#endif + default: + MCUBOOT_LOG_ERR("flash_area_open, unknown id %d", id); + return -1; + } + + fap->fa_id = id; + fap->fa_device_id = 0; // not relevant + + mbed::BlockDevice* bd = flash_map_bd[id]; + fap->fa_size = (uint32_t) bd->size(); + return bd->init(); +} + +void flash_area_close(const struct flash_area* fap) { + mbed::BlockDevice* bd = flash_map_bd[fap->fa_id]; + bd->deinit(); +} + +/* + * Read/write/erase. Offset is relative from beginning of flash area. + */ +int flash_area_read(const struct flash_area* fap, uint32_t off, void* dst, uint32_t len) { + mbed::BlockDevice* bd = flash_map_bd[fap->fa_id]; + + // Note: The address must be aligned to bd->get_read_size(). If MCUBOOT_READ_GRANULARITY + // is defined, the length does not need to be aligned. +#ifdef MCUBOOT_READ_GRANULARITY + uint32_t read_size = bd->get_read_size(); + if (read_size == 0) { + MCUBOOT_LOG_ERR("Invalid read size: must be non-zero"); + return -1; + } + if (MCUBOOT_READ_GRANULARITY < read_size) { + MCUBOOT_LOG_ERR("Please increase MCUBOOT_READ_GRANULARITY (currently %u) to be at least %u", + MCUBOOT_READ_GRANULARITY, read_size); + return -1; + } + + uint32_t remainder = len % read_size; + len -= remainder; + if (len != 0) { +#endif + if (!bd->is_valid_read(off, len)) { + MCUBOOT_LOG_ERR("Invalid read: fa_id %d offset 0x%x len 0x%x", fap->fa_id, off, len); + return -1; + } + else { + int ret = bd->read(dst, off, len); + if (ret != 0) { + MCUBOOT_LOG_ERR("Read failed: fa_id %d offset 0x%x len 0x%x", fap->fa_id, off, len); + return ret; + } + } +#ifdef MCUBOOT_READ_GRANULARITY + } + + if (remainder) { + if (!bd->is_valid_read(off + len, read_size)) { + MCUBOOT_LOG_ERR("Invalid read: fa_id %d offset 0x%x len 0x%x", fap->fa_id, off + len, read_size); + return -1; + } + else { + uint8_t buffer[MCUBOOT_READ_GRANULARITY]; + int ret = bd->read(buffer, off + len, read_size); + if (ret != 0) { + MCUBOOT_LOG_ERR("Read failed: %d", ret); + return ret; + } + memcpy((uint8_t *)dst + len, buffer, remainder); + } + } +#endif + + return 0; +} + +int flash_area_write(const struct flash_area* fap, uint32_t off, const void* src, uint32_t len) { + mbed::BlockDevice* bd = flash_map_bd[fap->fa_id]; + return bd->program(src, off, len); +} + +int flash_area_erase(const struct flash_area* fap, uint32_t off, uint32_t len) { + mbed::BlockDevice* bd = flash_map_bd[fap->fa_id]; + return bd->erase(off, len); +} + +uint8_t flash_area_align(const struct flash_area* fap) { + mbed::BlockDevice* bd = flash_map_bd[fap->fa_id]; + return bd->get_program_size(); +} + +uint8_t flash_area_erased_val(const struct flash_area* fap) { + mbed::BlockDevice* bd = flash_map_bd[fap->fa_id]; + return bd->get_erase_value(); +} + +int flash_area_get_sectors(int fa_id, uint32_t* count, struct flash_sector* sectors) { + mbed::BlockDevice* bd = flash_map_bd[fa_id]; + + // Loop through sectors and collect information on them + bd_addr_t offset = 0; + *count = 0; + while (*count < MCUBOOT_MAX_IMG_SECTORS && bd->is_valid_read(offset, bd->get_read_size())) { + + sectors[*count].fs_off = offset; + bd_size_t erase_size = bd->get_erase_size(offset); + sectors[*count].fs_size = erase_size; + + offset += erase_size; + *count += 1; + } + + return 0; +} + +int flash_area_id_from_image_slot(int slot) { + return slot; +} + +int flash_area_id_to_image_slot(int area_id) { + return area_id; +} + +/** + * Multi images support not implemented yet + */ +int flash_area_id_from_multi_image_slot(int image_index, int slot) +{ + assert(image_index == 0); + return slot; +} + +int flash_area_id_to_multi_image_slot(int image_index, int area_id) +{ + assert(image_index == 0); + return area_id; +} diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig index 3f2374b661..1386f5e8b6 100644 --- a/boot/zephyr/Kconfig +++ b/boot/zephyr/Kconfig @@ -224,9 +224,18 @@ config BOOT_DIRECT_XIP mode results in a simpler code path and smaller code size. endchoice -endif -if !SINGLE_APPLICATION_SLOT +config BOOT_DIRECT_XIP_REVERT + bool "Enable the revert mechanism in direct-xip mode" + default n + help + If y, enables the revert mechanism in direct-xip similar to the one in + swap mode. It requires the trailer magic to be added to the signed image. + When a reboot happens without the image being confirmed at runtime, the + bootloader considers the image faulty and erases it. After this it will + attempt to boot the previous image. The images can also be made permanent + (marked as confirmed in advance) just like in swap mode. + config BOOT_BOOTSTRAP bool "Bootstrap erased the primary slot from the secondary slot" default n @@ -276,7 +285,7 @@ config BOOT_ENCRYPT_X25519 back when swapping from the primary slot to the secondary slot. The encryption mechanism used in this case is ECIES using primitives described under "ECIES-X25519 encryption" in docs/encrypted_images.md. -endif +endif # !SINGLE_APPLICATION_SLOT config BOOT_MAX_IMG_SECTORS int "Maximum number of sectors per image slot" diff --git a/boot/zephyr/boards/nrf52840dk_qspi_nor.conf b/boot/zephyr/boards/nrf52840dk_qspi_nor.conf new file mode 100644 index 0000000000..3205eafd60 --- /dev/null +++ b/boot/zephyr/boards/nrf52840dk_qspi_nor.conf @@ -0,0 +1,3 @@ +CONFIG_NORDIC_QSPI_NOR=y +CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 diff --git a/boot/zephyr/boards/nrf52840dk_qspi_nor_secondary.overlay b/boot/zephyr/boards/nrf52840dk_qspi_nor_secondary.overlay new file mode 100644 index 0000000000..7c9a87c32e --- /dev/null +++ b/boot/zephyr/boards/nrf52840dk_qspi_nor_secondary.overlay @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/delete-node/ &boot_partition; +/delete-node/ &slot0_partition; +/delete-node/ &slot1_partition; +/delete-node/ &scratch_partition; + +&flash0 { + partitions { + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x000000000 0x00010000>; + }; + slot0_partition: partition@10000 { + label = "image-0"; + reg = <0x000010000 0x0000e8000>; + }; + }; +}; + +&mx25r64 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + slot1_partition: partition@0 { + label = "image-1"; + reg = <0x000000000 0x0000e8000>; + }; + }; +}; diff --git a/boot/zephyr/boards/nrf52840dk_qspi_secondary_boot.conf b/boot/zephyr/boards/nrf52840dk_qspi_secondary_boot.conf new file mode 100644 index 0000000000..9f984be4fe --- /dev/null +++ b/boot/zephyr/boards/nrf52840dk_qspi_secondary_boot.conf @@ -0,0 +1,2 @@ +CONFIG_MULTITHREADING=y +CONFIG_BOOT_MAX_IMG_SECTORS=256 diff --git a/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/boot/zephyr/include/mcuboot_config/mcuboot_config.h index 0ccf36dfdf..4f852bec00 100644 --- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h +++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h @@ -70,6 +70,10 @@ #define MCUBOOT_DIRECT_XIP #endif +#ifdef CONFIG_BOOT_DIRECT_XIP_REVERT +#define MCUBOOT_DIRECT_XIP_REVERT +#endif + #ifdef CONFIG_UPDATEABLE_IMAGE_NUMBER #define MCUBOOT_IMAGE_NUMBER CONFIG_UPDATEABLE_IMAGE_NUMBER #else diff --git a/boot/zephyr/sample.yaml b/boot/zephyr/sample.yaml index 048a58818f..a685159d21 100644 --- a/boot/zephyr/sample.yaml +++ b/boot/zephyr/sample.yaml @@ -23,3 +23,8 @@ tests: DTC_OVERLAY_FILE=./boards/nrf52840_single_slot.overlay platform_allow: nrf52840dk_nrf52840 tags: bootloader_mcuboot + sample.bootloader.mcuboot.qspi_nor_slot: + extra_args: DTC_OVERLAY_FILE=./boards/nrf52840dk_qspi_nor_secondary.overlay + OVERLAY_CONFIG="./boards/nrf52840dk_qspi_nor.conf;./boards/nrf52840dk_qspi_secondary_boot.conf" + platform_allow: nrf52840dk_nrf52840 + tags: bootloader_mcuboot diff --git a/ci/fih-tests_install.sh b/ci/fih-tests_install.sh new file mode 100755 index 0000000000..5acf42e478 --- /dev/null +++ b/ci/fih-tests_install.sh @@ -0,0 +1,28 @@ +#!/bin/bash -x + +# Copyright (c) 2020 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +# get mcuboot root; assumes running script is stored under REPO_DIR/ci/ +REPO_DIR=$(dirname $(dirname $(realpath $0))) +pushd $(mktemp -d) + +# copy mcuboot so that it is part of the docker build context +cp -r $REPO_DIR . +cp -r $REPO_DIR/ci/fih_test_docker/execute_test.sh . +cp -r $REPO_DIR/ci/fih_test_docker/Dockerfile . +./mcuboot/ci/fih_test_docker/build.sh +popd \ No newline at end of file diff --git a/ci/fih-tests_run.sh b/ci/fih-tests_run.sh new file mode 100755 index 0000000000..7948a3ecca --- /dev/null +++ b/ci/fih-tests_run.sh @@ -0,0 +1,23 @@ +#!/bin/bash -x + +# Copyright (c) 2020 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +if test -z "$FIH_LEVEL"; then + docker run mcuboot/fih-test /bin/sh -c '/root/execute_test.sh $0 $1 $2' $SKIP_SIZE $BUILD_TYPE $DAMAGE_TYPE +else + docker run mcuboot/fih-test /bin/sh -c '/root/execute_test.sh $0 $1 $2 $3' $SKIP_SIZE $BUILD_TYPE $DAMAGE_TYPE $FIH_LEVEL +fi \ No newline at end of file diff --git a/ci/fih_test_docker/Dockerfile b/ci/fih_test_docker/Dockerfile new file mode 100644 index 0000000000..f4b743a232 --- /dev/null +++ b/ci/fih_test_docker/Dockerfile @@ -0,0 +1,51 @@ +# Copyright (c) 2020 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM ubuntu:focal + +# get dependencies for retrieving and building TF-M with MCUBoot, and QEMU. +RUN apt-get update && \ + DEBIAN_FRONTEND="noninteractive" \ + apt-get install -y \ + cmake \ + curl \ + gcc-arm-none-eabi \ + gdb-multiarch \ + git \ + libncurses5 \ + python3 \ + python3-pip \ + qemu-system-arm + +RUN \ + # installing python packages + python3 -m pip install \ + imgtool==1.6.0 \ + Jinja2==2.10 \ + PyYAML==3.12 \ + pyasn1==0.1.9 + +# Clone TF-M and dependencies +RUN mkdir -p /root/work/tfm &&\ + cd /root/work/tfm &&\ + git clone https://git.trustedfirmware.org/TF-M/trusted-firmware-m.git -b TF-Mv1.2-RC1 &&\ + mkdir mcuboot + +# Copy the test execution script to the image +COPY execute_test.sh /root +# copy the MCUBoot under test to the image +COPY mcuboot /root/work/tfm/mcuboot + +# run the command +CMD ["bash"] \ No newline at end of file diff --git a/ci/fih_test_docker/build.sh b/ci/fih_test_docker/build.sh new file mode 100755 index 0000000000..304362e931 --- /dev/null +++ b/ci/fih_test_docker/build.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# Copyright (c) 2020 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +trap cleanup_exit INT TERM EXIT + +cleanup_exit() +{ + rm -f *.list *.key +} + +export LANG=C + +image=mcuboot/fih-test +docker build --pull --tag=$image . +echo $image > .docker-tag \ No newline at end of file diff --git a/ci/fih_test_docker/damage_image.py b/ci/fih_test_docker/damage_image.py new file mode 100644 index 0000000000..afa25b8378 --- /dev/null +++ b/ci/fih_test_docker/damage_image.py @@ -0,0 +1,211 @@ +# Copyright (c) 2020 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import logging +import struct +import sys + +from imgtool.image import (IMAGE_HEADER_SIZE, IMAGE_MAGIC, + TLV_INFO_MAGIC, TLV_PROT_INFO_MAGIC, TLV_VALUES) +from shutil import copyfile + + +def get_tlv_type_string(tlv_type): + tlvs = {v: f"IMAGE_TLV_{k}" for k, v in TLV_VALUES.items()} + return tlvs.get(tlv_type, "UNKNOWN({:d})".format(tlv_type)) + + +class ImageHeader: + + def __init__(self): + self.ih_magic = 0 + self.ih_load_addr = 0 + self.ih_hdr_size = 0 + self.ih_protect_tlv_size = 0 + self.ih_img_size = 0 + self.ih_flags = 0 + self.iv_major = 0 + self.iv_minor = 0 + self.iv_revision = 0 + self.iv_build_num = 0 + self._pad1 = 0 + + @staticmethod + def read_from_binary(in_file): + h = ImageHeader() + + (h.ih_magic, h.ih_load_addr, h.ih_hdr_size, h.ih_protect_tlv_size, h.ih_img_size, + h.ih_flags, h.iv_major, h.iv_minor, h.iv_revision, h.iv_build_num, h._pad1 + ) = struct.unpack(' fih_test_output.yaml + +echo "" +echo "test finished with" +echo " - BUILD_TYPE: $BUILD_TYPE" +echo " - FIH_LEVEL: $FIH_LEVEL" +echo " - SKIP_SIZE: $SKIP_SIZE" +echo " - DAMAGE_TYPE: $DAMAGE_TYPE" + +python3 $MCUBOOT_PATH/ci/fih_test_docker/generate_test_report.py fih_test_output.yaml \ No newline at end of file diff --git a/ci/fih_test_docker/fi_make_manifest.sh b/ci/fih_test_docker/fi_make_manifest.sh new file mode 100755 index 0000000000..190a316400 --- /dev/null +++ b/ci/fih_test_docker/fi_make_manifest.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2020 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +OBJDUMP=arm-none-eabi-objdump +GDB=gdb-multiarch + +# Check if the ELF file specified is compatible +if test $# -eq 0 || ! file $1 | grep "ELF" | grep "ARM" | grep "32" &>/dev/null; then + echo "Incompatible file: $1" 1>&2 + exit 1 +fi + +# Extract the full path +AXF_PATH=$(realpath $1) +#Dump all objects that have a name containing FIH_LABEL +ADDRESSES=$($OBJDUMP $AXF_PATH -t | grep "FIH_LABEL") +# strip all data except "address, label_name" +ADDRESSES=$(echo "$ADDRESSES" | sed "s/\([[:xdigit:]]*\).*\(FIH_LABEL_FIH_CALL_[a-zA-Z]*\)_.*/0x\1, \2/g") +# Sort by address in ascending order +ADDRESSES=$(echo "$ADDRESSES" | sort) +# In the case that there is a START followed by another START take the first one +ADDRESSES=$(echo "$ADDRESSES" | sed "N;s/\(.*START.*\)\n\(.*START.*\)/\1/;P;D") +# Same for END except take the second one +ADDRESSES=$(echo "$ADDRESSES" | sed "N;s/\(.*END.*\)\n\(.*END.*\)/\2/;P;D") + +# Output in CSV format with a label +echo "Address, Type" +echo "$ADDRESSES" diff --git a/ci/fih_test_docker/fi_tester_gdb.sh b/ci/fih_test_docker/fi_tester_gdb.sh new file mode 100755 index 0000000000..05f049200e --- /dev/null +++ b/ci/fih_test_docker/fi_tester_gdb.sh @@ -0,0 +1,199 @@ +#!/bin/bash + +# Copyright (c) 2020 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +function skip_instruction { + + local SKIP_ADDRESS=$1 + local SKIP_SIZE=$2 + + # Parse the ASM instruction from the address using gdb + INSTR=$($GDB $AXF_FILE --batch -ex "disassemble $SKIP_ADDRESS" | grep "^ *$SKIP_ADDRESS" | sed "s/.*:[ \t]*\(.*\)$/\1/g") + # Parse the C line from the address using gdb + LINE=$($GDB $AXF_FILE --batch -ex "info line *$SKIP_ADDRESS" | sed "s/Line \([0-9]*\).*\"\(.*\)\".*/\2:\1/g") + + # Sometimes an address is in the middle of a 4 byte instruction. In that case + # don't run the test + if test "$INSTR" == ""; then + return + fi + + # Print out the meta-info about the test, in YAML + echo "- skip_test:" + echo " addr: $SKIP_ADDRESS" + echo " asm: \"$INSTR\"" + echo " line: \"$LINE\"" + echo " skip: $SKIP_SIZE" + # echo -ne "$SKIP_ADDRESS | $INSTR...\t" + + cat >commands.gdb <&2 + + # start qemu, dump the serial output to $QEMU_LOG_FILE + QEMU_LOG_FILE=qemu.log + QEMU_PID_FILE=qemu_pid.txt + rm -f $QEMU_PID_FILE $QEMU_LOG_FILE + /usr/bin/qemu-system-arm \ + -M mps2-an521 \ + -s -S \ + -kernel $IMAGE_DIR/bl2.axf \ + -device loader,file=$IMAGE_DIR/tfm_s_ns_signed.bin,addr=0x10080000 \ + -chardev file,id=char0,path=$QEMU_LOG_FILE \ + -serial chardev:char0 \ + -display none \ + -pidfile $QEMU_PID_FILE \ + -daemonize + + # start qemu, skip the instruction, and continue execution + $GDB < ./commands.gdb &>gdb_out.txt + + # kill qemu + kill -9 `cat $QEMU_PID_FILE` + + # If "Secure image initializing" is seen the TFM booted, which means that a skip + # managed to defeat the signature check. Write out whether the image booted or + # not to the log in YAML + if cat $QEMU_LOG_FILE | grep -i "Starting bootloader" &>/dev/null; then + # bootloader started successfully + if cat gdb_out.txt | grep -i "Stopped at breakpoint" &>/dev/null; then + # The target was stopped at the desired address + if cat $QEMU_LOG_FILE | grep -i "Secure image initializing" &>/dev/null; then + echo " test_exec_ok: True" + echo " skipped: True" + echo " boot: True" + + #print the address that was skipped, and some context to the console + echo "" 1>&2 + echo "Boot success: address: $SKIP_ADDRESS skipped: $SKIP_SIZE" 1>&2 + arm-none-eabi-objdump -d $IMAGE_DIR/bl2.axf --start-address=$SKIP_ADDRESS -S | tail -n +7 | head -n 14 1>&2 + echo "" 1>&2 + echo "" 1>&2 + else + LAST_LINE=`tail -n 1 $QEMU_LOG_FILE | tr -dc '[:print:]'` + echo " test_exec_ok: True" + echo " skipped: True" + echo " boot: False" + echo " last_line: '$LAST_LINE' " + fi + else + # The target was not stopped at the desired address. + # The most probable reason is that the instruction for that address is + # on a call path that is not taken in this run (e.g. error handling) + if cat $QEMU_LOG_FILE | grep -i "Secure image initializing" &>/dev/null; then + # The image booted, although it shouldn't happen as the test is to + # be run with a corrupt image. + echo " test_exec_ok: False" + echo " test_exec_fail_reason: \"No instructions were skipped (e.g. branch was not executed), but booted successfully\"" + else + # the execution didn't stop at the address (e.g. the instruction + # is on a branch that is not taken) + echo " test_exec_ok: True" + echo " skipped: False" + fi + fi + else + # failed before the first printout + echo " test_exec_ok: True" + echo " skipped: True" + echo " boot: False" + echo " last_line: 'N/A' " + fi +} + +# Inform how the script is used +usage() { + echo "$0 [] [(-s | --skip) ]" +} + +#defaults +SKIP=2 +BIN_DIR=$(pwd)/install/outputs/MPS2/AN521 +AXF_FILE=$BIN_DIR/bl2.axf +GDB=gdb-multiarch +BOOTLOADER=true + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + -s|--skip) + SKIP="$2" + shift + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + if test -z "$IMAGE_DIR"; then + IMAGE_DIR=$1 + elif test -z "$START"; then + START=$1 + elif test -z "$END"; then + END=$1 + else + usage + exit 1 + fi + shift + ;; + esac +done + +# Check that image directory, start and end address have been supplied +if test -z "$IMAGE_DIR"; then + usage + exit 2 +fi + +if test -z "$START"; then + usage + exit 2 +fi + +if test -z "$END"; then + END=$START +fi + +if test -z "$SKIP"; then + SKIP='2' +fi + +# Create the start-end address range (step 2) +ADDRS=$(printf '0x%x\n' $(seq "$START" 2 "$END")) + +# For each address run the skip_instruction function on it +for ADDR in $ADDRS; do + skip_instruction $ADDR $SKIP +done diff --git a/ci/fih_test_docker/generate_test_report.py b/ci/fih_test_docker/generate_test_report.py new file mode 100644 index 0000000000..0eb13b4ddd --- /dev/null +++ b/ci/fih_test_docker/generate_test_report.py @@ -0,0 +1,88 @@ +# Copyright (c) 2020 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import yaml +import collections + +CATEGORIES = { + 'TOTAL': 'Total tests run', + 'SUCCESS': 'Tests executed successfully', + 'FAILED': 'Tests failed to execute successfully', + # the execution never reached the address + 'ADDRES_NOEXEC': 'Address was not executed', + # The address was successfully skipped by the debugger + 'SKIPPED': 'Address was skipped', + 'NO_BOOT': 'System not booted (desired behaviour)', + 'BOOT': 'System booted (undesired behaviour)' +} + + +def print_results(results): + test_stats = collections.Counter() + failed_boot_last_lines = collections.Counter() + exec_fail_reasons = collections.Counter() + + for test in results: + test = test["skip_test"] + + test_stats.update([CATEGORIES['TOTAL']]) + + if test["test_exec_ok"]: + test_stats.update([CATEGORIES['SUCCESS']]) + + if "skipped" in test.keys() and not test["skipped"]: + # The debugger didn't stop at this address + test_stats.update([CATEGORIES['ADDRES_NOEXEC']]) + continue + + if test["boot"]: + test_stats.update([CATEGORIES['BOOT']]) + continue + + failed_boot_last_lines.update([test["last_line"]]) + else: + exec_fail_reasons.update([test["test_exec_fail_reason"]]) + + print("{:s}: {:d}.".format(CATEGORIES['TOTAL'], test_stats[CATEGORIES['TOTAL']])) + print("{:s} ({:d}):".format(CATEGORIES['SUCCESS'], test_stats[CATEGORIES['SUCCESS']])) + print(" {:s}: ({:d}):".format(CATEGORIES['ADDRES_NOEXEC'], test_stats[CATEGORIES['ADDRES_NOEXEC']])) + test_with_skip = test_stats[CATEGORIES['SUCCESS']] - test_stats[CATEGORIES['ADDRES_NOEXEC']] + print(" {:s}: ({:d}):".format(CATEGORIES['SKIPPED'], test_with_skip)) + print(" {:s} ({:d}):".format(CATEGORIES['NO_BOOT'], test_with_skip - test_stats[CATEGORIES['BOOT']])) + for last_line in failed_boot_last_lines.keys(): + print(" last line: {:s} ({:d})".format(last_line, failed_boot_last_lines[last_line])) + print(" {:s} ({:d})".format(CATEGORIES['BOOT'], test_stats[CATEGORIES['BOOT']])) + print("{:s} ({:d}):".format(CATEGORIES['FAILED'], test_stats[CATEGORIES['TOTAL']] - test_stats[CATEGORIES['SUCCESS']])) + for reason in exec_fail_reasons.keys(): + print(" {:s} ({:d})".format(reason, exec_fail_reasons[reason])) + + +def main(): + parser = argparse.ArgumentParser(description='''Process a FIH test output yaml file, and output a human readable report''') + parser.add_argument('filename', help='yaml file to process') + + args = parser.parse_args() + + with open(args.filename) as output_yaml_file: + results = yaml.safe_load(output_yaml_file) + + if results: + print_results(results) + else: + print("Failed to parse output yaml file.") + + +if __name__ == "__main__": + main() diff --git a/ci/fih_test_docker/run_fi_test.sh b/ci/fih_test_docker/run_fi_test.sh new file mode 100755 index 0000000000..49567a2643 --- /dev/null +++ b/ci/fih_test_docker/run_fi_test.sh @@ -0,0 +1,87 @@ +#!/bin/bash + +# Copyright (c) 2020 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +# Get the dir this is running in and the dir the script is in. +PWD=$(pwd) +DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd ) + +# PAD is the amount of extra instructions that should be tested on each side of +# the critical region +PAD=6 + +MCUBOOT_AXF=$1 +SKIP_SIZES=$2 +DAMAGE_TYPE=$3 + +# Take an image and make it unbootable. This is done by replacing one of the +# strings in the image with a different string. This causes the signature check +# to fail +function damage_image +{ + IMAGEDIR=$(dirname $MCUBOOT_AXF) + local IMAGE_NAME=tfm_s_ns_signed.bin + local BACKUP_IMAGE_NAME=tfm_s_ns_signed.bin.orig + local IMAGE=$IMAGEDIR/$IMAGE_NAME + mv $IMAGE $IMAGEDIR/$BACKUP_IMAGE_NAME + + if [ "$DAMAGE_TYPE" = "SIGNATURE" ]; then + DAMAGE_PARAM="--signature" + elif [ "$DAMAGE_TYPE" = "IMAGE_HASH" ]; then + DAMAGE_PARAM="--image-hash" + else + echo "Failed to damage image $IMAGE with param $DAMAGE_TYPE" 1>&2 + exit -1 + fi + + python3 $DIR/damage_image.py -i $IMAGEDIR/$BACKUP_IMAGE_NAME -o $IMAGE $DAMAGE_PARAM 1>&2 +} + +function run_test +{ + local SKIP_SIZE=$1 + + $DIR/fi_make_manifest.sh $MCUBOOT_AXF > $PWD/fih_manifest.csv + + # Load the CSV FI manifest file, and output in START, END lines. Effectively + # join START and END lines together with a comma seperator. + REGIONS=$(sed "N;s/\(0x[[:xdigit:]]*\).*START\n\(0x[[:xdigit:]]*\).*END.*/\1,\2/g;P;D" $PWD/fih_manifest.csv) + # Ignore the first line, which includes the CSV header + REGIONS=$(echo "$REGIONS" | tail -n+2) + + for REGION in $REGIONS; do + #Split the START,END pairs into the two variables + START=$(echo $REGION | cut -d"," -f 1) + END=$(echo $REGION | cut -d"," -f 2) + + # Apply padding, converting back to hex + START=$(printf "0x%X" $((START - PAD))) + END=$(printf "0x%X" $((END + PAD))) + + # Invoke the fi tester script + $DIR/fi_tester_gdb.sh $IMAGEDIR $START $END --skip $SKIP_SIZE + done +} + +damage_image $MCUBOOT_AXF +# Run the run_test function with each skip length between min and max in turn. + +IFS=', ' read -r -a sizes <<< "$SKIP_SIZES" +for size in "${sizes[@]}"; do + echo "Run tests with skip size $size" 1>&2 + run_test $size +done diff --git a/ci/imgtool_install.sh b/ci/imgtool_install.sh index 94264f6c5c..389c36962f 100755 --- a/ci/imgtool_install.sh +++ b/ci/imgtool_install.sh @@ -12,10 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -if [[ $TRAVIS_PULL_REQUEST != "false" || $TRAVIS_BRANCH != "master" ]]; then - echo "Either a PR or not \"master\" branch, exiting" - exit 0 +if [[ $TRAVIS == "true" ]]; then + if [[ $TRAVIS_PULL_REQUEST != "false" || $TRAVIS_BRANCH != "master" ]]; then + echo "Either a PR or not \"master\" branch, exiting" + exit 0 + fi fi -pip3 install setuptools twine packaging -pip3 install --pre imgtool +pip3 install setuptools twine packaging wheel +pip3 install --pre imgtool --no-binary :all: diff --git a/ci/imgtool_run.sh b/ci/imgtool_run.sh index 1cf042d48a..20b5420d44 100755 --- a/ci/imgtool_run.sh +++ b/ci/imgtool_run.sh @@ -12,9 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -if [[ $TRAVIS_PULL_REQUEST != "false" || $TRAVIS_BRANCH != "master" ]]; then - echo "Either a PR or not \"master\" branch, exiting" - exit 0 +if [[ $TRAVIS == "true" ]]; then + if [[ $TRAVIS_PULL_REQUEST != "false" || $TRAVIS_BRANCH != "master" ]]; then + echo "Either a PR or not \"master\" branch, exiting" + exit 0 + fi fi IMGTOOL_VER_PREFIX="\+imgtool_version = " @@ -22,13 +24,13 @@ IMGTOOL_VER_FILE="imgtool/__init__.py" DIST_DIR="dist" if [[ -z "$TWINE_TOKEN" ]]; then - echo "\$TWINE_TOKEN must be set in travis settings" + echo "\$TWINE_TOKEN must be set in Travis or GH settings" exit 0 fi cd scripts/ -last_release=$(pip show imgtool | grep "Version: " | cut -d" " -f2) +last_release=$(pip3 show imgtool | grep "Version: " | cut -d" " -f2) repo_version=$(grep "imgtool_version = " imgtool/__init__.py | sed 's/^.* = "\(.*\)"/\1/g') python3 ../ci/compare_versions.py --old $last_release --new $repo_version @@ -46,6 +48,6 @@ elif [[ $rc -eq 3 ]]; then fi rm -rf $DIST_DIR -python setup.py sdist bdist_wheel +python3 setup.py sdist bdist_wheel twine upload --username __token__ --password "${TWINE_TOKEN}" "${DIST_DIR}/*" diff --git a/docs/SECURITY.md b/docs/SECURITY.md index ae003a4b4c..aa50858be6 100644 --- a/docs/SECURITY.md +++ b/docs/SECURITY.md @@ -39,7 +39,7 @@ vulnerabilities found within published code will undergo an embargo of Vulnerability reports and published fixes will be reported as follows: - Issues will be entered into Github's [Security Advisory - system](https://github.com/JuulLabs-OSS/mcuboot/security/advisories), with + system](https://github.com/mcu-tools/mcuboot/security/advisories), with the interested parties (including the reporter) added as viewers. - The release notes will contain a reference to any allocated CVE(s). diff --git a/docs/SubmittingPatches.md b/docs/SubmittingPatches.md index 8a6cf7c43d..55b9230c6d 100644 --- a/docs/SubmittingPatches.md +++ b/docs/SubmittingPatches.md @@ -1,7 +1,7 @@ # Submitting Patches Development on mcuboot primarily takes place in github, at: -https://github.com/JuulLabs-OSS/mcuboot +https://github.com/mcu-tools/mcuboot Changes should be submitted via github pull requests. Each commit should have a Signed-off-by line for the author (and the committer, if diff --git a/docs/design.md b/docs/design.md index 90fa1b1e4a..a50feeb124 100644 --- a/docs/design.md +++ b/docs/design.md @@ -233,6 +233,9 @@ then checks its validity (integrity check, signature verification etc.). If the image is invalid MCUboot erases its memory slot and starts to validate the other image. After a successful validation of the selected image the bootloader chain-loads it. + +An additional "revert" mechanism is also supported. For more information, please +read the [corresponding section](#direct-xip-revert). Handling the primary and secondary slots as equals has its drawbacks. Since the images are not moved between the slots, the on-the-fly image encryption/decryption can't be supported (it only applies to storing the image @@ -329,6 +332,33 @@ The "swap type" is a high-level representation of the outcome of the boot. Subsequent sections describe how mcuboot determines the swap type from the bit-level contents of flash. +### [Revert mechanism in direct-xip mode](#direct-xip-revert) + +The direct-xip mode also supports a "revert" mechanism which is the equivalent +of the swap mode's "revert" swap. It can be enabled with the +MCUBOOT_DIRECT_XIP_REVERT config option and an image trailer must also be added +to the signed images (the "--pad" option of the `imgtool` script must be used). +For more information on this please read the [Image Trailer](#image-trailer) +section and the [imgtool](imgtool.md) documentation. Making the images permanent +(marking them as confirmed in advance) is also supported just like in swap mode. +The individual steps of the direct-xip mode's "revert" mechanism are the +following: + +1. Select the slot which holds the newest potential image. +2. Was the image previously selected to run (during a previous boot)? + + Yes: Did the image mark itself "OK" (was the self-test successful)? + + Yes. + - Proceed to step 3. + + No. + - Erase the image from the slot to prevent it from being selected + again during the next boot. + - Return to step 1 (the bootloader will attempt to select and + possibly boot the previous image if there is one). + + No. + - Mark the image as "selected" (set the copy_done flag in the trailer). + - Proceed to step 3. +3. Proceed to image validation ... + ## [Image Trailer](#image-trailer) For the bootloader to be able to determine the current state and what actions @@ -1129,3 +1159,130 @@ this, the target must provide a definition for the `boot_save_shared_data()` function which is declared in `boot/bootutil/include/bootutil/boot_record.h`. The `boot_add_data_to_shared_area()` function can be used for adding new TLV entries to the shared data area. + +## [Testing in CI](#testing-in-ci) + +### [Testing Fault Injection Hardening (FIH)](#testing-fih) + +The CI currently tests the Fault Injection Hardening feature of MCUboot by +executing instruction skip during execution, and looking at whether a corrupted +image was booted by the bootloader or not. + +The main idea is that instruction skipping can be automated by scripting a +debugger to automatically execute the following steps: + +- Set breakpoint at specified address. +- Continue execution. +- On breakpoint hit increase the Program Counter. +- Continue execution. +- Detach from target after a timeout reached. + +Whether or not the corrupted image was booted or not can be decided by looking +for certain entries in the log. + +As MCUboot is deployed on a microcontroller, testing FI would not make much +sense in the simulator environment running on a host machine with different +architecture than the MCU's, as the degree of hardening depends on compiler +behavior. For example, (a bit counterintuitively) the code produced by gcc +with `-O0` optimisation is more resilient against FI attacks than the code +generated with `-O3` or `-Os` optimizations. + +To run on a desired architecture in the CI, the tests need to be executed on an +emulator (as real devices are not available in the CI environment). For this +implementation QEMU is selected. + +For the tests MCUboot needs a set of drivers and an implementation of a main +function. For the purpose of this test Trusted-Firmware-M has been selected as +it supports Armv8-M platforms that are also emulated by QEMU. + +The tests run in a docker container inside the CI VMs, to make it more easy to +deploy build and test environment (QEMU, compilers, interpreters). The CI VMs +seems to be using quite old Ubuntu (16.04). + +The sequence of the testing is the following (pseudo code): + +```sh +fn main() + # Implemented in ci/fih-tests_install.sh + generate_docker_image(Dockerfile) + + # See details below. Implemented in ci/fih-tests_run.sh. + # Calling the function with different parameters is done by Travis CI based on + # the values provided in the .travis.yaml + start_docker_image(skip_sizes, build_type, damage_type, fih_level) + +fn start_docker_image(skip_sizes, build_type, damage_type, fih_level) + # implemented in ci/fih_test_docker/execute_test.sh + compile_mcuboot(build_type) + + # implemented in ci/fih_test_docker/damage_image.py + damage_image(damage_type) + + # implemented in ci/fih_test_docker/run_fi_test.sh + ranges = generate_address_ranges() + for s in skip_sizes + for r in ranges + do_skip_in_qemu(s, r) # See details below + evaluate_logs() + +fn do_skip_in_qemu(size, range) + for a in r + run_qemu(a, size) # See details below + +# this part is implemented in ci/fih_test_docker/fi_tester_gdb.sh +fn run_qemu(a, size) + script = create_debugger_script(a, size) + start_qemu_in_bacground() # logs serial out to a file + gdb_attach_to_qemu(script) + kill_qemu() + + # This checks the debugger and the quemu logs, and decides whether the tets + # was executed successfully, and whether the image is booted or not. Then + # emits a yaml fragment on the standard out to be processed by the caller + # script + evaluate_run(qemu_log_file) +``` + +Further notes: + +- The image is corrupted by changing its signature. +- MCUBOOT_FIH_PROFILE_MAX is not tested as it requires TRNG, and the AN521 +platform has no support for it. However this profile adds the random +execution delay to the code, so should not affect the instruction skip results +too much, because break point is placed at exact address. But in practice this +makes harder the accurate timing of the attack. +- The test cases defined in .travis.yml always return `passed`, if they were +executed successfully. A yaml file is created during test execution with the +details of the test execution results. A summary of the collected results is +printed in the log at the end of the test. + +An advantage of having the tests running in a docker image is that it is +possible to run the tests on a local machine that has git and docker, without +installing any additional software. + +So, running the test on the host looks like the following (The commands below +are issued from the MCUboot source directory): + +```sh +$ ./ci/fih-tests_install.sh +$ FIH_LEVEL=MCUBOOT_FIH_PROFILE_MEDIUM BUILD_TYPE=RELEASE SKIP_SIZE=2 \ + DAMAGE_TYPE=SIGNATURE ./ci/fih-tests_run.sh +``` +On the travis CI the environment variables in the last command are set based on +the configs provided in the `.travis.yaml` + +This starts the tests, however the shell that it is running in is not +interactive, it is not possible to examine the results of the test run. To have +an interactive shell where the results can be examined, the following can be +done: + +- The docker image needs to be built with `ci/fih-tests_install.sh` as described + above. +- Start the docker image with the following command: + `docker run -i -t mcuboot/fih-test`. +- Execute the test with a command similar to the following: + `/root/execute_test.sh 8 RELEASE SIGNATURE MEDIUM`. After the test finishes, + the shell returns, and it is possible to investigate the results. It is also + possible to stop the test with _Ctrl+c_. The parameters to the + `execute_test.sh` are `SKIP_SIZE`, `BUILD_TYPE`, `DAMAGE_TYPE`, `FIH_LEVEL` in + order. \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index e0c11db5be..299916bdcb 100644 --- a/docs/index.md +++ b/docs/index.md @@ -25,6 +25,7 @@ target with a complete port planned. - [Zephyr](readme-zephyr.md) - [Mynewt](readme-mynewt.md) - [RIOT](readme-riot.md) + - [Mbed-OS](readme-mbed.md) - [Patch submission](SubmittingPatches.md) - information on how to contribute to mcuboot - Testing @@ -40,7 +41,7 @@ of date. You should use `imgtool.py` instead of these documents. The issues being planned and worked on are tracked using GitHub issues. To participate please visit: -[MCUboot Issues](https://github.com/JuulLabs-OSS/mcuboot/issues) +[MCUboot Issues](https://github.com/mcu-tools/mcuboot/issues) ~~Issues were previously tracked on [MCUboot JIRA](https://runtimeco.atlassian.net/projects/MCUB/summary) , but it is now deprecated.~~ @@ -54,12 +55,13 @@ Information and documentation on the bootloader is stored within the source. For more information in the source, here are some pointers: -- [boot/bootutil](https://github.com/JuulLabs-OSS/mcuboot/tree/master/boot/bootutil): The core of the bootloader itself. -- [boot/boot\_serial](https://github.com/JuulLabs-OSS/mcuboot/tree/master/boot/boot_serial): Support for serial upgrade within the bootloader itself. -- [boot/zephyr](https://github.com/JuulLabs-OSS/mcuboot/tree/master/boot/zephyr): Port of the bootloader to Zephyr -- [boot/mynewt](https://github.com/JuulLabs-OSS/mcuboot/tree/master/boot/mynewt): Mynewt bootloader app -- [imgtool](https://github.com/JuulLabs-OSS/mcuboot/tree/master/scripts/imgtool.py): A tool to securely sign firmware images for booting by MCUboot. -- [sim](https://github.com/JuulLabs-OSS/mcuboot/tree/master/sim): A bootloader simulator for testing and regression +- [boot/bootutil](https://github.com/mcu-tools/mcuboot/tree/master/boot/bootutil): The core of the bootloader itself. +- [boot/boot\_serial](https://github.com/mcu-tools/mcuboot/tree/master/boot/boot_serial): Support for serial upgrade within the bootloader itself. +- [boot/zephyr](https://github.com/mcu-tools/mcuboot/tree/master/boot/zephyr): Port of the bootloader to Zephyr +- [boot/mynewt](https://github.com/mcu-tools/mcuboot/tree/master/boot/mynewt): Mynewt bootloader app +- [boot/mbed](https://github.com/mcu-tools/mcuboot/tree/master/boot/mbed): Port of the bootloader to Mbed-OS +- [imgtool](https://github.com/mcu-tools/mcuboot/tree/master/scripts/imgtool.py): A tool to securely sign firmware images for booting by MCUboot. +- [sim](https://github.com/mcu-tools/mcuboot/tree/master/sim): A bootloader simulator for testing and regression ## Joining diff --git a/docs/readme-mbed.md b/docs/readme-mbed.md new file mode 100644 index 0000000000..189c7bfda5 --- /dev/null +++ b/docs/readme-mbed.md @@ -0,0 +1,41 @@ +# MCUboot port for Mbed OS + +This is an MCUboot port for Mbed OS. + +## Using MCUboot + +Note: The following is a general overview. It does not cover MCUboot or Mbed OS basics. + +See https://github.com/AGlass0fMilk/mbed-mcuboot-demo as a detailed example. + +### Basic configurations + +To use MCUboot, you need to create an Mbed OS project with the following configurations: +* `"mcuboot.primary-slot-address"`: address of the primary slot in the internal flash +* `"mcuboot.slot-size"`: size of an image slot (only one image, two slots are currently supported) +* `"mcuboot.max-img-sectors"`: maximum number of sectors, should be at least the number of sectors in each slot +* `"target.restrict_size"`: the maximum size of the bootloader, such that it does not overlap with the primary slot + +More configurations such as signing algorithm, slot swapping, etc. can be found in [mbed_lib.json](https://github.com/mcu-tools/mcuboot/tree/master/boot/mbed/mbed_lib.json). Please note that certain features are not currently supported. + +### Providing a secondary slot + +You need to provide an instance of `mbed::BlockDevice` as the secondary slot. It can be any types of internal or external storage provided that: +* Its size equals the `"mcuboot.slot-size"` you have set +* Its minimum supported read and write sizes (granularities) are _no larger than_ 16 byte, which MCUboot's read/write operations are aligned to. If the read size is larger than _one byte_, you need to set `"mcuboot.read-granularity"` to the read size of the storage - this buffers smaller read operations. + +In order for MCUboot to access your secondary slot, the interface to implement is +```cpp +mbed::BlockDevice* get_secondary_bd(void); +``` +which should return an uninitialized instance of BlockDevice. + +### Building the bootloader + +To build a bootloader based on MCUboot, make sure `"mcuboot.bootloader-build"` is `true` (already the default) and you have provided configurations and a secondary slot BlockDevice as explained above. + +### Building a user application + +To build a user application, set `"mcuboot.bootloader-build"` to `false` so MCUboot is built as a _library only_ without a bootloader application. This is useful if your user application needs to confirm the current image with `boot_set_confirmed()` after an update, or set a new image in the secondary slot as pending with `boot_set_pending()` in order to trigger an update upon reboot. + +As your application starts in the primary slots (instead of the beginning of the whole flash), you need to set the start address (`"target.mbed_app_start"`) to be equal to `"mcuboot.primary-slot-address"` + `"mcuboot.header-size"` of your bootloader. And its size (`"target.mbed_app_size"`) must be no larger than `"mcuboot.slot-size"` - `"mcuboot.header-size"`, and some space must be left for the image trailer too (see [this](design.md#image-trailer)). diff --git a/docs/readme-mynewt.md b/docs/readme-mynewt.md index c2e509dd63..0ead2a43c9 100644 --- a/docs/readme-mynewt.md +++ b/docs/readme-mynewt.md @@ -14,7 +14,7 @@ First you need to add the repo to your `project.yml`: repository.mcuboot: type: github vers: 0-dev - user: JuulLabs-OSS + user: mcu-tools repo: mcuboot ``` diff --git a/docs/release-notes.md b/docs/release-notes.md index c78d439de1..30804df167 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -3,6 +3,40 @@ - Table of Contents {:toc} +## Version 1.7.0 + +The 1.7.0 release of MCUBoot adds support for the Mbed-OS platform, +Equal slots (direct-xip) upgrade mode, RAM loading upgrade mode, +hardening against hardware level fault injection and timing attacks +and single image mode. +There are bug fixes, and associated imgtool updates as well. + +### About this release + +- Initial support for the Mbed-OS platform. +- Added possibility to enter deep sleep mode after mcuboot app execution + for cypress platform. +- Added hardening against hardware level fault injection and timing attacks. +- Introduced Abstract crypto primitives to simplify porting. +- Added RAM-load upgrade mode. +- Renamed single-image mode to single-slot mode. +- Allow larger primary slot in swap-move +- Fixed boostrapping in swap-move mode. +- Fixed issue causing that interrupted swap-move operation might brick device + if the primary image was padded. +- Abstracting mcuboot crypto functions for cleaner porting +- Droped flash_area_read_is_empty() porting API. +- boot/zephyr: Added watchdog feed on nRF devices. + See `CONFIG_BOOT_WATCHDOG_FEED` option. +- boot/zephyr: Added patch for turning off cache for Cortex M7 before + chain-loading. +- boot/zephyr: added option to relocate interrupts to application +- boot/zephyr: clean ARM core configuration only when selected by user +- boot/boot_serial: allow nonaligned last image data chunk +- imgtool: added custom TLV support. +- imgtool: added possibility to set confirm flag for hex files as well. +- imgtool: Print image digest during verify. + ## Version 1.6.0 The 1.6.0 release of MCUboot adds support for the PSOC6 platform, diff --git a/docs/release.md b/docs/release.md index 34bd5c3d05..3115b6d071 100644 --- a/docs/release.md +++ b/docs/release.md @@ -15,6 +15,9 @@ incremeting the numbers: We add pre-release tags of the format MAJOR.MINOR.PATCH-rc1. +We mark in documentation an MCUBoot development version using +the format MAJOR.MINOR.PATCH-dev. + ## Release Notes Before making a release, be sure to update the `docs/release-notes.md` @@ -81,4 +84,17 @@ git push origin HEAD:refs/heads/master git push origin va.b.c-rcn ``` +## Post release actions + +Mark the MCUBoot version as a development version. The version number used +should be specified for the next expected release. +It should be larger than the last release version by incrementing the MAJOR or +the MINOR number. It is not necessary to define the next version precisely as +the next release version might still be different as it might be needed to do: + +- a patch release +- a MINOR release while a MAJOR release was expected +- a MAJOR release while a MINOR release was expected + + [semver]: http://semver.org/ diff --git a/go.mod b/go.mod index bdb354fbcd..8d87521c7f 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module github.com/JuulLabs-OSS/mcuboot +module github.com/mcu-tools/mcuboot go 1.14 diff --git a/repository.yml b/repository.yml index 11c17ff0c6..f0e8e16d1a 100644 --- a/repository.yml +++ b/repository.yml @@ -30,6 +30,7 @@ repo.versions: "1.4.0": "v1.4.0" "1.5.0": "v1.5.0" "1.6.0": "v1.6.0" + "1.7.0": "v1.7.0-rc2" "0-dev": "0.0.0" # master "0-latest": "1.6.0" # latest stable release diff --git a/samples/mcuboot_config/mcuboot_config.template.h b/samples/mcuboot_config/mcuboot_config.template.h index 048ab18b66..2125792d13 100644 --- a/samples/mcuboot_config/mcuboot_config.template.h +++ b/samples/mcuboot_config/mcuboot_config.template.h @@ -57,6 +57,8 @@ /* Uncomment to enable the direct-xip code path. */ /* #define MCUBOOT_DIRECT_XIP */ +/* Uncomment to enable the revert mechanism in direct-xip mode. */ +/* #define MCUBOOT_DIRECT_XIP_REVERT */ /* Uncomment to enable the ram-load code path. */ /* #define MCUBOOT_RAM_LOAD */ diff --git a/samples/zephyr/mcutests/mcutests.go b/samples/zephyr/mcutests/mcutests.go index 159ab778d8..e250f98d23 100644 --- a/samples/zephyr/mcutests/mcutests.go +++ b/samples/zephyr/mcutests/mcutests.go @@ -1,5 +1,5 @@ // Package mcutests -package mcutests // github.com/JuulLabs-OSS/mcuboot/samples/zephyr/mcutests +package mcutests // github.com/mcu-tools/mcuboot/samples/zephyr/mcutests // The main driver of this consists of a series of tests. Each test // then contains a series of commands and expect results. diff --git a/samples/zephyr/run-tests.go b/samples/zephyr/run-tests.go index 56657f8966..f55b0a6d7b 100644 --- a/samples/zephyr/run-tests.go +++ b/samples/zephyr/run-tests.go @@ -25,7 +25,7 @@ import ( "strings" "time" - "github.com/JuulLabs-OSS/mcuboot/samples/zephyr/mcutests" + "github.com/mcu-tools/mcuboot/samples/zephyr/mcutests" ) // logIn gives the pathname of the log output from the Zephyr device. diff --git a/samples/zephyr/test-compile.go b/samples/zephyr/test-compile.go index fba613f431..3acfa3c886 100644 --- a/samples/zephyr/test-compile.go +++ b/samples/zephyr/test-compile.go @@ -18,7 +18,7 @@ import ( "os/exec" "path" - "github.com/JuulLabs-OSS/mcuboot/samples/zephyr/mcutests" + "github.com/mcu-tools/mcuboot/samples/zephyr/mcutests" ) var outFile = flag.String("out", "test-images.zip", "Name of zip file to put built tests into") diff --git a/scripts/imgtool/__init__.py b/scripts/imgtool/__init__.py index ae25718b97..c9d7eca139 100644 --- a/scripts/imgtool/__init__.py +++ b/scripts/imgtool/__init__.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -imgtool_version = "1.7.0a1" +imgtool_version = "1.7.0rc2" diff --git a/scripts/setup.py b/scripts/setup.py index bf35d9ffe0..b567a9a91a 100644 --- a/scripts/setup.py +++ b/scripts/setup.py @@ -8,7 +8,7 @@ author_email="mcuboot@groups.io", description=("MCUboot's image signing and key management"), license="Apache Software License", - url="http://github.com/JuulLabs-OSS/mcuboot", + url="http://github.com/mcu-tools/mcuboot", packages=setuptools.find_packages(), python_requires='>=3.6', install_requires=[ diff --git a/testplan/mynewt/project.yml b/testplan/mynewt/project.yml index a268f4fc3e..76c0e242a3 100644 --- a/testplan/mynewt/project.yml +++ b/testplan/mynewt/project.yml @@ -32,5 +32,5 @@ repository.apache-mynewt-core: repository.mcuboot: type: github vers: 0-dev - user: JuulLabs-OSS + user: mcu-tools repo: mcuboot