Skip to content
Merged
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
30 changes: 27 additions & 3 deletions modules/mcuboot/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,13 @@ config MCUBOOT_USE_ALL_AVAILABLE_RAM

endmenu

DT_CHOSEN_BOOTLOADER_REQUEST := nrf,bootloader-request
DT_CHOSEN_BOOTLOADER_REQUEST_BACKUP := nrf,bootloader-request-backup

config NRF_MCUBOOT_BOOT_REQUEST
bool "MCUboot bootloader requests"
imply MCUMGR_GRP_IMG_NRF
depends on $(dt_chosen_enabled,$(DT_CHOSEN_BOOTLOADER_REQUEST))
help
Handle bootloader requests.

Expand All @@ -88,12 +92,10 @@ choice NRF_MCUBOOT_BOOT_REQUEST_IMPL
prompt "Shared memory backend"
default NRF_MCUBOOT_BOOT_REQUEST_IMPL_RETENTION if RETENTION

DT_CHOSEN_BOOTLOADER_REQUEST := nrf,bootloader-request

config NRF_MCUBOOT_BOOT_REQUEST_IMPL_RETENTION
bool "Retention"
depends on RETENTION
depends on $(dt_chosen_enabled,$(DT_CHOSEN_BOOTLOADER_REQUEST))
depends on $(dt_chosen_has_compat,$(DT_CHOSEN_BOOTLOADER_REQUEST),$(DT_COMPAT_ZEPHYR_RETENTION))
help
Use zephyr,bootloader-request chosen node compatible with the
zephyr,retention driver as the memory area to store and read from the
Expand All @@ -102,8 +104,30 @@ config NRF_MCUBOOT_BOOT_REQUEST_IMPL_RETENTION
structure version to ensure compatibility between the bootloader and
the application.

config NRF_MCUBOOT_BOOT_REQUEST_IMPL_FLASH
bool "NVM partition"
depends on FLASH
depends on CRC
depends on !PARTITION_MANAGER_ENABLED # Partition manager uses an incompatible flash_map.h
help
Use zephyr,bootloader-request chosen node compatible with the
flash driver as the memory area to store and read from the bootloader
requests.

endchoice

config NRF_MCUBOOT_BOOT_REQUEST_PREFERENCE_KEEP
bool "Keep boot preference value across reboots"
depends on $(dt_chosen_enabled,$(DT_CHOSEN_BOOTLOADER_REQUEST_BACKUP))
default y
help
Use zephyr,bootloader-request-backup chosen node compatible with the
zephyr,retention driver as the memory area to store a copy of
bootloader requests.
If this optional node is defined, the module will keep a copy of data
inside a backup region, allowing to preserve the preferred slot
selection across device resets.

config NCS_MCUBOOT_BOOT_REQUEST_TEST_SETS_BOOT_PREFERENCE
bool "Set boot preference if a slot is marked for test"
help
Expand Down
21 changes: 21 additions & 0 deletions samples/dfu/ab/Kconfig.sysbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#
# Copyright (c) 2025 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

if SOC_SERIES_NRF54HX

# Enable radiocore
config NRF_DEFAULT_IPC_RADIO
default y

config NETCORE_IPC_RADIO_BT_HCI_IPC
default y

config MCUBOOT_IMAGES_ROM_END_OFFSET_AUTO
default "ipc_radio;ipc_radio_secondary_app"

endif # SOC_SERIES_NRF54HX

source "share/sysbuild/Kconfig"
4 changes: 3 additions & 1 deletion samples/dfu/ab/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ The following conditions decide which slot will be booted (active) on the next r
#. If none of the above conditions is met, slot A is selected as active.

You can set the preferred slot using the ``boot_request_set_preferred_slot`` function.
Currently, this only sets the boot preference for a single reboot.
If the :kconfig:option:`CONFIG_NRF_MCUBOOT_BOOT_REQUEST_PREFERENCE_KEEP` option is enabled, the slot preference remains persistent across reboots.
Otherwise, the slot preference is cleared on reboot.
To enable the persistence of a preferred slot, define a backup region for the bootloader request area by using the ``nrf,bootloader-request-backup`` chosen node in the devicetree.

Identifying the active slot
---------------------------
Expand Down
3 changes: 3 additions & 0 deletions samples/dfu/ab/boards/nrf54l15dk_nrf54l15_cpuapp.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Enable boot requests through retained memory.
CONFIG_RETAINED_MEM=y
CONFIG_RETENTION=y
7 changes: 1 addition & 6 deletions samples/dfu/ab/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,9 @@ CONFIG_MCUBOOT_UTIL_LOG_LEVEL_WRN=y
# Disable debug logging
CONFIG_LOG_MAX_LEVEL=3

# Enable boot requests through retained memory.
CONFIG_RETAINED_MEM=y
CONFIG_RETENTION=y
# Enable boot requests.
CONFIG_NRF_MCUBOOT_BOOT_REQUEST=y

CONFIG_RETENTION_BOOT_MODE=y
CONFIG_MCUMGR_GRP_OS_RESET_BOOT_MODE=y

# Enable DK LED/button library
CONFIG_DK_LIBRARY=y

Expand Down
126 changes: 79 additions & 47 deletions samples/dfu/ab/src/ab_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,6 @@ LOG_MODULE_DECLARE(ab_sample);
#define STATUS_LEDS_THREAD_PRIORITY (CONFIG_NUM_PREEMPT_PRIORITIES - 1)
K_THREAD_STACK_DEFINE(status_leds_thread_stack_area, STATUS_LEDS_THREAD_STACK_SIZE);

enum ab_boot_slot {
SLOT_A = 0,
SLOT_B = 1,
SLOT_INVALID,
};

/** @brief Radio firmware self test
*
* @details
Expand Down Expand Up @@ -91,41 +85,79 @@ static bool app_domain_healthy(void)
return true;
}

static enum ab_boot_slot active_boot_slot_get(void)
static enum boot_slot active_boot_slot_get(void)
{
enum ab_boot_slot active_slot = SLOT_INVALID;
enum boot_slot active_slot = BOOT_SLOT_NONE;

if (IS_SLOT_A) {
active_slot = SLOT_A;
active_slot = BOOT_SLOT_PRIMARY;
} else if (IS_SLOT_B) {
active_slot = SLOT_B;
active_slot = BOOT_SLOT_SECONDARY;
} else {
LOG_ERR("Cannot determine current slot");
}

return active_slot;
}

static void device_healthcheck(void)
static bool slot_confirmed(enum boot_slot slot)
{
int err;
char *img_set = NULL;
struct boot_swap_state state;
const struct flash_area *fa;
int area_id = -1;
enum ab_boot_slot active_slot = active_boot_slot_get();

if (active_slot == SLOT_INVALID) {
return;
}
char *img_set = NULL;
bool confirmed = false;
int ret;

/* Confirming only in non-degraded boot states
*/
if (active_slot == SLOT_A) {
if (slot == BOOT_SLOT_PRIMARY) {
img_set = "A";
area_id = SLOT_A_FLASH_AREA_ID;
} else if (active_slot == SLOT_B) {
} else if (slot == BOOT_SLOT_SECONDARY) {
img_set = "B";
area_id = SLOT_B_FLASH_AREA_ID;
} else {
LOG_ERR("Cannot determine slot to check for confirmation");
return false;
}

if (flash_area_open(area_id, &fa) != 0) {
LOG_ERR("Cannot open flash area for slot %s", img_set);
return false;
}

ret = boot_read_swap_state(fa, &state);
if (ret != 0) {
LOG_ERR("Cannot read swap state for slot %s", img_set);
} else if (state.image_ok == BOOT_FLAG_SET) {
confirmed = true;
} else {
confirmed = false;
}

flash_area_close(fa);

return confirmed;
}

static void device_healthcheck(void)
{
int err;
char *img_set = NULL;
enum boot_slot active_slot = active_boot_slot_get();

/* Confirming only in non-degraded boot states */
if (active_slot == BOOT_SLOT_PRIMARY) {
img_set = "A";
} else if (active_slot == BOOT_SLOT_SECONDARY) {
img_set = "B";
} else {
LOG_ERR("Cannot determine active slot for health check");
return;
}

if (slot_confirmed(active_slot)) {
LOG_INF("Slot %s already confirmed, no action needed", img_set);
return;
}

LOG_INF("Testing image set %s...", img_set);
Expand All @@ -149,40 +181,40 @@ static void device_healthcheck(void)

LOG_INF("Confirming...");

if (flash_area_open(area_id, &fa) != 0) {
LOG_ERR("Cannot open flash area for slot %s", img_set);
return;
}

err = boot_set_next(fa, true, true);

flash_area_close(fa);
err = boot_request_confirm_slot(ACTIVE_IMAGE, active_slot);
if (err == 0) {
LOG_INF("Confirmed\n");
} else {
LOG_ERR("Failed to confirm, err: %d", err);
}
}

static void select_slot_for_single_boot(enum ab_boot_slot slot)
static void select_slot_for_single_boot(enum boot_slot slot)
{
int err = 0;
char active_slot = (active_boot_slot_get() == SLOT_A) ? 'A' : 'B';
enum boot_slot new_slot = BOOT_SLOT_NONE;
char active_slot = (active_boot_slot_get() == BOOT_SLOT_PRIMARY) ? 'A' : 'B';

if (slot == SLOT_A) {
#ifdef CONFIG_NRF_MCUBOOT_BOOT_REQUEST_PREFERENCE_KEEP
if (slot == BOOT_SLOT_PRIMARY) {
LOG_INF("Switching slots (%c -> A)", active_slot);
} else if (slot == BOOT_SLOT_SECONDARY) {
LOG_INF("Switching slots (%c -> B)", active_slot);
} else {
LOG_ERR("Cannot determine active slot, cannot toggle");
return;
}
#else
if (slot == BOOT_SLOT_PRIMARY) {
LOG_INF("Temporarily switching slots (%c -> A)", active_slot);
new_slot = BOOT_SLOT_PRIMARY;
} else if (slot == SLOT_B) {
} else if (slot == BOOT_SLOT_SECONDARY) {
LOG_INF("Temporarily switching slots (%c -> B)", active_slot);
new_slot = BOOT_SLOT_SECONDARY;
} else {
LOG_ERR("Cannot determine active slot, cannot toggle");
return;
}
#endif

err = boot_request_set_preferred_slot(ACTIVE_IMAGE, new_slot);

err = boot_request_set_preferred_slot(ACTIVE_IMAGE, slot);
if (err == 0) {
LOG_INF("Slot toggled, restart the device to enforce");
} else {
Expand All @@ -192,11 +224,11 @@ static void select_slot_for_single_boot(enum ab_boot_slot slot)

static void boot_state_report(void)
{
enum ab_boot_slot active_slot = active_boot_slot_get();
enum boot_slot active_slot = active_boot_slot_get();

if (active_slot == SLOT_A) {
if (active_slot == BOOT_SLOT_PRIMARY) {
LOG_INF("Booted from slot A");
} else if (active_slot == SLOT_B) {
} else if (active_slot == BOOT_SLOT_SECONDARY) {
LOG_INF("Booted from slot B");
} else {
LOG_INF("Cannot determine active slot");
Expand All @@ -206,9 +238,9 @@ static void boot_state_report(void)
static void button_handler(uint32_t button_state, uint32_t has_changed)
{
if ((has_changed & DK_BTN1_MSK) && (button_state & DK_BTN1_MSK)) {
select_slot_for_single_boot(SLOT_A);
select_slot_for_single_boot(BOOT_SLOT_PRIMARY);
} else if ((has_changed & DK_BTN2_MSK) && (button_state & DK_BTN2_MSK)) {
select_slot_for_single_boot(SLOT_B);
select_slot_for_single_boot(BOOT_SLOT_SECONDARY);
}
}

Expand All @@ -217,11 +249,11 @@ struct k_thread status_leds_thread_data;
static void status_leds_thread_entry_point(void *p1, void *p2, void *p3)
{
int blinking_led = DK_LED1;
enum ab_boot_slot active_slot = active_boot_slot_get();
enum boot_slot active_slot = active_boot_slot_get();

if (active_slot == SLOT_A) {
if (active_slot == BOOT_SLOT_PRIMARY) {
blinking_led = DK_LED1;
} else if (active_slot == SLOT_B) {
} else if (active_slot == BOOT_SLOT_SECONDARY) {
blinking_led = DK_LED2;
} else {
return;
Expand Down
5 changes: 0 additions & 5 deletions samples/dfu/ab/sysbuild.conf
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
# Enable MCUboot bootloader support
SB_CONFIG_BOOTLOADER_MCUBOOT=y

# Enable radiocore
SB_CONFIG_NETCORE_IPC_RADIO=y
SB_CONFIG_NETCORE_IPC_RADIO_BT_HCI_IPC=y

# Enable direct XIP with revert support
SB_CONFIG_MCUBOOT_MODE_DIRECT_XIP_WITH_REVERT=y
SB_CONFIG_MCUBOOT_IMAGES_ROM_END_OFFSET_AUTO="ipc_radio;ipc_radio_secondary_app"
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ CONFIG_PM_PARTITION_SIZE_MCUBOOT=0x10800
# CONFIG_LOG=n
# CONFIG_FPROTECT=y
CONFIG_FPROTECT=n

# Enable boot requests through retained memory.
CONFIG_RETAINED_MEM=y
CONFIG_RETENTION=y
6 changes: 2 additions & 4 deletions samples/dfu/ab/sysbuild/mcuboot/prj.conf
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
# Enable boot requests through retained memory.
CONFIG_RETAINED_MEM=y
CONFIG_RETENTION=y
# Enable boot requests.
CONFIG_CRC=y
CONFIG_NRF_MCUBOOT_BOOT_REQUEST=y

CONFIG_NRF_SECURITY=y
CONFIG_MULTITHREADING=y


# Configuration below is copied from mcuboot/boot/zephyr/prj.conf, as creating
# the sysbuild/mcuboot directory inside a sample removes the default configuration.

Expand Down
Loading
Loading