From 75079eb1b836fe5d2b28d254b68730f1a9dcc732 Mon Sep 17 00:00:00 2001 From: Mateusz Michalek Date: Mon, 29 Jul 2024 15:12:28 +0200 Subject: [PATCH 01/10] fw_info: Change allowed offsets for firmware info struct Since the vector table of nRF54LX series is larger than before using it is nice to have an offset between `0x400` and `0x800` to store `fw_info` in to ensure you can tightly pack your application together with firmware info. One downside of this is that this new offset is not supported in any older version of NSIB. Signed-off-by: Mateusz Michalek --- doc/nrf/libraries/security/bootloader/fw_info.rst | 4 ++-- include/fw_info.h | 6 ++++-- include/fw_info_bare.h | 11 +++++++---- subsys/fw_info/Kconfig | 3 ++- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/doc/nrf/libraries/security/bootloader/fw_info.rst b/doc/nrf/libraries/security/bootloader/fw_info.rst index 053d54576bf5..211610263b55 100644 --- a/doc/nrf/libraries/security/bootloader/fw_info.rst +++ b/doc/nrf/libraries/security/bootloader/fw_info.rst @@ -17,10 +17,10 @@ Purpose The purpose of the firmware information is to allow other images such as bootloaders, or infrastructure such as firmware servers, to gain information about the firmware image. The firmware information structure has a 12-byte magic header and a verified binary layout to ensure that the format is portable and identifiable. -It must be located at one of the following offsets from the start of the image: 0x0, 0x200, 0x400, 0x800, 0x1000. +It must be located at one of the following offsets from the start of the image: 0x0, 0x200, 0x400, 0x600, 0x800, 0xe00, 0x1000. The reason that the structure is not located at 0x00 is that this can be problematic in some use cases, such as when the vector table must be located at 0x00. -These rules make it simple to retrieve the information by checking for each possible offset (0x0, 0x200, 0x400, 0x800, 0x1000) if the first 12 bytes match the magic value. +These rules make it simple to retrieve the information by checking for each possible offset (0x0, 0x200, 0x400, 0x600, 0x800, 0xe00, 0x1000) if the first 12 bytes match the magic value. If they do, the information can be retrieved according to the definition in :c:struct:`fw_info`. Information structure diff --git a/include/fw_info.h b/include/fw_info.h index 19aa2bdd4086..79124e5d673c 100644 --- a/include/fw_info.h +++ b/include/fw_info.h @@ -156,12 +156,14 @@ BUILD_ASSERT(ARRAY_SIZE(fw_info_allowed_offsets) == FW_INFO_OFFSET_COUNT, */ /* Build time check of CONFIG_FW_INFO_OFFSET. */ -#if (FW_INFO_OFFSET_COUNT != 5) \ +#if (FW_INFO_OFFSET_COUNT != 7) \ || ((FW_INFO_CURRENT_OFFSET) != (FW_INFO_OFFSET0) && \ (FW_INFO_CURRENT_OFFSET) != (FW_INFO_OFFSET1) && \ (FW_INFO_CURRENT_OFFSET) != (FW_INFO_OFFSET2) && \ (FW_INFO_CURRENT_OFFSET) != (FW_INFO_OFFSET3) && \ - (FW_INFO_CURRENT_OFFSET) != (FW_INFO_OFFSET4)) + (FW_INFO_CURRENT_OFFSET) != (FW_INFO_OFFSET4) && \ + (FW_INFO_CURRENT_OFFSET) != (FW_INFO_OFFSET5) && \ + (FW_INFO_CURRENT_OFFSET) != (FW_INFO_OFFSET6)) #error FW_INFO_OFFSET not set to one of the allowed values. #endif diff --git a/include/fw_info_bare.h b/include/fw_info_bare.h index e73bf01561a4..51a98675a97e 100644 --- a/include/fw_info_bare.h +++ b/include/fw_info_bare.h @@ -24,9 +24,11 @@ extern "C" { #define FW_INFO_OFFSET0 0x0 #define FW_INFO_OFFSET1 0x200 #define FW_INFO_OFFSET2 0x400 -#define FW_INFO_OFFSET3 0x800 -#define FW_INFO_OFFSET4 0x1000 -#define FW_INFO_OFFSET_COUNT 5 +#define FW_INFO_OFFSET3 0x600 +#define FW_INFO_OFFSET4 0x800 +#define FW_INFO_OFFSET5 0xe00 +#define FW_INFO_OFFSET6 0x1000 +#define FW_INFO_OFFSET_COUNT 7 /** * This struct serves as a header before an EXT_API. @@ -192,7 +194,8 @@ static inline const struct fw_info *fw_info_check(uint32_t fw_info_addr) static const uint32_t fw_info_allowed_offsets[] = { FW_INFO_OFFSET0, FW_INFO_OFFSET1, FW_INFO_OFFSET2, FW_INFO_OFFSET3, - FW_INFO_OFFSET4}; + FW_INFO_OFFSET4, FW_INFO_OFFSET5, + FW_INFO_OFFSET6}; /** Search for the firmware_info structure inside the firmware. diff --git a/subsys/fw_info/Kconfig b/subsys/fw_info/Kconfig index 90d1f1e4a085..77094651daf0 100644 --- a/subsys/fw_info/Kconfig +++ b/subsys/fw_info/Kconfig @@ -18,10 +18,11 @@ config FW_INFO_API config FW_INFO_OFFSET hex "The location of firmware info inside this firmware" + default 0x600 if SOC_SERIES_NRF54LX default 0x200 help The location of firmware information inside the current firmware - image. Valid values are 0x0, 0x200, 0x400, 0x800, and 0x1000. + image. Valid values are 0x0, 0x200, 0x400, 0x600, 0x800, 0xe00, and 0x1000. Compatible readers of firmware information should search all possible offsets. Note that all space between the vector table and this address is unused. From 8f51ae64c1825f56a9f6d20f8b24b463c95d8e91 Mon Sep 17 00:00:00 2001 From: Mateusz Michalek Date: Mon, 29 Jul 2024 15:35:49 +0200 Subject: [PATCH 02/10] cmake: partition_manager: Add OTP region in partition manager for nRF54L Added OTP region in partition manager for `nRF54L15X_ENGA` devices and modified the secure boot storage partition to use OTP for NRF54L15. Ref. NCSDK-25306 Signed-off-by: Mateusz Michalek --- cmake/partition_manager.cmake | 5 ++++- cmake/sysbuild/partition_manager.cmake | 6 +++++- subsys/bootloader/Kconfig | 7 ++++--- subsys/partition_manager/pm.yml.secure_boot_storage | 2 +- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/cmake/partition_manager.cmake b/cmake/partition_manager.cmake index 30f1fe006e46..8e331d4a0eee 100644 --- a/cmake/partition_manager.cmake +++ b/cmake/partition_manager.cmake @@ -227,6 +227,9 @@ elseif (DEFINED CONFIG_SOC_NRF5340_CPUAPP) # See nRF5340 Product Specification, chapter Application Core -> ... "UICR" set(otp_start_addr "0xff8100") set(otp_size 764) # 191 * 4 +elseif (DEFINED CONFIG_SOC_NRF54L15_CPUAPP) + set(otp_start_addr "0xffd500") + set(otp_size 1276) # 319 * 4 endif() if (DEFINED CONFIG_SOC_SERIES_NRF54LX) @@ -247,7 +250,7 @@ add_region( math(EXPR flash_size "${CONFIG_FLASH_SIZE} * 1024" OUTPUT_FORMAT HEXADECIMAL) -if (CONFIG_SOC_SERIES_NRF91X OR CONFIG_SOC_NRF5340_CPUAPP) +if (CONFIG_SOC_SERIES_NRF91X OR CONFIG_SOC_NRF5340_CPUAPP OR CONFIG_SOC_NRF54L15_CPUAPP) add_region( NAME otp SIZE ${otp_size} diff --git a/cmake/sysbuild/partition_manager.cmake b/cmake/sysbuild/partition_manager.cmake index 32209ee1cd7e..bf5067ddf5d8 100644 --- a/cmake/sysbuild/partition_manager.cmake +++ b/cmake/sysbuild/partition_manager.cmake @@ -525,6 +525,7 @@ foreach(d APP ${PM_DOMAINS}) sysbuild_get(${image_name}_CONFIG_SOC_SERIES_NRF91X IMAGE ${image_name} VAR CONFIG_SOC_SERIES_NRF91X KCONFIG) sysbuild_get(${image_name}_CONFIG_SOC_NRF5340_CPUAPP IMAGE ${image_name} VAR CONFIG_SOC_NRF5340_CPUAPP KCONFIG) + sysbuild_get(${image_name}_CONFIG_SOC_NRF54L15_CPUAPP IMAGE ${image_name} VAR CONFIG_SOC_NRF54L15_CPUAPP KCONFIG) if (${image_name}_CONFIG_SOC_SERIES_NRF91X) # See nRF9160 Product Specification, chapter "UICR" @@ -534,6 +535,9 @@ foreach(d APP ${PM_DOMAINS}) # See nRF5340 Product Specification, chapter Application Core -> ... "UICR" set(otp_start_addr "0xff8100") set(otp_size 764) # 191 * 4 + elseif (DEFINED ${image_name}_CONFIG_SOC_NRF54L15_CPUAPP) + set(otp_start_addr "0xffd500") + set(otp_size 1276) # 319 * 4 endif() sysbuild_get(${image_name}_CONFIG_SOC_SERIES_NRF54LX IMAGE ${image_name} VAR CONFIG_SOC_SERIES_NRF54LX KCONFIG) @@ -557,7 +561,7 @@ foreach(d APP ${PM_DOMAINS}) sysbuild_get(${image_name}_CONFIG_FLASH_SIZE IMAGE ${image_name} VAR CONFIG_FLASH_SIZE KCONFIG) math(EXPR flash_size "${${image_name}_CONFIG_FLASH_SIZE} * 1024" OUTPUT_FORMAT HEXADECIMAL) - if (${image_name}_CONFIG_SOC_SERIES_NRF91X OR ${image_name}_CONFIG_SOC_NRF5340_CPUAPP) + if (${image_name}_CONFIG_SOC_SERIES_NRF91X OR ${image_name}_CONFIG_SOC_NRF5340_CPUAPP OR ${image_name}_CONFIG_SOC_NRF54L15_CPUAPP) add_region( NAME otp SIZE ${otp_size} diff --git a/subsys/bootloader/Kconfig b/subsys/bootloader/Kconfig index 1d2948fe5c32..d1493589bebf 100644 --- a/subsys/bootloader/Kconfig +++ b/subsys/bootloader/Kconfig @@ -146,7 +146,7 @@ config SB_NUM_VER_COUNTER_SLOTS int "Number of monotonic counter slots used for the firmware version" default 20 if BOOTLOADER_MCUBOOT || MCUBOOT default 240 - range 2 300 if SOC_NRF5340_CPUAPP || SOC_SERIES_NRF91X + range 2 300 if SOC_NRF5340_CPUAPP || SOC_SERIES_NRF91X || SOC_SERIES_NRF54LX range 2 1800 if SOC_SERIES_NRF52X range 2 400 if SOC_SERIES_NRF51X depends on SB_MONOTONIC_COUNTER @@ -171,10 +171,10 @@ endif # SECURE_BOOT config PM_PARTITION_SIZE_PROVISION hex - default 0x280 if SOC_SERIES_NRF91X || SOC_NRF5340_CPUAPP # Stored in OTP region + default 0x280 if SOC_SERIES_NRF91X || SOC_NRF5340_CPUAPP || SOC_NRF54L15_CPUAPP # Stored in OTP region default 0x280 if SOC_NRF5340_CPUNET # Second instance stored in internal flash of NET default FPROTECT_BLOCK_SIZE - prompt "Flash space reserved for PROVISION" if !(SOC_NRF9160 || SOC_NRF5340_CPUAPP) + prompt "Flash space reserved for PROVISION" if !(SOC_NRF9160 || SOC_NRF5340_CPUAPP || SOC_NRF54L15_CPUAPP) help Flash space set aside for the PROVISION partition. @@ -187,6 +187,7 @@ config PM_PARTITION_SIZE_B0_IMAGE default 0x7800 if !B0_MIN_PARTITION_SIZE && (SOC_NRF5340_CPUNET) default FPROTECT_BLOCK_SIZE if SOC_SERIES_NRF91X || SOC_NRF5340_CPUAPP default 0x3800 if SOC_NRF5340_CPUNET + default 0x7800 if SOC_NRF54L15_CPUAPP default 0x7000 if !B0_MIN_PARTITION_SIZE default 0x4000 help diff --git a/subsys/partition_manager/pm.yml.secure_boot_storage b/subsys/partition_manager/pm.yml.secure_boot_storage index 14f15691ff8e..f7ac5ebac152 100644 --- a/subsys/partition_manager/pm.yml.secure_boot_storage +++ b/subsys/partition_manager/pm.yml.secure_boot_storage @@ -2,7 +2,7 @@ provision: size: CONFIG_PM_PARTITION_SIZE_PROVISION -#if defined(CONFIG_SOC_SERIES_NRF91X) || defined(CONFIG_SOC_NRF5340_CPUAPP) +#if defined(CONFIG_SOC_SERIES_NRF91X) || defined(CONFIG_SOC_NRF5340_CPUAPP) || defined(CONFIG_SOC_NRF54L15_CPUAPP) region: otp #elif defined(CONFIG_SOC_NRF5340_CPUNET) placement: From bd37ea57ae4dfcf9b9cb11df26cb6293a5372f85 Mon Sep 17 00:00:00 2001 From: Mateusz Michalek Date: Mon, 29 Jul 2024 15:57:49 +0200 Subject: [PATCH 03/10] bl_storage: fw_info/counters port to RRAMC Changed the implementation of bl_storage to be compatible with RRAMC and FLASH/NVMC. Due to the limitations in the RRAM controller OTP can only be written to by words not half-words as it can be in NVMC. This means we need to change the way we handle certain cases in `bl_storage.c`, `bl_validation.c` and `fw_info.c`. This commit moves some of the abstractions into two separate files `bl_storage_nvmc.c` and `bl_storage_rramc.c`. However there are multiple other changes needed which are done by using `#ifdef`'s. Ref. NCSDK-25306 Signed-off-by: Mateusz Michalek --- include/bl_storage.h | 169 ++++++++++++++++-- include/bl_validation.h | 7 +- samples/bootloader/src/main.c | 10 ++ subsys/bootloader/bl_storage/bl_storage.c | 81 +++------ .../bootloader/bl_validation/bl_validation.c | 23 ++- subsys/fw_info/fw_info.c | 10 +- 6 files changed, 214 insertions(+), 86 deletions(-) diff --git a/include/bl_storage.h b/include/bl_storage.h index de136388821f..c65625e308d6 100644 --- a/include/bl_storage.h +++ b/include/bl_storage.h @@ -9,8 +9,15 @@ #include #include +#include #include +#if defined(CONFIG_NRFX_NVMC) #include +#elif defined(CONFIG_NRFX_RRAMC) +#include +#else +#error "No NRFX storage technology supported backend selected" +#endif #include #include @@ -19,6 +26,17 @@ extern "C" { #endif +#if defined(CONFIG_NRFX_NVMC) +typedef uint16_t counter_t; +typedef uint16_t lcs_data_t; +typedef uint16_t lcs_reserved_t; +#elif defined(CONFIG_NRFX_RRAMC) +/* nRF54L15 only supports word writes */ +typedef uint32_t counter_t; +typedef uint32_t lcs_data_t; +typedef uint32_t lcs_reserved_t; +#endif + #define EHASHFF 113 /* A hash contains too many 0xFs. */ #define EREADLCS 114 /* LCS field of OTP is in an invalid state */ #define EINVALIDLCS 115 /* Invalid LCS*/ @@ -46,17 +64,52 @@ extern "C" { * This works as ASSEMBLY implies the OTP to be erased. */ struct life_cycle_state_data { - uint16_t provisioning; - uint16_t secure; - /* Pad to end the alignment at a 4-byte boundary as the UICR->OTP - * only supports 4-byte reads. We place the reserved padding in - * the middle of the struct in case we ever need to support + lcs_data_t provisioning; + lcs_data_t secure; + /* Pad to end the alignment at a 4-byte boundary as some devices + * are only supporting 4-byte UICR->OTP reads. We place the reserved + * padding in the middle of the struct in case we ever need to support * another state. */ - uint16_t reserved_for_padding; - uint16_t decommissioned; + lcs_reserved_t reserved_for_padding; + lcs_data_t decommissioned; +}; + +/** This library implements monotonic counters where each time the counter + * is increased, a new slot is written. + * This way, the counter can be updated without erase. This is, among other things, + * necessary so the counter can be used in the OTP section of the UICR + * (available on e.g. nRF91 and nRF53). + */ +struct monotonic_counter { + /* Counter description. What the counter is used for. See + * BL_MONOTONIC_COUNTERS_DESC_x. + */ + uint16_t description; + /* Number of entries in 'counter_slots' list. */ + uint16_t num_counter_slots; + counter_t counter_slots[1]; }; +/** The second data structure in the provision page. It has unknown length since + * 'counters' is repeated. Note that each entry in counters also has unknown + * length, and each entry can have different length from the others, so the + * entries beyond the first cannot be accessed via array indices. + */ +struct counter_collection { + uint16_t type; /* Must be "monotonic counter". */ + uint16_t num_counters; /* Number of entries in 'counters' list. */ + struct monotonic_counter counters[1]; +}; + +NRFX_STATIC_INLINE uint32_t bl_storage_word_read(uint32_t address); +NRFX_STATIC_INLINE uint32_t bl_storage_word_write(uint32_t address, uint32_t value); +NRFX_STATIC_INLINE counter_t bl_storage_counter_get(uint32_t address); +NRFX_STATIC_INLINE void bl_storage_counter_set(uint32_t address, counter_t value); + +const struct monotonic_counter *get_counter_struct(uint16_t description); +int get_counter(uint16_t counter_desc, counter_t *counter_value, const counter_t **free_slot); + /** The first data structure in the bootloader storage. It has unknown length * since 'key_data' is repeated. This data structure is immediately followed by * struct counter_collection. @@ -155,7 +208,7 @@ int num_monotonic_counter_slots(uint16_t counter_desc, uint16_t *counter_slots); * @retval -EINVAL Cannot find counters with description @p counter_desc or the pointer to * @p counter_value is NULL. */ -int get_monotonic_counter(uint16_t counter_desc, uint16_t *counter_value); +int get_monotonic_counter(uint16_t counter_desc, counter_t *counter_value); /** * @brief Set the current HW monotonic counter. @@ -174,7 +227,7 @@ int get_monotonic_counter(uint16_t counter_desc, uint16_t *counter_value); * @retval -ENOMEM There are no more free counter slots (see * @kconfig{CONFIG_SB_NUM_VER_COUNTER_SLOTS}). */ -int set_monotonic_counter(uint16_t counter_desc, uint16_t new_counter); +int set_monotonic_counter(uint16_t counter_desc, counter_t new_counter); /** * @brief The PSA life cycle states a device can be in. @@ -202,7 +255,7 @@ NRFX_STATIC_INLINE void otp_copy32(uint8_t *restrict dst, uint32_t volatile * re { for (int i = 0; i < size / 4; i++) { /* OTP is in UICR */ - uint32_t val = nrfx_nvmc_uicr_word_read(src + i); + uint32_t val = bl_storage_word_read((uint32_t)(src + i)); for (int j = 0; j < 4; j++) { dst[i * 4 + j] = (val >> 8 * j) & 0xFF; @@ -238,6 +291,87 @@ NRFX_STATIC_INLINE void read_implementation_id_from_otp(uint8_t *buf) * This is a temporary solution until TF-M has access to NSIB functions. */ +#if defined(CONFIG_NRFX_RRAMC) +NRFX_STATIC_INLINE uint32_t index_from_address(uint32_t address) +{ + return ((address - (uint32_t)BL_STORAGE)/sizeof(uint32_t)); +} +#endif + +NRFX_STATIC_INLINE counter_t bl_storage_counter_get(uint32_t address) +{ +#if defined(CONFIG_NRFX_NVMC) + return ~nrfx_nvmc_otp_halfword_read(address); +#elif defined(CONFIG_NRFX_RRAMC) + return ~nrfx_rramc_otp_word_read(index_from_address(address)); +#endif +} + +NRFX_STATIC_INLINE void bl_storage_counter_set(uint32_t address, counter_t value) +{ +#if defined(CONFIG_NRFX_NVMC) + nrfx_nvmc_halfword_write((uint32_t)address, ~value); +#elif defined(CONFIG_NRFX_RRAMC) + nrfx_rramc_otp_word_write(index_from_address((uint32_t)address), ~value); +#endif +} + +NRFX_STATIC_INLINE uint32_t bl_storage_word_read(uint32_t address) +{ +#if defined(CONFIG_NRFX_NVMC) + return nrfx_nvmc_uicr_word_read((uint32_t *)address); +#elif defined(CONFIG_NRFX_RRAMC) + return nrfx_rramc_word_read(address); +#endif +} + +NRFX_STATIC_INLINE uint32_t bl_storage_word_write(uint32_t address, uint32_t value) +{ +#if defined(CONFIG_NRFX_NVMC) + nrfx_nvmc_word_write(address, value); + return 0; +#elif defined(CONFIG_NRFX_RRAMC) + nrfx_rramc_word_write(address, value); + return 0; +#endif +} + +NRFX_STATIC_INLINE uint16_t bl_storage_otp_halfword_read(uint32_t address) +{ + uint16_t halfword; +#if defined(CONFIG_NRFX_NVMC) + halfword = nrfx_nvmc_otp_halfword_read(address); +#elif defined(CONFIG_NRFX_RRAMC) + uint32_t word = nrfx_rramc_otp_word_read(index_from_address(address)); + + if (!(address & 0x3)) { + halfword = (uint16_t)(word & 0x0000FFFF); /* C truncates the upper bits */ + } else { + halfword = (uint16_t)(word >> 16); /* Shift the upper half down */ + } +#endif + return halfword; +} + +NRFX_STATIC_INLINE lcs_data_t bl_storage_lcs_get(uint32_t address) +{ +#if defined(CONFIG_NRFX_NVMC) + return nrfx_nvmc_otp_halfword_read(address); +#elif defined(CONFIG_NRFX_RRAMC) + return nrfx_rramc_otp_word_read(index_from_address(address)); +#endif +} + +NRFX_STATIC_INLINE int bl_storage_lcs_set(uint32_t address, lcs_data_t state) +{ +#if defined(CONFIG_NRFX_NVMC) + nrfx_nvmc_halfword_write(address, state); +#elif defined(CONFIG_NRFX_RRAMC) + bl_storage_word_write(address, state); +#endif + return 0; +} + /** * @brief Read the current life cycle state the device is in from OTP, * @@ -252,10 +386,10 @@ NRFX_STATIC_INLINE int read_life_cycle_state(enum lcs *lcs) return -EINVAL; } - uint16_t provisioning = nrfx_nvmc_otp_halfword_read( + lcs_data_t provisioning = bl_storage_lcs_get( (uint32_t) &BL_STORAGE->lcs.provisioning); - uint16_t secure = nrfx_nvmc_otp_halfword_read((uint32_t) &BL_STORAGE->lcs.secure); - uint16_t decommissioned = nrfx_nvmc_otp_halfword_read( + lcs_data_t secure = bl_storage_lcs_get((uint32_t) &BL_STORAGE->lcs.secure); + lcs_data_t decommissioned = bl_storage_lcs_get( (uint32_t) &BL_STORAGE->lcs.decommissioned); if (provisioning == STATE_NOT_ENTERED @@ -318,18 +452,15 @@ NRFX_STATIC_INLINE int update_life_cycle_state(enum lcs next_lcs) /* As the device starts in ASSEMBLY, it is not possible to write it */ if (current_lcs == BL_STORAGE_LCS_ASSEMBLY && next_lcs == BL_STORAGE_LCS_PROVISIONING) { - nrfx_nvmc_halfword_write((uint32_t)&BL_STORAGE->lcs.provisioning, STATE_ENTERED); - return 0; + return bl_storage_lcs_set((uint32_t)&BL_STORAGE->lcs.provisioning, STATE_ENTERED); } if (current_lcs == BL_STORAGE_LCS_PROVISIONING && next_lcs == BL_STORAGE_LCS_SECURED) { - nrfx_nvmc_halfword_write((uint32_t)&BL_STORAGE->lcs.secure, STATE_ENTERED); - return 0; + return bl_storage_lcs_set((uint32_t)&BL_STORAGE->lcs.secure, STATE_ENTERED); } if (current_lcs == BL_STORAGE_LCS_SECURED && next_lcs == BL_STORAGE_LCS_DECOMMISSIONED) { - nrfx_nvmc_halfword_write((uint32_t)&BL_STORAGE->lcs.decommissioned, STATE_ENTERED); - return 0; + return bl_storage_lcs_set((uint32_t)&BL_STORAGE->lcs.decommissioned, STATE_ENTERED); } /* This will be the case if any invalid transition is tried */ diff --git a/include/bl_validation.h b/include/bl_validation.h index 7244633a17d2..7e64db647c07 100644 --- a/include/bl_validation.h +++ b/include/bl_validation.h @@ -14,6 +14,7 @@ extern "C" { #include #include #include +#include /** @defgroup bl_validation Bootloader firmware validation * @{ @@ -72,7 +73,7 @@ struct bl_validate_fw_ext_api { * * @return See @ref set_monotonic_counter. */ -int set_monotonic_version(uint16_t version, uint16_t slot); +int set_monotonic_version(counter_t version, uint16_t slot); /** Write the stored 15-bit version to the 16-bit output parameter 'version_out'. * @@ -81,7 +82,7 @@ int set_monotonic_version(uint16_t version, uint16_t slot); * @retval 0 Success * @retval -EINVAL Error during reading the version or version is NULL. */ -int get_monotonic_version(uint16_t *version_out); +int get_monotonic_version(counter_t *version_out); /** Write the stored slot to the output parameter 'slot_out'. * @@ -90,7 +91,7 @@ int get_monotonic_version(uint16_t *version_out); * @retval 0 Success * @retval -EINVAL Error during reading the version or version is NULL. */ -int get_monotonic_slot(uint16_t *slot_out); +int get_monotonic_slot(counter_t *slot_out); /** @} */ diff --git a/samples/bootloader/src/main.c b/samples/bootloader/src/main.c index 956dfa34e61d..16a3b387532b 100644 --- a/samples/bootloader/src/main.c +++ b/samples/bootloader/src/main.c @@ -13,7 +13,13 @@ #include #include #include +#ifdef CONFIG_NRFX_NVMC #include +#elif defined(CONFIG_NRFX_RRAMC) +#include +#else +#error "No NRFX memory backend selected" +#endif #if defined(CONFIG_HW_UNIQUE_KEY_LOAD) #include @@ -28,7 +34,11 @@ int load_huk(void) if (*(uint32_t *)huk_flag_addr == 0xFFFFFFFF) { printk("First boot, expecting app to write HUK.\n"); +#ifdef CONFIG_NRFX_NVMC nrfx_nvmc_word_write(huk_flag_addr, 0); +#elif defined(CONFIG_NRFX_RRAMC) + nrfx_rramc_word_write(huk_flag_addr, 0); +#endif return 0; } printk("Error: Hardware Unique Key not present.\n"); diff --git a/subsys/bootloader/bl_storage/bl_storage.c b/subsys/bootloader/bl_storage/bl_storage.c index cbbed0adb5fa..3f145617fe1f 100644 --- a/subsys/bootloader/bl_storage/bl_storage.c +++ b/subsys/bootloader/bl_storage/bl_storage.c @@ -4,12 +4,12 @@ * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ -#include "bl_storage.h" +#include +#include #include #include #include #include -#include #include #define TYPE_COUNTERS 1 /* Type referring to counter collection. */ @@ -25,33 +25,6 @@ BUILD_ASSERT(CONFIG_MCUBOOT_HW_DOWNGRADE_PREVENTION_COUNTER_SLOTS % 2 == 0, "CONFIG_MCUBOOT_HW_DOWNGRADE_PREVENTION_COUNTER_SLOTS was not an even number"); #endif -/** This library implements monotonic counters where each time the counter - * is increased, a new slot is written. - * This way, the counter can be updated without erase. This is, among other things, - * necessary so the counter can be used in the OTP section of the UICR - * (available on e.g. nRF91 and nRF53). - */ -struct monotonic_counter { - /* Counter description. What the counter is used for. See - * BL_MONOTONIC_COUNTERS_DESC_x. - */ - uint16_t description; - /* Number of entries in 'counter_slots' list. */ - uint16_t num_counter_slots; - uint16_t counter_slots[1]; -}; - -/** The second data structure in the provision page. It has unknown length since - * 'counters' is repeated. Note that each entry in counters also has unknown - * length, and each entry can have different length from the others, so the - * entries beyond the first cannot be accessed via array indices. - */ -struct counter_collection { - uint16_t type; /* Must be "monotonic counter". */ - uint16_t num_counters; /* Number of entries in 'counters' list. */ - struct monotonic_counter counters[1]; -}; - /* * BL_STORAGE is usually, but not always, in UICR. For code simplicity * we read it as if it were a UICR address as it is safe (although @@ -59,17 +32,17 @@ struct counter_collection { */ uint32_t s0_address_read(void) { - return nrfx_nvmc_uicr_word_read(&BL_STORAGE->s0_address); + return bl_storage_word_read((uint32_t)&BL_STORAGE->s0_address); } uint32_t s1_address_read(void) { - return nrfx_nvmc_uicr_word_read(&BL_STORAGE->s1_address); + return bl_storage_word_read((uint32_t)&BL_STORAGE->s1_address); } uint32_t num_public_keys_read(void) { - return nrfx_nvmc_uicr_word_read(&BL_STORAGE->num_public_keys); + return bl_storage_word_read((uint32_t)&BL_STORAGE->num_public_keys); } /* Value written to the invalidation token when invalidating an entry. */ @@ -85,7 +58,7 @@ static bool key_is_valid(uint32_t key_idx) return false; } - const uint32_t token = nrfx_nvmc_uicr_word_read(&BL_STORAGE->key_data[key_idx].valid); + const uint32_t token = bl_storage_word_read((uint32_t)&BL_STORAGE->key_data[key_idx].valid); const uint32_t invalid_val = INVALID_VAL | key_idx << TOKEN_IDX_OFFSET; const uint32_t valid_val = VALID_VAL | key_idx << TOKEN_IDX_OFFSET; @@ -111,7 +84,7 @@ int verify_public_keys(void) for (uint32_t i = 0; i < SB_PUBLIC_KEY_HASH_LEN / 2; i++) { const uint16_t *hash_as_halfwords = (const uint16_t *)BL_STORAGE->key_data[n].hash; - uint16_t halfword = nrfx_nvmc_otp_halfword_read( + uint16_t halfword = bl_storage_otp_halfword_read( (uint32_t)&hash_as_halfwords[i]); if (halfword == 0xFFFF) { return -EHASHFF; @@ -155,16 +128,16 @@ void invalidate_public_key(uint32_t key_idx) if (key_is_valid(key_idx)) { /* Write if not already written. */ - nrfx_nvmc_word_write((uint32_t)invalidation_token, INVALID_WRITE_VAL); + bl_storage_word_write((uint32_t)invalidation_token, INVALID_WRITE_VAL); } } /** Get the counter_collection data structure in the provision data. */ -static const struct counter_collection *get_counter_collection(void) +const struct counter_collection *get_counter_collection(void) { const struct counter_collection *collection = (struct counter_collection *) &BL_STORAGE->key_data[num_public_keys_read()]; - return nrfx_nvmc_otp_halfword_read((uint32_t)&collection->type) == TYPE_COUNTERS + return bl_storage_otp_halfword_read((uint32_t)&collection->type) == TYPE_COUNTERS ? collection : NULL; } @@ -172,7 +145,7 @@ static const struct counter_collection *get_counter_collection(void) * * param[in] description Which counter to get. See COUNTER_DESC_*. */ -static const struct monotonic_counter *get_counter_struct(uint16_t description) +const struct monotonic_counter *get_counter_struct(uint16_t description) { const struct counter_collection *counters = get_counter_collection(); @@ -182,12 +155,12 @@ static const struct monotonic_counter *get_counter_struct(uint16_t description) const struct monotonic_counter *current = counters->counters; - for (size_t i = 0; i < nrfx_nvmc_otp_halfword_read( + for (size_t i = 0; i < bl_storage_otp_halfword_read( (uint32_t)&counters->num_counters); i++) { - uint16_t num_slots = nrfx_nvmc_otp_halfword_read( + uint16_t num_slots = bl_storage_otp_halfword_read( (uint32_t)¤t->num_counter_slots); - if (nrfx_nvmc_otp_halfword_read((uint32_t)¤t->description) == description) { + if (bl_storage_otp_halfword_read((uint32_t)¤t->description) == description) { return current; } @@ -205,7 +178,7 @@ int num_monotonic_counter_slots(uint16_t counter_desc, uint16_t *counter_slots) return -EINVAL; } - uint16_t num_slots = nrfx_nvmc_otp_halfword_read((uint32_t)&counter->num_counter_slots); + uint16_t num_slots = bl_storage_otp_halfword_read((uint32_t)&counter->num_counter_slots); if (num_slots == 0xFFFF) { /* We consider the 0xFFFF as invalid since it is the default value of the OTP */ @@ -227,12 +200,12 @@ int num_monotonic_counter_slots(uint16_t counter_desc, uint16_t *counter_slots) * @retval -EINVAL Cannot find counters with description @p counter_desc or the pointer to * @p counter_value is NULL. */ -static int get_counter(uint16_t counter_desc, uint16_t *counter_value, const uint16_t **free_slot) +int get_counter(uint16_t counter_desc, counter_t *counter_value, const counter_t **free_slot) { - uint16_t highest_counter = 0; - const uint16_t *addr = NULL; + const counter_t *slots; + counter_t highest_counter = 0; + const counter_t *addr = NULL; const struct monotonic_counter *counter_obj = get_counter_struct(counter_desc); - const uint16_t *slots; uint16_t num_counter_slots; if (counter_obj == NULL || counter_value == NULL) { @@ -240,10 +213,10 @@ static int get_counter(uint16_t counter_desc, uint16_t *counter_value, const uin } slots = counter_obj->counter_slots; - num_counter_slots = nrfx_nvmc_otp_halfword_read((uint32_t)&counter_obj->num_counter_slots); + num_counter_slots = bl_storage_otp_halfword_read((uint32_t)&counter_obj->num_counter_slots); - for (uint32_t i = 0; i < num_counter_slots; i++) { - uint16_t counter = ~nrfx_nvmc_otp_halfword_read((uint32_t)&slots[i]); + for (uint16_t i = 0; i < num_counter_slots; i++) { + counter_t counter = bl_storage_counter_get((uint32_t)&slots[i]); if (counter == 0) { addr = &slots[i]; @@ -262,15 +235,15 @@ static int get_counter(uint16_t counter_desc, uint16_t *counter_value, const uin return 0; } -int get_monotonic_counter(uint16_t counter_desc, uint16_t *counter_value) +int get_monotonic_counter(uint16_t counter_desc, counter_t *counter_value) { return get_counter(counter_desc, counter_value, NULL); } -int set_monotonic_counter(uint16_t counter_desc, uint16_t new_counter) +int set_monotonic_counter(uint16_t counter_desc, counter_t new_counter) { - const uint16_t *next_counter_addr; - uint16_t current_cnt_value; + const counter_t *next_counter_addr; + counter_t current_cnt_value; int err; err = get_counter(counter_desc, ¤t_cnt_value, &next_counter_addr); @@ -288,6 +261,6 @@ int set_monotonic_counter(uint16_t counter_desc, uint16_t new_counter) return -ENOMEM; } - nrfx_nvmc_halfword_write((uint32_t)next_counter_addr, ~new_counter); + bl_storage_counter_set((uint32_t)next_counter_addr, new_counter); return 0; } diff --git a/subsys/bootloader/bl_validation/bl_validation.c b/subsys/bootloader/bl_validation/bl_validation.c index 91cc45de4b19..3e55ff00b91f 100644 --- a/subsys/bootloader/bl_validation/bl_validation.c +++ b/subsys/bootloader/bl_validation/bl_validation.c @@ -18,11 +18,11 @@ LOG_MODULE_REGISTER(bl_validation, CONFIG_SECURE_BOOT_VALIDATION_LOG_LEVEL); * into the least significant bit. */ -int set_monotonic_version(uint16_t version, uint16_t slot) +int set_monotonic_version(counter_t version, uint16_t slot) { __ASSERT(version <= 0x7FFF, "version too large.\r\n"); __ASSERT(slot <= 1, "Slot must be either 0 or 1.\r\n"); - LOG_INF("Setting monotonic counter (version: %d, slot: %d)\r\n", + LOG_INF("Setting monotonic counter (version: %d, slot: %d)", version, slot); uint16_t num_cnt_slots; @@ -48,9 +48,9 @@ int set_monotonic_version(uint16_t version, uint16_t slot) return err; } -int get_monotonic_version(uint16_t *version_out) +int get_monotonic_version(counter_t *version_out) { - uint16_t monotonic_version_and_slot; + counter_t monotonic_version_and_slot; int err; if (version_out == NULL) { @@ -59,6 +59,7 @@ int get_monotonic_version(uint16_t *version_out) err = get_monotonic_counter(BL_MONOTONIC_COUNTERS_DESC_NSIB, &monotonic_version_and_slot); if (err) { + LOG_ERR("Error getting monotonic counter"); return err; } @@ -67,9 +68,9 @@ int get_monotonic_version(uint16_t *version_out) return err; } -int get_monotonic_slot(uint16_t *slot_out) +int get_monotonic_slot(counter_t *slot_out) { - uint16_t monotonic_version_and_slot; + counter_t monotonic_version_and_slot; int err; if (slot_out == NULL) { @@ -193,7 +194,7 @@ validation_info_find(uint32_t start_address, uint32_t search_distance) return NULL; } -#ifdef CONFIG_SB_VALIDATE_FW_SIGNATURE +#if defined(CONFIG_SB_VALIDATE_FW_SIGNATURE) static bool validate_signature(const uint32_t fw_src_address, const uint32_t fw_size, const struct fw_validation_info *fw_val_info, bool external) @@ -338,7 +339,13 @@ static bool validate_firmware(uint32_t fw_dst_address, uint32_t fw_src_address, return false; } + LOG_INF("Trying to get Firmware version"); + +#if defined(CONFIG_NRFX_NVMC) uint16_t stored_version; +#elif defined(CONFIG_NRFX_RRAMC) + uint32_t stored_version; +#endif int err = get_monotonic_version(&stored_version); @@ -407,7 +414,7 @@ static bool validate_firmware(uint32_t fw_dst_address, uint32_t fw_src_address, return false; } -#ifdef CONFIG_SB_VALIDATE_FW_SIGNATURE +#if defined(CONFIG_SB_VALIDATE_FW_SIGNATURE) return validate_signature(fw_src_address, fwinfo->size, fw_val_info, external); #elif defined(CONFIG_SB_VALIDATE_FW_HASH) diff --git a/subsys/fw_info/fw_info.c b/subsys/fw_info/fw_info.c index a0292a2962f2..3f25fa329859 100644 --- a/subsys/fw_info/fw_info.c +++ b/subsys/fw_info/fw_info.c @@ -10,7 +10,11 @@ #include #include #include +#ifdef CONFIG_NRFX_NVMC #include +#elif defined(CONFIG_NRFX_RRAMC) +#include +#endif #include #include @@ -205,12 +209,14 @@ BUILD_ASSERT((INVALID_VAL & CONFIG_FW_INFO_VALID_VAL) "image cannot be invalidated. Change the value so that writing " "INVALID_VAL has an effect."); -#ifdef CONFIG_NRFX_NVMC void fw_info_invalidate(const struct fw_info *fw_info) { /* Check if value has been written. */ if (fw_info->valid == CONFIG_FW_INFO_VALID_VAL) { +#ifdef CONFIG_NRFX_NVMC nrfx_nvmc_word_write((uint32_t)&(fw_info->valid), INVALID_VAL); +#elif defined(CONFIG_NRFX_RRAMC) + nrfx_rramc_word_write((uint32_t)&(fw_info->valid), INVALID_VAL); +#endif } } -#endif From b1a76958371aa87c6ce0fdf8593fee32b7f3ef0a Mon Sep 17 00:00:00 2001 From: Mateusz Michalek Date: Mon, 29 Jul 2024 16:03:08 +0200 Subject: [PATCH 04/10] cmake: provision: Change provision_hex to account for larger LCS Changed the provisioning script to account for larger Life Cycle State structures. Since we can only do word writes in nRF54LX life cycle states needs to be stored in words. This increased the LCS struct by 1 word requiring us to change the way the provisioning script works. This change adds the LCS size as a value to the CMake file which uses the SOC to decide which size the LCS struct is which is then passed to the python script through the `--lcs-state-size` argument. Ref. NCSDK-25306 Signed-off-by: Mateusz Michalek Signed-off-by: Sigurd Hellesvik --- cmake/sysbuild/provision_hex.cmake | 9 +++++++++ scripts/bootloader/provision.py | 20 ++++++++++++-------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/cmake/sysbuild/provision_hex.cmake b/cmake/sysbuild/provision_hex.cmake index e88b86d1e3dd..81d727afc74d 100644 --- a/cmake/sysbuild/provision_hex.cmake +++ b/cmake/sysbuild/provision_hex.cmake @@ -18,6 +18,13 @@ function(provision application prefix_name) set(PROVISION_HEX_NAME ${prefix_name}provision.hex) set(PROVISION_HEX ${CMAKE_BINARY_DIR}/${PROVISION_HEX_NAME}) + # This calculation is based on the LCS cycle state datacycle state data struct in bl_storage.h + if(CONFIG_SOC_SERIES_NRF54LX) + set(lcs_state_struct_size 0x10) # 4 * 4 bytes + else() + set(lcs_state_struct_size 0x8) # 4 * 2 bytes + endif() + if(CONFIG_SECURE_BOOT) if(DEFINED CONFIG_SB_MONOTONIC_COUNTER) set(monotonic_counter_arg @@ -96,6 +103,7 @@ function(provision application prefix_name) ${monotonic_counter_arg} ${no_verify_hashes_arg} ${mcuboot_counters_slots} + --lcs-state-size ${lcs_state_struct_size} DEPENDS ${PROVISION_KEY_DEPENDS} ${PROVISION_DEPENDS} @@ -118,6 +126,7 @@ function(provision application prefix_name) --max-size ${CONFIG_PM_PARTITION_SIZE_PROVISION} ${mcuboot_counters_num} ${mcuboot_counters_slots} + --lcs-state-size ${lcs_state_struct_size} DEPENDS ${PROVISION_KEY_DEPENDS} WORKING_DIRECTORY diff --git a/scripts/bootloader/provision.py b/scripts/bootloader/provision.py index 191a5171a407..80128d0527e9 100644 --- a/scripts/bootloader/provision.py +++ b/scripts/bootloader/provision.py @@ -13,10 +13,8 @@ from hashlib import sha256 import os -# Size of LCS storage and implementation ID in OTP in bytes -LCS_STATE_SIZE = 0x8 +# Size of implementation ID in OTP in bytes IMPLEMENTATION_ID_SIZE = 0x20 -NUM_BYTES_PROVISIONED_ELSEWHERE = LCS_STATE_SIZE + IMPLEMENTATION_ID_SIZE # These variable names and values are copied from bl_storage.h and # should be kept in sync @@ -57,7 +55,7 @@ def add_hw_counters(provision_data, num_counter_slots_version, mcuboot_counters_ return provision_data -def generate_mcuboot_only_provision_hex_file(provision_address, output, max_size, mcuboot_counters_slots): +def generate_mcuboot_only_provision_hex_file(provision_address, output, max_size, mcuboot_counters_slots, lcs_state_sz): # This function generates a .hex file with the provisioned data # for the uncommon use-case where MCUBoot is present, but NSIB is # not. @@ -72,7 +70,7 @@ def generate_mcuboot_only_provision_hex_file(provision_address, output, max_size # which we set to 0 and which will be used by MCUBoot at runtime # to calculate where the counter are located. - num_bytes_in_lcs = LCS_STATE_SIZE + num_bytes_in_lcs = lcs_state_sz num_bytes_in_implementation_id = IMPLEMENTATION_ID_SIZE num_bytes_in_s0_address = 4 num_bytes_in_s1_address = 4 @@ -132,6 +130,8 @@ def parse_args(): help="The MCUBOOT bootloader is used without the NSIB bootloader. Only the provision address, the MCUBOOT counters and the MCUBOOT counters slots arguments will be used.") parser.add_argument('--mcuboot-counters-slots', required=False, type=int, default=0, help='Number of monotonic counter slots for every MCUBOOT counter.') + parser.add_argument('--lcs-state-size', required=False, type=lambda x: int(x, 0), default=0x8, + help='Size of life cycle state data structure in bytes.') return parser.parse_args() @@ -155,6 +155,9 @@ def get_hashes(public_key_files, verify_hashes): def main(): args = parse_args() + lcs_state_size = args.lcs_state_size + num_bytes_provisioned_elsewhere = lcs_state_size + IMPLEMENTATION_ID_SIZE + if not args.mcuboot_only and args.s0_addr is None: raise RuntimeError("Either --mcuboot-only or --s0-addr must be specified") @@ -163,7 +166,8 @@ def main(): provision_address=args.provision_addr, output=args.output, max_size=args.max_size, - mcuboot_counters_slots=args.mcuboot_counters_slots + mcuboot_counters_slots=args.mcuboot_counters_slots, + lcs_state_sz=lcs_state_size ) return @@ -174,8 +178,8 @@ def main(): # The LCS and implementation ID is stored in the OTP before the # rest of the provisioning data so add it to the given base # address - provision_address = args.provision_addr + NUM_BYTES_PROVISIONED_ELSEWHERE - max_size = args.max_size - NUM_BYTES_PROVISIONED_ELSEWHERE + provision_address = args.provision_addr + num_bytes_provisioned_elsewhere + max_size = args.max_size - num_bytes_provisioned_elsewhere hashes = [] if args.public_key_files: From 3a2a8bdf3fe8d2359c2860f1a7be14a64e4a5dd7 Mon Sep 17 00:00:00 2001 From: Mateusz Michalek Date: Thu, 1 Aug 2024 19:13:41 +0200 Subject: [PATCH 05/10] bootloader: Fix up lacking fprotect guards Instead of just failing on compilation this commit changes fprotect requirements so that it prints out a warning and error in log. Signed-off-by: Mateusz Michalek --- samples/bootloader/src/main.c | 17 ++++++++++++++--- subsys/bootloader/bl_boot/bl_boot.c | 14 +++++++++++--- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/samples/bootloader/src/main.c b/samples/bootloader/src/main.c index 16a3b387532b..4fcffcc8d649 100644 --- a/samples/bootloader/src/main.c +++ b/samples/bootloader/src/main.c @@ -9,7 +9,11 @@ #include #include #include +#ifdef CONFIG_FPROTECT #include +#else +#warning "FPROTECT not enabled, the bootloader will be unprotected." +#endif #include #include #include @@ -60,7 +64,7 @@ SYS_INIT(load_huk, PRE_KERNEL_2, 0); #endif -static void validate_and_boot(const struct fw_info *fw_info, uint16_t slot) +static void validate_and_boot(const struct fw_info *fw_info, counter_t slot) { printk("Attempting to boot slot %d.\r\n", slot); @@ -81,7 +85,8 @@ static void validate_and_boot(const struct fw_info *fw_info, uint16_t slot) printk("Firmware version %d\r\n", fw_info->version); - uint16_t stored_version; + counter_t stored_version; + int err = get_monotonic_version(&stored_version); if (err) { @@ -123,7 +128,13 @@ static void validate_and_boot(const struct fw_info *fw_info, uint16_t slot) int main(void) { - int err = fprotect_area(PM_B0_ADDRESS, PM_B0_SIZE); + int err = 0; + + if (IS_ENABLED(CONFIG_FPROTECT)) { + err = fprotect_area(PM_B0_ADDRESS, PM_B0_SIZE); + } else { + printk("Fprotect disabled. No protection applied.\n\r"); + } if (err) { printk("Failed to protect B0 flash, cancel startup.\n\r"); diff --git a/subsys/bootloader/bl_boot/bl_boot.c b/subsys/bootloader/bl_boot/bl_boot.c index 45575dc5a49d..93e511542ba9 100644 --- a/subsys/bootloader/bl_boot/bl_boot.c +++ b/subsys/bootloader/bl_boot/bl_boot.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -67,8 +68,9 @@ extern uint32_t _vector_table_pointer; void bl_boot(const struct fw_info *fw_info) { #if !(defined(CONFIG_SOC_SERIES_NRF91X) \ - || defined(CONFIG_SOC_NRF5340_CPUNET) \ - || defined(CONFIG_SOC_NRF5340_CPUAPP)) + || defined(CONFIG_SOC_SERIES_NRF54LX) \ + || defined(CONFIG_SOC_NRF5340_CPUNET) \ + || defined(CONFIG_SOC_NRF5340_CPUAPP)) /* Protect bootloader storage data after firmware is validated so * invalidation of public keys can be written into the page if needed. * Note that for some devices (for example, nRF9160 and the nRF5340 @@ -77,7 +79,13 @@ void bl_boot(const struct fw_info *fw_info) * bootloader storage data is locked together with the network core * application. */ - int err = fprotect_area(PM_PROVISION_ADDRESS, PM_PROVISION_SIZE); + int err = 0; + + if (IS_ENABLED(CONFIG_FPROTECT)) { + err = fprotect_area(PM_PROVISION_ADDRESS, PM_PROVISION_SIZE); + } else { + printk("Fprotect disabled. No protection applied.\n\r"); + } if (err) { printk("Failed to protect bootloader storage.\n\r"); From 95dccfaf6f90e02b2a95ad084bd793f15a420c2a Mon Sep 17 00:00:00 2001 From: Mateusz Michalek Date: Fri, 11 Oct 2024 17:00:24 +0200 Subject: [PATCH 06/10] lib: fprotect: enable RRAMC low level backend Selects NRFX_RRAMC symbol required by fprotect RRAM backend on 54L15 platform. Signed-off-by: Mateusz Michalek --- lib/fprotect/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/fprotect/Kconfig b/lib/fprotect/Kconfig index 7144e06bf940..bb70bee3963d 100644 --- a/lib/fprotect/Kconfig +++ b/lib/fprotect/Kconfig @@ -47,6 +47,7 @@ config FPROTECT_BLOCK_SIZE menuconfig FPROTECT bool "Enable FPROTECT" depends on SOC_FAMILY_NORDIC_NRF + select NRFX_RRAMC if SOC_SERIES_NRF54LX help Enable the software library FPROTECT that may or may not be used by other systems to protect flash from writes and possibly also From 187f9e77d40248b4c723282121d4b4d2ffdaca89 Mon Sep 17 00:00:00 2001 From: Mateusz Michalek Date: Sun, 13 Oct 2024 15:12:11 +0200 Subject: [PATCH 07/10] tests: bl_storage: nrf54l15 Adds bl_storage tests. Signed-off-by: Mateusz Michalek --- subsys/bootloader/bl_storage/Kconfig | 1 + tests/subsys/bootloader/bl_storage/src/main.c | 2 +- tests/subsys/bootloader/bl_storage/testcase.yaml | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/subsys/bootloader/bl_storage/Kconfig b/subsys/bootloader/bl_storage/Kconfig index 29d80352c2b8..38b7f6404e85 100644 --- a/subsys/bootloader/bl_storage/Kconfig +++ b/subsys/bootloader/bl_storage/Kconfig @@ -6,3 +6,4 @@ config SECURE_BOOT_STORAGE bool "Library for accessing the bootloader storage" + select NRFX_RRAMC if SOC_SERIES_NRF54LX diff --git a/tests/subsys/bootloader/bl_storage/src/main.c b/tests/subsys/bootloader/bl_storage/src/main.c index 48039afb8f43..765cf61d9310 100644 --- a/tests/subsys/bootloader/bl_storage/src/main.c +++ b/tests/subsys/bootloader/bl_storage/src/main.c @@ -11,7 +11,7 @@ ZTEST(bl_storage_test, test_monotonic_counter) { - uint16_t counter_value; + counter_t counter_value; uint16_t counter_slots; /* The initial counter is written by B0 and the function * set_monotonic_version. This function calculates the counter diff --git a/tests/subsys/bootloader/bl_storage/testcase.yaml b/tests/subsys/bootloader/bl_storage/testcase.yaml index 14f4b4efa003..276d1f4aab7f 100644 --- a/tests/subsys/bootloader/bl_storage/testcase.yaml +++ b/tests/subsys/bootloader/bl_storage/testcase.yaml @@ -2,11 +2,13 @@ tests: bootloader.bl_storage: sysbuild: true platform_allow: + - nrf54l15pdk/nrf54l15/cpuapp - nrf5340dk/nrf5340/cpuapp - nrf9151dk/nrf9151 - nrf9160dk/nrf9160 - nrf9161dk/nrf9161 integration_platforms: + - nrf54l15pdk/nrf54l15/cpuapp - nrf5340dk/nrf5340/cpuapp - nrf9151dk/nrf9151 - nrf9160dk/nrf9160 From c5844e7e316adc98cbbc64d242d636144b1532a5 Mon Sep 17 00:00:00 2001 From: Grzegorz Chwierut Date: Thu, 31 Oct 2024 13:05:52 +0100 Subject: [PATCH 08/10] tests: boot_chains: enable nrf54l15dk Add nrf54l15dk to boot_chains configurations. Signed-off-by: Grzegorz Chwierut --- tests/subsys/bootloader/bl_storage/testcase.yaml | 4 ++-- tests/subsys/bootloader/boot_chains/testcase.yaml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/subsys/bootloader/bl_storage/testcase.yaml b/tests/subsys/bootloader/bl_storage/testcase.yaml index 276d1f4aab7f..4d91a3105063 100644 --- a/tests/subsys/bootloader/bl_storage/testcase.yaml +++ b/tests/subsys/bootloader/bl_storage/testcase.yaml @@ -2,13 +2,13 @@ tests: bootloader.bl_storage: sysbuild: true platform_allow: - - nrf54l15pdk/nrf54l15/cpuapp + - nrf54l15dk/nrf54l15/cpuapp - nrf5340dk/nrf5340/cpuapp - nrf9151dk/nrf9151 - nrf9160dk/nrf9160 - nrf9161dk/nrf9161 integration_platforms: - - nrf54l15pdk/nrf54l15/cpuapp + - nrf54l15dk/nrf54l15/cpuapp - nrf5340dk/nrf5340/cpuapp - nrf9151dk/nrf9151 - nrf9160dk/nrf9160 diff --git a/tests/subsys/bootloader/boot_chains/testcase.yaml b/tests/subsys/bootloader/boot_chains/testcase.yaml index a97caa402381..19aa486c55f0 100644 --- a/tests/subsys/bootloader/boot_chains/testcase.yaml +++ b/tests/subsys/bootloader/boot_chains/testcase.yaml @@ -2,6 +2,7 @@ common: platform_allow: nrf52840dk/nrf52840 nrf5340dk/nrf5340/cpuapp + nrf54l15dk/nrf54l15/cpuapp # NB: It's not yet supported to boot TF-M from NSIB without # MCUBoot enabled as well harness: console From 2e423d8a71bbca17c1244a0a0cc639270c8e7e3c Mon Sep 17 00:00:00 2001 From: Mateusz Michalek Date: Mon, 4 Nov 2024 11:57:15 +0100 Subject: [PATCH 09/10] bootloader: bl_boot: uninit of UARTE20 adds another UART's uninit call. Signed-off-by: Mateusz Michalek --- subsys/bootloader/bl_boot/bl_boot.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/subsys/bootloader/bl_boot/bl_boot.c b/subsys/bootloader/bl_boot/bl_boot.c index 93e511542ba9..b5eb7c4021b1 100644 --- a/subsys/bootloader/bl_boot/bl_boot.c +++ b/subsys/bootloader/bl_boot/bl_boot.c @@ -53,6 +53,9 @@ static void uninit_used_peripherals(void) #if defined(CONFIG_HAS_HW_NRF_UARTE2) uninit_used_uarte(NRF_UARTE2); #endif +#if defined(CONFIG_HAS_HW_NRF_UARTE20) + uninit_used_uarte(NRF_UARTE20); +#endif #endif /* CONFIG_UART_NRFX */ nrf_clock_int_disable(NRF_CLOCK, 0xFFFFFFFF); From 74cce6bc8c325137702fa6207d9aef3f0eb3ef94 Mon Sep 17 00:00:00 2001 From: Mateusz Michalek Date: Wed, 7 Aug 2024 09:49:48 +0200 Subject: [PATCH 10/10] Kconfigs: adjust selection of NRFX_NVMC NRFX_NVMC is being selected conditionally as it is not the one and only memory backend anymore. Signed-off-by: Mateusz Michalek --- lib/hw_unique_key/Kconfig | 4 ++-- samples/bootloader/prj.conf | 1 - samples/bootloader/prj_minimal.conf | 1 - subsys/bootloader/Kconfig | 2 +- subsys/fw_info/Kconfig | 2 +- 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/hw_unique_key/Kconfig b/lib/hw_unique_key/Kconfig index b739b0094ba5..6b394572cd55 100644 --- a/lib/hw_unique_key/Kconfig +++ b/lib/hw_unique_key/Kconfig @@ -18,7 +18,7 @@ config HW_UNIQUE_KEY_LOAD depends on HAS_HW_NRF_CC310 depends on NRF_CC3XX_PLATFORM select FPROTECT - imply NRFX_NVMC + imply NRFX_NVMC if !SOC_SERIES_NRF54LX help Use this on devices with no KMU. Enable this in the immutable bootloader to load the HUK from the allocated flash area @@ -46,7 +46,7 @@ config HW_UNIQUE_KEY select PSA_WANT_ALG_GCM if CRACEN_HW_PRESENT select PSA_NEED_CRACEN_KMU_DRIVER if CRACEN_HW_PRESENT select FPROTECT if HAS_HW_NRF_ACL - imply NRFX_NVMC if HAS_HW_NRF_ACL + imply NRFX_NVMC if !SOC_SERIES_NRF54LX default y if BUILD_WITH_TFM help This option will load the Hardware Unique Key (HUK) in the KDR diff --git a/samples/bootloader/prj.conf b/samples/bootloader/prj.conf index dc6d126db27c..5ccb6b3f2089 100644 --- a/samples/bootloader/prj.conf +++ b/samples/bootloader/prj.conf @@ -22,7 +22,6 @@ CONFIG_BL_SHA256_EXT_API_ENABLED=y CONFIG_BL_SECP256R1_EXT_API_ENABLED=y CONFIG_BL_VALIDATE_FW_EXT_API_ENABLED=y CONFIG_EXT_API_PROVIDE_EXT_API_ENABLED=y -CONFIG_NRFX_NVMC=y CONFIG_MAIN_STACK_SIZE=2048 CONFIG_TIMEOUT_64BIT=n diff --git a/samples/bootloader/prj_minimal.conf b/samples/bootloader/prj_minimal.conf index 596a3e30390b..d730de7021e6 100644 --- a/samples/bootloader/prj_minimal.conf +++ b/samples/bootloader/prj_minimal.conf @@ -13,7 +13,6 @@ CONFIG_EXT_API_PROVIDE_EXT_API_ENABLED=y CONFIG_FPROTECT=y CONFIG_FW_INFO=y CONFIG_IS_SECURE_BOOTLOADER=y -CONFIG_NRFX_NVMC=y CONFIG_SECURE_BOOT_CRYPTO=y CONFIG_SECURE_BOOT_STORAGE=y CONFIG_SECURE_BOOT_VALIDATION=y diff --git a/subsys/bootloader/Kconfig b/subsys/bootloader/Kconfig index d1493589bebf..faad3a631381 100644 --- a/subsys/bootloader/Kconfig +++ b/subsys/bootloader/Kconfig @@ -198,7 +198,7 @@ menuconfig IS_SECURE_BOOTLOADER select SECURE_BOOT_VALIDATION select SECURE_BOOT_STORAGE select SW_VECTOR_RELAY if SOC_SERIES_NRF51X - imply NRFX_NVMC + imply NRFX_NVMC if !SOC_SERIES_NRF54LX help This option is set by the first stage bootloader app to include all files and set all the options required. diff --git a/subsys/fw_info/Kconfig b/subsys/fw_info/Kconfig index 77094651daf0..8fe17b4fd746 100644 --- a/subsys/fw_info/Kconfig +++ b/subsys/fw_info/Kconfig @@ -7,7 +7,7 @@ menuconfig FW_INFO bool prompt "Firmware Metadata" - imply NRFX_NVMC + imply NRFX_NVMC if !SOC_SERIES_NRF54LX if (FW_INFO)