Skip to content

Commit

Permalink
[nrf noup] treewide: add NCS partition manager support
Browse files Browse the repository at this point in the history
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 <haakon.amundsen@nordicsemi.no>
Signed-off-by: Øyvind Rønningstad <oyvind.ronningstad@nordicsemi.no>
Signed-off-by: Sebastian Bøe <sebastian.boe@nordicsemi.no>
Signed-off-by: Sigvart Hovland <sigvart.m@gmail.com>
Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no>
Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
Signed-off-by: Andrzej Puzdrowski <andrzej.puzdrowski@nordicsemi.no>
Signed-off-by: Dominik Ermel <dominik.ermel@nordicsemi.no>
Signed-off-by: Emil Obalski <emil.obalski@nordicsemi.no>
Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no>
Signed-off-by: Pawel Dunaj <pawel.dunaj@nordicsemi.no>
Signed-off-by: Ioannis Glaropoulos <Ioannis.Glaropoulos@nordicsemi.no>
Signed-off-by: Johann Fischer <johann.fischer@nordicsemi.no>
Signed-off-by: Vidar Berg <vidar.berg@nordicsemi.no>
Signed-off-by: Draus, Sebastian <sebastian.draus@nordicsemi.no>
Signed-off-by: Trond Einar Snekvik <Trond.Einar.Snekvik@nordicsemi.no>
Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
Signed-off-by: Georgios Vasilakis <georgios.vasilakis@nordicsemi.no>
(cherry picked from commit ed5f069)
(cherry picked from commit d2cac70)
(cherry picked from commit 1630628)
  • Loading branch information
SebastianBoe authored and de-nordic committed Jun 14, 2023
1 parent e81afaa commit 349361e
Show file tree
Hide file tree
Showing 12 changed files with 296 additions and 11 deletions.
95 changes: 86 additions & 9 deletions boot/bootutil/src/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ boot_read_image_headers(struct boot_loader_state *state, bool require_all,
*
* Failure to read any headers is a fatal error.
*/
#ifdef PM_S1_ADDRESS
/* Patch needed for NCS. The primary slot of the second image
* (image 1) will not contain a valid image header until an upgrade
* of mcuboot has happened (filling S1 with the new version).
*/
if (BOOT_CURR_IMG(state) == 1 && i == 0) {
continue;
}
#endif /* PM_S1_ADDRESS */
if (i > 0 && !require_all) {
return 0;
} else {
Expand Down Expand Up @@ -815,7 +824,24 @@ boot_validate_slot(struct boot_loader_state *state, int slot,
goto out;
}

if (reset_value < pri_fa->fa_off || reset_value> (pri_fa->fa_off + pri_fa->fa_size)) {
uint32_t min_addr, max_addr;

#ifdef PM_CPUNET_APP_ADDRESS
/* The primary slot for the network core is emulated in RAM.
* Its flash_area hasn't got relevant boundaries.
* Therfore need to override its boundaries for the check.
*/
if (BOOT_CURR_IMG(state) == 1) {
min_addr = PM_CPUNET_APP_ADDRESS;
max_addr = PM_CPUNET_APP_ADDRESS + PM_CPUNET_APP_SIZE;
} else
#endif
{
min_addr = pri_fa->fa_off;
max_addr = pri_fa->fa_off + pri_fa->fa_size;
}

if (reset_value < min_addr || reset_value> (max_addr)) {
BOOT_LOG_ERR("Reset address of image in secondary slot is not in the primary slot");
BOOT_LOG_ERR("Erasing image from secondary slot");

Expand Down Expand Up @@ -898,6 +924,42 @@ boot_validated_swap_type(struct boot_loader_state *state,
{
int swap_type;
FIH_DECLARE(fih_rc, FIH_FAILURE);
#ifdef PM_S1_ADDRESS
/* Patch needed for NCS. Since image 0 (the app) and image 1 (the other
* B1 slot S0 or S1) share the same secondary slot, we need to check
* whether the update candidate in the secondary slot is intended for
* image 0 or image 1 primary by looking at the address of the reset
* vector. Note that there are good reasons for not using img_num from
* the swap info.
*/
const struct flash_area *secondary_fa =
BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT);
struct image_header *hdr =
(struct image_header *)secondary_fa->fa_off;

if (hdr->ih_magic == IMAGE_MAGIC) {
const struct flash_area *primary_fa;
uint32_t vtable_addr = (uint32_t)hdr + hdr->ih_hdr_size;
uint32_t *vtable = (uint32_t *)(vtable_addr);
uint32_t reset_addr = vtable[1];
int rc = flash_area_open(
flash_area_id_from_multi_image_slot(
BOOT_CURR_IMG(state),
BOOT_PRIMARY_SLOT),
&primary_fa);

if (rc != 0) {
return BOOT_SWAP_TYPE_FAIL;
}
/* Get start and end of primary slot for current image */
if (reset_addr < primary_fa->fa_off ||
reset_addr > (primary_fa->fa_off + primary_fa->fa_size)) {
/* The image in the secondary slot is not intended for this image
*/
return BOOT_SWAP_TYPE_NONE;
}
}
#endif

swap_type = boot_swap_type_multi(BOOT_CURR_IMG(state));
if (BOOT_IS_UPGRADE(swap_type)) {
Expand Down Expand Up @@ -2206,15 +2268,25 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
}

#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT
FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, NULL);
/* Check for all possible values is redundant in normal operation it
* is meant to prevent FI attack.
#ifdef PM_S1_ADDRESS
/* Patch needed for NCS. Image 1 primary is the currently
* executing MCUBoot image, and is therefore already validated by NSIB and
* does not need to also be validated by MCUBoot.
*/
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS) ||
FIH_EQ(fih_rc, FIH_FAILURE) ||
FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) {
FIH_SET(fih_rc, FIH_FAILURE);
goto out;
bool image_validated_by_nsib = BOOT_CURR_IMG(state) == 1;
if (!image_validated_by_nsib)
#endif
{
FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, NULL);
/* Check for all possible values is redundant in normal operation it
* is meant to prevent FI attack.
*/
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS) ||
FIH_EQ(fih_rc, FIH_FAILURE) ||
FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) {
FIH_SET(fih_rc, FIH_FAILURE);
goto out;
}
}
#else
/* Even if we're not re-validating the primary slot, we could be booting
Expand All @@ -2231,11 +2303,16 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
}
#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */

#ifdef PM_S1_ADDRESS
if (!image_validated_by_nsib)
#endif
{
rc = boot_update_hw_rollback_protection(state);
if (rc != 0) {
FIH_SET(fih_rc, FIH_FAILURE);
goto out;
}
}

rc = boot_add_shared_data(state, BOOT_PRIMARY_SLOT);
if (rc != 0) {
Expand Down
13 changes: 13 additions & 0 deletions boot/bootutil/src/swap_move.c
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,18 @@ boot_status_internal_off(const struct boot_status *bs, int elem_sz)
int
boot_slots_compatible(struct boot_loader_state *state)
{
#ifdef PM_S1_ADDRESS
/* Patch needed for NCS. In this case, image 1 primary points to the other
* B1 slot (ie S0 or S1), and image 0 primary points to the app.
* With this configuration, image 0 and image 1 share the secondary slot.
* Hence, the primary slot of image 1 will be *smaller* than image 1's
* secondary slot. This is not allowed in upstream mcuboot, so we need
* this patch to allow it. Also, all of these checks are redundant when
* partition manager is in use, and since we have the same sector size
* in all of our flash.
*/
return 1;
#else
size_t num_sectors_pri;
size_t num_sectors_sec;
size_t sector_sz_pri = 0;
Expand Down Expand Up @@ -273,6 +285,7 @@ boot_slots_compatible(struct boot_loader_state *state)
}

return 1;
#endif /* PM_S1_ADDRESS */
}

#define BOOT_LOG_SWAP_STATE(area, state) \
Expand Down
13 changes: 13 additions & 0 deletions boot/bootutil/src/swap_scratch.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,18 @@ boot_status_internal_off(const struct boot_status *bs, int elem_sz)
int
boot_slots_compatible(struct boot_loader_state *state)
{
#ifdef PM_S1_ADDRESS
/* Patch needed for NCS. In this case, image 1 primary points to the other
* B1 slot (ie S0 or S1), and image 0 primary points to the app.
* With this configuration, image 0 and image 1 share the secondary slot.
* Hence, the primary slot of image 1 will be *smaller* than image 1's
* secondary slot. This is not allowed in upstream mcuboot, so we need
* this patch to allow it. Also, all of these checks are redundant when
* partition manager is in use, and since we have the same sector size
* in all of our flash.
*/
return 1;
#else
size_t num_sectors_primary;
size_t num_sectors_secondary;
size_t sz0, sz1;
Expand Down Expand Up @@ -255,6 +267,7 @@ boot_slots_compatible(struct boot_loader_state *state)
}

return 1;
#endif /* PM_S1_ADDRESS */
}

#define BOOT_LOG_SWAP_STATE(area, state) \
Expand Down
7 changes: 7 additions & 0 deletions boot/zephyr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,13 @@ if(NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "")
endif()
message("MCUBoot bootloader key file: ${KEY_FILE}")

set_property(
GLOBAL
PROPERTY
KEY_FILE
${KEY_FILE}
)

set(GENERATED_PUBKEY ${ZEPHYR_BINARY_DIR}/autogen-pubkey.c)
add_custom_command(
OUTPUT ${GENERATED_PUBKEY}
Expand Down
2 changes: 2 additions & 0 deletions boot/zephyr/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ mainmenu "MCUboot configuration"

comment "MCUboot-specific configuration options"

source "$(ZEPHYR_NRF_MODULE_DIR)/modules/mcuboot/boot/zephyr/Kconfig"

# Hidden option to mark a project as MCUboot
config MCUBOOT
default y
Expand Down
48 changes: 48 additions & 0 deletions boot/zephyr/include/sysflash/sysflash.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,52 @@
#ifndef __SYSFLASH_H__
#define __SYSFLASH_H__

#if USE_PARTITION_MANAGER
#include <pm_config.h>
#include <mcuboot_config/mcuboot_config.h>

#ifndef CONFIG_SINGLE_APPLICATION_SLOT

#if (MCUBOOT_IMAGE_NUMBER == 1)

#define FLASH_AREA_IMAGE_PRIMARY(x) PM_MCUBOOT_PRIMARY_ID
#define FLASH_AREA_IMAGE_SECONDARY(x) PM_MCUBOOT_SECONDARY_ID

#elif (MCUBOOT_IMAGE_NUMBER == 2)

extern uint32_t _image_1_primary_slot_id[];

#define FLASH_AREA_IMAGE_PRIMARY(x) \
((x == 0) ? \
PM_MCUBOOT_PRIMARY_ID : \
(x == 1) ? \
(uint32_t)_image_1_primary_slot_id : \
255 )

#define FLASH_AREA_IMAGE_SECONDARY(x) \
((x == 0) ? \
PM_MCUBOOT_SECONDARY_ID: \
(x == 1) ? \
PM_MCUBOOT_SECONDARY_ID: \
255 )
#endif
#define FLASH_AREA_IMAGE_SCRATCH PM_MCUBOOT_SCRATCH_ID

#else /* CONFIG_SINGLE_APPLICATION_SLOT */

#define FLASH_AREA_IMAGE_PRIMARY(x) PM_MCUBOOT_PRIMARY_ID
#define FLASH_AREA_IMAGE_SECONDARY(x) PM_MCUBOOT_PRIMARY_ID
/* NOTE: Scratch parition is not used by single image DFU but some of
* functions in common files reference it, so the definitions has been
* provided to allow compilation of common units.
*/
#define FLASH_AREA_IMAGE_SCRATCH 0

#endif /* CONFIG_SINGLE_APPLICATION_SLOT */

#else

#include <zephyr/devicetree.h>
#include <mcuboot_config/mcuboot_config.h>
#include <zephyr/devicetree.h>
#include <zephyr/storage/flash_map.h>
Expand Down Expand Up @@ -51,4 +97,6 @@

#endif /* CONFIG_SINGLE_APPLICATION_SLOT */

#endif /* USE_PARTITION_MANAGER */

#endif /* __SYSFLASH_H__ */
4 changes: 4 additions & 0 deletions boot/zephyr/include/target.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#ifndef H_TARGETS_TARGET_
#define H_TARGETS_TARGET_

#ifndef USE_PARTITION_MANAGER

#if defined(MCUBOOT_TARGET_CONFIG)
/*
* Target-specific definitions are permitted in legacy cases that
Expand Down Expand Up @@ -45,4 +47,6 @@
#error "Target support is incomplete; cannot build mcuboot."
#endif

#endif /* ifndef USE_PARTITION_MANAGER */

#endif /* H_TARGETS_TARGET_ */
45 changes: 45 additions & 0 deletions boot/zephyr/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@

#endif /* CONFIG_SOC_FAMILY_ESP32 */

#ifdef CONFIG_FW_INFO
#include <fw_info.h>
#endif

#ifdef CONFIG_MCUBOOT_SERIAL
#include "boot_serial/boot_serial.h"
#include "serial_adapter/serial_adapter.h"
Expand Down Expand Up @@ -134,6 +138,11 @@ K_SEM_DEFINE(boot_log_sem, 1, 1);
* !defined(ZEPHYR_LOG_MODE_MINIMAL)
*/

#if USE_PARTITION_MANAGER && CONFIG_FPROTECT
#include <fprotect.h>
#include <pm_config.h>
#endif

#ifdef CONFIG_SOC_FAMILY_NRF
#include <helpers/nrfx_reset_reason.h>

Expand Down Expand Up @@ -241,6 +250,19 @@ static void do_boot(struct boot_rsp *rsp)
/* Disable the USB to prevent it from firing interrupts */
usb_disable();
#endif

#if defined(CONFIG_FW_INFO) && !defined(CONFIG_EXT_API_PROVIDE_EXT_API_UNUSED)
bool provided = fw_info_ext_api_provide(fw_info_find((uint32_t)vt), true);

#ifdef PM_S0_ADDRESS
/* Only fail if the immutable bootloader is present. */
if (!provided) {
BOOT_LOG_ERR("Failed to provide EXT_APIs\n");
return;
}
#endif
#endif

#if CONFIG_MCUBOOT_CLEANUP_ARM_CORE
cleanup_arm_nvic(); /* cleanup NVIC registers */

Expand Down Expand Up @@ -675,7 +697,30 @@ int main(void)

mcuboot_status_change(MCUBOOT_STATUS_BOOTABLE_IMAGE_FOUND);

#if USE_PARTITION_MANAGER && CONFIG_FPROTECT

#ifdef PM_S1_ADDRESS
/* MCUBoot is stored in either S0 or S1, protect both */
#define PROTECT_SIZE (PM_MCUBOOT_PRIMARY_ADDRESS - PM_S0_ADDRESS)
#define PROTECT_ADDR PM_S0_ADDRESS
#else
/* There is only one instance of MCUBoot */
#define PROTECT_SIZE (PM_MCUBOOT_PRIMARY_ADDRESS - PM_MCUBOOT_ADDRESS)
#define PROTECT_ADDR PM_MCUBOOT_ADDRESS
#endif

rc = fprotect_area(PROTECT_ADDR, PROTECT_SIZE);

if (rc != 0) {
BOOT_LOG_ERR("Protect mcuboot flash failed, cancel startup.");
while (1)
;
}

#endif /* USE_PARTITION_MANAGER && CONFIG_FPROTECT */

ZEPHYR_BOOT_LOG_STOP();

do_boot(&rsp);

mcuboot_status_change(MCUBOOT_STATUS_BOOT_FAILED);
Expand Down
Loading

0 comments on commit 349361e

Please sign in to comment.