Skip to content

Commit d97a84c

Browse files
committed
tests: bootloader: Add SB_CLEANUP_RAM tests
This commit adds tests to verify if the NSIB RAM cleanup functionality works as expected. Signed-off-by: Artur Hadasz <[email protected]>
1 parent 4ff7ae9 commit d97a84c

File tree

9 files changed

+248
-0
lines changed

9 files changed

+248
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#
2+
# Copyright (c) 2025 Nordic Semiconductor
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
cmake_minimum_required(VERSION 3.20.0)
8+
9+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
10+
project(NONE)
11+
12+
FILE(GLOB app_sources src/*.c)
13+
target_sources(app PRIVATE ${app_sources})
14+
target_include_directories(app PRIVATE .)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#
2+
# Copyright (c) 2025 Nordic Semiconductor
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
cmake_minimum_required(VERSION 3.20.0)
8+
9+
zephyr_library()
10+
zephyr_library_sources(src/b0_cleanup_ram_test_prepare.c)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
build:
2+
cmake: zephyr
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
#include <zephyr/init.h>
8+
#include <zephyr/device.h>
9+
#include <zephyr/sys/printk.h>
10+
#include <hal/nrf_timer.h>
11+
#include <zephyr/linker/linker-defs.h>
12+
13+
#define CLEANUP_RAM_GAP_START ((uint32_t)__ramfunc_start)
14+
#define CLEANUP_RAM_GAP_END ((uint32_t) __ramfunc_end)
15+
16+
/* Only the RAM outside of the defined linker sections will be populated,
17+
* as modifying the defined linker sections could lead to NSIB malfunction.
18+
*/
19+
#define RAM_TO_POPULATE_START ((uint32_t) _image_ram_end)
20+
#define RAM_TO_POPULATE_END (CONFIG_SRAM_SIZE * 1024)
21+
#define VALUE_TO_POPULATE 0xDEADBEAF
22+
23+
/* As peripheral registers are not cleared during RAM cleanup, we can
24+
* use a register of a peripheral unused by NSIB to save data which can then
25+
* be read by the application.
26+
* We use this trick to save the start and end addresses of the RAMFUNC region.
27+
* This region is not erased during the RAM cleanup, as it contains the code responsible
28+
* for performing the RAM cleanup, so it must be skipped when checking if the RAM cleanup
29+
* has been successfully performed.
30+
* The TIMER00 is not used by NSIB, and is therefore its CC[0] and CC[1] registers
31+
* are used for this purpose.
32+
*/
33+
#define RAMFUNC_START_SAVE_REGISTER NRF_TIMER00->CC[0]
34+
#define RAMFUNC_END_SAVE_REGISTER NRF_TIMER00->CC[1]
35+
36+
static int populate_ram(void)
37+
{
38+
RAMFUNC_START_SAVE_REGISTER = CLEANUP_RAM_GAP_START;
39+
RAMFUNC_END_SAVE_REGISTER = CLEANUP_RAM_GAP_END;
40+
41+
for (uint32_t addr = RAM_TO_POPULATE_START; addr < RAM_TO_POPULATE_END;
42+
addr += sizeof(uint32_t)) {
43+
*(uint32_t *) addr = VALUE_TO_POPULATE;
44+
}
45+
46+
return 0;
47+
}
48+
49+
SYS_INIT(populate_ram, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#
2+
# Copyright (c) 2025 Nordic Semiconductor ASA
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
CONFIG_ZTEST=y
7+
CONFIG_ARM_MPU=n
8+
CONFIG_SOC_EARLY_RESET_HOOK=y
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
#include <zephyr/ztest.h>
8+
#include <hal/nrf_timer.h>
9+
10+
/* As peripheral registers are not cleared during RAM cleanup, we can
11+
* use a register of a peripheral unused by NSIB to save data which can then
12+
* be read by the application.
13+
* We use this trick to save the start and end addresses of the RAMFUNC region.
14+
* This region is not cleared during the RAM cleanup, as it contains the code responsible
15+
* for performing the RAM cleanup, so it must be skipped when checking if the RAM cleanup
16+
* has been successfully performed.
17+
* The TIMER00 is not used by NSIB, and is therefore its CC[0] and CC[1] registers
18+
* are used for this purpose.
19+
*/
20+
#define RAMFUNC_START_SAVE_REGISTER NRF_TIMER00->CC[0]
21+
#define RAMFUNC_END_SAVE_REGISTER NRF_TIMER00->CC[1]
22+
23+
/* Using 2-byte magic values allows to load the whole value with movw */
24+
#define RAM_CLEANUP_SUCCESS_MAGIC 0x5678
25+
#define RAM_CLEANUP_FAILURE_MAGIC 0x4321
26+
27+
static uint32_t __noinit ram_cleanup_result;
28+
static uint32_t __noinit uncleared_address;
29+
static uint32_t __noinit uncleared_value;
30+
31+
/**
32+
* This hook needs to have the attribute __attribute__((naked)),
33+
* as otherwise the stack would be modified when calling it, leading to
34+
* test failure, as part of the RAM in which the stack resides wouldn't
35+
* be zeroed.
36+
*/
37+
__attribute__((naked)) void soc_early_reset_hook(void)
38+
{
39+
__asm__ volatile (
40+
/* Load zero (value used to clear memory) into r0 */
41+
" mov r0, #0\n"
42+
/* Explicitly clear the ram_cleanup_result variable */
43+
" mov r2, %6\n"
44+
" str r0, [r2]\n"
45+
/* Load the location of the saved ram func start address to r1 */
46+
" mov r1, %0\n"
47+
/* Load the ram func start address to r2 */
48+
" ldr r2, [r1]\n"
49+
/* Load the location of the saved ram func end address to r1 */
50+
" mov r1, %1\n"
51+
/* Load the ram func end address to r3*/
52+
" ldr r3, [r1]\n"
53+
/* Clear memory from ram func start to ram func end.
54+
* The area is skipped during the RAM cleanup,
55+
* so it is not cleared.
56+
* Simply cleaning it up here is quicker
57+
* than verifying if the current address is within the gap
58+
* in each iteration of the loop.
59+
*/
60+
"ram_func_zero_loop:\n"
61+
" cmp r2, r3\n"
62+
" bge ram_func_zero_loop_done\n"
63+
" str r0, [r2]\n"
64+
/* Increment the address by 4 (word size) */
65+
" add r2, r2, #4\n"
66+
" b ram_func_zero_loop\n"
67+
"ram_func_zero_loop_done:\n"
68+
/* Verify that all of the RAM memory has been cleared */
69+
/* Load SRAM base address to r1 */
70+
" mov r1, %2\n"
71+
/* Load SRAM size to r2 */
72+
" mov r2, %3\n"
73+
/* Calculate SRAM end address (base + size) */
74+
" add r2, r1, r2\n"
75+
"verify_ram_loop:\n"
76+
" cmp r1, r2\n"
77+
" bge verify_ram_done\n"
78+
/* Load value at current address and verify if it equals 0 */
79+
" ldr r3, [r1]\n"
80+
" cmp r3, #0\n"
81+
" bne ram_cleanup_failed\n"
82+
/* Increment the address by 4 (word size) */
83+
" add r1, r1, #4\n"
84+
" b verify_ram_loop\n"
85+
"ram_cleanup_failed:\n"
86+
/* Load RAM_CLEANUP_FAILURE_MAGIC into r0 */
87+
" movw r0, %4\n"
88+
/* Save the uncleared address in uncleared_address variable */
89+
" mov r2, %7\n"
90+
" str r1, [r2]\n"
91+
/* Save the uncleared value in uncleared_value variable */
92+
" mov r2, %8\n"
93+
" str r3, [r2]\n"
94+
" b verification_done\n"
95+
"verify_ram_done:\n"
96+
/* Load RAM_CLEANUP_SUCCESS_MAGIC */
97+
" movw r0, %5\n"
98+
"verification_done:\n"
99+
/* Store result in ram_cleanup_result variable */
100+
" mov r2, %6\n"
101+
" str r0, [r2]\n"
102+
/* __attribute__((naked)) requires manual branching */
103+
" bx lr\n"
104+
:
105+
: "r" (&RAMFUNC_START_SAVE_REGISTER),
106+
"r" (&RAMFUNC_END_SAVE_REGISTER),
107+
"r" (CONFIG_SRAM_BASE_ADDRESS),
108+
"r" (CONFIG_SRAM_SIZE * 1024),
109+
"i" (RAM_CLEANUP_FAILURE_MAGIC),
110+
"i" (RAM_CLEANUP_SUCCESS_MAGIC),
111+
"r" (&ram_cleanup_result),
112+
"r" (&uncleared_address),
113+
"r" (&uncleared_value)
114+
: "r0", "r1", "r2", "r3", "lr", "memory"
115+
);
116+
}
117+
118+
ZTEST(b0_ram_cleanup, test_EARLY_RESET_HOOK)
119+
{
120+
zassert_true((ram_cleanup_result == RAM_CLEANUP_SUCCESS_MAGIC) ||
121+
(ram_cleanup_result == RAM_CLEANUP_FAILURE_MAGIC),
122+
"RAM cleanup result should be either success or failure");
123+
zassert_equal(ram_cleanup_result, RAM_CLEANUP_SUCCESS_MAGIC,
124+
"Uncleared word detected at address %p, value 0x%x", uncleared_address,
125+
uncleared_value);
126+
}
127+
128+
ZTEST_SUITE(b0_ram_cleanup, NULL, NULL, NULL, NULL, NULL);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#
2+
# Copyright (c) 2025 Nordic Semiconductor
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
# Adding a module to b0 is needed as a way to inject source code into
8+
# the non-default sysbuild image (in this case b0).
9+
set(b0_EXTRA_ZEPHYR_MODULES ${CMAKE_CURRENT_LIST_DIR}/modules/b0_cleanup_ram_test_prepare CACHE INTERNAL "")
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#
2+
# Copyright (c) 2025 Nordic Semiconductor
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
SB_CONFIG_SECURE_BOOT_APPCORE=y
8+
SB_CONFIG_SECURE_BOOT_GENERATE_DEFAULT_KMU_KEYFILE=y
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
common:
2+
sysbuild: true
3+
tags:
4+
- b0
5+
- ci_tests_subsys_bootloader
6+
7+
tests:
8+
b0.cleanup_ram:
9+
platform_allow:
10+
- nrf54l15dk/nrf54l15/cpuapp
11+
- nrf54l15dk/nrf54l10/cpuapp
12+
- nrf54l15dk/nrf54l05/cpuapp
13+
- nrf54lv10dk/nrf54lv10a/cpuapp
14+
- nrf54lm20dk/nrf54lm20a/cpuapp
15+
integration_platforms:
16+
- nrf54l15dk/nrf54l15/cpuapp
17+
- nrf54l15dk/nrf54l10/cpuapp
18+
- nrf54l15dk/nrf54l05/cpuapp
19+
- nrf54lv10dk/nrf54lv10a/cpuapp
20+
- nrf54lm20dk/nrf54lm20a/cpuapp

0 commit comments

Comments
 (0)