Skip to content

Commit d91e83e

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 6256d9f commit d91e83e

File tree

2 files changed

+138
-58
lines changed

2 files changed

+138
-58
lines changed

boot/bootutil/src/loader.c

Lines changed: 129 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,57 @@ int pcd_version_cmp_net(const struct flash_area *fap, struct image_header *hdr);
8888
void s2ram_designate_slot(uint8_t slot);
8989
#endif
9090

91+
#ifdef CONFIG_PARTITION_MANAGER_ENABLED
92+
#define THREE_CAT_(a, b, c) (a##b##c)
93+
#define THREE_CAT(a, b, c) THREE_CAT_(a, b, c)
94+
95+
/* Current S0/S1 slot macros.
96+
* These macros are used to generate min/max address of the alternate,
97+
* potential DFU target, Sx slot from the running one.
98+
*/
99+
#ifdef CONFIG_NCS_IS_VARIANT_IMAGE
100+
/* Running from slot 1, checking slot 0 */
101+
#define NCS_VARIANT_SLOT_ID 0
102+
#else /* CONFIG_NCS_IS_VARIANT_IMAGE */
103+
/* Running from slot 0, checking slot 1 */
104+
#define NCS_VARIANT_SLOT_ID 1
105+
#endif /* CONFIG_NCS_IS_VARIANT_IMAGE */
106+
107+
#define NCS_VARIANT_SLOT_MIN_ADDR THREE_CAT(PM_S, NCS_VARIANT_SLOT_ID, _ADDRESS)
108+
#define NCS_VARIANT_SLOT_MAX_ADDR ((NCS_VARIANT_SLOT_MIN_ADDR) + THREE_CAT(PM_S, NCS_VARIANT_SLOT_ID, _SIZE))
109+
/* End: Current S0/S1 slot macros */
110+
111+
#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER == -1
112+
#define CHECK_MCUBOOT_IMAGE 0
113+
#else
114+
#define CHECK_MCUBOOT_IMAGE 1
115+
#endif
116+
117+
/* PM partition check macros.
118+
* These macros are universal, regardles of slot the MCUboot is called from
119+
* and may be invoked to figure out to which area given address belongs to.
120+
*/
121+
122+
/* Net core address check */
123+
#define IS_IN_RANGE_CPUNET_APP_ADDR(_addr) (((_addr) >= PM_CPUNET_APP_ADDRESS) && ((_addr) < PM_CPUNET_APP_END_ADDRESS))
124+
125+
/* Current/alternate Sx slot check macros */
126+
#define _IS_IN_RANGE_S_VARIANT_ADDR(_addr, x) (((_addr) >= PM_S##x##_ADDRESS) && ((_addr) < (PM_S##x##_ADDRESS + PM_S##x##_SIZE)))
127+
#if defined(CONFIG_NCS_IS_VARIANT_IMAGE)
128+
#define IS_IN_RANGE_S_ALTERNATE_ADDR(_addr) _IS_IN_RANGE_S_VARIANT_ADDR(_addr, 0)
129+
#define IS_IN_RANGE_S_CURRENT_ADDR(_addr) _IS_IN_RANGE_S_VARIANT_ADDR(_addr, 1)
130+
#else
131+
#define IS_IN_RANGE_S_ALTERNATE_ADDR(_addr) _IS_IN_RANGE_S_VARIANT_ADDR(_addr, 1)
132+
#define IS_IN_RANGE_S_CURRENT_ADDR(_addr) _IS_IN_RANGE_S_VARIANT_ADDR(_addr, 0)
133+
#endif
134+
135+
/* Application image partition check */
136+
#define IS_IN_RANGE_IMAGE_ADDR(_addr, _fa) \
137+
(((_addr) >= flash_area_get_off(_fa)) && (_addr) < (flash_area_get_off(_fa) + flash_area_get_size(_fa)))
138+
/* End: PM partition check macros */
139+
140+
#endif /* CONFIG_PARTITION_MANAGER_ENABLED */
141+
91142
BOOT_LOG_MODULE_DECLARE(mcuboot);
92143

93144
static struct boot_loader_state boot_data;
@@ -1154,7 +1205,8 @@ boot_validate_slot(struct boot_loader_state *state, int slot,
11541205
goto out;
11551206
}
11561207

1157-
#if MCUBOOT_IMAGE_NUMBER > 1 && !defined(MCUBOOT_ENC_IMAGES) && defined(MCUBOOT_VERIFY_IMG_ADDRESS)
1208+
#if defined(MCUBOOT_VERIFY_IMG_ADDRESS) && !defined(MCUBOOT_ENC_IMAGES) || \
1209+
defined(CONFIG_MCUBOOT_USE_CHECK_LOAD_ADDRESS)
11581210
/* Verify that the image in the secondary slot has a reset address
11591211
* located in the primary slot. This is done to avoid users incorrectly
11601212
* overwriting an application written to the incorrect slot.
@@ -1171,58 +1223,72 @@ boot_validate_slot(struct boot_loader_state *state, int slot,
11711223
}
11721224
#endif
11731225
if (fap == BOOT_IMG_AREA(state, BOOT_SLOT_SECONDARY)) {
1174-
const struct flash_area *pri_fa = BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY);
11751226
struct image_header *secondary_hdr = boot_img_hdr(state, slot);
1176-
uint32_t reset_value = 0;
1177-
uint32_t reset_addr = secondary_hdr->ih_hdr_size + sizeof(reset_value);
1178-
uint32_t min_addr, max_addr;
1179-
bool check_addresses = false;
1227+
uint32_t internal_img_addr = 0; /* either the reset handler addres or the image beginning address */
1228+
uint32_t min_addr;
1229+
uint32_t max_addr;
1230+
1231+
min_addr = flash_area_get_off(BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY));
1232+
max_addr = flash_area_get_size(BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY)) + min_addr;
11801233

1181-
if (flash_area_read(fap, reset_addr, &reset_value, sizeof(reset_value)) != 0) {
1234+
#ifdef CONFIG_MCUBOOT_USE_CHECK_LOAD_ADDRESS
1235+
internal_img_addr = secondary_hdr->ih_load_addr;
1236+
#else
1237+
const uint32_t offset = secondary_hdr->ih_hdr_size + sizeof(internal_img_addr);
1238+
BOOT_LOG_DBG("Getting image %d internal addr from offset %u",
1239+
BOOT_CURR_IMG(state), offset);
1240+
/* Read reset vector from ARM vector table */
1241+
if (flash_area_read(fap, offset, &internal_img_addr, sizeof(internal_img_addr)) != 0) {
1242+
BOOT_LOG_ERR("Failed to read image %d load address", BOOT_CURR_IMG(state));
11821243
fih_rc = FIH_NO_BOOTABLE_IMAGE;
11831244
goto out;
11841245
}
1246+
#endif
1247+
BOOT_LOG_DBG("Image %d expected load address 0x%x", BOOT_CURR_IMG(state), internal_img_addr);
11851248

1186-
#ifdef PM_CPUNET_APP_ADDRESS
1249+
#ifdef CONFIG_PARTITION_MANAGER_ENABLED
11871250
/* The primary slot for the network core is emulated in RAM.
11881251
* Its flash_area hasn't got relevant boundaries.
11891252
* Therfore need to override its boundaries for the check.
11901253
*/
1254+
#ifdef PM_CPUNET_APP_ADDRESS
11911255
if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) {
1256+
1257+
BOOT_LOG_DBG("Image %d is NETCORE image", BOOT_CURR_IMG(state));
1258+
11921259
min_addr = PM_CPUNET_APP_ADDRESS;
11931260
max_addr = PM_CPUNET_APP_ADDRESS + PM_CPUNET_APP_SIZE;
1194-
check_addresses = true;
1195-
} else
1196-
#endif
1197-
#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1
1261+
}
1262+
#endif /* PM_CPUNET_APP_SIZE */
1263+
1264+
#if CHECK_MCUBOOT_IMAGE == 1
11981265
if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) {
1199-
#if (CONFIG_NCS_IS_VARIANT_IMAGE)
1200-
min_addr = PM_S0_ADDRESS;
1201-
max_addr = (PM_S0_ADDRESS + PM_S0_SIZE);
1202-
#else
1203-
min_addr = PM_S1_ADDRESS;
1204-
max_addr = (PM_S1_ADDRESS + PM_S1_SIZE);
1205-
#endif
1206-
check_addresses = true;
1207-
} else
1208-
#endif
1266+
1267+
BOOT_LOG_DBG("Image %d is another stage MCBoot image", BOOT_CURR_IMG(state));
1268+
1269+
min_addr = NCS_VARIANT_SLOT_MIN_ADDR;
1270+
max_addr = NCS_VARIANT_SLOT_MAX_ADDR;
1271+
}
1272+
#endif /* CHECK_MCUBOOT_IMAGE */
1273+
1274+
#if CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER != -1
12091275
if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) {
1210-
#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1
1211-
#if (CONFIG_NCS_IS_VARIANT_IMAGE)
1212-
min_addr = MIN(pri_fa->fa_off, PM_S0_ADDRESS);
1213-
max_addr = MAX((pri_fa->fa_off + pri_fa->fa_size), (PM_S0_ADDRESS + PM_S0_SIZE));
1214-
#else
1215-
min_addr = MIN(pri_fa->fa_off, PM_S1_ADDRESS);
1216-
max_addr = MAX((pri_fa->fa_off + pri_fa->fa_size), (PM_S1_ADDRESS + PM_S1_SIZE));
1217-
#endif
1218-
#else
1219-
min_addr = pri_fa->fa_off;
1220-
max_addr = pri_fa->fa_off + pri_fa->fa_size;
1276+
1277+
BOOT_LOG_DBG("Image %d is application MCBoot image", BOOT_CURR_IMG(state));
1278+
1279+
/* If not checking the MCUboot image, then we leave the original
1280+
* min/max assignments from the primary slot read. */
1281+
#if CHECK_MCUBOOT_IMAGE == 1
1282+
min_addr = MIN(min_addr, NCS_VARIANT_SLOT_MIN_ADDR);
1283+
max_addr = MAX(max_addr, NCS_VARIANT_SLOT_MAX_ADDR);
12211284
#endif
1222-
check_addresses = true;
12231285
}
1286+
#endif
1287+
#endif /* CONFIG_PARTITION_MANAGER_ENABLED */
12241288

1225-
if (check_addresses == true && (reset_value < min_addr || reset_value > max_addr)) {
1289+
BOOT_LOG_DBG("Check 0x%x is within [min_addr, max_addr] = [0x%x, 0x%x)",
1290+
internal_img_addr, min_addr, max_addr);
1291+
if (internal_img_addr < min_addr || internal_img_addr >= max_addr) {
12261292
BOOT_LOG_ERR("Reset address of image in secondary slot is not in the primary slot");
12271293
BOOT_LOG_ERR("Erasing image from secondary slot");
12281294

@@ -1236,6 +1302,7 @@ boot_validate_slot(struct boot_loader_state *state, int slot,
12361302
fih_rc = FIH_NO_BOOTABLE_IMAGE;
12371303
goto out;
12381304
}
1305+
BOOT_LOG_DBG("Image %d validation OK", BOOT_CURR_IMG(state));
12391306
}
12401307
#endif
12411308

@@ -1462,8 +1529,9 @@ boot_validated_swap_type(struct boot_loader_state *state,
14621529
const struct flash_area *secondary_fa =
14631530
BOOT_IMG_AREA(state, BOOT_SLOT_SECONDARY);
14641531
struct image_header *hdr = boot_img_hdr(state, BOOT_SLOT_SECONDARY);
1465-
uint32_t reset_addr = 0;
1532+
uint32_t internal_img_addr = 0; /* either the reset handler addres or the image beginning addres */
14661533
int rc = 0;
1534+
14671535
/* Patch needed for NCS. Since image 0 (the app) and image 1 (the other
14681536
* B1 slot S0 or S1) share the same secondary slot, we need to check
14691537
* whether the update candidate in the secondary slot is intended for
@@ -1472,60 +1540,65 @@ boot_validated_swap_type(struct boot_loader_state *state,
14721540
* the swap info.
14731541
*/
14741542

1543+
BOOT_LOG_DBG("boot_validated_swap_type: current image %d", BOOT_CURR_IMG(state));
14751544
if (hdr->ih_magic == IMAGE_MAGIC) {
1545+
#ifdef CONFIG_MCUBOOT_USE_CHECK_LOAD_ADDRESS
1546+
internal_img_addr = hdr->ih_load_addr;
1547+
#else
14761548
rc = flash_area_read(secondary_fa, hdr->ih_hdr_size +
1477-
sizeof(uint32_t), &reset_addr,
1478-
sizeof(reset_addr));
1549+
sizeof(uint32_t), &internal_img_addr,
1550+
sizeof(internal_img_addr));
14791551
if (rc != 0) {
14801552
return BOOT_SWAP_TYPE_FAIL;
14811553
}
1554+
#endif /* CONFIG_MCUBOOT_USE_CHECK_LOAD_ADDRESS */
1555+
BOOT_LOG_DBG("boot_validated_swap_type: check addr 0x%x", internal_img_addr);
14821556

14831557
sec_slot_touch(state);
14841558

14851559
#ifdef PM_S1_ADDRESS
14861560
#ifdef PM_CPUNET_B0N_ADDRESS
1487-
if(!(reset_addr >= PM_CPUNET_APP_ADDRESS && reset_addr < PM_CPUNET_APP_END_ADDRESS))
1561+
if(!IS_IN_RANGE_CPUNET_APP_ADDR(internal_img_addr))
14881562
#endif
14891563
{
1490-
const struct flash_area *primary_fa;
1491-
rc = flash_area_open(flash_area_id_from_multi_image_slot(
1492-
BOOT_CURR_IMG(state), BOOT_SLOT_PRIMARY),
1493-
&primary_fa);
1564+
const struct flash_area *primary_fa = BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY);
1565+
const uint32_t pfa_off = flash_area_get_off(primary_fa);
1566+
14941567
if (rc != 0) {
14951568
return BOOT_SWAP_TYPE_FAIL;
14961569
}
14971570

14981571
/* Check start and end of primary slot for current image */
1499-
#if (CONFIG_NCS_IS_VARIANT_IMAGE)
1500-
if (reset_addr >= PM_S0_ADDRESS && reset_addr <= (PM_S0_ADDRESS + PM_S0_SIZE)) {
1501-
#else
1502-
if (reset_addr >= PM_S1_ADDRESS && reset_addr <= (PM_S1_ADDRESS + PM_S1_SIZE)) {
1503-
#endif
1572+
BOOT_LOG_DBG("boot_validated_swap_type: S%d is alternative", NCS_VARIANT_SLOT_ID);
1573+
BOOT_LOG_DBG("boot_validated_swap_type: S0 [0x%x, 0x%x)", PM_S0_ADDRESS, PM_S0_ADDRESS + PM_S0_SIZE);
1574+
BOOT_LOG_DBG("boot_validated_swap_type: S1 [0x%x, 0x%x)", PM_S1_ADDRESS, PM_S1_ADDRESS + PM_S1_SIZE);
1575+
if (IS_IN_RANGE_S_ALTERNATE_ADDR(internal_img_addr)) {
1576+
BOOT_LOG_DBG("boot_validated_swap_type: matched alternative");
15041577
if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) {
15051578
/* This is not the s0/s1 upgrade image but the application image, pretend
15061579
* there is no image so the NSIB update can be loaded
15071580
*/
1581+
BOOT_LOG_DBG("boot_validated_swap_type: ignoring application image");
15081582
return BOOT_SWAP_TYPE_NONE;
15091583
}
15101584

15111585
owner_nsib[BOOT_CURR_IMG(state)] = true;
1512-
#if (CONFIG_NCS_IS_VARIANT_IMAGE)
1513-
} else if (reset_addr >= PM_S1_ADDRESS && reset_addr <= (PM_S1_ADDRESS + PM_S1_SIZE)) {
1514-
#else
1515-
} else if (reset_addr >= PM_S0_ADDRESS && reset_addr <= (PM_S0_ADDRESS + PM_S0_SIZE)) {
1516-
#endif
1586+
} else if (IS_IN_RANGE_S_CURRENT_ADDR(internal_img_addr)) {
15171587
/* NSIB upgrade but for the wrong slot, must be erased */
15181588
BOOT_LOG_ERR("Image in slot is for wrong s0/s1 image");
15191589
flash_area_erase(secondary_fa, 0, secondary_fa->fa_size);
15201590
sec_slot_untouch(state);
15211591
BOOT_LOG_ERR("Cleaned-up secondary slot of image %d", BOOT_CURR_IMG(state));
15221592
return BOOT_SWAP_TYPE_FAIL;
1523-
} else if (reset_addr < primary_fa->fa_off || reset_addr > (primary_fa->fa_off + primary_fa->fa_size)) {
1593+
} else if (!IS_IN_RANGE_IMAGE_ADDR(internal_img_addr, primary_fa)) {
15241594
/* The image in the secondary slot is not intended for any */
1595+
BOOT_LOG_DBG("boot_validated_swap_type: no match");
15251596
return BOOT_SWAP_TYPE_NONE;
15261597
}
15271598

1528-
if ((primary_fa->fa_off == PM_S0_ADDRESS) || (primary_fa->fa_off == PM_S1_ADDRESS)) {
1599+
BOOT_LOG_DBG("boot_validated_swap_type: matching primary slot offset to S0/S1");
1600+
if ((pfa_off == PM_S0_ADDRESS) || (pfa_off == PM_S1_ADDRESS)) {
1601+
BOOT_LOG_DBG("boot_validated_swap_type: setting nsib ownership over image %d", BOOT_CURR_IMG(state));
15291602
owner_nsib[BOOT_CURR_IMG(state)] = true;
15301603
}
15311604
}
@@ -1557,8 +1630,7 @@ boot_validated_swap_type(struct boot_loader_state *state,
15571630
* update and indicate to the caller of this function that no update is
15581631
* available
15591632
*/
1560-
if (upgrade_valid && reset_addr >= PM_CPUNET_APP_ADDRESS &&
1561-
reset_addr < PM_CPUNET_APP_END_ADDRESS) {
1633+
if (upgrade_valid && IS_IN_RANGE_CPUNET_APP_ADDR(internal_img_addr)) {
15621634
struct image_header *hdr = (struct image_header *)secondary_fa->fa_off;
15631635
uint32_t vtable_addr = (uint32_t)hdr + hdr->ih_hdr_size;
15641636
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
@@ -1409,10 +1409,18 @@ config NRF_MCUBOOT_BOOT_REQUEST
14091409
bool
14101410
imply FIND_NEXT_SLOT_HOOKS if BOOT_DIRECT_XIP || BOOT_RAM_LOAD
14111411

1412+
config MCUBOOT_USE_CHECK_LOAD_ADDRESS
1413+
bool "Use load address to verify application is in proper slot"
1414+
help
1415+
The bootloader will use the load address, from the image header,
1416+
to verify that binary is in slot designated for its execution.
1417+
When not selected reset vector, read from image, is used for
1418+
the same purpose.
1419+
14121420
config MCUBOOT_VERIFY_IMG_ADDRESS
14131421
bool "Verify reset address of image in secondary slot"
14141422
depends on UPDATEABLE_IMAGE_NUMBER > 1
1415-
depends on !BOOT_ENCRYPT_IMAGE
1423+
depends on !BOOT_ENCRYPT_IMAGE || MCUBOOT_USE_CHECK_LOAD_ADDRESS
14161424
depends on ARM
14171425
default y if BOOT_UPGRADE_ONLY
14181426
help

0 commit comments

Comments
 (0)