Skip to content

Commit ed5f069

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 f7de93f) (cherry picked from commit 20f8d86) (cherry picked from commit 7a3357e) (cherry picked from commit 4f775a8) (cherry picked from commit 34b3ac7) (cherry picked from commit e4f6886) Signed-off-by: Dominik Ermel <[email protected]>
1 parent 21fa70e commit ed5f069

File tree

12 files changed

+295
-11
lines changed

12 files changed

+295
-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 {
@@ -803,7 +812,24 @@ boot_validate_slot(struct boot_loader_state *state, int slot,
803812
goto out;
804813
}
805814

806-
if (reset_value < pri_fa->fa_off || reset_value> (pri_fa->fa_off + pri_fa->fa_size)) {
815+
uint32_t min_addr, max_addr;
816+
817+
#ifdef PM_CPUNET_APP_ADDRESS
818+
/* The primary slot for the network core is emulated in RAM.
819+
* Its flash_area hasn't got relevant boundaries.
820+
* Therfore need to override its boundaries for the check.
821+
*/
822+
if (BOOT_CURR_IMG(state) == 1) {
823+
min_addr = PM_CPUNET_APP_ADDRESS;
824+
max_addr = PM_CPUNET_APP_ADDRESS + PM_CPUNET_APP_SIZE;
825+
} else
826+
#endif
827+
{
828+
min_addr = pri_fa->fa_off;
829+
max_addr = pri_fa->fa_off + pri_fa->fa_size;
830+
}
831+
832+
if (reset_value < min_addr || reset_value> (max_addr)) {
807833
BOOT_LOG_ERR("Reset address of image in secondary slot is not in the primary slot");
808834
BOOT_LOG_ERR("Erasing image from secondary slot");
809835

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

890952
swap_type = boot_swap_type_multi(BOOT_CURR_IMG(state));
891953
if (BOOT_IS_UPGRADE(swap_type)) {
@@ -2176,15 +2238,25 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
21762238
}
21772239

21782240
#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT
2179-
FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, NULL);
2180-
/* Check for all possible values is redundant in normal operation it
2181-
* is meant to prevent FI attack.
2241+
#ifdef PM_S1_ADDRESS
2242+
/* Patch needed for NCS. Image 1 primary is the currently
2243+
* executing MCUBoot image, and is therefore already validated by NSIB and
2244+
* does not need to also be validated by MCUBoot.
21822245
*/
2183-
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS) ||
2184-
FIH_EQ(fih_rc, FIH_FAILURE) ||
2185-
FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) {
2186-
FIH_SET(fih_rc, FIH_FAILURE);
2187-
goto out;
2246+
bool image_validated_by_nsib = BOOT_CURR_IMG(state) == 1;
2247+
if (!image_validated_by_nsib)
2248+
#endif
2249+
{
2250+
FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, NULL);
2251+
/* Check for all possible values is redundant in normal operation it
2252+
* is meant to prevent FI attack.
2253+
*/
2254+
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS) ||
2255+
FIH_EQ(fih_rc, FIH_FAILURE) ||
2256+
FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) {
2257+
FIH_SET(fih_rc, FIH_FAILURE);
2258+
goto out;
2259+
}
21882260
}
21892261
#else
21902262
/* Even if we're not re-validating the primary slot, we could be booting
@@ -2201,11 +2273,16 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
22012273
}
22022274
#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */
22032275

2276+
#ifdef PM_S1_ADDRESS
2277+
if (!image_validated_by_nsib)
2278+
#endif
2279+
{
22042280
rc = boot_update_hw_rollback_protection(state);
22052281
if (rc != 0) {
22062282
FIH_SET(fih_rc, FIH_FAILURE);
22072283
goto out;
22082284
}
2285+
}
22092286

22102287
rc = boot_add_shared_data(state, BOOT_PRIMARY_SLOT);
22112288
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
@@ -279,6 +279,13 @@ if(NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "")
279279
endif()
280280
message("MCUBoot bootloader key file: ${KEY_FILE}")
281281

282+
set_property(
283+
GLOBAL
284+
PROPERTY
285+
KEY_FILE
286+
${KEY_FILE}
287+
)
288+
282289
set(GENERATED_PUBKEY ${ZEPHYR_BINARY_DIR}/autogen-pubkey.c)
283290
add_custom_command(
284291
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: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,51 @@
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+
651
#include <zephyr/devicetree.h>
752
#include <mcuboot_config/mcuboot_config.h>
853

@@ -50,4 +95,6 @@
5095

5196
#endif /* CONFIG_SINGLE_APPLICATION_SLOT */
5297

98+
#endif /* USE_PARTITION_MANAGER */
99+
53100
#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
@@ -44,4 +46,6 @@
4446
#error "Target support is incomplete; cannot build mcuboot."
4547
#endif
4648

49+
#endif /* ifndef USE_PARTITION_MANAGER */
50+
4751
#endif /* H_TARGETS_TARGET_ */

boot/zephyr/main.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@
4242
#include "bootutil/mcuboot_status.h"
4343
#include "flash_map_backend/flash_map_backend.h"
4444

45+
#ifdef CONFIG_FW_INFO
46+
#include <fw_info.h>
47+
#endif
48+
4549
#ifdef CONFIG_MCUBOOT_SERIAL
4650
#include "boot_serial/boot_serial.h"
4751
#include "serial_adapter/serial_adapter.h"
@@ -102,6 +106,11 @@ K_SEM_DEFINE(boot_log_sem, 1, 1);
102106
* !defined(ZEPHYR_LOG_MODE_MINIMAL)
103107
*/
104108

109+
#if USE_PARTITION_MANAGER && CONFIG_FPROTECT
110+
#include <fprotect.h>
111+
#include <pm_config.h>
112+
#endif
113+
105114
#ifdef CONFIG_SOC_FAMILY_NRF
106115
#include <helpers/nrfx_reset_reason.h>
107116

@@ -198,6 +207,19 @@ static void do_boot(struct boot_rsp *rsp)
198207
/* Disable the USB to prevent it from firing interrupts */
199208
usb_disable();
200209
#endif
210+
211+
#if defined(CONFIG_FW_INFO) && !defined(CONFIG_EXT_API_PROVIDE_EXT_API_UNUSED)
212+
bool provided = fw_info_ext_api_provide(fw_info_find((uint32_t)vt), true);
213+
214+
#ifdef PM_S0_ADDRESS
215+
/* Only fail if the immutable bootloader is present. */
216+
if (!provided) {
217+
BOOT_LOG_ERR("Failed to provide EXT_APIs\n");
218+
return;
219+
}
220+
#endif
221+
#endif
222+
201223
#if CONFIG_MCUBOOT_CLEANUP_ARM_CORE
202224
cleanup_arm_nvic(); /* cleanup NVIC registers */
203225

@@ -618,7 +640,30 @@ void main(void)
618640

619641
mcuboot_status_change(MCUBOOT_STATUS_BOOTABLE_IMAGE_FOUND);
620642

643+
#if USE_PARTITION_MANAGER && CONFIG_FPROTECT
644+
645+
#ifdef PM_S1_ADDRESS
646+
/* MCUBoot is stored in either S0 or S1, protect both */
647+
#define PROTECT_SIZE (PM_MCUBOOT_PRIMARY_ADDRESS - PM_S0_ADDRESS)
648+
#define PROTECT_ADDR PM_S0_ADDRESS
649+
#else
650+
/* There is only one instance of MCUBoot */
651+
#define PROTECT_SIZE (PM_MCUBOOT_PRIMARY_ADDRESS - PM_MCUBOOT_ADDRESS)
652+
#define PROTECT_ADDR PM_MCUBOOT_ADDRESS
653+
#endif
654+
655+
rc = fprotect_area(PROTECT_ADDR, PROTECT_SIZE);
656+
657+
if (rc != 0) {
658+
BOOT_LOG_ERR("Protect mcuboot flash failed, cancel startup.");
659+
while (1)
660+
;
661+
}
662+
663+
#endif /* USE_PARTITION_MANAGER && CONFIG_FPROTECT */
664+
621665
ZEPHYR_BOOT_LOG_STOP();
666+
622667
do_boot(&rsp);
623668

624669
mcuboot_status_change(MCUBOOT_STATUS_BOOT_FAILED);

0 commit comments

Comments
 (0)