Skip to content

Commit 7d603f8

Browse files
SebastianBoeanangl
authored andcommitted
[nrf noup] treewide: add NCS partition manager support
Partition Manager is an nRF Connect SDK component which uses yaml files to resolve flash partition placement with a holistic view of the device. This component's MCUboot portions began life as upstream mcuboot PR#430. This added support for being built as a sub image from the downstream Nordic patch set for a zephyr multi image build system (mcuboot 430 was combined with effor submitted to upstream zephyr as PR#13672, which was ultimately reworked after being rejected for mainline at the ELCE 2019 conference in Lyon). It has since evolved over time. This is the version that will go into NCS v1.3. It features: - page size aligned partitions for all partitions used by mcuboot. - image swaps without scratch partitions Add support for configurations where there exists two primary slots but only one secondary slot, which is shared. These two primary slots are the regular application and B1. B1 can be either S0 or S1 depending on the state of the device. Decide where an upgrade should be stored by looking at the vector table. Provide update candidates for both s0 and s1. These candidates must be signed with mcuboot after being signed by b0. Additional notes: - we make update.hex without trailer data This is needed for serial recovery to work using hex files. Prior to this the update.hex got TLV data at the end of the partition, which caused many blank pages to be included, which made it hard to use in a serial recovery scheme. Instead, make update.hex without TLV data at the end, and provide a new file test_update.hex which contains the TLV data, and can be directly flashed to test the upgrade procedure. - we use a function for signing the application as future-proofing for when other components must be signed as well - this includes an update to single image applications that enables support for partition manager; when single image DFU is used, a scratch partition is not needed. - In NCS, image 1 primary slot is the upgrade bank for mcuboot (IE S0 or S1 depending on the active slot). It is not required that this slot contains any valid data. - The nRF boards all have a single flash page size, and partition manager deals with the size of the update partitions and so on, so we must skip a boot_slots_compatible() check to avoid getting an error. - There is no need to verify the target when using partition manager. - We lock mcuboot using fprotect before jumping, to enable the secure boot property of the system. - Call fw_info_ext_api_provide() before booting if EXT_API_PROVIDE EXT_API is enabled. This is relevant only when the immutable bootloader has booted mcuboot. Signed-off-by: Håkon Øye Amundsen <haakon.amundsen@nordicsemi.no> Signed-off-by: Øyvind Rønningstad <oyvind.ronningstad@nordicsemi.no> Signed-off-by: Sebastian Bøe <sebastian.boe@nordicsemi.no> Signed-off-by: Sigvart Hovland <sigvart.m@gmail.com> Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no> Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no> Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no> Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no> Signed-off-by: Andrzej Puzdrowski <andrzej.puzdrowski@nordicsemi.no> Signed-off-by: Emil Obalski <emil.obalski@nordicsemi.no> Signed-off-by: Pawel Dunaj <pawel.dunaj@nordicsemi.no> Signed-off-by: Ioannis Glaropoulos <Ioannis.Glaropoulos@nordicsemi.no> Signed-off-by: Johann Fischer <johann.fischer@nordicsemi.no> Signed-off-by: Vidar Berg <vidar.berg@nordicsemi.no> Signed-off-by: Draus, Sebastian <sebastian.draus@nordicsemi.no> Signed-off-by: Trond Einar Snekvik <Trond.Einar.Snekvik@nordicsemi.no> Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no> Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no> Signed-off-by: Georgios Vasilakis <georgios.vasilakis@nordicsemi.no> Signed-off-by: Dominik Ermel <dominik.ermel@nordicsemi.no> (cherry picked from commit 6a5dc04)
1 parent 7a4b0a8 commit 7d603f8

File tree

12 files changed

+299
-11
lines changed

12 files changed

+299
-11
lines changed

boot/bootutil/src/loader.c

+86-9
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,15 @@ boot_read_image_headers(struct boot_loader_state *state, bool require_all,
131131
*
132132
* Failure to read any headers is a fatal error.
133133
*/
134+
#ifdef PM_S1_ADDRESS
135+
/* Patch needed for NCS. The primary slot of the second image
136+
* (image 1) will not contain a valid image header until an upgrade
137+
* of mcuboot has happened (filling S1 with the new version).
138+
*/
139+
if (BOOT_CURR_IMG(state) == 1 && i == 0) {
140+
continue;
141+
}
142+
#endif /* PM_S1_ADDRESS */
134143
if (i > 0 && !require_all) {
135144
return 0;
136145
} else {
@@ -1071,7 +1080,24 @@ boot_validate_slot(struct boot_loader_state *state, int slot,
10711080
goto out;
10721081
}
10731082

1074-
if (reset_value < pri_fa->fa_off || reset_value> (pri_fa->fa_off + pri_fa->fa_size)) {
1083+
uint32_t min_addr, max_addr;
1084+
1085+
#ifdef PM_CPUNET_APP_ADDRESS
1086+
/* The primary slot for the network core is emulated in RAM.
1087+
* Its flash_area hasn't got relevant boundaries.
1088+
* Therfore need to override its boundaries for the check.
1089+
*/
1090+
if (BOOT_CURR_IMG(state) == 1) {
1091+
min_addr = PM_CPUNET_APP_ADDRESS;
1092+
max_addr = PM_CPUNET_APP_ADDRESS + PM_CPUNET_APP_SIZE;
1093+
} else
1094+
#endif
1095+
{
1096+
min_addr = pri_fa->fa_off;
1097+
max_addr = pri_fa->fa_off + pri_fa->fa_size;
1098+
}
1099+
1100+
if (reset_value < min_addr || reset_value> (max_addr)) {
10751101
BOOT_LOG_ERR("Reset address of image in secondary slot is not in the primary slot");
10761102
BOOT_LOG_ERR("Erasing image from secondary slot");
10771103

@@ -1154,6 +1180,42 @@ boot_validated_swap_type(struct boot_loader_state *state,
11541180
{
11551181
int swap_type;
11561182
FIH_DECLARE(fih_rc, FIH_FAILURE);
1183+
#ifdef PM_S1_ADDRESS
1184+
/* Patch needed for NCS. Since image 0 (the app) and image 1 (the other
1185+
* B1 slot S0 or S1) share the same secondary slot, we need to check
1186+
* whether the update candidate in the secondary slot is intended for
1187+
* image 0 or image 1 primary by looking at the address of the reset
1188+
* vector. Note that there are good reasons for not using img_num from
1189+
* the swap info.
1190+
*/
1191+
const struct flash_area *secondary_fa =
1192+
BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT);
1193+
struct image_header *hdr =
1194+
(struct image_header *)secondary_fa->fa_off;
1195+
1196+
if (hdr->ih_magic == IMAGE_MAGIC) {
1197+
const struct flash_area *primary_fa;
1198+
uint32_t vtable_addr = (uint32_t)hdr + hdr->ih_hdr_size;
1199+
uint32_t *vtable = (uint32_t *)(vtable_addr);
1200+
uint32_t reset_addr = vtable[1];
1201+
int rc = flash_area_open(
1202+
flash_area_id_from_multi_image_slot(
1203+
BOOT_CURR_IMG(state),
1204+
BOOT_PRIMARY_SLOT),
1205+
&primary_fa);
1206+
1207+
if (rc != 0) {
1208+
return BOOT_SWAP_TYPE_FAIL;
1209+
}
1210+
/* Get start and end of primary slot for current image */
1211+
if (reset_addr < primary_fa->fa_off ||
1212+
reset_addr > (primary_fa->fa_off + primary_fa->fa_size)) {
1213+
/* The image in the secondary slot is not intended for this image
1214+
*/
1215+
return BOOT_SWAP_TYPE_NONE;
1216+
}
1217+
}
1218+
#endif
11571219

11581220
swap_type = boot_swap_type_multi(BOOT_CURR_IMG(state));
11591221
if (BOOT_IS_UPGRADE(swap_type)) {
@@ -2374,15 +2436,25 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
23742436
}
23752437

23762438
#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT
2377-
FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, NULL);
2378-
/* Check for all possible values is redundant in normal operation it
2379-
* is meant to prevent FI attack.
2439+
#ifdef PM_S1_ADDRESS
2440+
/* Patch needed for NCS. Image 1 primary is the currently
2441+
* executing MCUBoot image, and is therefore already validated by NSIB and
2442+
* does not need to also be validated by MCUBoot.
23802443
*/
2381-
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS) ||
2382-
FIH_EQ(fih_rc, FIH_FAILURE) ||
2383-
FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) {
2384-
FIH_SET(fih_rc, FIH_FAILURE);
2385-
goto out;
2444+
bool image_validated_by_nsib = BOOT_CURR_IMG(state) == 1;
2445+
if (!image_validated_by_nsib)
2446+
#endif
2447+
{
2448+
FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, NULL);
2449+
/* Check for all possible values is redundant in normal operation it
2450+
* is meant to prevent FI attack.
2451+
*/
2452+
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS) ||
2453+
FIH_EQ(fih_rc, FIH_FAILURE) ||
2454+
FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) {
2455+
FIH_SET(fih_rc, FIH_FAILURE);
2456+
goto out;
2457+
}
23862458
}
23872459
#else
23882460
/* Even if we're not re-validating the primary slot, we could be booting
@@ -2399,11 +2471,16 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
23992471
}
24002472
#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */
24012473

2474+
#ifdef PM_S1_ADDRESS
2475+
if (!image_validated_by_nsib)
2476+
#endif
2477+
{
24022478
rc = boot_update_hw_rollback_protection(state);
24032479
if (rc != 0) {
24042480
FIH_SET(fih_rc, FIH_FAILURE);
24052481
goto out;
24062482
}
2483+
}
24072484

24082485
rc = boot_add_shared_data(state, BOOT_PRIMARY_SLOT);
24092486
if (rc != 0) {

boot/bootutil/src/swap_move.c

+13
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,18 @@ static int app_max_sectors(struct boot_loader_state *state)
259259
int
260260
boot_slots_compatible(struct boot_loader_state *state)
261261
{
262+
#ifdef PM_S1_ADDRESS
263+
/* Patch needed for NCS. In this case, image 1 primary points to the other
264+
* B1 slot (ie S0 or S1), and image 0 primary points to the app.
265+
* With this configuration, image 0 and image 1 share the secondary slot.
266+
* Hence, the primary slot of image 1 will be *smaller* than image 1's
267+
* secondary slot. This is not allowed in upstream mcuboot, so we need
268+
* this patch to allow it. Also, all of these checks are redundant when
269+
* partition manager is in use, and since we have the same sector size
270+
* in all of our flash.
271+
*/
272+
return 1;
273+
#else
262274
size_t num_sectors_pri;
263275
size_t num_sectors_sec;
264276
size_t sector_sz_pri = 0;
@@ -326,6 +338,7 @@ boot_slots_compatible(struct boot_loader_state *state)
326338
}
327339

328340
return 1;
341+
#endif /* PM_S1_ADDRESS */
329342
}
330343

331344
#define BOOT_LOG_SWAP_STATE(area, state) \

boot/bootutil/src/swap_scratch.c

+13
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,18 @@ boot_status_internal_off(const struct boot_status *bs, int elem_sz)
141141
int
142142
boot_slots_compatible(struct boot_loader_state *state)
143143
{
144+
#ifdef PM_S1_ADDRESS
145+
/* Patch needed for NCS. In this case, image 1 primary points to the other
146+
* B1 slot (ie S0 or S1), and image 0 primary points to the app.
147+
* With this configuration, image 0 and image 1 share the secondary slot.
148+
* Hence, the primary slot of image 1 will be *smaller* than image 1's
149+
* secondary slot. This is not allowed in upstream mcuboot, so we need
150+
* this patch to allow it. Also, all of these checks are redundant when
151+
* partition manager is in use, and since we have the same sector size
152+
* in all of our flash.
153+
*/
154+
return 1;
155+
#else
144156
size_t num_sectors_primary;
145157
size_t num_sectors_secondary;
146158
size_t sz0, sz1;
@@ -238,6 +250,7 @@ boot_slots_compatible(struct boot_loader_state *state)
238250
#endif
239251

240252
return 1;
253+
#endif /* PM_S1_ADDRESS */
241254
}
242255

243256
#define BOOT_LOG_SWAP_STATE(area, state) \

boot/zephyr/CMakeLists.txt

+7
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,13 @@ if(NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "")
297297
endif()
298298
message("MCUBoot bootloader key file: ${KEY_FILE}")
299299

300+
set_property(
301+
GLOBAL
302+
PROPERTY
303+
KEY_FILE
304+
${KEY_FILE}
305+
)
306+
300307
set(mcuboot_default_signature_files
301308
${MCUBOOT_DIR}/root-ec-p256-pkcs8.pem
302309
${MCUBOOT_DIR}/root-ec-p384.pem

boot/zephyr/Kconfig

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ mainmenu "MCUboot configuration"
99

1010
comment "MCUboot-specific configuration options"
1111

12+
source "$(ZEPHYR_NRF_MODULE_DIR)/modules/mcuboot/boot/zephyr/Kconfig"
13+
1214
# Hidden option to mark a project as MCUboot
1315
config MCUBOOT
1416
default y

boot/zephyr/include/sysflash/sysflash.h

+48
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,52 @@
77
#ifndef __SYSFLASH_H__
88
#define __SYSFLASH_H__
99

10+
#if USE_PARTITION_MANAGER
11+
#include <pm_config.h>
12+
#include <mcuboot_config/mcuboot_config.h>
13+
14+
#ifndef CONFIG_SINGLE_APPLICATION_SLOT
15+
16+
#if (MCUBOOT_IMAGE_NUMBER == 1)
17+
18+
#define FLASH_AREA_IMAGE_PRIMARY(x) PM_MCUBOOT_PRIMARY_ID
19+
#define FLASH_AREA_IMAGE_SECONDARY(x) PM_MCUBOOT_SECONDARY_ID
20+
21+
#elif (MCUBOOT_IMAGE_NUMBER == 2)
22+
23+
extern uint32_t _image_1_primary_slot_id[];
24+
25+
#define FLASH_AREA_IMAGE_PRIMARY(x) \
26+
((x == 0) ? \
27+
PM_MCUBOOT_PRIMARY_ID : \
28+
(x == 1) ? \
29+
(uint32_t)_image_1_primary_slot_id : \
30+
255 )
31+
32+
#define FLASH_AREA_IMAGE_SECONDARY(x) \
33+
((x == 0) ? \
34+
PM_MCUBOOT_SECONDARY_ID: \
35+
(x == 1) ? \
36+
PM_MCUBOOT_SECONDARY_ID: \
37+
255 )
38+
#endif
39+
#define FLASH_AREA_IMAGE_SCRATCH PM_MCUBOOT_SCRATCH_ID
40+
41+
#else /* CONFIG_SINGLE_APPLICATION_SLOT */
42+
43+
#define FLASH_AREA_IMAGE_PRIMARY(x) PM_MCUBOOT_PRIMARY_ID
44+
#define FLASH_AREA_IMAGE_SECONDARY(x) PM_MCUBOOT_PRIMARY_ID
45+
/* NOTE: Scratch parition is not used by single image DFU but some of
46+
* functions in common files reference it, so the definitions has been
47+
* provided to allow compilation of common units.
48+
*/
49+
#define FLASH_AREA_IMAGE_SCRATCH 0
50+
51+
#endif /* CONFIG_SINGLE_APPLICATION_SLOT */
52+
53+
#else
54+
55+
#include <zephyr/devicetree.h>
1056
#include <mcuboot_config/mcuboot_config.h>
1157
#include <zephyr/devicetree.h>
1258
#include <zephyr/storage/flash_map.h>
@@ -65,4 +111,6 @@ static inline uint32_t __flash_area_ids_for_slot(int img, int slot)
65111

66112
#endif /* CONFIG_SINGLE_APPLICATION_SLOT */
67113

114+
#endif /* USE_PARTITION_MANAGER */
115+
68116
#endif /* __SYSFLASH_H__ */

boot/zephyr/include/target.h

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#ifndef H_TARGETS_TARGET_
99
#define H_TARGETS_TARGET_
1010

11+
#ifndef USE_PARTITION_MANAGER
12+
1113
#if defined(MCUBOOT_TARGET_CONFIG)
1214
/*
1315
* Target-specific definitions are permitted in legacy cases that
@@ -45,4 +47,6 @@
4547
#error "Target support is incomplete; cannot build mcuboot."
4648
#endif
4749

50+
#endif /* ifndef USE_PARTITION_MANAGER */
51+
4852
#endif /* H_TARGETS_TARGET_ */

boot/zephyr/main.c

+45
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@
6969

7070
#endif /* CONFIG_SOC_FAMILY_ESPRESSIF_ESP32 */
7171

72+
#ifdef CONFIG_FW_INFO
73+
#include <fw_info.h>
74+
#endif
75+
7276
#ifdef CONFIG_MCUBOOT_SERIAL
7377
#include "boot_serial/boot_serial.h"
7478
#include "serial_adapter/serial_adapter.h"
@@ -129,6 +133,11 @@ K_SEM_DEFINE(boot_log_sem, 1, 1);
129133
* !defined(ZEPHYR_LOG_MODE_MINIMAL)
130134
*/
131135

136+
#if USE_PARTITION_MANAGER && CONFIG_FPROTECT
137+
#include <fprotect.h>
138+
#include <pm_config.h>
139+
#endif
140+
132141
BOOT_LOG_MODULE_REGISTER(mcuboot);
133142

134143
void os_heap_init(void);
@@ -187,6 +196,19 @@ static void do_boot(struct boot_rsp *rsp)
187196
/* Disable the USB to prevent it from firing interrupts */
188197
usb_disable();
189198
#endif
199+
200+
#if defined(CONFIG_FW_INFO) && !defined(CONFIG_EXT_API_PROVIDE_EXT_API_UNUSED)
201+
bool provided = fw_info_ext_api_provide(fw_info_find((uint32_t)vt), true);
202+
203+
#ifdef PM_S0_ADDRESS
204+
/* Only fail if the immutable bootloader is present. */
205+
if (!provided) {
206+
BOOT_LOG_ERR("Failed to provide EXT_APIs\n");
207+
return;
208+
}
209+
#endif
210+
#endif
211+
190212
#if CONFIG_MCUBOOT_CLEANUP_ARM_CORE
191213
cleanup_arm_nvic(); /* cleanup NVIC registers */
192214

@@ -592,7 +614,30 @@ int main(void)
592614

593615
mcuboot_status_change(MCUBOOT_STATUS_BOOTABLE_IMAGE_FOUND);
594616

617+
#if USE_PARTITION_MANAGER && CONFIG_FPROTECT
618+
619+
#ifdef PM_S1_ADDRESS
620+
/* MCUBoot is stored in either S0 or S1, protect both */
621+
#define PROTECT_SIZE (PM_MCUBOOT_PRIMARY_ADDRESS - PM_S0_ADDRESS)
622+
#define PROTECT_ADDR PM_S0_ADDRESS
623+
#else
624+
/* There is only one instance of MCUBoot */
625+
#define PROTECT_SIZE (PM_MCUBOOT_PRIMARY_ADDRESS - PM_MCUBOOT_ADDRESS)
626+
#define PROTECT_ADDR PM_MCUBOOT_ADDRESS
627+
#endif
628+
629+
rc = fprotect_area(PROTECT_ADDR, PROTECT_SIZE);
630+
631+
if (rc != 0) {
632+
BOOT_LOG_ERR("Protect mcuboot flash failed, cancel startup.");
633+
while (1)
634+
;
635+
}
636+
637+
#endif /* USE_PARTITION_MANAGER && CONFIG_FPROTECT */
638+
595639
ZEPHYR_BOOT_LOG_STOP();
640+
596641
do_boot(&rsp);
597642

598643
mcuboot_status_change(MCUBOOT_STATUS_BOOT_FAILED);

0 commit comments

Comments
 (0)