Skip to content

Commit 9da4fd3

Browse files
nvlsianpude-nordic
authored andcommitted
[nrf noup] boot/bootutil/loader: image discovery by ih_load_address
nrf-squash! [nrf noup] treewide: Add support for sysbuild assigned images Introduce alternative procedure for detecting to which partition image candidate belongs. This method uses ih_load_address field of the image header instead of reset vector address. This allows to match incoming image to the partition even when it is for instance encrypted, as the image header is always plain-text. This new procedure can be enabled using CONFIG_MCUBOOT_USE_CHECK_LOAD_ADDR=y. Firmware need to be signed with imgtool.py sign --rom-fixed <partition_address> parameter. ref.: NCSIDB-1173 Signed-off-by: Andrzej Puzdrowski <[email protected]>
1 parent a999034 commit 9da4fd3

File tree

2 files changed

+106
-50
lines changed

2 files changed

+106
-50
lines changed

boot/bootutil/src/loader.c

Lines changed: 97 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,28 @@ int pcd_version_cmp_net(const struct flash_area *fap, struct image_header *hdr);
8484
#include "bootutil/key_revocation.h"
8585
#endif
8686

87+
#ifdef CONFIG_PARTITION_MANAGER_ENABLED
88+
#define THREE_CAT(a, b, c) (a##b##c)
89+
#ifndef CONFIG_NCS_IS_VARIANT_IMAGE
90+
/* Running from slot 0 */
91+
#define NCS_VARIANT_SLOT_ID 0
92+
#else /* CONFIG_NCS_IS_VARIANT_IMAGE */
93+
/* Running from slot 1 */
94+
#define NCS_VARIANT_SLOT_ID 1
95+
#endif /* CONFIG_NCS_IS_VARIANT_IMAGE */
96+
97+
#define SLOT_MIN_ADDR THREE_CAT(PM_S, NCS_VARIANT_SLOT_ID, _ADDRESS)
98+
#define SLOT_MAX_ADDR (SLOT_MIN_ADDR + THREE_CAT(PM_S, NCS_VARIANT_SLOT_ID, _SIZE)
99+
100+
#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER == -1
101+
#define CHECK_MCUBOOT_IMAGE 0
102+
#else
103+
#define CHECK_MCUBOOT_IMAGE 1
104+
#endif
105+
106+
#undef THREE_CAT
107+
#endif /* CONFIG_PARTITION_MANAGER_ENABLED */
108+
87109
BOOT_LOG_MODULE_DECLARE(mcuboot);
88110

89111
static struct boot_loader_state boot_data;
@@ -1150,12 +1172,15 @@ boot_validate_slot(struct boot_loader_state *state, int slot,
11501172
goto out;
11511173
}
11521174

1153-
#if MCUBOOT_IMAGE_NUMBER > 1 && !defined(MCUBOOT_ENC_IMAGES) && defined(MCUBOOT_VERIFY_IMG_ADDRESS)
1175+
#if MCUBOOT_IMAGE_NUMBER > 1 && \
1176+
(!defined(MCUBOOT_ENC_IMAGES) || defined(CONFIG_MCUBOOT_USE_CHECK_LOAD_ADDRESS)) && \
1177+
defined(MCUBOOT_VERIFY_IMG_ADDRESS)
11541178
/* Verify that the image in the secondary slot has a reset address
11551179
* located in the primary slot. This is done to avoid users incorrectly
11561180
* overwriting an application written to the incorrect slot.
11571181
* This feature is only supported by ARM platforms.
11581182
*/
1183+
11591184
#if MCUBOOT_IMAGE_NUMBER >= 3
11601185
/* Currently the MCUboot can be configured for up to 3 image, where image number 2 is
11611186
* designated for XIP, where it is the second part of image stored in slots of image
@@ -1167,58 +1192,72 @@ boot_validate_slot(struct boot_loader_state *state, int slot,
11671192
}
11681193
#endif
11691194
if (fap == BOOT_IMG_AREA(state, BOOT_SLOT_SECONDARY)) {
1170-
const struct flash_area *pri_fa = BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY);
11711195
struct image_header *secondary_hdr = boot_img_hdr(state, slot);
1172-
uint32_t reset_value = 0;
1173-
uint32_t reset_addr = secondary_hdr->ih_hdr_size + sizeof(reset_value);
1174-
uint32_t min_addr, max_addr;
1196+
uint32_t internal_img_addr = 0; /* either the reset handler addres or the image beginning addres */
1197+
uint32_t min_addr;
1198+
uint32_t max_addr;
1199+
uint32_t offset = secondary_hdr->ih_hdr_size + sizeof(internal_img_addr);
11751200
bool check_addresses = false;
11761201

1177-
if (flash_area_read(fap, reset_addr, &reset_value, sizeof(reset_value)) != 0) {
1202+
min_addr = flash_area_get_off(BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY));
1203+
max_addr = flash_area_get_size(BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY)) + min_addr;
1204+
1205+
#ifdef CONFIG_MCUBOOT_USE_CHECK_LOAD_ADDRESS
1206+
internal_img_addr = secondary_hdr->ih_load_addr;
1207+
#else
1208+
/* Read reset vector from ARM vector table */
1209+
if (flash_area_read(fap, offset, &internal_img_addr, sizeof(internal_img_addr)) != 0) {
1210+
LOG_ERR("Failed to read image load address");
11781211
fih_rc = FIH_NO_BOOTABLE_IMAGE;
11791212
goto out;
11801213
}
1214+
#endif
1215+
BOOT_LOG_DBG("Image %d expected load address 0x%x", BOOT_CURR_IMG(state), internal_img_addr);
11811216

1182-
#ifdef PM_CPUNET_APP_ADDRESS
1217+
#ifdef CONFIG_PARTITION_MANAGER_ENABLED
11831218
/* The primary slot for the network core is emulated in RAM.
11841219
* Its flash_area hasn't got relevant boundaries.
11851220
* Therfore need to override its boundaries for the check.
11861221
*/
1222+
#ifdef PM_CPUNET_APP_ADDRESS
11871223
if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) {
11881224
min_addr = PM_CPUNET_APP_ADDRESS;
11891225
max_addr = PM_CPUNET_APP_ADDRESS + PM_CPUNET_APP_SIZE;
11901226
check_addresses = true;
1191-
} else
1192-
#endif
1193-
#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1
1227+
}
1228+
#endif /* PM_CPUNET_APP_SIZE */
1229+
1230+
#if CHECK_MCUBOOT_IMAGE == 1
11941231
if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) {
1195-
#if (CONFIG_NCS_IS_VARIANT_IMAGE)
1196-
min_addr = PM_S0_ADDRESS;
1197-
max_addr = (PM_S0_ADDRESS + PM_S0_SIZE);
1198-
#else
1199-
min_addr = PM_S1_ADDRESS;
1200-
max_addr = (PM_S1_ADDRESS + PM_S1_SIZE);
1201-
#endif
1232+
1233+
LOG_DBG("Image %d is another stage MCBoot image", BOOT_CURR_IMG(state));
1234+
1235+
min_addr = SLOT_MIN_ADDR;
1236+
max_addr = SLOT_MAX_ADDR;
12021237
check_addresses = true;
1203-
} else
1204-
#endif
1205-
if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) {
1238+
}
1239+
#endif /* CHECK_MCUBOOT_IMAGE */
1240+
12061241
#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1
1207-
#if (CONFIG_NCS_IS_VARIANT_IMAGE)
1208-
min_addr = MIN(pri_fa->fa_off, PM_S0_ADDRESS);
1209-
max_addr = MAX((pri_fa->fa_off + pri_fa->fa_size), (PM_S0_ADDRESS + PM_S0_SIZE));
1210-
#else
1211-
min_addr = MIN(pri_fa->fa_off, PM_S1_ADDRESS);
1212-
max_addr = MAX((pri_fa->fa_off + pri_fa->fa_size), (PM_S1_ADDRESS + PM_S1_SIZE));
1213-
#endif
1214-
#else
1215-
min_addr = pri_fa->fa_off;
1216-
max_addr = pri_fa->fa_off + pri_fa->fa_size;
1217-
#endif
1242+
if (CHECK_MCUBOOT_IMAGE && BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) {
1243+
1244+
LOG_DBG("Image %d is application MCBoot image", BOOT_CURR_IMG(state));
1245+
1246+
if (CHECK_MCUBOOT_IMAGE) {
1247+
min_addr = MIN(min_addr, SLOT_MIN_ADDR);
1248+
max_addr = MAX(max_addr, (SLOT_MAX_ADDR));
1249+
}
12181250
check_addresses = true;
12191251
}
1252+
#endif
1253+
if (check_addresses == false) {
1254+
goto out;
1255+
}
1256+
#endif /* CONFIG_PARTITION_MANAGER_ENABLED */
12201257

1221-
if (check_addresses == true && (reset_value < min_addr || reset_value > max_addr)) {
1258+
LOG_DBG("Check 0x%x is within [min_addr, max_addr] = [0x%x, 0x%x)",
1259+
internal_img_addr, min_addr, max_addr);
1260+
if (internal_img_addr < min_addr || internal_img_addr >= max_addr) {
12221261
BOOT_LOG_ERR("Reset address of image in secondary slot is not in the primary slot");
12231262
BOOT_LOG_ERR("Erasing image from secondary slot");
12241263

@@ -1232,6 +1271,7 @@ boot_validate_slot(struct boot_loader_state *state, int slot,
12321271
fih_rc = FIH_NO_BOOTABLE_IMAGE;
12331272
goto out;
12341273
}
1274+
LOG_DBG("Image %d validation OK", BOOT_CURR_IMG(state));
12351275
}
12361276
#endif
12371277

@@ -1435,6 +1475,18 @@ static inline void sec_slot_cleanup_if_unusable(void)
14351475
#endif /* defined(CONFIG_MCUBOOT_CLEANUP_UNUSABLE_SECONDARY) &&\
14361476
defined(PM_S1_ADDRESS) || defined(CONFIG_SOC_NRF5340_CPUAPP) */
14371477

1478+
#define IS_IN_RANGE_CPUNET_APP_ADDR(_addr) (((_addr) >= PM_CPUNET_APP_ADDRESS) && ((_addr) < PM_CPUNET_APP_END_ADDRESS))
1479+
#define _IS_IN_RANGE_S_VARIANT_ADDR(_addr, x) (((_addr) >= PM_S##x##_ADDRESS) && ((_addr) <= (PM_S##x##_ADDRESS + PM_S##x##_SIZE)))
1480+
#if (CONFIG_NCS_IS_VARIANT_IMAGE)
1481+
#define IS_IN_RANGE_S_ALTERNATE_ADDR(_addr) _IS_IN_RANGE_S_VARIANT_ADDR(_addr, 0)
1482+
#define IS_IN_RANGE_S_CURRENT_ADDR(_addr) _IS_IN_RANGE_S_VARIANT_ADDR(_addr, 1)
1483+
#else
1484+
#define IS_IN_RANGE_S_ALTERNATE_ADDR(_addr) _IS_IN_RANGE_S_VARIANT_ADDR(_addr, 1)
1485+
#define IS_IN_RANGE_S_CURRENT_ADDR(_addr) _IS_IN_RANGE_S_VARIANT_ADDR(_addr, 0)
1486+
#endif
1487+
#define IS_IN_RANGE_IMAGE_ADDR(_addr, _fa) \
1488+
(((_addr) >= flash_area_get_off(_fa)) && (_addr) < (flash_area_get_off(_fa) + flash_area_get_size(_fa)))
1489+
14381490
/**
14391491
* Determines which swap operation to perform, if any. If it is determined
14401492
* that a swap operation is required, the image in the secondary slot is checked
@@ -1458,8 +1510,9 @@ boot_validated_swap_type(struct boot_loader_state *state,
14581510
const struct flash_area *secondary_fa =
14591511
BOOT_IMG_AREA(state, BOOT_SLOT_SECONDARY);
14601512
struct image_header *hdr = boot_img_hdr(state, BOOT_SLOT_SECONDARY);
1461-
uint32_t reset_addr = 0;
1513+
uint32_t internal_img_addr = 0; /* either the reset handler addres or the image beginning addres */
14621514
int rc = 0;
1515+
14631516
/* Patch needed for NCS. Since image 0 (the app) and image 1 (the other
14641517
* B1 slot S0 or S1) share the same secondary slot, we need to check
14651518
* whether the update candidate in the secondary slot is intended for
@@ -1469,18 +1522,22 @@ boot_validated_swap_type(struct boot_loader_state *state,
14691522
*/
14701523

14711524
if (hdr->ih_magic == IMAGE_MAGIC) {
1525+
#ifdef CONFIG_MCUBOOT_USE_CHECK_LOAD_ADDRESS
1526+
internal_img_addr = hdr->ih_load_addr;
1527+
#else
14721528
rc = flash_area_read(secondary_fa, hdr->ih_hdr_size +
1473-
sizeof(uint32_t), &reset_addr,
1474-
sizeof(reset_addr));
1529+
sizeof(uint32_t), &internal_img_addr,
1530+
sizeof(internal_img_addr));
14751531
if (rc != 0) {
14761532
return BOOT_SWAP_TYPE_FAIL;
14771533
}
1534+
#endif /* CONFIG_MCUBOOT_USE_CHECK_LOAD_ADDRESS */
14781535

14791536
sec_slot_touch(state);
14801537

14811538
#ifdef PM_S1_ADDRESS
14821539
#ifdef PM_CPUNET_B0N_ADDRESS
1483-
if(!(reset_addr >= PM_CPUNET_APP_ADDRESS && reset_addr < PM_CPUNET_APP_END_ADDRESS))
1540+
if(!IS_IN_RANGE_CPUNET_APP_ADDR(internal_img_addr))
14841541
#endif
14851542
{
14861543
const struct flash_area *primary_fa;
@@ -1492,11 +1549,7 @@ boot_validated_swap_type(struct boot_loader_state *state,
14921549
}
14931550

14941551
/* Check start and end of primary slot for current image */
1495-
#if (CONFIG_NCS_IS_VARIANT_IMAGE)
1496-
if (reset_addr >= PM_S0_ADDRESS && reset_addr <= (PM_S0_ADDRESS + PM_S0_SIZE)) {
1497-
#else
1498-
if (reset_addr >= PM_S1_ADDRESS && reset_addr <= (PM_S1_ADDRESS + PM_S1_SIZE)) {
1499-
#endif
1552+
if (IS_IN_RANGE_S_ALTERNATE_ADDR(internal_img_addr)) {
15001553
if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) {
15011554
/* This is not the s0/s1 upgrade image but the application image, pretend
15021555
* there is no image so the NSIB update can be loaded
@@ -1505,18 +1558,14 @@ boot_validated_swap_type(struct boot_loader_state *state,
15051558
}
15061559

15071560
owner_nsib[BOOT_CURR_IMG(state)] = true;
1508-
#if (CONFIG_NCS_IS_VARIANT_IMAGE)
1509-
} else if (reset_addr >= PM_S1_ADDRESS && reset_addr <= (PM_S1_ADDRESS + PM_S1_SIZE)) {
1510-
#else
1511-
} else if (reset_addr >= PM_S0_ADDRESS && reset_addr <= (PM_S0_ADDRESS + PM_S0_SIZE)) {
1512-
#endif
1561+
} else if (IS_IN_RANGE_S_CURRENT_ADDR(internal_img_addr)) {
15131562
/* NSIB upgrade but for the wrong slot, must be erased */
15141563
BOOT_LOG_ERR("Image in slot is for wrong s0/s1 image");
15151564
flash_area_erase(secondary_fa, 0, secondary_fa->fa_size);
15161565
sec_slot_untouch(state);
15171566
BOOT_LOG_ERR("Cleaned-up secondary slot of image %d", BOOT_CURR_IMG(state));
15181567
return BOOT_SWAP_TYPE_FAIL;
1519-
} else if (reset_addr < primary_fa->fa_off || reset_addr > (primary_fa->fa_off + primary_fa->fa_size)) {
1568+
} else if (!IS_IN_RANGE_IMAGE_ADDR(internal_img_addr, primary_fa)) {
15201569
/* The image in the secondary slot is not intended for any */
15211570
return BOOT_SWAP_TYPE_NONE;
15221571
}
@@ -1553,8 +1602,7 @@ boot_validated_swap_type(struct boot_loader_state *state,
15531602
* update and indicate to the caller of this function that no update is
15541603
* available
15551604
*/
1556-
if (upgrade_valid && reset_addr >= PM_CPUNET_APP_ADDRESS &&
1557-
reset_addr < PM_CPUNET_APP_END_ADDRESS) {
1605+
if (upgrade_valid && IS_IN_RANGE_CPUNET_APP_ADDR(internal_img_addr)) {
15581606
struct image_header *hdr = (struct image_header *)secondary_fa->fa_off;
15591607
uint32_t vtable_addr = (uint32_t)hdr + hdr->ih_hdr_size;
15601608
uint32_t *net_core_fw_addr = (uint32_t *)(vtable_addr);

boot/zephyr/Kconfig

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1398,10 +1398,18 @@ config NRF_MCUBOOT_BOOT_REQUEST
13981398
bool
13991399
imply FIND_NEXT_SLOT_HOOKS if BOOT_DIRECT_XIP || BOOT_RAM_LOAD
14001400

1401+
config MCUBOOT_USE_CHECK_LOAD_ADDRESS
1402+
bool "Use load address to verify application is in proper slot"
1403+
help
1404+
The bootloader will use the load address, from the image header,
1405+
to verify that binary is in slot designated for its execution.
1406+
When not selected reset vector, read from image, is used for
1407+
the same purpose.
1408+
14011409
config MCUBOOT_VERIFY_IMG_ADDRESS
14021410
bool "Verify reset address of image in secondary slot"
14031411
depends on UPDATEABLE_IMAGE_NUMBER > 1
1404-
depends on !BOOT_ENCRYPT_IMAGE
1412+
depends on !BOOT_ENCRYPT_IMAGE || MCUBOOT_USE_CHECK_LOAD_ADDRESS
14051413
depends on ARM
14061414
default y if BOOT_UPGRADE_ONLY
14071415
help

0 commit comments

Comments
 (0)