Skip to content

Commit

Permalink
samples: Add a sample for runtime chosen image
Browse files Browse the repository at this point in the history
A sample for runtime chose image on FRDM K64F. It provides implementation
for Zephyr flash_area_open_custom(), so the right flash map
implementation is used, and MCUboot flash_map_id_get_next() and
flash_map_id_get_current() to prioritize sources. It should show what is
expected from an application to be able to use non-flash sources for
images.

In this sample, one can influence from which slot image will be loaded
by pressing a button on the board.

For more details on how to build and test the samples, check the
provided README.md.

Signed-off-by: Ederson de Souza <[email protected]>
  • Loading branch information
edersondisouza committed Aug 21, 2024
1 parent e997c81 commit ad5cba6
Show file tree
Hide file tree
Showing 11 changed files with 339 additions and 0 deletions.
6 changes: 6 additions & 0 deletions boot/zephyr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,12 @@ if(CONFIG_BOOT_MAX_IMG_SECTORS_AUTO)
endif()
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
11 changes: 11 additions & 0 deletions boot/zephyr/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,17 @@ config FLASH_RUNTIME_SOURCES
and `flash_map_id_get_next` functions to tell mcuboot where to find
the images.

if FLASH_MAP_CUSTOM_SOURCES
config AARDVARK_I2C_FLASH
bool "Use aardvark to emulate an I2C device from where a firmware image is read"
default n
depends on I2C
help
If you have an I2C device that acts as a flash memory, you can enable this
option to read the firmware image from it.

endif

config BOOT_ENCRYPTION_SUPPORT
bool
help
Expand Down
4 changes: 4 additions & 0 deletions 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
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"
161 changes: 161 additions & 0 deletions samples/runtime-source/zephyr/app/scripts/aardvark_i2c_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# SPDX-License-Identifier: Apache-2.0

import argparse
import signal
import sys
import time

from aardvark_py import *

done = False
contents = None


def parse_args():
global args
parser = argparse.ArgumentParser(description='Aardvark I2C "Storage Device"')
parser.add_argument("-p", "--port", default=0, help="Aardvark port number")
parser.add_argument("device_addr", type=str, help="I2C device address")
parser.add_argument("file", type=str, help="File to provide")
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
args = parser.parse_args()


def signal_handler(sig, frame):
global done
global contents

if sig == signal.SIGINT:
done = True
elif sig == signal.SIGQUIT:
print("Reloading file")
with open(args.file, "rb") as f:
contents = f.read()


def watch(handle):
print("Watching I2C data (Use ^\\ to reload file) ...")

# Loop until aa_async_poll times out
while True:
result = aa_async_poll(handle, 100)
# Read the I2C message.
if (result & AA_ASYNC_I2C_READ) == AA_ASYNC_I2C_READ:
# Get data written by master
(num_bytes, addr, data_in) = aa_i2c_slave_read(handle, 6)

if num_bytes < 0:
print("slave_read error: %s" % aa_status_string(num_bytes))
return

if num_bytes == 0:
continue

if data_in[0] == 0x1:
if args.verbose:
print(
f"Got 0x1, asking size of file. Responding with {len(contents)}"
)
ret = aa_i2c_slave_set_response(
handle,
array(
"B",
[
(len(contents) >> 24) & 0xFF,
(len(contents) >> 16) & 0xFF,
(len(contents) >> 8) & 0xFF,
len(contents) & 0xFF,
],
),
)
elif data_in[0] == 0x2:
addr = (
data_in[1] << 24 | data_in[2] << 16 | data_in[3] << 8 | data_in[4]
)
size = data_in[5]

if args.verbose:
print(f"Got 0x2, asking for data, at {addr} with size {size}.")

if addr < 0 or addr + size > len(contents):
print("Requested data is out of bounds, responding with 0x0")
ret = aa_i2c_slave_set_response(handle, array("B", [0x0]))
else:
ret = aa_i2c_slave_set_response(
handle, array("B", contents[addr : addr + size])
)
else:
print("Got unknown data, responding with 0x0")
ret = aa_i2c_slave_set_response(handle, array("B", [0x0]))

elif (result & AA_ASYNC_I2C_WRITE) == AA_ASYNC_I2C_WRITE:
# Get number of bytes written to master
num_bytes = aa_i2c_slave_write_stats(handle)

if num_bytes < 0:
print("slave_write_stats error: %s" % aa_status_string(num_bytes))
return

# Print status information to the screen
if args.verbose:
print(f"Number of bytes written to master: {num_bytes:04}\n")

elif result == AA_ASYNC_NO_DATA:
if done:
break
else:
print("error: non-I2C asynchronous message is pending", result)
return


def main():
global contents

parse_args()

signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGQUIT, signal_handler)

port = args.port
addr = int(args.device_addr, 0)

# Open the device
handle = aa_open(port)
if handle <= 0:
print("Unable to open Aardvark device on port %d" % port)
print("Error code = %d" % handle)
sys.exit()

# Ensure that the I2C subsystem is enabled
aa_configure(handle, AA_CONFIG_SPI_I2C)

# Disable the I2C bus pullup resistors (2.2k resistors).
# This command is only effective on v2.0 hardware or greater.
# The pullup resistors on the v1.02 hardware are enabled by default.
aa_i2c_pullup(handle, AA_I2C_PULLUP_NONE)

# Power the EEPROM using the Aardvark adapter's power supply.
# This command is only effective on v2.0 hardware or greater.
# The power pins on the v1.02 hardware are not enabled by default.
aa_target_power(handle, AA_TARGET_POWER_BOTH)

# Set default response
aa_i2c_slave_set_response(handle, array("B", [0]))

# Enabled the device
aa_i2c_slave_enable(handle, addr, 64, 6)

# Read the file
with open(args.file, "rb") as f:
contents = f.read()

# Watch the I2C port
watch(handle)

# Disable and close the device
aa_i2c_slave_disable(handle)
aa_close(handle)


if __name__ == "__main__":
main()
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

0 comments on commit ad5cba6

Please sign in to comment.