Skip to content
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

[RFC] Support for choosing image to boot in runtime #2044

Open
wants to merge 2 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
6 changes: 6 additions & 0 deletions boot/zephyr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,12 @@ if((CONFIG_BOOT_SWAP_USING_SCRATCH OR CONFIG_BOOT_SWAP_USING_MOVE) AND (DEFINED
zephyr_library_sources(flash_check.c)
endif()

if(BUILD_RUNTIME_SOURCE_SAMPLE)
zephyr_library_sources(
${MCUBOOT_DIR}/samples/runtime-source/zephyr/flash_map_dispatcher.c
)
endif()

if(SYSBUILD)
if(CONFIG_SINGLE_APPLICATION_SLOT OR CONFIG_BOOT_FIRMWARE_LOADER OR CONFIG_BOOT_SWAP_USING_SCRATCH OR CONFIG_BOOT_SWAP_USING_MOVE OR CONFIG_BOOT_UPGRADE_ONLY OR CONFIG_BOOT_DIRECT_XIP OR CONFIG_BOOT_RAM_LOAD)
# TODO: RAM LOAD support
Expand Down
12 changes: 12 additions & 0 deletions boot/zephyr/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,18 @@ config BOOT_IMAGE_EXECUTABLE_RAM_SIZE
default $(dt_chosen_reg_size_int,$(DT_CHOSEN_Z_SRAM),0)
endif

config FLASH_RUNTIME_SOURCES
bool "Images are read from flash partitions defined at runtime"
select SINGLE_APPLICATION_SLOT
help
Instead of using information on the flash slots to decide which images
to load/update, the application provides the information from which
flash slot to load in runtime. This is useful when the application
reads the state for hardware straps or other sources to decide which
image to load. The application must provide `flash_map_id_get_next`
and `flash_map_id_get_next` functions to tell mcuboot where to find
the images.

config BOOT_ENCRYPTION_SUPPORT
bool
help
Expand Down
15 changes: 14 additions & 1 deletion boot/zephyr/flash_map_extended.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
#include <flash_map_backend/flash_map_backend.h>
#include <sysflash/sysflash.h>

#if defined(CONFIG_FLASH_RUNTIME_SOURCES)
#include <flash_runtime_sources.h>
#endif

#include "bootutil/bootutil_log.h"

BOOT_LOG_MODULE_DECLARE(mcuboot);
Expand Down Expand Up @@ -58,6 +62,15 @@ int flash_device_base(uint8_t fd_id, uintptr_t *ret)
*/
int flash_area_id_from_multi_image_slot(int image_index, int slot)
{
#if defined(CONFIG_FLASH_RUNTIME_SOURCES)
uint8_t id;

if (flash_map_id_get_current(&id)) {
return id;
}
return -1;
#endif

switch (slot) {
case 0: return FLASH_AREA_IMAGE_PRIMARY(image_index);
#if !defined(CONFIG_SINGLE_APPLICATION_SLOT)
Expand Down Expand Up @@ -141,7 +154,7 @@ int flash_area_sector_from_off(off_t off, struct flash_sector *sector)

uint8_t flash_area_get_device_id(const struct flash_area *fa)
{
#if defined(CONFIG_ARM)
#if defined(CONFIG_ARM) || defined(CONFIG_FLASH_RUNTIME_SOURCES)
return fa->fa_id;
#else
(void)fa;
Expand Down
46 changes: 46 additions & 0 deletions boot/zephyr/include/flash_runtime_sources.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2024 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef __FLASH_RUNTIME_SOURCES_H__
#define __FLASH_RUNTIME_SOURCES_H__

#include <inttypes.h>

#ifdef __cplusplus
extern "C" {
#endif

/*
* Get next flash map id.
*
* Implement this function to get the next flash map id. The function should
* return true if the flash map id was successfully updated. If the reset
* parameter is true, the function should reset the flash map id to the first
* one.
*
* @param id Pointer to the flash map id.
* @param reset If true, the function will reset the flash map id to the first
* one.
* @retval true If the flash map id was successfully updated.
*/
bool flash_map_id_get_next(uint8_t *id, bool reset);

/*
* Get current flash map id.
*
* Implement this function to get the current flash map id. The function should
* return true if the flash map id was successfully read.
*
* @param id Pointer to the flash map id.
* @retval true If the flash map id was successfully read.
*/
bool flash_map_id_get_current(uint8_t *id);

#ifdef __cplusplus
}
#endif

#endif /* __FLASH_RUNTIME_SOURCES_H__ */
85 changes: 62 additions & 23 deletions boot/zephyr/single_loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,30 @@ BOOT_LOG_MODULE_DECLARE(mcuboot);
static const struct flash_area *_fa_p;
static struct image_header _hdr = { 0 };

__weak bool
flash_map_id_get_next(uint8_t *id, bool reset)
{
if (!reset || !id) {
return false;
}

*id = FLASH_AREA_IMAGE_PRIMARY(0);

return true;
}

__weak bool
flash_map_id_get_current(uint8_t *id)
{
if (!id) {
return false;
}

*id = FLASH_AREA_IMAGE_PRIMARY(0);

return true;
}

#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) || defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE)
/**
* Validate hash of a primary boot image.
Expand Down Expand Up @@ -102,50 +126,65 @@ fih_ret
boot_go(struct boot_rsp *rsp)
{
int rc = -1;
uint8_t flash_id;
bool reset = true;
FIH_DECLARE(fih_rc, FIH_FAILURE);

rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(0), &_fa_p);
assert(rc == 0);
while (flash_map_id_get_next(&flash_id, reset)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

avoid looping when feature not enabled.

reset = false;
rc = flash_area_open(flash_id, &_fa_p);
if (rc != 0) {
continue;
}

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

#ifdef MCUBOOT_RAM_LOAD
static struct boot_loader_state state;
state.imgs[0][0].hdr = _hdr;

rc = boot_load_image_to_sram(&state);
if (rc != 0)
goto out;
if (rc != 0) {
flash_area_close(_fa_p);
continue;
}
#endif

#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT
FIH_CALL(boot_image_validate, fih_rc, _fa_p, &_hdr);
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
FIH_CALL(boot_image_validate, fih_rc, _fa_p, &_hdr);
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
flash_area_close(_fa_p);
#ifdef MCUBOOT_RAM_LOAD
boot_remove_image_from_sram(&state);
boot_remove_image_from_sram(&state);
#endif
goto out;
}
continue;
}
#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)) {
FIH_CALL(boot_image_validate_once, fih_rc, _fa_p, &_hdr);
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
flash_area_close(_fa_p);
#ifdef MCUBOOT_RAM_LOAD
boot_remove_image_from_sram(&state);
boot_remove_image_from_sram(&state);
#endif
goto out;
}
continue;
}
#else
fih_rc = FIH_SUCCESS;
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;
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;

flash_area_close(_fa_p);

break;
}

out:
flash_area_close(_fa_p);

FIH_RET(fih_rc);
}
56 changes: 56 additions & 0 deletions samples/runtime-source/zephyr/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Runtime chosen image sample application

This sample demonstrates how to use a non flash storage to retrieve the image
being booted. It was tested on a FRDM K64F. Both slots are used to store two
different images. The image to be booted is selected based on a button press.

## Build

Build mcuboot. First, ensure ZEPHYR_SDK_INSTALL_DIR is defined. From the
mcuboot directory, run the following commands:

```
source <path-to-zephyr>/zephyr-env.sh
west build -p -b frdm_k64f boot/zephyr/ -- -DBUILD_RUNTIME_SOURCE_SAMPLE=1 \
-DEXTRA_CONF_FILE="../../samples/runtime-source/zephyr/sample.conf"
-DEXTRA_DTC_OVERLAY_FILE=../../samples/runtime-source/zephyr/boards/frdm_k64f.overlay
west build -t flash
```

Then, build the sample application to be loaded. We need to build it twice, one
for each slot. From the sample
app directory (mcuboot/samples/non-flash-source/zephyr/app), run:

```
west build -p -b frdm_k64f .
west flash
```

Then change the overlay file to use the second slot. For instance, open
`boards/frdm_k64f.overlay` and change the line:

```
zephyr,code-partition = &slot0_partition;
```

to:

```
zephyr,code-partition = &slot1_partition;
```

And build and flash again:

```
west build -b frdm_k64f .
west flash
```

## Run

Open a serial terminal to see the output and reset the board. It shall boot the
image on slot0 by default. By keeping the SW2 button pressed during reset, the
bootloader will randomly select the image to be booted.
14 changes: 14 additions & 0 deletions samples/runtime-source/zephyr/app/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(non_flash_backend_app)

if (NOT DEFINED FROM_WHO)
set(FROM_WHO Zephyr)
endif()

target_compile_definitions(app PRIVATE "-DMCUBOOT_HELLO_WORLD_FROM=\"${FROM_WHO}\"")

target_sources(app PRIVATE src/main.c)
5 changes: 5 additions & 0 deletions samples/runtime-source/zephyr/app/boards/frdm_k64f.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/ {
chosen {
zephyr,code-partition = &slot0_partition;
};
};
3 changes: 3 additions & 0 deletions samples/runtime-source/zephyr/app/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CONFIG_BOOTLOADER_MCUBOOT=y

CONFIG_MCUBOOT_SIGNATURE_KEY_FILE="./bootloader/mcuboot/root-rsa-2048.pem"
15 changes: 15 additions & 0 deletions samples/runtime-source/zephyr/app/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright (c) 2017 Linaro, Ltd.
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

int main(void)
{
printk("Hello World from %s on %s, slot %s!\n",
MCUBOOT_HELLO_WORLD_FROM, CONFIG_BOARD,
DT_PROP(DT_CHOSEN(zephyr_code_partition), label));
}
60 changes: 60 additions & 0 deletions samples/runtime-source/zephyr/flash_map_dispatcher.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2024 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <flash_map_backend/flash_map_backend.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/kernel.h>
#include <zephyr/random/random.h>

#define SW1_NODE DT_ALIAS(sw1)
#if DT_NODE_HAS_STATUS(SW1_NODE, okay)
static struct gpio_dt_spec sw1_spec = GPIO_DT_SPEC_GET(SW1_NODE, gpios);
#endif

static int curr_idx = -1;

static uint8_t known_ids[] = {
FIXED_PARTITION_ID(slot0_partition),
FIXED_PARTITION_ID(slot1_partition),
};

bool
flash_map_id_get_next(uint8_t *id, bool reset)
{
if (reset) {
curr_idx = 0;
#if DT_NODE_HAS_STATUS(SW1_NODE, okay)
if (gpio_pin_configure_dt(&sw1_spec, GPIO_INPUT) == 0) {
if (gpio_pin_get_dt(&sw1_spec) == 1) {
curr_idx = sys_rand8_get() % ARRAY_SIZE(known_ids);
printk("Booting from curr_idx = %d\n", curr_idx);
}
}
#endif
} else {
curr_idx++;
}

if (curr_idx >= ARRAY_SIZE(known_ids)) {
return false;
}

*id = known_ids[curr_idx];

return true;
}

bool
flash_map_id_get_current(uint8_t *id)
{
if (curr_idx == -1 || curr_idx >= ARRAY_SIZE(known_ids)) {
return false;
}

*id = known_ids[curr_idx];

return true;
}
4 changes: 4 additions & 0 deletions samples/runtime-source/zephyr/sample.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CONFIG_FLASH_RUNTIME_SOURCES=y
CONFIG_BOOT_SIGNATURE_TYPE_RSA=y
CONFIG_TEST_RANDOM_GENERATOR=y
CONFIG_ENTROPY_GENERATOR=y