Skip to content

Zephyr: introduce bootloader requests #477

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 3 commits 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
24 changes: 24 additions & 0 deletions boot/bootutil/include/bootutil/boot_hooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@

#endif /* MCUBOOT_BOOT_GO_HOOKS */

#ifdef MCUBOOT_FIND_NEXT_SLOT_HOOKS

#define BOOT_HOOK_FIND_SLOT_CALL(f, ret_default, ...) \
DO_HOOK_CALL(f, ret_default, __VA_ARGS__)

#else

#define BOOT_HOOK_FIND_SLOT_CALL(f, ret_default, ...) \
HOOK_CALL_NOP(f, ret_default, __VA_ARGS__)

#endif /* MCUBOOT_FIND_NEXT_SLOT_HOOKS */

#ifdef MCUBOOT_FLASH_AREA_HOOKS

#define BOOT_HOOK_FLASH_AREA_CALL(f, ret_default, ...) \
Expand Down Expand Up @@ -260,4 +272,16 @@ int flash_area_get_device_id_hook(const struct flash_area *fa,
#define BOOT_RESET_REQUEST_HOOK_CHECK_FAILED 3
#define BOOT_RESET_REQUEST_HOOK_INTERNAL_ERROR 4

/**
* Finds the preferred slot containing the image.
*
* @param[in] state Boot loader status information.
* @param[in] image Image, for which the slot should be found.
* @param[out] active_slot Number of the preferred slot.
*
* @return 0 if a slot was requested;
* BOOT_HOOK_REGULAR follow the normal execution path.
*/
int boot_find_next_slot_hook(struct boot_loader_state *state, uint8_t image, uint32_t *active_slot);

#endif /*H_BOOTUTIL_HOOKS*/
105 changes: 105 additions & 0 deletions boot/bootutil/include/bootutil/boot_request.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright (c) 2025 Nordic Semiconductor ASA
*/

#ifndef __BOOT_REQUEST_H__
#define __BOOT_REQUEST_H__

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include <stdbool.h>

/** Special value, indicating that there is no preferred slot. */
#define BOOT_REQUEST_NO_PREFERRED_SLOT UINT32_MAX

/**
* @brief Request a bootloader to confirm the specified slot of an image.
*
* @param[in] image Image number.
* @param[in] slot Slot number.
*
* @return 0 if requested, negative error code otherwise.
*/
int boot_request_confirm_slot(uint8_t image, uint32_t slot);

/**
* @brief Request a bootloader to boot the specified slot of an image.
*
* @param[in] image Image number.
* @param[in] slot Slot number.
*
* @return 0 if requested, negative error code otherwise.
*/
int boot_request_set_preferred_slot(uint8_t image, uint32_t slot);

/**
* @brief Request a bootloader to boot recovery image.
*
* @return 0 if requested, negative error code otherwise.
*/
int boot_request_enter_recovery(void);

/**
* @brief Request a bootloader to boot firmware loader image.
*
* @return 0 if requested, negative error code otherwise.
*/
int boot_request_enter_firmware_loader(void);

/**
* @brief Check if there is a request to confirm the specified slot of an image.
*
* @param[in] image Image number.
* @param[in] slot Slot number.
*
* @return true if requested, false otherwise.
*/
bool boot_request_check_confirmed_slot(uint8_t image, uint32_t slot);

/**
* @brief Find if there is a request to boot certain slot of the specified image.
*
* @param[in] image Image number.
*
* @return slot number if requested, BOOT_REQUEST_NO_PREFERRED_SLOT otherwise.
*/
uint32_t boot_request_get_preferred_slot(uint8_t image);

/**
* @brief Check if there is a request to boot recovery image.
*
* @return true if requested, false otherwise.
*/
bool boot_request_detect_recovery(void);

/**
* @brief Check if there is a request to boot firmware loader image.
*
* @return true if requested, false otherwise.
*/
bool boot_request_detect_firmware_loader(void);

/**
* @brief Initialize boot requests module.
*
* @return 0 if successful, negative error code otherwise.
*/
int boot_request_init(void);

/**
* @brief Clear/drop all requests.
*
* @return 0 if successful, negative error code otherwise.
*/
int boot_request_clear(void);

#ifdef __cplusplus
}
#endif

#endif /* __BOOT_REQUEST_H__ */
96 changes: 91 additions & 5 deletions boot/bootutil/src/bootutil_public.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@
#include "bootutil_priv.h"
#include "bootutil_misc.h"

#if defined(CONFIG_MCUBOOT_BOOT_REQUEST) && !defined(CONFIG_MCUBOOT)
#include <bootutil/boot_request.h>
#define SEND_BOOT_REQUEST
#endif /* CONFIG_MCUBOOT_BOOT_REQUEST && !CONFIG_MCUBOOT */

#ifdef CONFIG_MCUBOOT
BOOT_LOG_MODULE_DECLARE(mcuboot);
#else
Expand Down Expand Up @@ -503,32 +508,93 @@ boot_write_copy_done(const struct flash_area *fap)
return boot_write_trailer_flag(fap, off, BOOT_FLAG_SET);
}

#ifndef MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP
#ifdef SEND_BOOT_REQUEST
bool
boot_send_requests(uint8_t magic, bool active, bool confirm, int image_id, uint32_t slot_id)
{
int rc;

/* Handle write-protected active image. */
switch (magic) {
case BOOT_MAGIC_GOOD:
/* fall-through */

case BOOT_MAGIC_UNSET:
if (confirm) {
BOOT_LOG_DBG("Confirm image: %d, %d", image_id, slot_id);
rc = boot_request_confirm_slot(image_id, slot_id);
} else {
BOOT_LOG_DBG("Set image preference: %d, %d", image_id, slot_id);
rc = boot_request_set_preferred_slot(image_id, slot_id);
}
if (rc != 0) {
rc = BOOT_EBADIMAGE;
}
break;

case BOOT_MAGIC_BAD:
default:
rc = BOOT_EBADIMAGE;
break;
}

if (active && (rc ==0)) {
return true;
}

return false;
}
#endif /* SEND_BOOT_REQUEST */

static int flash_area_to_image(const struct flash_area *fa)
#if defined(SEND_BOOT_REQUEST) || (!defined(MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP))
static int flash_area_to_image_slot(const struct flash_area *fa, uint32_t *slot)
{
int id = flash_area_get_id(fa);
#if BOOT_IMAGE_NUMBER > 1
uint8_t i = 0;
int id = flash_area_get_id(fa);

while (i < BOOT_IMAGE_NUMBER) {
if (FLASH_AREA_IMAGE_PRIMARY(i) == id || (FLASH_AREA_IMAGE_SECONDARY(i) == id)) {
if (FLASH_AREA_IMAGE_PRIMARY(i) == id) {
if (slot != NULL) {
*slot = 0;
}
return i;
} else if (FLASH_AREA_IMAGE_SECONDARY(i) == id) {
if (slot != NULL) {
*slot = 1;
}
return i;
}

++i;
}
#else
(void)fa;
if (slot != NULL) {
if (FLASH_AREA_IMAGE_PRIMARY(0) == id) {
*slot = 0;
} else if (FLASH_AREA_IMAGE_SECONDARY(0) == id) {
*slot = 1;
} else {
*slot = UINT32_MAX;
}
}
#endif
return 0;
}
#endif /* defined(SEND_BOOT_REQUEST) || (!defined(MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP)) */

#ifndef MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP
int
boot_set_next(const struct flash_area *fa, bool active, bool confirm)
{
struct boot_swap_state slot_state;
int rc;
int image_id;
uint32_t slot_id;

BOOT_LOG_DBG("boot_set_next: fa %p active == %d, confirm == %d",
fa, (int)active, (int)confirm);

if (active) {
confirm = true;
Expand All @@ -539,6 +605,14 @@ boot_set_next(const struct flash_area *fa, bool active, bool confirm)
return rc;
}

image_id = flash_area_to_image_slot(fa, &slot_id);

#ifdef SEND_BOOT_REQUEST
if (boot_send_requests(slot_state.magic, active, confirm, image_id, slot_id)) {
return rc;
}
#endif

switch (slot_state.magic) {
case BOOT_MAGIC_GOOD:
/* If non-active then swap already scheduled, else confirm needed.*/
Expand Down Expand Up @@ -569,7 +643,7 @@ boot_set_next(const struct flash_area *fa, bool active, bool confirm)
} else {
swap_type = BOOT_SWAP_TYPE_TEST;
}
rc = boot_write_swap_info(fa, swap_type, flash_area_to_image(fa));
rc = boot_write_swap_info(fa, swap_type, image_id);
}
}
break;
Expand Down Expand Up @@ -600,6 +674,10 @@ boot_set_next(const struct flash_area *fa, bool active, bool confirm)
{
struct boot_swap_state slot_state;
int rc;
#ifdef SEND_BOOT_REQUEST
int image_id;
uint32_t slot_id;
#endif

BOOT_LOG_DBG("boot_set_next: fa %p active == %d, confirm == %d",
fa, (int)active, (int)confirm);
Expand All @@ -618,6 +696,14 @@ boot_set_next(const struct flash_area *fa, bool active, bool confirm)
return rc;
}

#ifdef SEND_BOOT_REQUEST
image_id = flash_area_to_image_slot(fa, &slot_id);

if (boot_send_requests(slot_state.magic, active, confirm, image_id, slot_id)) {
return rc;
}
#endif

switch (slot_state.magic) {
case BOOT_MAGIC_UNSET:
/* Magic is needed for MCUboot to even consider booting an image */
Expand Down
14 changes: 12 additions & 2 deletions boot/bootutil/src/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -3605,7 +3605,12 @@ boot_load_and_validate_images(struct boot_loader_state *state)
break;
}

active_slot = find_slot_with_highest_version(state);
rc = BOOT_HOOK_FIND_SLOT_CALL(boot_find_next_slot_hook, BOOT_HOOK_REGULAR,
state, BOOT_CURR_IMG(state), &active_slot);
if (rc == BOOT_HOOK_REGULAR) {
active_slot = find_slot_with_highest_version(state);
}

if (active_slot == NO_ACTIVE_SLOT) {
BOOT_LOG_INF("No slot to load for image %d",
BOOT_CURR_IMG(state));
Expand Down Expand Up @@ -3652,7 +3657,12 @@ boot_load_and_validate_images(struct boot_loader_state *state)
break;
}

active_slot = find_slot_with_highest_version(state);
rc = BOOT_HOOK_FIND_SLOT_CALL(boot_find_next_slot_hook, BOOT_HOOK_REGULAR,
state, BOOT_CURR_IMG(state), &active_slot);
if (rc == BOOT_HOOK_REGULAR) {
active_slot = find_slot_with_highest_version(state);
}

if (active_slot == NO_ACTIVE_SLOT) {
BOOT_LOG_INF("No slot to load for image %d",
BOOT_CURR_IMG(state));
Expand Down
5 changes: 5 additions & 0 deletions boot/bootutil/zephyr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ zephyr_library_named(mcuboot_util)
zephyr_library_sources(
../src/bootutil_public.c
)
if(CONFIG_MCUBOOT_BOOT_REQUEST)
zephyr_library_sources_ifdef(CONFIG_MCUBOOT_BOOT_REQUEST_IMPL_RETENTION
src/boot_request_retention.c
)
endif()

# Sensitivity to the TEST_BOOT_IMAGE_ACCESS_HOOKS define is implemented for
# allowing the test-build with the hooks feature enabled.
Expand Down
Loading