Skip to content

Commit ad5cba6

Browse files
samples: Add a sample for runtime chosen image
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]>
1 parent e997c81 commit ad5cba6

File tree

11 files changed

+339
-0
lines changed

11 files changed

+339
-0
lines changed

boot/zephyr/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,12 @@ if(CONFIG_BOOT_MAX_IMG_SECTORS_AUTO)
428428
endif()
429429
endif()
430430

431+
if(BUILD_RUNTIME_SOURCE_SAMPLE)
432+
zephyr_library_sources(
433+
${MCUBOOT_DIR}/samples/runtime-source/zephyr/flash_map_dispatcher.c
434+
)
435+
endif()
436+
431437
if(SYSBUILD)
432438
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)
433439
# TODO: RAM LOAD support

boot/zephyr/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,17 @@ config FLASH_RUNTIME_SOURCES
332332
and `flash_map_id_get_next` functions to tell mcuboot where to find
333333
the images.
334334

335+
if FLASH_MAP_CUSTOM_SOURCES
336+
config AARDVARK_I2C_FLASH
337+
bool "Use aardvark to emulate an I2C device from where a firmware image is read"
338+
default n
339+
depends on I2C
340+
help
341+
If you have an I2C device that acts as a flash memory, you can enable this
342+
option to read the firmware image from it.
343+
344+
endif
345+
335346
config BOOT_ENCRYPTION_SUPPORT
336347
bool
337348
help

boot/zephyr/flash_map_extended.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
#include <flash_map_backend/flash_map_backend.h>
1515
#include <sysflash/sysflash.h>
1616

17+
#if defined(CONFIG_FLASH_RUNTIME_SOURCES)
18+
#include <flash_runtime_sources.h>
19+
#endif
20+
1721
#include "bootutil/bootutil_log.h"
1822

1923
BOOT_LOG_MODULE_DECLARE(mcuboot);
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Runtime chosen image sample application
2+
3+
This sample demonstrates how to use a non flash storage to retrieve the image
4+
being booted. It was tested on a FRDM K64F. Both slots are used to store two
5+
different images. The image to be booted is selected based on a button press.
6+
7+
## Build
8+
9+
Build mcuboot. First, ensure ZEPHYR_SDK_INSTALL_DIR is defined. From the
10+
mcuboot directory, run the following commands:
11+
12+
```
13+
source <path-to-zephyr>/zephyr-env.sh
14+
15+
west build -p -b frdm_k64f boot/zephyr/ -- -DBUILD_RUNTIME_SOURCE_SAMPLE=1 \
16+
-DEXTRA_CONF_FILE="../../samples/runtime-source/zephyr/sample.conf"
17+
-DEXTRA_DTC_OVERLAY_FILE=../../samples/runtime-source/zephyr/boards/frdm_k64f.overlay
18+
19+
west build -t flash
20+
```
21+
22+
Then, build the sample application to be loaded. We need to build it twice, one
23+
for each slot. From the sample
24+
app directory (mcuboot/samples/non-flash-source/zephyr/app), run:
25+
26+
```
27+
west build -p -b frdm_k64f .
28+
west flash
29+
```
30+
31+
Then change the overlay file to use the second slot. For instance, open
32+
`boards/frdm_k64f.overlay` and change the line:
33+
34+
```
35+
zephyr,code-partition = &slot0_partition;
36+
37+
```
38+
39+
to:
40+
41+
```
42+
zephyr,code-partition = &slot1_partition;
43+
```
44+
45+
And build and flash again:
46+
47+
```
48+
west build -b frdm_k64f .
49+
west flash
50+
```
51+
52+
## Run
53+
54+
Open a serial terminal to see the output and reset the board. It shall boot the
55+
image on slot0 by default. By keeping the SW2 button pressed during reset, the
56+
bootloader will randomly select the image to be booted.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
5+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
project(non_flash_backend_app)
7+
8+
if (NOT DEFINED FROM_WHO)
9+
set(FROM_WHO Zephyr)
10+
endif()
11+
12+
target_compile_definitions(app PRIVATE "-DMCUBOOT_HELLO_WORLD_FROM=\"${FROM_WHO}\"")
13+
14+
target_sources(app PRIVATE src/main.c)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/ {
2+
chosen {
3+
zephyr,code-partition = &slot0_partition;
4+
};
5+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CONFIG_BOOTLOADER_MCUBOOT=y
2+
3+
CONFIG_MCUBOOT_SIGNATURE_KEY_FILE="./bootloader/mcuboot/root-rsa-2048.pem"
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
import argparse
4+
import signal
5+
import sys
6+
import time
7+
8+
from aardvark_py import *
9+
10+
done = False
11+
contents = None
12+
13+
14+
def parse_args():
15+
global args
16+
parser = argparse.ArgumentParser(description='Aardvark I2C "Storage Device"')
17+
parser.add_argument("-p", "--port", default=0, help="Aardvark port number")
18+
parser.add_argument("device_addr", type=str, help="I2C device address")
19+
parser.add_argument("file", type=str, help="File to provide")
20+
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
21+
args = parser.parse_args()
22+
23+
24+
def signal_handler(sig, frame):
25+
global done
26+
global contents
27+
28+
if sig == signal.SIGINT:
29+
done = True
30+
elif sig == signal.SIGQUIT:
31+
print("Reloading file")
32+
with open(args.file, "rb") as f:
33+
contents = f.read()
34+
35+
36+
def watch(handle):
37+
print("Watching I2C data (Use ^\\ to reload file) ...")
38+
39+
# Loop until aa_async_poll times out
40+
while True:
41+
result = aa_async_poll(handle, 100)
42+
# Read the I2C message.
43+
if (result & AA_ASYNC_I2C_READ) == AA_ASYNC_I2C_READ:
44+
# Get data written by master
45+
(num_bytes, addr, data_in) = aa_i2c_slave_read(handle, 6)
46+
47+
if num_bytes < 0:
48+
print("slave_read error: %s" % aa_status_string(num_bytes))
49+
return
50+
51+
if num_bytes == 0:
52+
continue
53+
54+
if data_in[0] == 0x1:
55+
if args.verbose:
56+
print(
57+
f"Got 0x1, asking size of file. Responding with {len(contents)}"
58+
)
59+
ret = aa_i2c_slave_set_response(
60+
handle,
61+
array(
62+
"B",
63+
[
64+
(len(contents) >> 24) & 0xFF,
65+
(len(contents) >> 16) & 0xFF,
66+
(len(contents) >> 8) & 0xFF,
67+
len(contents) & 0xFF,
68+
],
69+
),
70+
)
71+
elif data_in[0] == 0x2:
72+
addr = (
73+
data_in[1] << 24 | data_in[2] << 16 | data_in[3] << 8 | data_in[4]
74+
)
75+
size = data_in[5]
76+
77+
if args.verbose:
78+
print(f"Got 0x2, asking for data, at {addr} with size {size}.")
79+
80+
if addr < 0 or addr + size > len(contents):
81+
print("Requested data is out of bounds, responding with 0x0")
82+
ret = aa_i2c_slave_set_response(handle, array("B", [0x0]))
83+
else:
84+
ret = aa_i2c_slave_set_response(
85+
handle, array("B", contents[addr : addr + size])
86+
)
87+
else:
88+
print("Got unknown data, responding with 0x0")
89+
ret = aa_i2c_slave_set_response(handle, array("B", [0x0]))
90+
91+
elif (result & AA_ASYNC_I2C_WRITE) == AA_ASYNC_I2C_WRITE:
92+
# Get number of bytes written to master
93+
num_bytes = aa_i2c_slave_write_stats(handle)
94+
95+
if num_bytes < 0:
96+
print("slave_write_stats error: %s" % aa_status_string(num_bytes))
97+
return
98+
99+
# Print status information to the screen
100+
if args.verbose:
101+
print(f"Number of bytes written to master: {num_bytes:04}\n")
102+
103+
elif result == AA_ASYNC_NO_DATA:
104+
if done:
105+
break
106+
else:
107+
print("error: non-I2C asynchronous message is pending", result)
108+
return
109+
110+
111+
def main():
112+
global contents
113+
114+
parse_args()
115+
116+
signal.signal(signal.SIGINT, signal_handler)
117+
signal.signal(signal.SIGQUIT, signal_handler)
118+
119+
port = args.port
120+
addr = int(args.device_addr, 0)
121+
122+
# Open the device
123+
handle = aa_open(port)
124+
if handle <= 0:
125+
print("Unable to open Aardvark device on port %d" % port)
126+
print("Error code = %d" % handle)
127+
sys.exit()
128+
129+
# Ensure that the I2C subsystem is enabled
130+
aa_configure(handle, AA_CONFIG_SPI_I2C)
131+
132+
# Disable the I2C bus pullup resistors (2.2k resistors).
133+
# This command is only effective on v2.0 hardware or greater.
134+
# The pullup resistors on the v1.02 hardware are enabled by default.
135+
aa_i2c_pullup(handle, AA_I2C_PULLUP_NONE)
136+
137+
# Power the EEPROM using the Aardvark adapter's power supply.
138+
# This command is only effective on v2.0 hardware or greater.
139+
# The power pins on the v1.02 hardware are not enabled by default.
140+
aa_target_power(handle, AA_TARGET_POWER_BOTH)
141+
142+
# Set default response
143+
aa_i2c_slave_set_response(handle, array("B", [0]))
144+
145+
# Enabled the device
146+
aa_i2c_slave_enable(handle, addr, 64, 6)
147+
148+
# Read the file
149+
with open(args.file, "rb") as f:
150+
contents = f.read()
151+
152+
# Watch the I2C port
153+
watch(handle)
154+
155+
# Disable and close the device
156+
aa_i2c_slave_disable(handle)
157+
aa_close(handle)
158+
159+
160+
if __name__ == "__main__":
161+
main()
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright (c) 2017 Linaro, Ltd.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/kernel.h>
8+
#include <zephyr/sys/printk.h>
9+
10+
int main(void)
11+
{
12+
printk("Hello World from %s on %s, slot %s!\n",
13+
MCUBOOT_HELLO_WORLD_FROM, CONFIG_BOARD,
14+
DT_PROP(DT_CHOSEN(zephyr_code_partition), label));
15+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2024 Intel Corporation
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <flash_map_backend/flash_map_backend.h>
8+
#include <zephyr/drivers/gpio.h>
9+
#include <zephyr/kernel.h>
10+
#include <zephyr/random/random.h>
11+
12+
#define SW1_NODE DT_ALIAS(sw1)
13+
#if DT_NODE_HAS_STATUS(SW1_NODE, okay)
14+
static struct gpio_dt_spec sw1_spec = GPIO_DT_SPEC_GET(SW1_NODE, gpios);
15+
#endif
16+
17+
static int curr_idx = -1;
18+
19+
static uint8_t known_ids[] = {
20+
FIXED_PARTITION_ID(slot0_partition),
21+
FIXED_PARTITION_ID(slot1_partition),
22+
};
23+
24+
bool
25+
flash_map_id_get_next(uint8_t *id, bool reset)
26+
{
27+
if (reset) {
28+
curr_idx = 0;
29+
#if DT_NODE_HAS_STATUS(SW1_NODE, okay)
30+
if (gpio_pin_configure_dt(&sw1_spec, GPIO_INPUT) == 0) {
31+
if (gpio_pin_get_dt(&sw1_spec) == 1) {
32+
curr_idx = sys_rand8_get() % ARRAY_SIZE(known_ids);
33+
printk("Booting from curr_idx = %d\n", curr_idx);
34+
}
35+
}
36+
#endif
37+
} else {
38+
curr_idx++;
39+
}
40+
41+
if (curr_idx >= ARRAY_SIZE(known_ids)) {
42+
return false;
43+
}
44+
45+
*id = known_ids[curr_idx];
46+
47+
return true;
48+
}
49+
50+
bool
51+
flash_map_id_get_current(uint8_t *id)
52+
{
53+
if (curr_idx == -1 || curr_idx >= ARRAY_SIZE(known_ids)) {
54+
return false;
55+
}
56+
57+
*id = known_ids[curr_idx];
58+
59+
return true;
60+
}

0 commit comments

Comments
 (0)