Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions doc/nrf/libraries/security/bootloader/fw_info.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ External APIs

The firmware information structure allows for exchange of arbitrary tagged and versioned interfaces called *external APIs* (EXT_APIs).

.. note::
EXT_APIs are currently only supported on the nRF52 and nRF53 Series devices.

An EXT_API structure is a structure consisting of a header followed by arbitrary data.
The header consists of the following information:

Expand Down
5 changes: 0 additions & 5 deletions samples/bootloader/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@ CONFIG_LOG_DEFAULT_LEVEL=0
CONFIG_SECURE_BOOT_VALIDATION=y
CONFIG_SECURE_BOOT_VALIDATION_LOG_LEVEL_INF=y
CONFIG_SECURE_BOOT_STORAGE=y
CONFIG_BL_ROT_VERIFY_EXT_API_ENABLED=y
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_MAIN_STACK_SIZE=2048
CONFIG_TIMEOUT_64BIT=n

Expand Down
5 changes: 0 additions & 5 deletions samples/bootloader/prj_minimal.conf
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@
#

CONFIG_B0_MIN_PARTITION_SIZE=y
CONFIG_BL_ROT_VERIFY_EXT_API_ENABLED=y
CONFIG_BL_SECP256R1_EXT_API_ENABLED=y
CONFIG_BL_SHA256_EXT_API_ENABLED=y
CONFIG_BL_VALIDATE_FW_EXT_API_ENABLED=y
CONFIG_EXT_API_PROVIDE_EXT_API_ENABLED=y
CONFIG_FPROTECT=y
CONFIG_FW_INFO=y
CONFIG_IS_SECURE_BOOTLOADER=y
Expand Down
1 change: 1 addition & 0 deletions subsys/bootloader/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ config SB_BPROT_IN_DEBUG
config SB_CLEANUP_RAM
bool "Perform RAM cleanup"
depends on !FW_INFO_PROVIDE_ENABLE
depends on !MPU_STACK_GUARD
depends on CPU_CORTEX_M4 || CPU_CORTEX_M33
help
Sets contents of memory to 0 before jumping to application.
Expand Down
4 changes: 2 additions & 2 deletions subsys/bootloader/bl_boot/bl_boot.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,8 @@ static void __ramfunc jump_in(uint32_t reset)
" bx r0\n"
:
: "r" (reset),
"i" (CONFIG_SRAM_BASE_ADDRESS),
"i" (CONFIG_SRAM_SIZE * 1024),
"r" (CONFIG_SRAM_BASE_ADDRESS),
"r" (CONFIG_SRAM_SIZE * 1024),
"r" (CLEANUP_RAM_GAP_START),
"r" (CLEANUP_RAM_GAP_SIZE),
"i" (0)
Expand Down
8 changes: 8 additions & 0 deletions subsys/fw_info/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ config FW_INFO_PROVIDE_ENABLE
help
Hidden option, set if at least one *_EXT_API_ENABLED option is enabled.

config EXT_API_SUPPORTED
bool
default y if SOC_SERIES_NRF52X
default y if SOC_NRF5340_CPUAPP
default y if SOC_SERIES_NRF91X
help
External APIs are supported on the current platform.

EXT_API = EXT_API_PROVIDE
id = 0x1200
flags = 0
Expand Down
1 change: 1 addition & 0 deletions subsys/fw_info/Kconfig.template.fw_info_ext_api
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ config $(EXT_API)_EXT_API_AT_LEAST_REQUIRED

config $(EXT_API)_EXT_API_ENABLED
bool "Provide the $(EXT_API) EXT_API to other images"
default y if IS_SECURE_BOOTLOADER && EXT_API_SUPPORTED
select FW_INFO_PROVIDE_ENABLE
help
Provide this EXT_API to other images.
Expand Down
14 changes: 14 additions & 0 deletions tests/subsys/bootloader/b0_ram_cleanup/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#
# Copyright (c) 2025 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(b0_ram_cleanup)

FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})
target_include_directories(app PRIVATE .)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/

#include "../nrf54l15dk_nrf54l15_cpuapp_common.dtsi"
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#
# Copyright (c) 2025 Nordic Semiconductor
#
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this in a zephyr sub folder?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By mistake, moved

# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

cmake_minimum_required(VERSION 3.20.0)

zephyr_library()
zephyr_library_sources(src/b0_cleanup_ram_test_prepare.c)
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/

#include <zephyr/init.h>
#include <zephyr/device.h>
#include <zephyr/sys/printk.h>
#include <hal/nrf_timer.h>
#include <zephyr/linker/linker-defs.h>
#include <zephyr/drivers/retained_mem.h>

#define CLEANUP_RAM_GAP_START ((uint32_t)__ramfunc_start)
#define CLEANUP_RAM_GAP_END ((uint32_t) __ramfunc_end)

/* Only the RAM outside of the defined linker sections will be populated,
* as modifying the defined linker sections could lead to NSIB malfunction.
*/
#define RAM_TO_POPULATE_START ((uint32_t) _image_ram_end)
#define RAM_TO_POPULATE_END (CONFIG_SRAM_SIZE * 1024)
#define VALUE_TO_POPULATE 0xDEADBEAF

/* As peripheral registers are not cleared during RAM cleanup, we can
* use a register of a peripheral unused by NSIB to save data which can then
* be read by the application.
* We use this trick to save the start and end addresses of the RAMFUNC region.
* This region is not erased during the RAM cleanup, as it contains the code responsible
* for performing the RAM cleanup, so it must be skipped when checking if the RAM cleanup
* has been successfully performed.
* The TIMER00 is not used by NSIB, and is therefore its CC[0] and CC[1] registers
* are used for this purpose.
*/
#define RAMFUNC_START_SAVE_REGISTER NRF_TIMER00->CC[0]
#define RAMFUNC_END_SAVE_REGISTER NRF_TIMER00->CC[1]

#define RETAINED_DATA "RETAINED"

static int populate_ram(void)
{
RAMFUNC_START_SAVE_REGISTER = CLEANUP_RAM_GAP_START;
RAMFUNC_END_SAVE_REGISTER = CLEANUP_RAM_GAP_END;

for (uint32_t addr = RAM_TO_POPULATE_START; addr < RAM_TO_POPULATE_END;
addr += sizeof(uint32_t)) {
*(uint32_t *) addr = VALUE_TO_POPULATE;
}

#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_retained_ram)
const struct device *retained_mem_dev =
DEVICE_DT_GET(DT_INST(0, zephyr_retained_ram));
size_t retained_size;

if (!device_is_ready(retained_mem_dev)) {
printk("Retained memory device is not ready");
return -ENODEV;
}

retained_size = retained_mem_size(retained_mem_dev);

if (retained_size < strlen(RETAINED_DATA)) {
printk("Retained memory size is too small");
return -EINVAL;
}

retained_mem_write(retained_mem_dev, 0, RETAINED_DATA, strlen(RETAINED_DATA));
#endif
return 0;
}

SYS_INIT(populate_ram, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build:
cmake: .
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/

/ {
cpuapp_sram@2003FFF0 {
compatible = "zephyr,memory-region", "mmio-sram";
reg = <0x2003FFF0 0x10>;
zephyr,memory-region = "RetainedMem";
status = "okay";

retainedmem {
compatible = "zephyr,retained-ram";
status = "okay";
#address-cells = <1>;
#size-cells = <1>;

test_retention_area: test_retention_area@0 {
compatible = "zephyr,retention";
status = "okay";
reg = <0x0 0x10>;
prefix = [0B 01];
checksum = <4>;
};
};
};
};
12 changes: 12 additions & 0 deletions tests/subsys/bootloader/b0_ram_cleanup/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#
# Copyright (c) 2025 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
CONFIG_ZTEST=y
CONFIG_ARM_MPU=n
CONFIG_SOC_EARLY_RESET_HOOK=y

CONFIG_RETAINED_MEM=y
CONFIG_RETAINED_MEM_ZEPHYR_RAM=y
CONFIG_RETAINED_MEM_NRF_GPREGRET=n
149 changes: 149 additions & 0 deletions tests/subsys/bootloader/b0_ram_cleanup/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/

#include <zephyr/ztest.h>
#include <hal/nrf_timer.h>
#include <zephyr/drivers/retained_mem.h>

/* As peripheral registers are not cleared during RAM cleanup, we can
* use a register of a peripheral unused by NSIB to save data which can then
* be read by the application.
* We use this trick to save the start and end addresses of the RAMFUNC region.
* This region is not cleared during the RAM cleanup, as it contains the code responsible
* for performing the RAM cleanup, so it must be skipped when checking if the RAM cleanup
* has been successfully performed.
* The TIMER00 is not used by NSIB, and is therefore its CC[0] and CC[1] registers
* are used for this purpose.
*/
#define RAMFUNC_START_SAVE_REGISTER NRF_TIMER00->CC[0]
#define RAMFUNC_END_SAVE_REGISTER NRF_TIMER00->CC[1]

/* Using 2-byte magic values allows to load the whole value with movw */
#define RAM_CLEANUP_SUCCESS_MAGIC 0x5678
#define RAM_CLEANUP_FAILURE_MAGIC 0x4321

static uint32_t __noinit ram_cleanup_result;
static uint32_t __noinit uncleared_address;
static uint32_t __noinit uncleared_value;

#define EXPECTED_RETAINED_DATA "RETAINED"

/**
* This hook needs to have the attribute __attribute__((naked)),
* as otherwise the stack would be modified when calling it, leading to
* test failure, as part of the RAM in which the stack resides wouldn't
* be zeroed.
*/
__attribute__((naked)) void soc_early_reset_hook(void)
{
__asm__ volatile (
/* Load zero (value used to clear memory) into r0 */
" mov r0, #0\n"
/* Explicitly clear the ram_cleanup_result variable */
" mov r2, %6\n"
" str r0, [r2]\n"
/* Load the location of the saved ram func start address to r1 */
" mov r1, %0\n"
/* Load the ram func start address to r2 */
" ldr r2, [r1]\n"
/* Load the location of the saved ram func end address to r1 */
" mov r1, %1\n"
/* Load the ram func end address to r3*/
" ldr r3, [r1]\n"
/* Clear memory from ram func start to ram func end.
* The area is skipped during the RAM cleanup,
* so it is not cleared.
* Simply cleaning it up here is quicker
* than verifying if the current address is within the gap
* in each iteration of the loop.
*/
"ram_func_zero_loop:\n"
" cmp r2, r3\n"
" bge ram_func_zero_loop_done\n"
" str r0, [r2]\n"
/* Increment the address by 4 (word size) */
" add r2, r2, #4\n"
" b ram_func_zero_loop\n"
"ram_func_zero_loop_done:\n"
/* Verify that all of the RAM memory has been cleared */
/* Load SRAM base address to r1 */
" mov r1, %2\n"
/* Load SRAM size to r2 */
" mov r2, %3\n"
/* Calculate SRAM end address (base + size) */
" add r2, r1, r2\n"
"verify_ram_loop:\n"
" cmp r1, r2\n"
" bge verify_ram_done\n"
/* Load value at current address and verify if it equals 0 */
" ldr r3, [r1]\n"
" cmp r3, #0\n"
" bne ram_cleanup_failed\n"
/* Increment the address by 4 (word size) */
" add r1, r1, #4\n"
" b verify_ram_loop\n"
"ram_cleanup_failed:\n"
/* Load RAM_CLEANUP_FAILURE_MAGIC into r0 */
" movw r0, %4\n"
/* Save the uncleared address in uncleared_address variable */
" mov r2, %7\n"
" str r1, [r2]\n"
/* Save the uncleared value in uncleared_value variable */
" mov r2, %8\n"
" str r3, [r2]\n"
" b verification_done\n"
"verify_ram_done:\n"
/* Load RAM_CLEANUP_SUCCESS_MAGIC */
" movw r0, %5\n"
"verification_done:\n"
/* Store result in ram_cleanup_result variable */
" mov r2, %6\n"
" str r0, [r2]\n"
/* __attribute__((naked)) requires manual branching */
" bx lr\n"
:
: "r" (&RAMFUNC_START_SAVE_REGISTER),
"r" (&RAMFUNC_END_SAVE_REGISTER),
"r" (CONFIG_SRAM_BASE_ADDRESS),
"r" (CONFIG_SRAM_SIZE * 1024),
"i" (RAM_CLEANUP_FAILURE_MAGIC),
"i" (RAM_CLEANUP_SUCCESS_MAGIC),
"r" (&ram_cleanup_result),
"r" (&uncleared_address),
"r" (&uncleared_value)
: "r0", "r1", "r2", "r3", "lr", "memory"
);
}

ZTEST(b0_ram_cleanup, test_ram_cleanup)
{
zassert_true((ram_cleanup_result == RAM_CLEANUP_SUCCESS_MAGIC) ||
(ram_cleanup_result == RAM_CLEANUP_FAILURE_MAGIC),
"RAM cleanup result should be either success or failure");
zassert_equal(ram_cleanup_result, RAM_CLEANUP_SUCCESS_MAGIC,
"Uncleared word detected at address %p, value 0x%x", uncleared_address,
uncleared_value);

#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_retained_ram)
const struct device *retained_mem_dev =
DEVICE_DT_GET(DT_INST(0, zephyr_retained_ram));
size_t retained_size;
uint8_t buffer[strlen(EXPECTED_RETAINED_DATA)+1];

zassert_true(device_is_ready(retained_mem_dev), "Retained memory device is not ready");

retained_size = retained_mem_size(retained_mem_dev);

zassert_true(retained_size >= strlen(EXPECTED_RETAINED_DATA),
"Retained memory size is too small");

retained_mem_read(retained_mem_dev, 0, buffer, strlen(EXPECTED_RETAINED_DATA));
zassert_mem_equal(buffer, EXPECTED_RETAINED_DATA, strlen(EXPECTED_RETAINED_DATA),
"Retained data is not correct");
#endif
}

ZTEST_SUITE(b0_ram_cleanup, NULL, NULL, NULL, NULL, NULL);
9 changes: 9 additions & 0 deletions tests/subsys/bootloader/b0_ram_cleanup/sysbuild.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#
# Copyright (c) 2025 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

# Adding a module to b0 is needed as a way to inject source code into
# the non-default sysbuild image (in this case b0).
set(b0_EXTRA_ZEPHYR_MODULES ${CMAKE_CURRENT_LIST_DIR}/modules/b0_cleanup_ram_test_prepare CACHE INTERNAL "")
8 changes: 8 additions & 0 deletions tests/subsys/bootloader/b0_ram_cleanup/sysbuild.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#
# Copyright (c) 2025 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

SB_CONFIG_SECURE_BOOT_APPCORE=y
SB_CONFIG_SECURE_BOOT_GENERATE_DEFAULT_KMU_KEYFILE=y
Loading