Skip to content

Commit 80016fd

Browse files
SebastianBoejfischer-no
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: Emil Obalski <[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]> Signed-off-by: Dominik Ermel <[email protected]> (cherry picked from commit 71fe2df)
1 parent a01d30a commit 80016fd

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
@@ -109,6 +109,15 @@ boot_read_image_headers(struct boot_loader_state *state, bool require_all,
109109
*
110110
* Failure to read any headers is a fatal error.
111111
*/
112+
#ifdef PM_S1_ADDRESS
113+
/* Patch needed for NCS. The primary slot of the second image
114+
* (image 1) will not contain a valid image header until an upgrade
115+
* of mcuboot has happened (filling S1 with the new version).
116+
*/
117+
if (BOOT_CURR_IMG(state) == 1 && i == 0) {
118+
continue;
119+
}
120+
#endif /* PM_S1_ADDRESS */
112121
if (i > 0 && !require_all) {
113122
return 0;
114123
} else {
@@ -822,7 +831,24 @@ boot_validate_slot(struct boot_loader_state *state, int slot,
822831
goto out;
823832
}
824833

825-
if (reset_value < pri_fa->fa_off || reset_value> (pri_fa->fa_off + pri_fa->fa_size)) {
834+
uint32_t min_addr, max_addr;
835+
836+
#ifdef PM_CPUNET_APP_ADDRESS
837+
/* The primary slot for the network core is emulated in RAM.
838+
* Its flash_area hasn't got relevant boundaries.
839+
* Therfore need to override its boundaries for the check.
840+
*/
841+
if (BOOT_CURR_IMG(state) == 1) {
842+
min_addr = PM_CPUNET_APP_ADDRESS;
843+
max_addr = PM_CPUNET_APP_ADDRESS + PM_CPUNET_APP_SIZE;
844+
} else
845+
#endif
846+
{
847+
min_addr = pri_fa->fa_off;
848+
max_addr = pri_fa->fa_off + pri_fa->fa_size;
849+
}
850+
851+
if (reset_value < min_addr || reset_value> (max_addr)) {
826852
BOOT_LOG_ERR("Reset address of image in secondary slot is not in the primary slot");
827853
BOOT_LOG_ERR("Erasing image from secondary slot");
828854

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

909971
swap_type = boot_swap_type_multi(BOOT_CURR_IMG(state));
910972
if (BOOT_IS_UPGRADE(swap_type)) {
@@ -2230,15 +2292,25 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
22302292
}
22312293

22322294
#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT
2233-
FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, NULL);
2234-
/* Check for all possible values is redundant in normal operation it
2235-
* is meant to prevent FI attack.
2295+
#ifdef PM_S1_ADDRESS
2296+
/* Patch needed for NCS. Image 1 primary is the currently
2297+
* executing MCUBoot image, and is therefore already validated by NSIB and
2298+
* does not need to also be validated by MCUBoot.
22362299
*/
2237-
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS) ||
2238-
FIH_EQ(fih_rc, FIH_FAILURE) ||
2239-
FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) {
2240-
FIH_SET(fih_rc, FIH_FAILURE);
2241-
goto out;
2300+
bool image_validated_by_nsib = BOOT_CURR_IMG(state) == 1;
2301+
if (!image_validated_by_nsib)
2302+
#endif
2303+
{
2304+
FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, NULL);
2305+
/* Check for all possible values is redundant in normal operation it
2306+
* is meant to prevent FI attack.
2307+
*/
2308+
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS) ||
2309+
FIH_EQ(fih_rc, FIH_FAILURE) ||
2310+
FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) {
2311+
FIH_SET(fih_rc, FIH_FAILURE);
2312+
goto out;
2313+
}
22422314
}
22432315
#else
22442316
/* Even if we're not re-validating the primary slot, we could be booting
@@ -2255,11 +2327,16 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
22552327
}
22562328
#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */
22572329

2330+
#ifdef PM_S1_ADDRESS
2331+
if (!image_validated_by_nsib)
2332+
#endif
2333+
{
22582334
rc = boot_update_hw_rollback_protection(state);
22592335
if (rc != 0) {
22602336
FIH_SET(fih_rc, FIH_FAILURE);
22612337
goto out;
22622338
}
2339+
}
22632340

22642341
rc = boot_add_shared_data(state, BOOT_PRIMARY_SLOT);
22652342
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
@@ -290,6 +290,13 @@ if(NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "")
290290
endif()
291291
message("MCUBoot bootloader key file: ${KEY_FILE}")
292292

293+
set_property(
294+
GLOBAL
295+
PROPERTY
296+
KEY_FILE
297+
${KEY_FILE}
298+
)
299+
293300
set(GENERATED_PUBKEY ${ZEPHYR_BINARY_DIR}/autogen-pubkey.c)
294301
add_custom_command(
295302
OUTPUT ${GENERATED_PUBKEY}

boot/zephyr/Kconfig

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

Lines changed: 48 additions & 0 deletions
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>
@@ -57,4 +103,6 @@ static inline uint32_t __flash_area_ids_for_slot(int img, int slot)
57103

58104
#endif /* CONFIG_SINGLE_APPLICATION_SLOT */
59105

106+
#endif /* USE_PARTITION_MANAGER */
107+
60108
#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
@@ -64,6 +64,10 @@
6464

6565
#endif /* CONFIG_SOC_FAMILY_ESP32 */
6666

67+
#ifdef CONFIG_FW_INFO
68+
#include <fw_info.h>
69+
#endif
70+
6771
#ifdef CONFIG_MCUBOOT_SERIAL
6872
#include "boot_serial/boot_serial.h"
6973
#include "serial_adapter/serial_adapter.h"
@@ -132,6 +136,11 @@ K_SEM_DEFINE(boot_log_sem, 1, 1);
132136
* !defined(ZEPHYR_LOG_MODE_MINIMAL)
133137
*/
134138

139+
#if USE_PARTITION_MANAGER && CONFIG_FPROTECT
140+
#include <fprotect.h>
141+
#include <pm_config.h>
142+
#endif
143+
135144
#ifdef CONFIG_SOC_FAMILY_NRF
136145
#include <helpers/nrfx_reset_reason.h>
137146

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

@@ -681,7 +703,30 @@ int main(void)
681703

682704
mcuboot_status_change(MCUBOOT_STATUS_BOOTABLE_IMAGE_FOUND);
683705

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

687732
mcuboot_status_change(MCUBOOT_STATUS_BOOT_FAILED);

0 commit comments

Comments
 (0)