From 3f5bf0f11111f99b7d2fc6e5bf716da8876f04bb Mon Sep 17 00:00:00 2001 From: Artur Hadasz Date: Tue, 7 Oct 2025 15:26:36 +0200 Subject: [PATCH] boot: Adjustments to region protection code for all nRF54L platforms This commit fixes a couple of issues regarding B0 and MCUBoot region protection for nRF54L. It also adds some tests verifying that the issues are no longer present. Also, support for region and BOOTCONF protection is added for nRF54LM20 and nRF54LV10 platforms. Note: due nRF54LM20 and nRF54LV10 devices being shipped in the TEST mode, the BOOTCONF configuration is not copied to the appropriate REGION[n].CONFIG register and is not automatically applied at startup. Thus, the BOOTCONF configuration does not work with the shipped devices. However, all the code needed for that is present. Signed-off-by: Artur Hadasz --- cmake/sysbuild/bootconf.cmake | 1 + scripts/reglock.py | 66 ++++++++++---- subsys/bootloader/Kconfig | 13 ++- subsys/bootloader/bl_boot/bl_boot.c | 64 ++++++++++--- sysbuild/Kconfig.secureboot | 8 +- tests/subsys/bootloader/b0_lock_rwx/Kconfig | 3 + .../modules/run_from_s1/zephyr/CMakeLists.txt | 12 +++ .../modules/run_from_s1/zephyr/Kconfig | 11 +++ .../modules/run_from_s1/zephyr/module.yml | 3 + .../run_from_s1/zephyr/src/run_from_s1.c | 23 +++++ .../subsys/bootloader/b0_lock_rwx/src/main.c | 37 ++++++-- .../bootloader/b0_lock_rwx/sysbuild.cmake | 7 ++ .../bootloader/b0_lock_rwx/testcase.yaml | 89 ++++++++++++++++++- west.yml | 2 +- 14 files changed, 294 insertions(+), 45 deletions(-) create mode 100644 tests/subsys/bootloader/b0_lock_rwx/modules/run_from_s1/zephyr/CMakeLists.txt create mode 100644 tests/subsys/bootloader/b0_lock_rwx/modules/run_from_s1/zephyr/Kconfig create mode 100644 tests/subsys/bootloader/b0_lock_rwx/modules/run_from_s1/zephyr/module.yml create mode 100644 tests/subsys/bootloader/b0_lock_rwx/modules/run_from_s1/zephyr/src/run_from_s1.c create mode 100644 tests/subsys/bootloader/b0_lock_rwx/sysbuild.cmake diff --git a/cmake/sysbuild/bootconf.cmake b/cmake/sysbuild/bootconf.cmake index db0ee35ce86..d92ed0ea5da 100644 --- a/cmake/sysbuild/bootconf.cmake +++ b/cmake/sysbuild/bootconf.cmake @@ -15,6 +15,7 @@ function(setup_bootconf_data) ${ZEPHYR_NRF_MODULE_DIR}/scripts/reglock.py --output ${CMAKE_BINARY_DIR}/bootconf.hex --size $ + --soc ${SB_CONFIG_SOC} DEPENDS ${APPLICATION_BINARY_DIR}/pm.config VERBATIM ) diff --git a/scripts/reglock.py b/scripts/reglock.py index adf7397ef5b..a33d922a540 100644 --- a/scripts/reglock.py +++ b/scripts/reglock.py @@ -22,8 +22,7 @@ import argparse import sys import warnings - -SIZE_MAX_KB = 31 +import struct BOOTCONF_ADDR = 0x00FFD080 @@ -31,11 +30,46 @@ WRITE_ALLOWED = 0x02 EXECUTE_ALLOWED = 0x04 SECURE = 0x08 -OWNER_NONE = 0x00 -OWNER_APP = 0x10 -OWNER_KMU = 0x20 WRITEONCE = 0x10 -LOCK = 0x20 +LOCK = 0x2000 + +SIZE_OFFSET = 16 + + +supported_socs = [ + "nrf54l05", + "nrf54l10", + "nrf54l15", + "nrf54lm20a", + "nrf54lv10a", + "nrf54ls05b", +] + +def get_max_size_kb(soc): + if soc in ["nrf54l05", "nrf54l10", "nrf54l15"]: + return 31 + elif soc in ["nrf54lm20a", "nrf54lv10a"]: + return 127 + elif soc in ["nrf54ls05b"]: + return 1023 + else: + sys.exit("error: unsupported SoC") + +def get_bootconf_reg_32bit_value(soc, size): + value = READ_ALLOWED | EXECUTE_ALLOWED | LOCK + + if soc not in ["nrf54ls05b"]: + value |= SECURE + + size_kb = size // 1024 + max_size_kb = get_max_size_kb(soc) + + if size_kb > max_size_kb: + warnings.warn("warning: requested size too big; Setting to allowed maximum") + size_kb = max_size_kb + + value |= size_kb << SIZE_OFFSET + return value def parse_args(): parser = argparse.ArgumentParser( @@ -48,6 +82,9 @@ def parse_args(): parser.add_argument("-s", "--size", required=False, default="0x7C00", type=lambda x: hex(int(x, 0)), help="Size to lock.") + parser.add_argument("--soc", required=True, + type=str, + help="SoC for which to generate the bootconf.") return parser.parse_args() @@ -56,17 +93,12 @@ def main(): size = int(args.size, 16) if size % 1024: sys.exit("error: requested size not aligned to 1k") - size = size // 1024 - if size > SIZE_MAX_KB: - warnings.warn("warning: requested size too big; Setting to allowed maximum") - size = SIZE_MAX_KB - - payload = bytearray([ - READ_ALLOWED | EXECUTE_ALLOWED | SECURE | OWNER_NONE, - LOCK, - size, - 0x0 - ]) + if args.soc not in supported_socs: + sys.exit("error: unsupported SoC") + + reg_value = get_bootconf_reg_32bit_value(args.soc, size) + + payload = struct.pack('address; #if defined(CONFIG_SB_DISABLE_NEXT_W) - if (disable_next_w()) { - printk("Unable to disable writes on next stage."); + if (disable_next_w(fw_info->address)) { + printk("Unable to disable writes on next stage"); return; } #endif diff --git a/sysbuild/Kconfig.secureboot b/sysbuild/Kconfig.secureboot index 480e2e25f04..ec79086ff4a 100644 --- a/sysbuild/Kconfig.secureboot +++ b/sysbuild/Kconfig.secureboot @@ -336,9 +336,15 @@ config SECURE_BOOT_PUBLIC_KEY_FILES empty string then only the public key hash corresponding to the private signing key used to sign the image is included in provision.hex. +config SECURE_BOOT_BOOTCONF_LOCK_WRITES_SUPPORTED + bool + default y if SOC_NRF54L15_CPUAPP || SOC_NRF54L05_CPUAPP || SOC_NRF54L10_CPUAPP + default y if SOC_NRF54LV10A_ENGA_CPUAPP + default y if SOC_NRF54LM20A_ENGA_CPUAPP + config SECURE_BOOT_BOOTCONF_LOCK_WRITES bool "Protect bootloader's NVM from writes" - depends on SOC_NRF54L15_CPUAPP || SOC_NRF54L05_CPUAPP || SOC_NRF54L10_CPUAPP + depends on SECURE_BOOT_BOOTCONF_LOCK_WRITES_SUPPORTED default y help Sets RRAMC's BOOTCONF region protection to disable writes. diff --git a/tests/subsys/bootloader/b0_lock_rwx/Kconfig b/tests/subsys/bootloader/b0_lock_rwx/Kconfig index f7007f5eea1..32ed45f23f3 100644 --- a/tests/subsys/bootloader/b0_lock_rwx/Kconfig +++ b/tests/subsys/bootloader/b0_lock_rwx/Kconfig @@ -18,6 +18,9 @@ config TEST_B0_LOCK_REGION help Region 3 is used for NSIB protection. Other one for MCUBoot. +config TEST_B0_LOCK_USE_S1 + bool "MCUBoot is running from S1 image for the test" + menu "Zephyr" source "Kconfig.zephyr" endmenu diff --git a/tests/subsys/bootloader/b0_lock_rwx/modules/run_from_s1/zephyr/CMakeLists.txt b/tests/subsys/bootloader/b0_lock_rwx/modules/run_from_s1/zephyr/CMakeLists.txt new file mode 100644 index 00000000000..8360e99fac6 --- /dev/null +++ b/tests/subsys/bootloader/b0_lock_rwx/modules/run_from_s1/zephyr/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# Copyright (c) 2025 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +cmake_minimum_required(VERSION 3.20.0) + +if(CONFIG_TEST_B0_LOCK_USE_S1) + zephyr_library() + zephyr_library_sources(src/run_from_s1.c) +endif() diff --git a/tests/subsys/bootloader/b0_lock_rwx/modules/run_from_s1/zephyr/Kconfig b/tests/subsys/bootloader/b0_lock_rwx/modules/run_from_s1/zephyr/Kconfig new file mode 100644 index 00000000000..2b2b9594c51 --- /dev/null +++ b/tests/subsys/bootloader/b0_lock_rwx/modules/run_from_s1/zephyr/Kconfig @@ -0,0 +1,11 @@ +# +# Copyright (c) 2025 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +config TEST_B0_LOCK_USE_S1 + bool "Ensures the S1 image is started by b0" + help + This option causes b0 to invalidate the S0 image during startup, + which ensures that the main function starts the S1 image. diff --git a/tests/subsys/bootloader/b0_lock_rwx/modules/run_from_s1/zephyr/module.yml b/tests/subsys/bootloader/b0_lock_rwx/modules/run_from_s1/zephyr/module.yml new file mode 100644 index 00000000000..cbff6a1aefc --- /dev/null +++ b/tests/subsys/bootloader/b0_lock_rwx/modules/run_from_s1/zephyr/module.yml @@ -0,0 +1,3 @@ +build: + cmake: zephyr + kconfig: zephyr/Kconfig diff --git a/tests/subsys/bootloader/b0_lock_rwx/modules/run_from_s1/zephyr/src/run_from_s1.c b/tests/subsys/bootloader/b0_lock_rwx/modules/run_from_s1/zephyr/src/run_from_s1.c new file mode 100644 index 00000000000..349c24ed3cb --- /dev/null +++ b/tests/subsys/bootloader/b0_lock_rwx/modules/run_from_s1/zephyr/src/run_from_s1.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include + +static int invalidate_s0(void) +{ + uint32_t s0_addr = s0_address_read(); + const struct fw_info *s0_info = fw_info_find(s0_addr); + + printk("Invalidating S0 in order to ensure the S1 image is started\n"); + fw_info_invalidate(s0_info); + + return 0; +} + +SYS_INIT(invalidate_s0, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); diff --git a/tests/subsys/bootloader/b0_lock_rwx/src/main.c b/tests/subsys/bootloader/b0_lock_rwx/src/main.c index d979fcffdc9..d7c1c59b9f6 100644 --- a/tests/subsys/bootloader/b0_lock_rwx/src/main.c +++ b/tests/subsys/bootloader/b0_lock_rwx/src/main.c @@ -7,9 +7,26 @@ #include #include #include +#include + +#define B0_RRAMC_REGION 3 +#define MCUBOOT_RRAMC_REGION 4 #define RRAMC_REGION_FOR_TEST CONFIG_TEST_B0_LOCK_REGION +#if RRAMC_REGION_FOR_TEST == B0_RRAMC_REGION +#define RRAMC_REGION_FOR_TEST_SIZE PM_B0_SIZE +#define RRAM_REGION_FOR_TEST_ADDRESS PM_B0_ADDRESS +#elif RRAMC_REGION_FOR_TEST == MCUBOOT_RRAMC_REGION +#if !defined(CONFIG_TEST_B0_LOCK_USE_S1) +#define RRAMC_REGION_FOR_TEST_SIZE PM_MCUBOOT_SIZE +#define RRAM_REGION_FOR_TEST_ADDRESS PM_MCUBOOT_ADDRESS +#else +#define RRAMC_REGION_FOR_TEST_SIZE PM_S1_IMAGE_SIZE +#define RRAM_REGION_FOR_TEST_ADDRESS PM_S1_IMAGE_ADDRESS +#endif +#endif + static uint32_t expected_fatal; static uint32_t actual_fatal; static nrf_rramc_region_config_t config; @@ -43,17 +60,24 @@ void *get_config(void) (NRF_RRAMC_REGION_PERM_WRITE_MASK), "Write permission isn't cleared"); #endif - zassert_true(config.size_kb > 0, "Protected region has zero size."); + zassert_equal(config.size_kb, RRAMC_REGION_FOR_TEST_SIZE / 1024, + "Protected region size doesn't match protected partition size."); + zassert_equal( + config.address, RRAM_REGION_FOR_TEST_ADDRESS, + "Protected region start address doesn't match protected partition start address."); return NULL; } -ZTEST(b0_self_lock_test, test_reading_b0_image) +ZTEST(b0_self_lock_test, test_rwx_locked_region) { printk("Region %d\n", RRAMC_REGION_FOR_TEST); - uint32_t protected_end_address = 1024 * config.size_kb; + uint32_t protected_end_address = config.address + (1024 * config.size_kb); volatile uint32_t *unprotected_word = (volatile uint32_t *)protected_end_address; volatile uint32_t *protected_word = (volatile uint32_t *)protected_end_address - sizeof(uint32_t); + uint32_t original_config = nrf_rramc_region_config_raw_get(NRF_RRAMC, + RRAMC_REGION_FOR_TEST); + uint32_t updated_config = 0xFFFFFFFF; config.permissions = NRF_RRAMC_REGION_PERM_READ_MASK | NRF_RRAMC_REGION_PERM_WRITE_MASK | @@ -61,6 +85,10 @@ ZTEST(b0_self_lock_test, test_reading_b0_image) /* Try unlocking. This should take no effect at this point */ nrf_rramc_region_config_set(NRF_RRAMC, RRAMC_REGION_FOR_TEST, &config); + updated_config = nrf_rramc_region_config_raw_get(NRF_RRAMC, RRAMC_REGION_FOR_TEST); + zassert_equal(updated_config, original_config, + "Managed to update the locked config."); + #if defined(CONFIG_TEST_B0_LOCK_READS) printk("Legal read\n"); int val = *unprotected_word; @@ -76,8 +104,6 @@ ZTEST(b0_self_lock_test, test_reading_b0_image) nrfx_rramc_write_enable_set(true, 0); /* Next line corrupts application header. * It is ok since we need to run test once per flashing. - * Moreover after reboot slot will be invalidated and - * application will boot from second one. */ nrf_rramc_word_write((uint32_t)unprotected_word, test_value); zassert_equal(test_value, *unprotected_word, @@ -86,7 +112,6 @@ ZTEST(b0_self_lock_test, test_reading_b0_image) expected_fatal++; __DSB(); nrf_rramc_word_write((uint32_t)protected_word, test_value); - #endif } diff --git a/tests/subsys/bootloader/b0_lock_rwx/sysbuild.cmake b/tests/subsys/bootloader/b0_lock_rwx/sysbuild.cmake new file mode 100644 index 00000000000..81aade0df3d --- /dev/null +++ b/tests/subsys/bootloader/b0_lock_rwx/sysbuild.cmake @@ -0,0 +1,7 @@ +# +# Copyright (c) 2025 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +set(b0_EXTRA_ZEPHYR_MODULES ${CMAKE_CURRENT_LIST_DIR}/modules/run_from_s1 CACHE INTERNAL "") diff --git a/tests/subsys/bootloader/b0_lock_rwx/testcase.yaml b/tests/subsys/bootloader/b0_lock_rwx/testcase.yaml index ce04931c5f0..3085a80e10e 100644 --- a/tests/subsys/bootloader/b0_lock_rwx/testcase.yaml +++ b/tests/subsys/bootloader/b0_lock_rwx/testcase.yaml @@ -13,9 +13,14 @@ tests: extra_args: - b0_CONFIG_SB_DISABLE_SELF_RWX=y - CONFIG_TEST_B0_LOCK_READS=y - platform_allow: nrf54l15dk/nrf54l15/cpuapp + platform_allow: + - nrf54l15dk/nrf54l15/cpuapp + - nrf54lv10dk/nrf54lv10a/cpuapp + - nrf54lm20dk/nrf54lm20a/cpuapp integration_platforms: - nrf54l15dk/nrf54l15/cpuapp + - nrf54lv10dk/nrf54lv10a/cpuapp + - nrf54lm20dk/nrf54lm20a/cpuapp b0.mcuboot.w: extra_args: - b0_CONFIG_SB_DISABLE_SELF_RWX=y @@ -23,9 +28,14 @@ tests: - SB_CONFIG_BOOTLOADER_MCUBOOT=y - SB_CONFIG_MCUBOOT_GENERATE_DEFAULT_KMU_KEYFILE=y - CONFIG_TEST_B0_LOCK_REGION=4 - platform_allow: nrf54l15dk/nrf54l15/cpuapp + platform_allow: + - nrf54l15dk/nrf54l15/cpuapp + - nrf54lv10dk/nrf54lv10a/cpuapp + - nrf54lm20dk/nrf54lm20a/cpuapp integration_platforms: - nrf54l15dk/nrf54l15/cpuapp + - nrf54lv10dk/nrf54lv10a/cpuapp + - nrf54lm20dk/nrf54lm20a/cpuapp b0.mcuboot.rwx: extra_args: - b0_CONFIG_SB_DISABLE_SELF_RWX=y @@ -35,6 +45,79 @@ tests: - SB_CONFIG_MCUBOOT_GENERATE_DEFAULT_KMU_KEYFILE=y - CONFIG_TEST_B0_LOCK_READS=y - CONFIG_TEST_B0_LOCK_REGION=4 - platform_allow: nrf54l15dk/nrf54l15/cpuapp + platform_allow: + - nrf54l15dk/nrf54l15/cpuapp + - nrf54lv10dk/nrf54lv10a/cpuapp + - nrf54lm20dk/nrf54lm20a/cpuapp + integration_platforms: + - nrf54l15dk/nrf54l15/cpuapp + - nrf54lv10dk/nrf54lv10a/cpuapp + - nrf54lm20dk/nrf54lm20a/cpuapp + b0.mcuboot.s1.w: + extra_args: + - b0_CONFIG_SB_DISABLE_SELF_RWX=y + - b0_CONFIG_SB_DISABLE_NEXT_W=y + - b0_CONFIG_TEST_B0_LOCK_USE_S1=y + - SB_CONFIG_BOOTLOADER_MCUBOOT=y + - SB_CONFIG_MCUBOOT_GENERATE_DEFAULT_KMU_KEYFILE=y + - CONFIG_TEST_B0_LOCK_REGION=4 + - CONFIG_TEST_B0_LOCK_USE_S1=y + platform_allow: + - nrf54l15dk/nrf54l15/cpuapp + - nrf54lv10dk/nrf54lv10a/cpuapp + - nrf54lm20dk/nrf54lm20a/cpuapp + integration_platforms: + - nrf54l15dk/nrf54l15/cpuapp + - nrf54lv10dk/nrf54lv10a/cpuapp + - nrf54lm20dk/nrf54lm20a/cpuapp + b0.mcuboot.s1.rwx: + extra_args: + - b0_CONFIG_SB_DISABLE_SELF_RWX=y + - b0_CONFIG_SB_DISABLE_NEXT_W=y + - b0_CONFIG_TEST_B0_LOCK_USE_S1=y + - mcuboot_CONFIG_NCS_MCUBOOT_DISABLE_SELF_RWX=y + - SB_CONFIG_BOOTLOADER_MCUBOOT=y + - SB_CONFIG_MCUBOOT_GENERATE_DEFAULT_KMU_KEYFILE=y + - CONFIG_TEST_B0_LOCK_READS=y + - CONFIG_TEST_B0_LOCK_REGION=4 + - CONFIG_TEST_B0_LOCK_USE_S1=y + platform_allow: + - nrf54l15dk/nrf54l15/cpuapp + - nrf54lv10dk/nrf54lv10a/cpuapp + - nrf54lm20dk/nrf54lm20a/cpuapp + integration_platforms: + - nrf54l15dk/nrf54l15/cpuapp + - nrf54lv10dk/nrf54lv10a/cpuapp + - nrf54lm20dk/nrf54lm20a/cpuapp + b0.mcuboot.rwx.b0_did_not_lock: + extra_args: + - mcuboot_CONFIG_NCS_MCUBOOT_DISABLE_SELF_RWX=y + - SB_CONFIG_BOOTLOADER_MCUBOOT=y + - SB_CONFIG_MCUBOOT_GENERATE_DEFAULT_KMU_KEYFILE=y + - CONFIG_TEST_B0_LOCK_READS=y + - CONFIG_TEST_B0_LOCK_REGION=4 + platform_allow: + - nrf54l15dk/nrf54l15/cpuapp + - nrf54lv10dk/nrf54lv10a/cpuapp + - nrf54lm20dk/nrf54lm20a/cpuapp + integration_platforms: + - nrf54l15dk/nrf54l15/cpuapp + - nrf54lv10dk/nrf54lv10a/cpuapp + - nrf54lm20dk/nrf54lm20a/cpuapp + b0.mcuboot.s1.rwx.b0_did_not_lock: + extra_args: + - b0_CONFIG_TEST_B0_LOCK_USE_S1=y + - mcuboot_CONFIG_NCS_MCUBOOT_DISABLE_SELF_RWX=y + - SB_CONFIG_BOOTLOADER_MCUBOOT=y + - SB_CONFIG_MCUBOOT_GENERATE_DEFAULT_KMU_KEYFILE=y + - CONFIG_TEST_B0_LOCK_READS=y + - CONFIG_TEST_B0_LOCK_REGION=4 + - CONFIG_TEST_B0_LOCK_USE_S1=y + platform_allow: + - nrf54l15dk/nrf54l15/cpuapp + - nrf54lv10dk/nrf54lv10a/cpuapp + - nrf54lm20dk/nrf54lm20a/cpuapp integration_platforms: - nrf54l15dk/nrf54l15/cpuapp + - nrf54lv10dk/nrf54lv10a/cpuapp + - nrf54lm20dk/nrf54lm20a/cpuapp diff --git a/west.yml b/west.yml index 7abef9901c3..60045e82899 100644 --- a/west.yml +++ b/west.yml @@ -128,7 +128,7 @@ manifest: compare-by-default: true - name: mcuboot repo-path: sdk-mcuboot - revision: 92b8fd3308f968da0143cbee1d6bb2cfa440f73b + revision: 697ca33a67777cf55045d1b33ababc9511eaa227 path: bootloader/mcuboot - name: qcbor url: https://github.com/laurencelundblade/QCBOR