Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NSIB support for nRF54LX series #16765

Merged
merged 10 commits into from
Nov 5, 2024
5 changes: 4 additions & 1 deletion cmake/partition_manager.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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}
Expand Down
6 changes: 5 additions & 1 deletion cmake/sysbuild/partition_manager.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
Expand All @@ -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}
Expand Down
9 changes: 9 additions & 0 deletions cmake/sysbuild/provision_hex.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions doc/nrf/libraries/security/bootloader/fw_info.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
169 changes: 150 additions & 19 deletions include/bl_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@

#include <string.h>
#include <zephyr/types.h>
#include <zephyr/autoconf.h>
#include <drivers/nrfx_common.h>
#if defined(CONFIG_NRFX_NVMC)
#include <nrfx_nvmc.h>
#elif defined(CONFIG_NRFX_RRAMC)
#include <nrfx_rramc.h>
#else
#error "No NRFX storage technology supported backend selected"
michalek-no marked this conversation as resolved.
Show resolved Hide resolved
#endif
#include <errno.h>
#include <pm_config.h>

Expand All @@ -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*/
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
*
Expand All @@ -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
Expand Down Expand Up @@ -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 */
Expand Down
7 changes: 4 additions & 3 deletions include/bl_validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ extern "C" {
#include <stdbool.h>
#include <fw_info.h>
#include <zephyr/types.h>
#include <bl_storage.h>

/** @defgroup bl_validation Bootloader firmware validation
* @{
Expand Down Expand Up @@ -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'.
*
Expand All @@ -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'.
*
Expand All @@ -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);

/** @} */

Expand Down
6 changes: 4 additions & 2 deletions include/fw_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading
Loading