Skip to content

[nrf noup] boot: zephyr: Add bm firmware loader code #451

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions boot/zephyr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,17 @@ endif()
# Zephyr port-specific sources.
zephyr_library_sources(
main.c
io.c
flash_map_extended.c
os.c
keys.c
)

if(CONFIG_NRF_BM)
zephyr_library_sources(io_bm.c)
else()
zephyr_library_sources(io.c)
endif()

if(DEFINED CONFIG_ENABLE_MGMT_PERUSER)
zephyr_library_sources(
boot_serial_extensions.c
Expand Down Expand Up @@ -156,9 +161,15 @@ elseif(CONFIG_SINGLE_APPLICATION_SLOT)
)
zephyr_library_include_directories(${BOOT_DIR}/bootutil/src)
elseif(CONFIG_BOOT_FIRMWARE_LOADER)
zephyr_library_sources(
${BOOT_DIR}/zephyr/firmware_loader.c
)
if(CONFIG_NRF_BM)
zephyr_library_sources(
${BOOT_DIR}/zephyr/firmware_loader_bm.c
)
else()
zephyr_library_sources(
${BOOT_DIR}/zephyr/firmware_loader.c
)
endif()
zephyr_library_include_directories(${BOOT_DIR}/bootutil/src)
else()
zephyr_library_sources(
Expand Down
283 changes: 283 additions & 0 deletions boot/zephyr/firmware_loader_bm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <assert.h>
#include <zephyr/kernel.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/storage/flash_map.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 <bm_installs.h>

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

#define IMAGE_TLV_INSTALLER_IMAGE 0xa0

BOOT_LOG_MODULE_DECLARE(mcuboot);

static struct flash_area fa_app_installer = {
.fa_id = 1,
.fa_off = FIXED_PARTITION_OFFSET(slot0_partition),
.fa_size = FIXED_PARTITION_SIZE(slot0_partition),
.fa_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller)),
};

static struct image_header hdr_app_installer = { 0 };

static struct flash_area fa_softdevice = {
.fa_id = 2,
.fa_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller)),
};

static struct image_header hdr_softdevice = { 0 };

#ifdef CONFIG_BOOT_FIRMWARE_LOADER
static struct flash_area fa_firmware_loader = {
.fa_id = 3,
.fa_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller)),
};

static struct image_header hdr_firmware_loader = { 0 };
#endif

/**
* 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
*/
static fih_ret validate_image(const struct flash_area *fap, struct image_header *hdr)
{
static uint8_t tmpbuf[BOOT_TMPBUF_SZ];
FIH_DECLARE(fih_rc, FIH_FAILURE);

FIH_CALL(bootutil_img_validate, fih_rc, NULL, hdr, fap, tmpbuf, BOOT_TMPBUF_SZ, NULL, 0, NULL);
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);
bool softdevice_area_valid = false;
bool firmware_loader_area_valid = false;
int rc;
bool app_installer_image_valid = false;
bool softdevice_image_valid = false;
bool firmware_loader_image_valid = false;
bool app_installer_is_installer_image = false;

bm_installs_init();

if (bm_installs_is_valid()) {
off_t start_address = 0;
size_t image_size = 0;

rc = bm_installs_get_image_data(BM_INSTALLS_IMAGE_INDEX_SOFTDEVICE, &start_address,
&image_size);

if (!rc) {
fa_softdevice.fa_off = start_address;
fa_softdevice.fa_size = image_size;

if (start_address < fa_app_installer.fa_off) {
/* Invalid start address for SoftDevice */
goto invalid_softdevice;
}

fa_app_installer.fa_size = start_address - fa_app_installer.fa_off;

rc = boot_image_load_header(&fa_softdevice, &hdr_softdevice);

if (!rc) {
softdevice_area_valid = true;
}
}

invalid_softdevice:
#ifdef CONFIG_BOOT_FIRMWARE_LOADER
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't that code purposed for CONFIG_BOOT_FIRMWARE_LOADER=y only?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now yes, but can be reused in future to allow for single slot mode (not firmware loader mode) with serial recovery for application and softdevice

start_address = 0;
image_size = 0;
rc = bm_installs_get_image_data(BM_INSTALLS_IMAGE_INDEX_FIRMWARE_LOADER, &start_address,
&image_size);

if (!rc) {
fa_firmware_loader.fa_off = start_address;
fa_firmware_loader.fa_size = image_size;

if (start_address < fa_app_installer.fa_off) {
/* Invalid start address for firmware loader */
goto invalid_firmware_loader;
}

fa_app_installer.fa_size = start_address - fa_app_installer.fa_off;

rc = boot_image_load_header(&fa_firmware_loader, &hdr_softdevice);

if (!rc) {
firmware_loader_area_valid = true;
}
}
#endif
}

invalid_firmware_loader:
rc = boot_image_load_header(&fa_app_installer, &hdr_app_installer);

if (rc) {
BOOT_LOG_ERR("Failed loading application/installer image header: %d", rc);
} else {
FIH_CALL(validate_image, fih_rc, &fa_app_installer, &hdr_app_installer);

if (FIH_EQ(fih_rc, FIH_SUCCESS)) {
struct image_tlv_iter it;
uint32_t off2;
uint16_t len2;

app_installer_image_valid = true;

if (hdr_app_installer.ih_protect_tlv_size > 0) {
rc = bootutil_tlv_iter_begin(&it, &hdr_app_installer, &fa_app_installer,
IMAGE_TLV_INSTALLER_IMAGE, true);

if (rc == 0) {
rc = bootutil_tlv_iter_next(&it, &off2, &len2, NULL);

if (rc == 0 && len2 == sizeof(app_installer_is_installer_image)) {
rc = LOAD_IMAGE_DATA(&hdr_app_installer, &fa_app_installer, off2,
&app_installer_is_installer_image, len2);

if (rc != 0) {
app_installer_is_installer_image = false;
}
}
}
}
}
}

if (softdevice_area_valid) {
fih_rc = FIH_FAILURE;
rc = boot_image_load_header(&fa_softdevice, &hdr_softdevice);

if (rc) {
BOOT_LOG_ERR("Failed loading SoftDevice image header: %d", rc);
} else {
FIH_CALL(validate_image, fih_rc, &fa_softdevice, &hdr_softdevice);

if (FIH_EQ(fih_rc, FIH_SUCCESS)) {
softdevice_image_valid = true;
}
}
}

#ifdef CONFIG_BOOT_FIRMWARE_LOADER
if (firmware_loader_area_valid) {
fih_rc = FIH_FAILURE;
rc = boot_image_load_header(&fa_firmware_loader, &hdr_firmware_loader);

if (rc) {
BOOT_LOG_ERR("Failed loading firmware loader image header: %d", rc);
} else {
FIH_CALL(validate_image, fih_rc, &fa_firmware_loader, &hdr_firmware_loader);

if (FIH_EQ(fih_rc, FIH_SUCCESS)) {
firmware_loader_image_valid = true;
}
}
}
#endif

BOOT_LOG_DBG("Application/installer partition offset: 0x%lx, size: 0x%x, type: %d",
fa_app_installer.fa_off, fa_app_installer.fa_size,
app_installer_is_installer_image);
BOOT_LOG_DBG("SoftDevice partition offset: 0x%lx, size: 0x%x", fa_softdevice.fa_off,
fa_softdevice.fa_size);
#ifdef CONFIG_BOOT_FIRMWARE_LOADER
BOOT_LOG_DBG("Firmware loader off: 0x%lx, size: 0x%x", fa_firmware_loader.fa_off,
fa_firmware_loader.fa_size);
BOOT_LOG_DBG("SoftDevice area valid: %d, Firmware loader area valid: %d, "
"Application/installer image valid: %d, SoftDevice image valid: %d, "
"Firmware loader image valid: %d", softdevice_area_valid,
firmware_loader_area_valid, app_installer_image_valid, softdevice_image_valid,
firmware_loader_image_valid);
#else
BOOT_LOG_DBG("SoftDevice area valid: %d, Application/installer image valid: %d, "
"SoftDevice image valid: %d", softdevice_area_valid, app_installer_image_valid,
softdevice_image_valid);
#endif

#ifdef CONFIG_BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO
if (io_detect_pin() && !io_boot_skip_serial_recovery()) {
BOOT_LOG_DBG("GPIO detected for firmware loader mode");
boot_firmware_loader = true;
}
#endif

#ifdef CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET
if (io_detect_pin_reset()) {
BOOT_LOG_DBG("Pin reset detected for firmware loader mode");
boot_firmware_loader = true;
}
#endif

#ifdef CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE
if (io_detect_boot_mode()) {
BOOT_LOG_DBG("Boot mode detected for firmware loader mode");
boot_firmware_loader = true;
}
#endif

if (app_installer_image_valid == true && app_installer_is_installer_image == true) {
/* Installer image is present, this gets priority */
BOOT_LOG_DBG("Booting installer");
rsp->br_image_off = flash_area_get_off(&fa_app_installer);
rsp->br_hdr = &hdr_app_installer;
} else if (boot_firmware_loader == true && softdevice_image_valid == true &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fully understand that when valid app_installer_image was detected then triggering the loader by the app must have been spurious. For entering by pin/pin reset
Doesn't assignment to boot app_installer_image open possibility for bricking or hanging the device by installer which has a bug which caused that it might hangs?
I would move firmware_loader_image boot assignment ahead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the installer image runs and deletes itself (well, the header) so it should not brick itself. Once the installer image runs, the integrity of the firmware loader is invalid, because it will be replacing it and the softdevice, and both must be present for the firmware loader image to be usable

firmware_loader_image_valid == true) {
/* Boot firmware loader */
BOOT_LOG_INF("Bootinf firmware loader");
rsp->br_image_off = flash_area_get_off(&fa_firmware_loader);
rsp->br_hdr = &hdr_firmware_loader;
} else if (app_installer_image_valid == true && softdevice_image_valid == true) {
/* Boot main application */
BOOT_LOG_INF("Booting main application");
rsp->br_image_off = flash_area_get_off(&fa_app_installer);
rsp->br_hdr = &hdr_app_installer;
} else if (app_installer_image_valid == false && softdevice_image_valid == true &&
firmware_loader_image_valid == true) {
/* Boot firmware loader due to missing main image */
BOOT_LOG_INF("Booting firmware loader due to missing application image");
rsp->br_image_off = flash_area_get_off(&fa_firmware_loader);
rsp->br_hdr = &hdr_firmware_loader;
} else {
/* Cannot boot in this configuration */
BOOT_LOG_ERR("Error: no bootable configuration found");
return -1;
}

rsp->br_flash_dev_id = flash_area_get_device_id(&fa_app_installer);

return 0;
}
Loading