Skip to content

Commit 215345f

Browse files
committed
zephyr: Add firmware loader MCUboot operation style
Adds a new operation style in which the secondary slot has an image which is used to update the primary image only. Signed-off-by: Jamie McCrae <[email protected]>
1 parent 433b848 commit 215345f

File tree

11 files changed

+294
-10
lines changed

11 files changed

+294
-10
lines changed

boot/bootutil/include/bootutil/boot_status.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ enum mcuboot_mode {
122122
MCUBOOT_MODE_SWAP_USING_MOVE,
123123
MCUBOOT_MODE_DIRECT_XIP,
124124
MCUBOOT_MODE_DIRECT_XIP_WITH_REVERT,
125-
MCUBOOT_MODE_RAM_LOAD
125+
MCUBOOT_MODE_RAM_LOAD,
126+
MCUBOOT_MODE_FIRMWARE_LOADER
126127
};
127128

128129
enum mcuboot_signature_type {

boot/bootutil/src/boot_record.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ int boot_save_shared_data(const struct image_header *hdr, const struct flash_are
250250
#endif
251251
#elif defined(MCUBOOT_RAM_LOAD)
252252
uint8_t mode = MCUBOOT_MODE_RAM_LOAD;
253+
#elif defined(MCUBOOT_FIRMWARE_LOADER)
254+
uint8_t mode = MCUBOOT_MODE_FIRMWARE_LOADER;
253255
#else
254256
#error "Unknown mcuboot operating mode"
255257
#endif

boot/bootutil/src/bootutil_misc.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,8 @@ boot_write_enc_key(const struct flash_area *fap, uint8_t slot,
333333

334334
uint32_t bootutil_max_image_size(const struct flash_area *fap)
335335
{
336-
#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SINGLE_APPLICATION_SLOT)
336+
#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SINGLE_APPLICATION_SLOT) || \
337+
defined(MCUBOOT_FIRMWARE_LOADER)
337338
return boot_status_off(fap);
338339
#elif defined(MCUBOOT_SWAP_USING_MOVE)
339340
struct flash_sector sector;

boot/bootutil/src/bootutil_priv.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,17 @@ struct flash_area;
5757
#if (defined(MCUBOOT_OVERWRITE_ONLY) + \
5858
defined(MCUBOOT_SWAP_USING_MOVE) + \
5959
defined(MCUBOOT_DIRECT_XIP) + \
60-
defined(MCUBOOT_RAM_LOAD)) > 1
61-
#error "Please enable only one of MCUBOOT_OVERWRITE_ONLY, MCUBOOT_SWAP_USING_MOVE, MCUBOOT_DIRECT_XIP or MCUBOOT_RAM_LOAD"
60+
defined(MCUBOOT_RAM_LOAD) + \
61+
defined(MCUBOOT_FIRMWARE_LOADER)) > 1
62+
#error "Please enable only one of MCUBOOT_OVERWRITE_ONLY, MCUBOOT_SWAP_USING_MOVE, MCUBOOT_DIRECT_XIP, MCUBOOT_RAM_LOAD or MCUBOOT_FIRMWARE_LOADER"
6263
#endif
6364

6465
#if !defined(MCUBOOT_OVERWRITE_ONLY) && \
6566
!defined(MCUBOOT_SWAP_USING_MOVE) && \
6667
!defined(MCUBOOT_DIRECT_XIP) && \
6768
!defined(MCUBOOT_RAM_LOAD) && \
68-
!defined(MCUBOOT_SINGLE_APPLICATION_SLOT)
69+
!defined(MCUBOOT_SINGLE_APPLICATION_SLOT) && \
70+
!defined(MCUBOOT_FIRMWARE_LOADER)
6971
#define MCUBOOT_SWAP_USING_SCRATCH 1
7072
#endif
7173

boot/zephyr/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@ zephyr_library_sources(
131131
${BOOT_DIR}/zephyr/single_loader.c
132132
)
133133
zephyr_library_include_directories(${BOOT_DIR}/bootutil/src)
134+
elseif(CONFIG_BOOT_FIRMWARE_LOADER)
135+
zephyr_library_sources(
136+
${BOOT_DIR}/zephyr/firmware_loader.c
137+
)
138+
zephyr_library_include_directories(${BOOT_DIR}/bootutil/src)
134139
else()
135140
zephyr_library_sources(
136141
${BOOT_DIR}/bootutil/src/loader.c

boot/zephyr/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,18 @@ config BOOT_RAM_LOAD
256256
The address that the image is copied to is specified using the load-addr
257257
argument to the imgtool.py script which writes it to the image header.
258258

259+
config BOOT_FIRMWARE_LOADER
260+
bool "Firmware loader"
261+
help
262+
If y, mcuboot will have a single application slot, and the secondary
263+
slot will be for a non-upgradeable firmware loaded image (e.g. for
264+
loading firmware via Bluetooth). The main application will boot by
265+
default unless there is an error with it or the boot mode has been
266+
forced to the firmware loader.
267+
268+
Note: The firmware loader image must be signed with the same signing
269+
key as the primary image.
270+
259271
endchoice
260272

261273
# Workaround for not being able to have commas in macro arguments
@@ -582,6 +594,8 @@ config MCUBOOT_INDICATION_LED
582594

583595
rsource "Kconfig.serial_recovery"
584596

597+
rsource "Kconfig.firmware_loader"
598+
585599
config BOOT_INTR_VEC_RELOC
586600
bool "Relocate the interrupt vector to the application"
587601
default n

boot/zephyr/Kconfig.firmware_loader

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Copyright (c) 2023 Nordic Semiconductor ASA
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
if BOOT_FIRMWARE_LOADER
6+
7+
menu "Firmware loader entrance methods"
8+
9+
menuconfig BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO
10+
bool "GPIO"
11+
depends on GPIO
12+
help
13+
Use a GPIO to enter firmware loader mode.
14+
15+
config BOOT_FIRMWARE_LOADER_DETECT_DELAY
16+
int "Serial detect pin detection delay time [ms]"
17+
default 0
18+
depends on BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO
19+
help
20+
Used to prevent the bootloader from loading on button press.
21+
Useful for powering on when using the same button as
22+
the one used to place the device in bootloader mode.
23+
24+
config BOOT_FIRMWARE_LOADER_BOOT_MODE
25+
bool "Check boot mode via retention subsystem"
26+
depends on RETENTION_BOOT_MODE
27+
help
28+
Allows for entering firmware loader mode by using Zephyr's boot mode
29+
retention system (i.e. an application must set the boot mode to stay
30+
in firmware loader mode and reboot the module).
31+
32+
config BOOT_FIRMWARE_LOADER_NO_APPLICATION
33+
bool "Stay in bootloader if no application"
34+
help
35+
Allows for entering firmware loader mode if there is no bootable
36+
application that the bootloader can jump to.
37+
38+
config BOOT_FIRMWARE_LOADER_PIN_RESET
39+
bool "Check for device reset by pin"
40+
select HWINFO
41+
help
42+
Checks if the module reset was caused by the reset pin and will
43+
remain in bootloader firmware loader mode if it was.
44+
45+
endmenu
46+
47+
endif

boot/zephyr/Kconfig.serial_recovery

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ menuconfig MCUBOOT_SERIAL
1313
select BASE64
1414
select CRC
1515
select ZCBOR
16+
depends on !BOOT_FIRMWARE_LOADER
1617
help
1718
If y, enables a serial-port based update mode. This allows
1819
MCUboot itself to load update images into flash over a UART.

boot/zephyr/firmware_loader.c

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* Copyright (c) 2020 Arm Limited
5+
* Copyright (c) 2020-2023 Nordic Semiconductor ASA
6+
*/
7+
8+
#include <assert.h>
9+
#include <zephyr/kernel.h>
10+
#include <zephyr/devicetree.h>
11+
#include <zephyr/drivers/gpio.h>
12+
#include "bootutil/image.h"
13+
#include "bootutil_priv.h"
14+
#include "bootutil/bootutil_log.h"
15+
#include "bootutil/bootutil_public.h"
16+
#include "bootutil/fault_injection_hardening.h"
17+
18+
#include "io/io.h"
19+
#include "mcuboot_config/mcuboot_config.h"
20+
21+
BOOT_LOG_MODULE_DECLARE(mcuboot);
22+
23+
/* Variables passed outside of unit via poiters. */
24+
static const struct flash_area *_fa_p;
25+
static struct image_header _hdr = { 0 };
26+
27+
#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) || defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE)
28+
/**
29+
* Validate hash of a primary boot image.
30+
*
31+
* @param[in] fa_p flash area pointer
32+
* @param[in] hdr boot image header pointer
33+
*
34+
* @return FIH_SUCCESS on success, error code otherwise
35+
*/
36+
fih_ret
37+
boot_image_validate(const struct flash_area *fa_p,
38+
struct image_header *hdr)
39+
{
40+
static uint8_t tmpbuf[BOOT_TMPBUF_SZ];
41+
FIH_DECLARE(fih_rc, FIH_FAILURE);
42+
43+
/* NOTE: The first argument to boot_image_validate, for enc_state pointer,
44+
* is allowed to be NULL only because the single image loader compiles
45+
* with BOOT_IMAGE_NUMBER == 1, which excludes the code that uses
46+
* the pointer from compilation.
47+
*/
48+
/* Validate hash */
49+
if (IS_ENCRYPTED(hdr))
50+
{
51+
/* Clear the encrypted flag we didn't supply a key
52+
* This flag could be set if there was a decryption in place
53+
* was performed. We will try to validate the image, and if still
54+
* encrypted the validation will fail, and go in panic mode
55+
*/
56+
hdr->ih_flags &= ~(ENCRYPTIONFLAGS);
57+
}
58+
FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, hdr, fa_p, tmpbuf,
59+
BOOT_TMPBUF_SZ, NULL, 0, NULL);
60+
61+
FIH_RET(fih_rc);
62+
}
63+
#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT || MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE*/
64+
65+
inline static fih_ret
66+
boot_image_validate_once(const struct flash_area *fa_p,
67+
struct image_header *hdr)
68+
{
69+
static struct boot_swap_state state;
70+
int rc;
71+
FIH_DECLARE(fih_rc, FIH_FAILURE);
72+
73+
memset(&state, 0, sizeof(struct boot_swap_state));
74+
rc = boot_read_swap_state(fa_p, &state);
75+
if (rc != 0)
76+
FIH_RET(FIH_FAILURE);
77+
if (state.magic != BOOT_MAGIC_GOOD
78+
|| state.image_ok != BOOT_FLAG_SET) {
79+
/* At least validate the image once */
80+
FIH_CALL(boot_image_validate, fih_rc, fa_p, hdr);
81+
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
82+
FIH_RET(FIH_FAILURE);
83+
}
84+
if (state.magic != BOOT_MAGIC_GOOD) {
85+
rc = boot_write_magic(fa_p);
86+
if (rc != 0)
87+
FIH_RET(FIH_FAILURE);
88+
}
89+
rc = boot_write_image_ok(fa_p);
90+
if (rc != 0)
91+
FIH_RET(FIH_FAILURE);
92+
}
93+
FIH_RET(FIH_SUCCESS);
94+
}
95+
96+
/**
97+
* Validates that an image in a slot is OK to boot.
98+
*
99+
* @param[in] slot Slot number to check
100+
* @param[out] rsp Parameters for booting image, on success
101+
*
102+
* @return FIH_SUCCESS on success; non-zero on failure.
103+
*/
104+
static fih_ret validate_image_slot(int slot, struct boot_rsp *rsp)
105+
{
106+
int rc = -1;
107+
FIH_DECLARE(fih_rc, FIH_FAILURE);
108+
109+
rc = flash_area_open(slot, &_fa_p);
110+
assert(rc == 0);
111+
112+
rc = boot_image_load_header(_fa_p, &_hdr);
113+
if (rc != 0) {
114+
goto other;
115+
}
116+
117+
#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT
118+
FIH_CALL(boot_image_validate, fih_rc, _fa_p, &_hdr);
119+
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
120+
goto other;
121+
}
122+
#elif defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE)
123+
FIH_CALL(boot_image_validate_once, fih_rc, _fa_p, &_hdr);
124+
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
125+
goto other;
126+
}
127+
#else
128+
fih_rc = FIH_SUCCESS;
129+
#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */
130+
131+
rsp->br_flash_dev_id = flash_area_get_device_id(_fa_p);
132+
rsp->br_image_off = flash_area_get_off(_fa_p);
133+
rsp->br_hdr = &_hdr;
134+
135+
other:
136+
flash_area_close(_fa_p);
137+
138+
FIH_RET(fih_rc);
139+
}
140+
141+
/**
142+
* Gather information on image and prepare for booting. Will boot from main
143+
* image if none of the enabled entrance modes for the firmware loader are set,
144+
* otherwise will boot the firmware loader. Note: firmware loader must be a
145+
* valid signed image with the same signing key as the application image.
146+
*
147+
* @param[out] rsp Parameters for booting image, on success
148+
*
149+
* @return FIH_SUCCESS on success; non-zero on failure.
150+
*/
151+
fih_ret
152+
boot_go(struct boot_rsp *rsp)
153+
{
154+
bool boot_firmware_loader = false;
155+
FIH_DECLARE(fih_rc, FIH_FAILURE);
156+
157+
#ifdef CONFIG_BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO
158+
if (io_detect_pin() &&
159+
!io_boot_skip_serial_recovery()) {
160+
boot_firmware_loader = true;
161+
}
162+
#endif
163+
164+
#ifdef CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET
165+
if (io_detect_pin_reset()) {
166+
boot_firmware_loader = true;
167+
}
168+
#endif
169+
170+
#ifdef CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE
171+
if (io_detect_boot_mode()) {
172+
boot_firmware_loader = true;
173+
}
174+
#endif
175+
176+
/* Check if firmware loader button is pressed. TODO: check all entrance methods */
177+
if (boot_firmware_loader == true) {
178+
FIH_CALL(validate_image_slot, fih_rc, FLASH_AREA_IMAGE_SECONDARY(0), rsp);
179+
180+
if (FIH_EQ(fih_rc, FIH_SUCCESS)) {
181+
FIH_RET(fih_rc);
182+
}
183+
}
184+
185+
FIH_CALL(validate_image_slot, fih_rc, FLASH_AREA_IMAGE_PRIMARY(0), rsp);
186+
187+
#ifdef CONFIG_BOOT_FIRMWARE_LOADER_NO_APPLICATION
188+
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
189+
FIH_CALL(validate_image_slot, fih_rc, FLASH_AREA_IMAGE_SECONDARY(0), rsp);
190+
}
191+
#endif
192+
193+
FIH_RET(fih_rc);
194+
}

boot/zephyr/include/mcuboot_config/mcuboot_config.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@
8888
#define IMAGE_EXECUTABLE_RAM_SIZE CONFIG_BOOT_IMAGE_EXECUTABLE_RAM_SIZE
8989
#endif
9090

91+
#ifdef CONFIG_BOOT_FIRMWARE_LOADER
92+
#define MCUBOOT_FIRMWARE_LOADER
93+
#endif
94+
9195
#ifdef CONFIG_UPDATEABLE_IMAGE_NUMBER
9296
#define MCUBOOT_IMAGE_NUMBER CONFIG_UPDATEABLE_IMAGE_NUMBER
9397
#else

0 commit comments

Comments
 (0)