zephyr: Add firmware loader MCUboot operation style
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>
This commit is contained in:
parent
433b8480f7
commit
215345f76a
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -29,11 +29,11 @@
|
|||
|
||||
#include "target.h"
|
||||
|
||||
#if defined(CONFIG_BOOT_SERIAL_PIN_RESET)
|
||||
#if defined(CONFIG_BOOT_SERIAL_PIN_RESET) || defined(CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET)
|
||||
#include <zephyr/drivers/hwinfo.h>
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_BOOT_SERIAL_BOOT_MODE)
|
||||
#if defined(CONFIG_BOOT_SERIAL_BOOT_MODE) || defined(CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE)
|
||||
#include <zephyr/retention/bootmode.h>
|
||||
#endif
|
||||
|
||||
|
@ -48,6 +48,16 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/* Validate firmware loader configuration */
|
||||
#ifdef CONFIG_BOOT_FIRMWARE_LOADER
|
||||
#if !defined(CONFIG_BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO) && \
|
||||
!defined(CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE) && \
|
||||
!defined(CONFIG_BOOT_FIRMWARE_LOADER_NO_APPLICATION) && \
|
||||
!defined(CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET)
|
||||
#error "Firmware loader selected without an entrance mode set"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MCUBOOT_INDICATION_LED
|
||||
|
||||
/*
|
||||
|
@ -80,10 +90,13 @@ void io_led_init(void)
|
|||
}
|
||||
#endif /* CONFIG_MCUBOOT_INDICATION_LED */
|
||||
|
||||
#if defined(CONFIG_BOOT_SERIAL_ENTRANCE_GPIO) || defined(CONFIG_BOOT_USB_DFU_GPIO)
|
||||
#if defined(CONFIG_BOOT_SERIAL_ENTRANCE_GPIO) || defined(CONFIG_BOOT_USB_DFU_GPIO) || \
|
||||
defined(CONFIG_BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO)
|
||||
|
||||
#if defined(CONFIG_MCUBOOT_SERIAL)
|
||||
#define BUTTON_0_DETECT_DELAY CONFIG_BOOT_SERIAL_DETECT_DELAY
|
||||
#elif defined(CONFIG_BOOT_FIRMWARE_LOADER)
|
||||
#define BUTTON_0_DETECT_DELAY CONFIG_BOOT_FIRMWARE_LOADER_DETECT_DELAY
|
||||
#else
|
||||
#define BUTTON_0_DETECT_DELAY CONFIG_BOOT_USB_DFU_DETECT_DELAY
|
||||
#endif
|
||||
|
@ -152,7 +165,7 @@ bool io_detect_pin(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_BOOT_SERIAL_PIN_RESET)
|
||||
#if defined(CONFIG_BOOT_SERIAL_PIN_RESET) || defined(CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET)
|
||||
bool io_detect_pin_reset(void)
|
||||
{
|
||||
uint32_t reset_cause;
|
||||
|
@ -169,7 +182,7 @@ bool io_detect_pin_reset(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_BOOT_SERIAL_BOOT_MODE)
|
||||
#if defined(CONFIG_BOOT_SERIAL_BOOT_MODE) || defined(CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE)
|
||||
bool io_detect_boot_mode(void)
|
||||
{
|
||||
int32_t boot_mode;
|
||||
|
|
Loading…
Reference in New Issue