Skip to content

Commit 349361e

Browse files
SebastianBoede-nordic
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 <[email protected]> Signed-off-by: Øyvind Rønningstad <[email protected]> Signed-off-by: Sebastian Bøe <[email protected]> Signed-off-by: Sigvart Hovland <[email protected]> Signed-off-by: Martí Bolívar <[email protected]> Signed-off-by: Torsten Rasmussen <[email protected]> Signed-off-by: Andrzej Głąbek <[email protected]> Signed-off-by: Robert Lubos <[email protected]> Signed-off-by: Andrzej Puzdrowski <[email protected]> Signed-off-by: Dominik Ermel <[email protected]> Signed-off-by: Emil Obalski <[email protected]> Signed-off-by: Torsten Rasmussen <[email protected]> Signed-off-by: Pawel Dunaj <[email protected]> Signed-off-by: Ioannis Glaropoulos <[email protected]> Signed-off-by: Johann Fischer <[email protected]> Signed-off-by: Vidar Berg <[email protected]> Signed-off-by: Draus, Sebastian <[email protected]> Signed-off-by: Trond Einar Snekvik <[email protected]> Signed-off-by: Jamie McCrae <[email protected]> Signed-off-by: Joakim Andersson <[email protected]> Signed-off-by: Georgios Vasilakis <[email protected]> (cherry picked from commit ed5f069) (cherry picked from commit d2cac70) (cherry picked from commit 1630628)
1 parent e81afaa commit 349361e

File tree

12 files changed

+296
-11
lines changed

12 files changed

+296
-11
lines changed

boot/bootutil/src/loader.c

Lines changed: 86 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,15 @@ boot_read_image_headers(struct boot_loader_state *state, bool require_all,
108108
*
109109
* Failure to read any headers is a fatal error.
110110
*/
111+
#ifdef PM_S1_ADDRESS
112+
/* Patch needed for NCS. The primary slot of the second image
113+
* (image 1) will not contain a valid image header until an upgrade
114+
* of mcuboot has happened (filling S1 with the new version).
115+
*/
116+
if (BOOT_CURR_IMG(state) == 1 && i == 0) {
117+
continue;
118+
}
119+
#endif /* PM_S1_ADDRESS */
111120
if (i > 0 && !require_all) {
112121
return 0;
113122
} else {
@@ -815,7 +824,24 @@ boot_validate_slot(struct boot_loader_state *state, int slot,
815824
goto out;
816825
}
817826

818-
if (reset_value < pri_fa->fa_off || reset_value> (pri_fa->fa_off + pri_fa->fa_size)) {
827+
uint32_t min_addr, max_addr;
828+
829+
#ifdef PM_CPUNET_APP_ADDRESS
830+
/* The primary slot for the network core is emulated in RAM.
831+
* Its flash_area hasn't got relevant boundaries.
832+
* Therfore need to override its boundaries for the check.
833+
*/
834+
if (BOOT_CURR_IMG(state) == 1) {
835+
min_addr = PM_CPUNET_APP_ADDRESS;
836+
max_addr = PM_CPUNET_APP_ADDRESS + PM_CPUNET_APP_SIZE;
837+
} else
838+
#endif
839+
{
840+
min_addr = pri_fa->fa_off;
841+
max_addr = pri_fa->fa_off + pri_fa->fa_size;
842+
}
843+
844+
if (reset_value < min_addr || reset_value> (max_addr)) {
819845
BOOT_LOG_ERR("Reset address of image in secondary slot is not in the primary slot");
820846
BOOT_LOG_ERR("Erasing image from secondary slot");
821847

@@ -898,6 +924,42 @@ boot_validated_swap_type(struct boot_loader_state *state,
898924
{
899925
int swap_type;
900926
FIH_DECLARE(fih_rc, FIH_FAILURE);
927+
#ifdef PM_S1_ADDRESS
928+
/* Patch needed for NCS. Since image 0 (the app) and image 1 (the other
929+
* B1 slot S0 or S1) share the same secondary slot, we need to check
930+
* whether the update candidate in the secondary slot is intended for
931+
* image 0 or image 1 primary by looking at the address of the reset
932+
* vector. Note that there are good reasons for not using img_num from
933+
* the swap info.
934+
*/
935+
const struct flash_area *secondary_fa =
936+
BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT);
937+
struct image_header *hdr =
938+
(struct image_header *)secondary_fa->fa_off;
939+
940+
if (hdr->ih_magic == IMAGE_MAGIC) {
941+
const struct flash_area *primary_fa;
942+
uint32_t vtable_addr = (uint32_t)hdr + hdr->ih_hdr_size;
943+
uint32_t *vtable = (uint32_t *)(vtable_addr);
944+
uint32_t reset_addr = vtable[1];
945+
int rc = flash_area_open(
946+
flash_area_id_from_multi_image_slot(
947+
BOOT_CURR_IMG(state),
948+
BOOT_PRIMARY_SLOT),
949+
&primary_fa);
950+
951+
if (rc != 0) {
952+
return BOOT_SWAP_TYPE_FAIL;
953+
}
954+
/* Get start and end of primary slot for current image */
955+
if (reset_addr < primary_fa->fa_off ||
956+
reset_addr > (primary_fa->fa_off + primary_fa->fa_size)) {
957+
/* The image in the secondary slot is not intended for this image
958+
*/
959+
return BOOT_SWAP_TYPE_NONE;
960+
}
961+
}
962+
#endif
901963

902964
swap_type = boot_swap_type_multi(BOOT_CURR_IMG(state));
903965
if (BOOT_IS_UPGRADE(swap_type)) {
@@ -2206,15 +2268,25 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
22062268
}
22072269

22082270
#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT
2209-
FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, NULL);
2210-
/* Check for all possible values is redundant in normal operation it
2211-
* is meant to prevent FI attack.
2271+
#ifdef PM_S1_ADDRESS
2272+
/* Patch needed for NCS. Image 1 primary is the currently
2273+
* executing MCUBoot image, and is therefore already validated by NSIB and
2274+
* does not need to also be validated by MCUBoot.
22122275
*/
2213-
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS) ||
2214-
FIH_EQ(fih_rc, FIH_FAILURE) ||
2215-
FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) {
2216-
FIH_SET(fih_rc, FIH_FAILURE);
2217-
goto out;
2276+
bool image_validated_by_nsib = BOOT_CURR_IMG(state) == 1;
2277+
if (!image_validated_by_nsib)
2278+
#endif
2279+
{
2280+
FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, NULL);
2281+
/* Check for all possible values is redundant in normal operation it
2282+
* is meant to prevent FI attack.
2283+
*/
2284+
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS) ||
2285+
FIH_EQ(fih_rc, FIH_FAILURE) ||
2286+
FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) {
2287+
FIH_SET(fih_rc, FIH_FAILURE);
2288+
goto out;
2289+
}
22182290
}
22192291
#else
22202292
/* Even if we're not re-validating the primary slot, we could be booting
@@ -2231,11 +2303,16 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
22312303
}
22322304
#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */
22332305

2306+
#ifdef PM_S1_ADDRESS
2307+
if (!image_validated_by_nsib)
2308+
#endif
2309+
{
22342310
rc = boot_update_hw_rollback_protection(state);
22352311
if (rc != 0) {
22362312
FIH_SET(fih_rc, FIH_FAILURE);
22372313
goto out;
22382314
}
2315+
}
22392316

22402317
rc = boot_add_shared_data(state, BOOT_PRIMARY_SLOT);
22412318
if (rc != 0) {

boot/bootutil/src/swap_move.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,18 @@ boot_status_internal_off(const struct boot_status *bs, int elem_sz)
237237
int
238238
boot_slots_compatible(struct boot_loader_state *state)
239239
{
240+
#ifdef PM_S1_ADDRESS
241+
/* Patch needed for NCS. In this case, image 1 primary points to the other
242+
* B1 slot (ie S0 or S1), and image 0 primary points to the app.
243+
* With this configuration, image 0 and image 1 share the secondary slot.
244+
* Hence, the primary slot of image 1 will be *smaller* than image 1's
245+
* secondary slot. This is not allowed in upstream mcuboot, so we need
246+
* this patch to allow it. Also, all of these checks are redundant when
247+
* partition manager is in use, and since we have the same sector size
248+
* in all of our flash.
249+
*/
250+
return 1;
251+
#else
240252
size_t num_sectors_pri;
241253
size_t num_sectors_sec;
242254
size_t sector_sz_pri = 0;
@@ -273,6 +285,7 @@ boot_slots_compatible(struct boot_loader_state *state)
273285
}
274286

275287
return 1;
288+
#endif /* PM_S1_ADDRESS */
276289
}
277290

278291
#define BOOT_LOG_SWAP_STATE(area, state) \

boot/bootutil/src/swap_scratch.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,18 @@ boot_status_internal_off(const struct boot_status *bs, int elem_sz)
170170
int
171171
boot_slots_compatible(struct boot_loader_state *state)
172172
{
173+
#ifdef PM_S1_ADDRESS
174+
/* Patch needed for NCS. In this case, image 1 primary points to the other
175+
* B1 slot (ie S0 or S1), and image 0 primary points to the app.
176+
* With this configuration, image 0 and image 1 share the secondary slot.
177+
* Hence, the primary slot of image 1 will be *smaller* than image 1's
178+
* secondary slot. This is not allowed in upstream mcuboot, so we need
179+
* this patch to allow it. Also, all of these checks are redundant when
180+
* partition manager is in use, and since we have the same sector size
181+
* in all of our flash.
182+
*/
183+
return 1;
184+
#else
173185
size_t num_sectors_primary;
174186
size_t num_sectors_secondary;
175187
size_t sz0, sz1;
@@ -255,6 +267,7 @@ boot_slots_compatible(struct boot_loader_state *state)
255267
}
256268

257269
return 1;
270+
#endif /* PM_S1_ADDRESS */
258271
}
259272

260273
#define BOOT_LOG_SWAP_STATE(area, state) \

boot/zephyr/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,13 @@ if(NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "")
272272
endif()
273273
message("MCUBoot bootloader key file: ${KEY_FILE}")
274274

275+
set_property(
276+
GLOBAL
277+
PROPERTY
278+
KEY_FILE
279+
${KEY_FILE}
280+
)
281+
275282
set(GENERATED_PUBKEY ${ZEPHYR_BINARY_DIR}/autogen-pubkey.c)
276283
add_custom_command(
277284
OUTPUT ${GENERATED_PUBKEY}

boot/zephyr/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ mainmenu "MCUboot configuration"
88

99
comment "MCUboot-specific configuration options"
1010

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

boot/zephyr/include/sysflash/sysflash.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,52 @@
33
#ifndef __SYSFLASH_H__
44
#define __SYSFLASH_H__
55

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

5298
#endif /* CONFIG_SINGLE_APPLICATION_SLOT */
5399

100+
#endif /* USE_PARTITION_MANAGER */
101+
54102
#endif /* __SYSFLASH_H__ */

boot/zephyr/include/target.h

Lines changed: 4 additions & 0 deletions
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

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@
6666

6767
#endif /* CONFIG_SOC_FAMILY_ESP32 */
6868

69+
#ifdef CONFIG_FW_INFO
70+
#include <fw_info.h>
71+
#endif
72+
6973
#ifdef CONFIG_MCUBOOT_SERIAL
7074
#include "boot_serial/boot_serial.h"
7175
#include "serial_adapter/serial_adapter.h"
@@ -134,6 +138,11 @@ K_SEM_DEFINE(boot_log_sem, 1, 1);
134138
* !defined(ZEPHYR_LOG_MODE_MINIMAL)
135139
*/
136140

141+
#if USE_PARTITION_MANAGER && CONFIG_FPROTECT
142+
#include <fprotect.h>
143+
#include <pm_config.h>
144+
#endif
145+
137146
#ifdef CONFIG_SOC_FAMILY_NRF
138147
#include <helpers/nrfx_reset_reason.h>
139148

@@ -241,6 +250,19 @@ static void do_boot(struct boot_rsp *rsp)
241250
/* Disable the USB to prevent it from firing interrupts */
242251
usb_disable();
243252
#endif
253+
254+
#if defined(CONFIG_FW_INFO) && !defined(CONFIG_EXT_API_PROVIDE_EXT_API_UNUSED)
255+
bool provided = fw_info_ext_api_provide(fw_info_find((uint32_t)vt), true);
256+
257+
#ifdef PM_S0_ADDRESS
258+
/* Only fail if the immutable bootloader is present. */
259+
if (!provided) {
260+
BOOT_LOG_ERR("Failed to provide EXT_APIs\n");
261+
return;
262+
}
263+
#endif
264+
#endif
265+
244266
#if CONFIG_MCUBOOT_CLEANUP_ARM_CORE
245267
cleanup_arm_nvic(); /* cleanup NVIC registers */
246268

@@ -675,7 +697,30 @@ int main(void)
675697

676698
mcuboot_status_change(MCUBOOT_STATUS_BOOTABLE_IMAGE_FOUND);
677699

700+
#if USE_PARTITION_MANAGER && CONFIG_FPROTECT
701+
702+
#ifdef PM_S1_ADDRESS
703+
/* MCUBoot is stored in either S0 or S1, protect both */
704+
#define PROTECT_SIZE (PM_MCUBOOT_PRIMARY_ADDRESS - PM_S0_ADDRESS)
705+
#define PROTECT_ADDR PM_S0_ADDRESS
706+
#else
707+
/* There is only one instance of MCUBoot */
708+
#define PROTECT_SIZE (PM_MCUBOOT_PRIMARY_ADDRESS - PM_MCUBOOT_ADDRESS)
709+
#define PROTECT_ADDR PM_MCUBOOT_ADDRESS
710+
#endif
711+
712+
rc = fprotect_area(PROTECT_ADDR, PROTECT_SIZE);
713+
714+
if (rc != 0) {
715+
BOOT_LOG_ERR("Protect mcuboot flash failed, cancel startup.");
716+
while (1)
717+
;
718+
}
719+
720+
#endif /* USE_PARTITION_MANAGER && CONFIG_FPROTECT */
721+
678722
ZEPHYR_BOOT_LOG_STOP();
723+
679724
do_boot(&rsp);
680725

681726
mcuboot_status_change(MCUBOOT_STATUS_BOOT_FAILED);

0 commit comments

Comments
 (0)