Skip to content

Commit

Permalink
zephyr: Add firmware loader MCUboot operation style
Browse files Browse the repository at this point in the history
Adds a new operation style in which the secondary slot has an
image which is used to update the primary image only.

Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
  • Loading branch information
nordicjm committed Nov 28, 2023
1 parent 433b848 commit 215345f
Show file tree
Hide file tree
Showing 11 changed files with 294 additions and 10 deletions.
3 changes: 2 additions & 1 deletion boot/bootutil/include/bootutil/boot_status.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ enum mcuboot_mode {
MCUBOOT_MODE_SWAP_USING_MOVE,
MCUBOOT_MODE_DIRECT_XIP,
MCUBOOT_MODE_DIRECT_XIP_WITH_REVERT,
MCUBOOT_MODE_RAM_LOAD
MCUBOOT_MODE_RAM_LOAD,
MCUBOOT_MODE_FIRMWARE_LOADER
};

enum mcuboot_signature_type {
Expand Down
2 changes: 2 additions & 0 deletions boot/bootutil/src/boot_record.c
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ int boot_save_shared_data(const struct image_header *hdr, const struct flash_are
#endif
#elif defined(MCUBOOT_RAM_LOAD)
uint8_t mode = MCUBOOT_MODE_RAM_LOAD;
#elif defined(MCUBOOT_FIRMWARE_LOADER)
uint8_t mode = MCUBOOT_MODE_FIRMWARE_LOADER;
#else
#error "Unknown mcuboot operating mode"
#endif
Expand Down
3 changes: 2 additions & 1 deletion boot/bootutil/src/bootutil_misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,8 @@ boot_write_enc_key(const struct flash_area *fap, uint8_t slot,

uint32_t bootutil_max_image_size(const struct flash_area *fap)
{
#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SINGLE_APPLICATION_SLOT)
#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SINGLE_APPLICATION_SLOT) || \
defined(MCUBOOT_FIRMWARE_LOADER)
return boot_status_off(fap);
#elif defined(MCUBOOT_SWAP_USING_MOVE)
struct flash_sector sector;
Expand Down
8 changes: 5 additions & 3 deletions boot/bootutil/src/bootutil_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,17 @@ struct flash_area;
#if (defined(MCUBOOT_OVERWRITE_ONLY) + \
defined(MCUBOOT_SWAP_USING_MOVE) + \
defined(MCUBOOT_DIRECT_XIP) + \
defined(MCUBOOT_RAM_LOAD)) > 1
#error "Please enable only one of MCUBOOT_OVERWRITE_ONLY, MCUBOOT_SWAP_USING_MOVE, MCUBOOT_DIRECT_XIP or MCUBOOT_RAM_LOAD"
defined(MCUBOOT_RAM_LOAD) + \
defined(MCUBOOT_FIRMWARE_LOADER)) > 1
#error "Please enable only one of MCUBOOT_OVERWRITE_ONLY, MCUBOOT_SWAP_USING_MOVE, MCUBOOT_DIRECT_XIP, MCUBOOT_RAM_LOAD or MCUBOOT_FIRMWARE_LOADER"
#endif

#if !defined(MCUBOOT_OVERWRITE_ONLY) && \
!defined(MCUBOOT_SWAP_USING_MOVE) && \
!defined(MCUBOOT_DIRECT_XIP) && \
!defined(MCUBOOT_RAM_LOAD) && \
!defined(MCUBOOT_SINGLE_APPLICATION_SLOT)
!defined(MCUBOOT_SINGLE_APPLICATION_SLOT) && \
!defined(MCUBOOT_FIRMWARE_LOADER)
#define MCUBOOT_SWAP_USING_SCRATCH 1
#endif

Expand Down
5 changes: 5 additions & 0 deletions boot/zephyr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ zephyr_library_sources(
${BOOT_DIR}/zephyr/single_loader.c
)
zephyr_library_include_directories(${BOOT_DIR}/bootutil/src)
elseif(CONFIG_BOOT_FIRMWARE_LOADER)
zephyr_library_sources(
${BOOT_DIR}/zephyr/firmware_loader.c
)
zephyr_library_include_directories(${BOOT_DIR}/bootutil/src)
else()
zephyr_library_sources(
${BOOT_DIR}/bootutil/src/loader.c
Expand Down
14 changes: 14 additions & 0 deletions boot/zephyr/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,18 @@ config BOOT_RAM_LOAD
The address that the image is copied to is specified using the load-addr
argument to the imgtool.py script which writes it to the image header.

config BOOT_FIRMWARE_LOADER
bool "Firmware loader"
help
If y, mcuboot will have a single application slot, and the secondary
slot will be for a non-upgradeable firmware loaded image (e.g. for
loading firmware via Bluetooth). The main application will boot by
default unless there is an error with it or the boot mode has been
forced to the firmware loader.

Note: The firmware loader image must be signed with the same signing
key as the primary image.

endchoice

# Workaround for not being able to have commas in macro arguments
Expand Down Expand Up @@ -582,6 +594,8 @@ config MCUBOOT_INDICATION_LED

rsource "Kconfig.serial_recovery"

rsource "Kconfig.firmware_loader"

config BOOT_INTR_VEC_RELOC
bool "Relocate the interrupt vector to the application"
default n
Expand Down
47 changes: 47 additions & 0 deletions boot/zephyr/Kconfig.firmware_loader
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright (c) 2023 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0

if BOOT_FIRMWARE_LOADER

menu "Firmware loader entrance methods"

menuconfig BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO
bool "GPIO"
depends on GPIO
help
Use a GPIO to enter firmware loader mode.

config BOOT_FIRMWARE_LOADER_DETECT_DELAY
int "Serial detect pin detection delay time [ms]"
default 0
depends on BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO
help
Used to prevent the bootloader from loading on button press.
Useful for powering on when using the same button as
the one used to place the device in bootloader mode.

config BOOT_FIRMWARE_LOADER_BOOT_MODE
bool "Check boot mode via retention subsystem"
depends on RETENTION_BOOT_MODE
help
Allows for entering firmware loader mode by using Zephyr's boot mode
retention system (i.e. an application must set the boot mode to stay
in firmware loader mode and reboot the module).

config BOOT_FIRMWARE_LOADER_NO_APPLICATION
bool "Stay in bootloader if no application"
help
Allows for entering firmware loader mode if there is no bootable
application that the bootloader can jump to.

config BOOT_FIRMWARE_LOADER_PIN_RESET
bool "Check for device reset by pin"
select HWINFO
help
Checks if the module reset was caused by the reset pin and will
remain in bootloader firmware loader mode if it was.

endmenu

endif
1 change: 1 addition & 0 deletions boot/zephyr/Kconfig.serial_recovery
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ menuconfig MCUBOOT_SERIAL
select BASE64
select CRC
select ZCBOR
depends on !BOOT_FIRMWARE_LOADER
help
If y, enables a serial-port based update mode. This allows
MCUboot itself to load update images into flash over a UART.
Expand Down
194 changes: 194 additions & 0 deletions boot/zephyr/firmware_loader.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright (c) 2020 Arm Limited
* Copyright (c) 2020-2023 Nordic Semiconductor ASA
*/

#include <assert.h>
#include <zephyr/kernel.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/gpio.h>
#include "bootutil/image.h"
#include "bootutil_priv.h"
#include "bootutil/bootutil_log.h"
#include "bootutil/bootutil_public.h"
#include "bootutil/fault_injection_hardening.h"

#include "io/io.h"
#include "mcuboot_config/mcuboot_config.h"

BOOT_LOG_MODULE_DECLARE(mcuboot);

/* Variables passed outside of unit via poiters. */
static const struct flash_area *_fa_p;
static struct image_header _hdr = { 0 };

#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) || defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE)
/**
* Validate hash of a primary boot image.
*
* @param[in] fa_p flash area pointer
* @param[in] hdr boot image header pointer
*
* @return FIH_SUCCESS on success, error code otherwise
*/
fih_ret
boot_image_validate(const struct flash_area *fa_p,
struct image_header *hdr)
{
static uint8_t tmpbuf[BOOT_TMPBUF_SZ];
FIH_DECLARE(fih_rc, FIH_FAILURE);

/* NOTE: The first argument to boot_image_validate, for enc_state pointer,
* is allowed to be NULL only because the single image loader compiles
* with BOOT_IMAGE_NUMBER == 1, which excludes the code that uses
* the pointer from compilation.
*/
/* Validate hash */
if (IS_ENCRYPTED(hdr))
{
/* Clear the encrypted flag we didn't supply a key
* This flag could be set if there was a decryption in place
* was performed. We will try to validate the image, and if still
* encrypted the validation will fail, and go in panic mode
*/
hdr->ih_flags &= ~(ENCRYPTIONFLAGS);
}
FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, hdr, fa_p, tmpbuf,
BOOT_TMPBUF_SZ, NULL, 0, NULL);

FIH_RET(fih_rc);
}
#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT || MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE*/

inline static fih_ret
boot_image_validate_once(const struct flash_area *fa_p,
struct image_header *hdr)
{
static struct boot_swap_state state;
int rc;
FIH_DECLARE(fih_rc, FIH_FAILURE);

memset(&state, 0, sizeof(struct boot_swap_state));
rc = boot_read_swap_state(fa_p, &state);
if (rc != 0)
FIH_RET(FIH_FAILURE);
if (state.magic != BOOT_MAGIC_GOOD
|| state.image_ok != BOOT_FLAG_SET) {
/* At least validate the image once */
FIH_CALL(boot_image_validate, fih_rc, fa_p, hdr);
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
FIH_RET(FIH_FAILURE);
}
if (state.magic != BOOT_MAGIC_GOOD) {
rc = boot_write_magic(fa_p);
if (rc != 0)
FIH_RET(FIH_FAILURE);
}
rc = boot_write_image_ok(fa_p);
if (rc != 0)
FIH_RET(FIH_FAILURE);
}
FIH_RET(FIH_SUCCESS);
}

/**
* Validates that an image in a slot is OK to boot.
*
* @param[in] slot Slot number to check
* @param[out] rsp Parameters for booting image, on success
*
* @return FIH_SUCCESS on success; non-zero on failure.
*/
static fih_ret validate_image_slot(int slot, struct boot_rsp *rsp)
{
int rc = -1;
FIH_DECLARE(fih_rc, FIH_FAILURE);

rc = flash_area_open(slot, &_fa_p);
assert(rc == 0);

rc = boot_image_load_header(_fa_p, &_hdr);
if (rc != 0) {
goto other;
}

#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT
FIH_CALL(boot_image_validate, fih_rc, _fa_p, &_hdr);
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
goto other;
}
#elif defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE)
FIH_CALL(boot_image_validate_once, fih_rc, _fa_p, &_hdr);
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
goto other;
}
#else
fih_rc = FIH_SUCCESS;
#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */

rsp->br_flash_dev_id = flash_area_get_device_id(_fa_p);
rsp->br_image_off = flash_area_get_off(_fa_p);
rsp->br_hdr = &_hdr;

other:
flash_area_close(_fa_p);

FIH_RET(fih_rc);
}

/**
* Gather information on image and prepare for booting. Will boot from main
* image if none of the enabled entrance modes for the firmware loader are set,
* otherwise will boot the firmware loader. Note: firmware loader must be a
* valid signed image with the same signing key as the application image.
*
* @param[out] rsp Parameters for booting image, on success
*
* @return FIH_SUCCESS on success; non-zero on failure.
*/
fih_ret
boot_go(struct boot_rsp *rsp)
{
bool boot_firmware_loader = false;
FIH_DECLARE(fih_rc, FIH_FAILURE);

#ifdef CONFIG_BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO
if (io_detect_pin() &&
!io_boot_skip_serial_recovery()) {
boot_firmware_loader = true;
}
#endif

#ifdef CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET
if (io_detect_pin_reset()) {
boot_firmware_loader = true;
}
#endif

#ifdef CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE
if (io_detect_boot_mode()) {
boot_firmware_loader = true;
}
#endif

/* Check if firmware loader button is pressed. TODO: check all entrance methods */
if (boot_firmware_loader == true) {
FIH_CALL(validate_image_slot, fih_rc, FLASH_AREA_IMAGE_SECONDARY(0), rsp);

if (FIH_EQ(fih_rc, FIH_SUCCESS)) {
FIH_RET(fih_rc);
}
}

FIH_CALL(validate_image_slot, fih_rc, FLASH_AREA_IMAGE_PRIMARY(0), rsp);

#ifdef CONFIG_BOOT_FIRMWARE_LOADER_NO_APPLICATION
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
FIH_CALL(validate_image_slot, fih_rc, FLASH_AREA_IMAGE_SECONDARY(0), rsp);
}
#endif

FIH_RET(fih_rc);
}
4 changes: 4 additions & 0 deletions boot/zephyr/include/mcuboot_config/mcuboot_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@
#define IMAGE_EXECUTABLE_RAM_SIZE CONFIG_BOOT_IMAGE_EXECUTABLE_RAM_SIZE
#endif

#ifdef CONFIG_BOOT_FIRMWARE_LOADER
#define MCUBOOT_FIRMWARE_LOADER
#endif

#ifdef CONFIG_UPDATEABLE_IMAGE_NUMBER
#define MCUBOOT_IMAGE_NUMBER CONFIG_UPDATEABLE_IMAGE_NUMBER
#else
Expand Down
Loading

0 comments on commit 215345f

Please sign in to comment.