Skip to content

Commit acb4612

Browse files
committed
tests: bootloader: NSIB MCUBoot locks
complete set of tests for region 3 and 4 lockouts. Signed-off-by: Mateusz Michalek <[email protected]>
1 parent 15d1605 commit acb4612

File tree

10 files changed

+291
-110
lines changed

10 files changed

+291
-110
lines changed

subsys/bootloader/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ config SB_DISABLE_SELF_RWX
112112
Sets RRAMC's BOOTCONF region protection before jumping to application.
113113
It disables reads writes and execution memory area which holds NSIB.
114114

115+
config SB_DISABLE_NEXT_W
116+
bool "Disable writes for next stage"
117+
depends on (SOC_NRF54L15_CPUAPP || SOC_NRF54L05_CPUAPP || SOC_NRF54L10_CPUAPP) && !FPROTECT
118+
help
119+
NSIB disables writes on next stage in bootloading chain.
120+
It uses RRAMC's region 4 and is limited to 31KB.
121+
115122
endif # IS_SECURE_BOOTLOADER
116123

117124
config IS_BOOTLOADER_IMG

subsys/bootloader/bl_boot/bl_boot.c

Lines changed: 137 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,55 @@
1919
#include <hal/nrf_gpio.h>
2020
#endif
2121

22+
#include <zephyr/linker/linker-defs.h>
23+
#define CLEANUP_RAM_GAP_START ((int)__ramfunc_region_start)
24+
#define CLEANUP_RAM_GAP_END ((int)__ramfunc_end)
25+
#define CLEANUP_RAM_FIRST_START (CONFIG_SRAM_BASE_ADDRESS)
26+
#define CLEANUP_RAM_FIRST_SIZE (CLEANUP_RAM_GAP_START)
27+
#define CLEANUP_RAM_SECOND_START (CLEANUP_RAM_GAP_END)
28+
#define CLEANUP_RAM_SECOND_SIZE (CONFIG_SRAM_SIZE * 1024 - CLEANUP_RAM_GAP_END)
29+
30+
#if defined(CONFIG_SB_DISABLE_NEXT_W)
31+
#include <hal/nrf_rramc.h>
32+
#define RRAMC_REGION_FOR_NEXT_W 4
33+
#define NRF_RRAM_REGION_SIZE_UNIT 0x400
34+
#define NRF_RRAM_REGION_ADDRESS_RESOLUTION 0x400
35+
#define NEXT_W_SIZE_KB (PM_MCUBOOT_SIZE / NRF_RRAM_REGION_SIZE_UNIT)
36+
37+
BUILD_ASSERT((PM_MCUBOOT_ADDRESS % NRF_RRAM_REGION_ADDRESS_RESOLUTION) == 0,
38+
"Start of protected region is not aligned");
39+
40+
BUILD_ASSERT((PM_MCUBOOT_SIZE % NRF_RRAM_REGION_SIZE_UNIT) == 0,
41+
"Size of protected region is not aligned");
42+
43+
BUILD_ASSERT(NEXT_W_SIZE_KB < 31,
44+
"Size of requested protection is too big");
45+
46+
static int disable_next_w(void)
47+
{
48+
nrf_rramc_region_config_t config = {
49+
.address = PM_MCUBOOT_ADDRESS,
50+
.permissions = NRF_RRAMC_REGION_PERM_READ_MASK |
51+
NRF_RRAMC_REGION_PERM_EXECUTE_MASK,
52+
.writeonce = false,
53+
.lock = false,
54+
.size_kb = NEXT_W_SIZE_KB,
55+
};
56+
57+
nrf_rramc_region_config_set(NRF_RRAMC, RRAMC_REGION_FOR_NEXT_W, &config);
58+
nrf_rramc_region_config_get(NRF_RRAMC, RRAMC_REGION_FOR_NEXT_W, &config);
59+
if (config.permissions & (NRF_RRAMC_REGION_PERM_WRITE_MASK)) {
60+
return -ENOSPC;
61+
}
62+
if (config.size_kb != NEXT_W_SIZE_KB) {
63+
return -ENOSPC;
64+
}
65+
66+
return 0;
67+
}
68+
69+
#endif
70+
2271
#if defined(CONFIG_SB_DISABLE_SELF_RWX)
2372
/* Disabling R_X has to be done while running from RAM for obvious reasons.
2473
* Moreover as a last step before jumping to application it must work even after
@@ -29,15 +78,93 @@
2978
*/
3079
#include <hal/nrf_rramc.h>
3180

32-
#define FUNCTION_BUFFER_LEN 64
3381
#define RRAMC_REGION_RWX_LSB 0
3482
#define RRAMC_REGION_RWX_WIDTH 3
3583
#define RRAMC_REGION_TO_LOCK_ADDR NRF_RRAMC->REGION[3].CONFIG
3684
#define RRAMC_REGION_TO_LOCK_ADDR_H (((uint32_t)(&(RRAMC_REGION_TO_LOCK_ADDR))) >> 16)
3785
#define RRAMC_REGION_TO_LOCK_ADDR_L (((uint32_t)(&(RRAMC_REGION_TO_LOCK_ADDR))) & 0x0000fffful)
38-
static uint8_t ram_exec_buf[FUNCTION_BUFFER_LEN];
3986
#endif /* CONFIG_SB_DISABLE_SELF_RWX */
4087

88+
static void __ramfunc jump_in(uint32_t *vector_table)
89+
{
90+
__asm__ volatile (
91+
92+
/* vector_table[1] -> r0 */
93+
" mov r0, %0\n"
94+
#ifdef CONFIG_SB_CLEANUP_RAM
95+
/* Base to write -> r1 */
96+
" mov r1, %1\n"
97+
/* Size to write -> r2 */
98+
" mov r2, %2\n"
99+
/* Value to write -> r3 */
100+
" movw r3, %5\n"
101+
"clear_first:\n"
102+
" str r3, [r1]\n"
103+
" add r1, r1, #4\n"
104+
" sub r2, r2, #4\n"
105+
" cbz r2, clear_next\n"
106+
" b clear_first\n"
107+
"clear_next:\n"
108+
/* Base to write -> r1 */
109+
" mov r1, %3\n"
110+
/* Size to write -> r2 */
111+
" mov r2, %4\n"
112+
"clear_second:\n"
113+
" str r3, [r1]\n"
114+
" add r1, r1, #4\n"
115+
" sub r2, r2, #4\n"
116+
" cbz r2, out\n"
117+
" b clear_second\n"
118+
"out:\n"
119+
" dsb\n"
120+
#endif /* CONFIG_SB_CLEANUP_RAM */
121+
122+
#ifdef CONFIG_SB_DISABLE_SELF_RWX
123+
".thumb_func\n"
124+
"bootconf_disable_rwx:\n"
125+
" mov r0, %0\n"
126+
" movw r1, %6\n"
127+
" movt r1, %7\n"
128+
" ldr r2, [r1]\n"
129+
/* Size of the region should be set at this point
130+
* by provisioning through BOOTCONF.
131+
* If not, set it according partition size.
132+
*/
133+
" ands r4, r2, %12\n"
134+
" cbnz r4, rrclear_rwx\n"
135+
" movt r2, %8\n"
136+
"rrclear_rwx:\n"
137+
" bfc r2, %9, %10\n"
138+
/* Disallow further modifications */
139+
" orr r2, %11\n"
140+
" str r2, [r1]\n"
141+
" dsb\n"
142+
/* Next assembly line is important for current function */
143+
144+
#endif /* CONFIG_SB_DISABLE_SELF_RWX */
145+
146+
/* Jump to reset vector of an app */
147+
" bx r0\n"
148+
:
149+
: "r" (vector_table),
150+
"i" (CLEANUP_RAM_FIRST_START),
151+
"i" (CLEANUP_RAM_FIRST_SIZE),
152+
"r" (CLEANUP_RAM_SECOND_START),
153+
"r" (CLEANUP_RAM_SECOND_SIZE),
154+
"i" (0),
155+
#ifdef CONFIG_SB_DISABLE_SELF_RWX
156+
"i" (RRAMC_REGION_TO_LOCK_ADDR_L),
157+
"i" (RRAMC_REGION_TO_LOCK_ADDR_H),
158+
"i" (CONFIG_PM_PARTITION_SIZE_B0_IMAGE / 1024),
159+
"i" (RRAMC_REGION_RWX_LSB),
160+
"i" (RRAMC_REGION_RWX_WIDTH),
161+
"i" (RRAMC_REGION_CONFIG_LOCK_Msk),
162+
"i" (RRAMC_REGION_CONFIG_SIZE_Msk)
163+
#endif /* CONFIG_SB_DISABLE_SELF_RWX */
164+
: "r0", "r1", "r2", "r3", "r4", "r5", "memory"
165+
);
166+
}
167+
41168
#ifdef CONFIG_UART_NRFX_UARTE
42169
static void uninit_used_uarte(NRF_UARTE_Type *p_reg)
43170
{
@@ -163,6 +290,13 @@ void bl_boot(const struct fw_info *fw_info)
163290
VTOR = fw_info->address;
164291
uint32_t *vector_table = (uint32_t *)fw_info->address;
165292

293+
#if defined(CONFIG_SB_DISABLE_NEXT_W)
294+
if (disable_next_w()) {
295+
printk("Unable to disable writes on next stage.");
296+
return;
297+
}
298+
#endif
299+
166300
#if defined(CONFIG_BUILTIN_STACK_GUARD) && \
167301
defined(CONFIG_CPU_CORTEX_M_HAS_SPLIM)
168302
/* Reset limit registers to avoid inflicting stack overflow on image
@@ -175,96 +309,7 @@ void bl_boot(const struct fw_info *fw_info)
175309
__set_MSP(vector_table[0]);
176310
__set_PSP(0);
177311

178-
__asm__ volatile (
179-
/* vector_table[1] -> r0 */
180-
" mov r0, %0\n"
181-
#ifdef CONFIG_SB_CLEANUP_RAM
182-
/* Base to write -> r1 */
183-
" mov r1, %1\n"
184-
/* Size to write -> r2 */
185-
" mov r2, %2\n"
186-
/* Value to write -> r3 */
187-
" movw r3, %3\n"
188-
"clear:\n"
189-
" str r3, [r1]\n"
190-
" add r1, r1, #4\n"
191-
" sub r2, r2, #4\n"
192-
" cbz r2, out\n"
193-
" b clear\n"
194-
"out:\n"
195-
" dsb\n"
196-
#endif /* CONFIG_SB_CLEANUP_RAM */
197-
198-
#ifdef CONFIG_SB_DISABLE_SELF_RWX
199-
/* FUNCTION_BUFFER_LEN */
200-
" movw r4, %4\n"
201-
/* Address of ram_exec_buf goes to r2 */
202-
" mov r2, %5\n"
203-
" movw r3, :lower16:bootconf_disable_rwx\n"
204-
" movt r3, :upper16:bootconf_disable_rwx\n"
205-
/* Adjust address for thumb */
206-
" and r3, #0xfffffffe\n"
207-
/* Address of ram_exec_buf also goes to r5 */
208-
" mov r5, %5\n"
209-
/* Adjust buffer address for thumb */
210-
" orr r5, #0x1\n"
211-
/* End of the copy address in r4 */
212-
" add r4, r2\n"
213-
"ram_cpy:\n"
214-
/* Read and increment */
215-
" ldrb r1, [r3], #1\n"
216-
/* Write and increment */
217-
" strb r1, [r2], #1\n"
218-
/* Check if end address is reached */
219-
" cmp r2, r4\n"
220-
" bne ram_cpy\n"
221-
" dsb\n"
222-
/* Jump to ram */
223-
" bx r5\n"
224-
/* CODE_UNREACHABLE */
225-
226-
".thumb_func\n"
227-
"bootconf_disable_rwx:\n"
228-
" movw r1, %6\n"
229-
" movt r1, %7\n"
230-
" ldr r2, [r1]\n"
231-
/* Size of the region should be set at this point
232-
* by provisioning through BOOTCONF.
233-
* If not, set it according partition size.
234-
*/
235-
" ands r4, r2, %12\n"
236-
" cbnz r4, clear_rwx\n"
237-
" movt r2, %8\n"
238-
"clear_rwx:\n"
239-
" bfc r2, %9, %10\n"
240-
/* Disallow further modifications */
241-
" orr r2, %11\n"
242-
" str r2, [r1]\n"
243-
" dsb\n"
244-
/* Next assembly line is important for current function */
245-
246-
#endif /* CONFIG_SB_DISABLE_SELF_RWX */
247-
248-
/* Jump to reset vector of an app */
249-
" bx r0\n"
250-
:
251-
: "r" (vector_table[1]),
252-
"i" (CONFIG_SRAM_BASE_ADDRESS),
253-
"i" (CONFIG_SRAM_SIZE * 1024),
254-
"i" (0)
255-
#ifdef CONFIG_SB_DISABLE_SELF_RWX
256-
, "i" (FUNCTION_BUFFER_LEN),
257-
"r" (ram_exec_buf),
258-
"i" (RRAMC_REGION_TO_LOCK_ADDR_L),
259-
"i" (RRAMC_REGION_TO_LOCK_ADDR_H),
260-
"i" (CONFIG_PM_PARTITION_SIZE_B0_IMAGE / 1024),
261-
"i" (RRAMC_REGION_RWX_LSB),
262-
"i" (RRAMC_REGION_RWX_WIDTH),
263-
"i" (RRAMC_REGION_CONFIG_LOCK_Msk),
264-
"i" (RRAMC_REGION_CONFIG_SIZE_Msk)
265-
#endif /* CONFIG_SB_DISABLE_SELF_RWX */
266-
: "r0", "r1", "r2", "r3", "r4", "r5", "memory"
267-
);
312+
jump_in((vector_table[1]));
268313

269314
CODE_UNREACHABLE;
270315
}

tests/subsys/bootloader/b0_lock/testcase.yaml

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#
2+
# Copyright (c) 2022 Nordic Semiconductor
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
config TEST_B0_LOCK_READS
8+
bool "Enable PSA backend and dependencies"
9+
help
10+
Reads are disabled after writes has been disabled, therefore testing against reads
11+
implies portion of the memory cannot be accessed at all.
12+
In case this symbol isn't selected, tests against writes are performed.
13+
14+
config TEST_B0_LOCK_REGION
15+
int "Region number"
16+
range 3 4
17+
default 3
18+
help
19+
Region 3 is used for NSIB protection. Other one for MCUBoot.
20+
21+
22+
menu "Zephyr"
23+
source "Kconfig.zephyr"
24+
endmenu

tests/subsys/bootloader/b0_lock/src/main.c renamed to tests/subsys/bootloader/b0_lock_rwx/src/main.c

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
#include <hal/nrf_rramc.h>
99
#include <nrfx_rramc.h>
1010

11-
#define RRAMC_REGION_FOR_BOOTCONF 3
11+
#define RRAMC_REGION_FOR_TEST CONFIG_TEST_B0_LOCK_REGION
12+
1213
static uint32_t expected_fatal;
1314
static uint32_t actual_fatal;
1415
static nrf_rramc_region_config_t config;
@@ -29,23 +30,46 @@ void check_fatal(void *unused)
2930
void *get_config(void)
3031
{
3132
nrf_rramc_region_config_get(NRF_RRAMC,
32-
RRAMC_REGION_FOR_BOOTCONF,
33+
RRAMC_REGION_FOR_TEST,
3334
&config);
35+
#if defined(CONFIG_TEST_B0_LOCK_READS)
36+
zassert_equal(0, config.permissions &
37+
(NRF_RRAMC_REGION_PERM_READ_MASK |
38+
NRF_RRAMC_REGION_PERM_WRITE_MASK |
39+
NRF_RRAMC_REGION_PERM_EXECUTE_MASK),
40+
"Read Write and eXecute permissions aren't cleared");
41+
#else
3442
zassert_equal(0, config.permissions &
3543
(NRF_RRAMC_REGION_PERM_WRITE_MASK),
3644
"Write permission isn't cleared");
45+
#endif
3746
zassert_true(config.size_kb > 0, "Protected region has zero size.");
3847
return NULL;
3948
}
4049

4150
ZTEST(b0_self_lock_test, test_reading_b0_image)
4251
{
52+
printk("Region %d\n", RRAMC_REGION_FOR_TEST);
4353
uint32_t protected_end_address = 1024 * config.size_kb;
4454
volatile uint32_t *unprotected_word = (volatile uint32_t *)protected_end_address;
4555
volatile uint32_t *protected_word =
4656
(volatile uint32_t *)protected_end_address - sizeof(uint32_t);
4757
uint32_t test_value = ~(*unprotected_word);
4858

59+
config.permissions = NRF_RRAMC_REGION_PERM_READ_MASK |
60+
NRF_RRAMC_REGION_PERM_WRITE_MASK |
61+
NRF_RRAMC_REGION_PERM_EXECUTE_MASK;
62+
/* Try unlocking. This should take no effect at this point */
63+
nrf_rramc_region_config_set(NRF_RRAMC, RRAMC_REGION_FOR_TEST, &config);
64+
65+
#if defined(CONFIG_TEST_B0_LOCK_READS)
66+
printk("Legal read\n");
67+
int val = *unprotected_word;
68+
printk("Illegal read\n");
69+
expected_fatal++;
70+
__DSB();
71+
val = *protected_word;
72+
#else
4973
printk("Legal write\n");
5074
nrfx_rramc_write_enable_set(true, 0);
5175
/* Next line corrupts application header.
@@ -56,15 +80,14 @@ ZTEST(b0_self_lock_test, test_reading_b0_image)
5680
nrf_rramc_word_write((uint32_t)unprotected_word, test_value);
5781
zassert_equal(test_value, *unprotected_word,
5882
"Legal write value doesn't match.");
59-
config.permissions = NRF_RRAMC_REGION_PERM_READ_MASK |
60-
NRF_RRAMC_REGION_PERM_WRITE_MASK |
61-
NRF_RRAMC_REGION_PERM_EXECUTE_MASK;
62-
/* Try unlocking. This should take no effect at this point */
63-
nrf_rramc_region_config_set(NRF_RRAMC, RRAMC_REGION_FOR_BOOTCONF, &config);
6483
printk("Illegal write\n");
6584
expected_fatal++;
6685
__DSB();
6786
nrf_rramc_word_write((uint32_t)protected_word, test_value);
87+
88+
#endif
89+
6890
}
6991

92+
7093
ZTEST_SUITE(b0_self_lock_test, NULL, get_config, NULL, check_fatal, NULL);

tests/subsys/bootloader/b0_lock/sysbuild.conf renamed to tests/subsys/bootloader/b0_lock_rwx/sysbuild.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@
55
#
66

77
SB_CONFIG_SECURE_BOOT_APPCORE=y
8+
SB_CONFIG_SECURE_BOOT_GENERATE_DEFAULT_KMU_KEYFILE=y
89
SB_CONFIG_SECURE_BOOT_BOOTCONF_LOCK_WRITES=y

0 commit comments

Comments
 (0)