Skip to content

Commit 96aa097

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 451dae3 commit 96aa097

File tree

9 files changed

+254
-0
lines changed

9 files changed

+254
-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: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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+
37+
struct test_ram_area_t {
38+
uint32_t area_start;
39+
uint32_t area_end;
40+
};
41+
42+
43+
static int populate_ram(void)
44+
{
45+
RAMFUNC_START_SAVE_REGISTER = CLEANUP_RAM_GAP_START;
46+
RAMFUNC_END_SAVE_REGISTER = CLEANUP_RAM_GAP_END;
47+
48+
for (uint32_t addr = RAM_TO_POPULATE_START; addr < RAM_TO_POPULATE_END;
49+
addr += sizeof(uint32_t)) {
50+
*(uint32_t *) addr = VALUE_TO_POPULATE;
51+
}
52+
53+
return 0;
54+
}
55+
56+
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: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
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+
21+
#define RAMFUNC_START_SAVE_REGISTER NRF_TIMER00->CC[0]
22+
#define RAMFUNC_END_SAVE_REGISTER NRF_TIMER00->CC[1]
23+
24+
/* Using 2-byte magic values allows to load the whole value with movw */
25+
#define RAM_CLEANUP_SUCCESS_MAGIC 0x5678
26+
#define RAM_CLEANUP_FAILURE_MAGIC 0x4321
27+
28+
static uint32_t __noinit ram_cleanup_result;
29+
static uint32_t __noinit uncleared_address;
30+
static uint32_t __noinit uncleared_value;
31+
32+
/**
33+
* This hook needs to have the attribute __attribute__((naked)),
34+
* as otherwise the stack would be modified when calling it, leading to
35+
* test failure, as part of the RAM in which the stack resides wouldn't
36+
* be zeroed.
37+
*/
38+
__attribute__((naked)) void soc_early_reset_hook(void)
39+
{
40+
__asm__ volatile (
41+
/* Load zero (value used to clear memory) into r0 */
42+
" mov r0, #0\n"
43+
/* Explicitly clear the ram_cleanup_result variable */
44+
" mov r2, %6\n"
45+
" str r0, [r2]\n"
46+
/* Load the location of the saved ram func start address to r1 */
47+
" mov r1, %0\n"
48+
/* Load the ram func start address to r2 */
49+
" ldr r2, [r1]\n"
50+
/* Load the location of the saved ram func end address to r1 */
51+
" mov r1, %1\n"
52+
/* Load the ram func end address to r3*/
53+
" ldr r3, [r1]\n"
54+
/* Clear memory from ram func start to ram func end.
55+
* The area is skipped during the RAM cleanup,
56+
* so it is not cleared.
57+
* Simply cleaning it up here is quicker
58+
* than verifying if the current address is within the gap
59+
* in each iteration of the loop.
60+
*/
61+
"ram_func_zero_loop:\n"
62+
" cmp r2, r3\n"
63+
" bge ram_func_zero_loop_done\n"
64+
" str r0, [r2]\n"
65+
/* Increment the address by 4 (word size) */
66+
" add r2, r2, #4\n"
67+
" b ram_func_zero_loop\n"
68+
"ram_func_zero_loop_done:\n"
69+
/* Verify that all of the RAM memory has been cleared */
70+
/* Load SRAM base address to r1 */
71+
" mov r1, %2\n"
72+
/* Load SRAM size to r2 */
73+
" mov r2, %3\n"
74+
/* Calculate SRAM end address (base + size) */
75+
" add r2, r1, r2\n"
76+
"verify_ram_loop:\n"
77+
" cmp r1, r2\n"
78+
" bge verify_ram_done\n"
79+
/* Load value at current address and verify if it equals 0 */
80+
" ldr r3, [r1]\n"
81+
" cmp r3, #0\n"
82+
" bne ram_cleanup_failed\n"
83+
/* Increment the address by 4 (word size) */
84+
" add r1, r1, #4\n"
85+
" b verify_ram_loop\n"
86+
"ram_cleanup_failed:\n"
87+
/* Load RAM_CLEANUP_FAILURE_MAGIC into r0 */
88+
" movw r0, %4\n"
89+
/* Save the uncleared address in uncleared_address variable */
90+
" mov r2, %7\n"
91+
" str r1, [r2]\n"
92+
/* Save the uncleared value in uncleared_value variable */
93+
" mov r2, %8\n"
94+
" str r3, [r2]\n"
95+
" b verification_done\n"
96+
"verify_ram_done:\n"
97+
/* Load RAM_CLEANUP_SUCCESS_MAGIC */
98+
" movw r0, %5\n"
99+
"verification_done:\n"
100+
/* Store result in ram_cleanup_result variable */
101+
" mov r2, %6\n"
102+
" str r0, [r2]\n"
103+
/* __attribute__((naked)) requires manual branching */
104+
" bx lr\n"
105+
:
106+
: "r" (&RAMFUNC_START_SAVE_REGISTER),
107+
"r" (&RAMFUNC_END_SAVE_REGISTER),
108+
"r" (CONFIG_SRAM_BASE_ADDRESS),
109+
"r" (CONFIG_SRAM_SIZE * 1024),
110+
"i" (RAM_CLEANUP_FAILURE_MAGIC),
111+
"i" (RAM_CLEANUP_SUCCESS_MAGIC),
112+
"r" (&ram_cleanup_result),
113+
"r" (&uncleared_address),
114+
"r" (&uncleared_value)
115+
: "r0", "r1", "r2", "r3", "lr", "memory"
116+
);
117+
}
118+
119+
ZTEST(b0_ram_cleanup, test_EARLY_RESET_HOOK)
120+
{
121+
zassert_true((ram_cleanup_result == RAM_CLEANUP_SUCCESS_MAGIC) ||
122+
(ram_cleanup_result == RAM_CLEANUP_FAILURE_MAGIC),
123+
"RAM cleanup result should be either success or failure");
124+
zassert_equal(ram_cleanup_result, RAM_CLEANUP_SUCCESS_MAGIC,
125+
"Uncleared word detected at address %p, value 0x%x", uncleared_address,
126+
uncleared_value);
127+
}
128+
129+
ZTEST_SUITE(b0_ram_cleanup, NULL, NULL, NULL, NULL, NULL);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#
2+
# Copyright (c) 2025 Nordic Semiconductor
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
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)