From 39463d63745bb82b5ceea184a530cf9ece4e263d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=B8e?= Date: Fri, 19 Sep 2025 11:44:19 +0200 Subject: [PATCH 1/5] [nrf fromtree] soc: nordic: uicr: Add support for SECURESTORAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add UICR.SECURESTORAGE configuration based on device tree partitions. Validates partition layout and populates size fields in 1KB units. Handles missing partitions gracefully. Signed-off-by: Sebastian Bøe (cherry picked from commit 38a0f713a683dc6fa9d9444511d7bde243c3ee05) --- scripts/ci/check_compliance.py | 1 + soc/nordic/common/uicr/gen_uicr.py | 198 +++++++++++++++++- .../common/uicr/gen_uicr/CMakeLists.txt | 46 ++++ soc/nordic/common/uicr/gen_uicr/Kconfig | 22 ++ 4 files changed, 266 insertions(+), 1 deletion(-) diff --git a/scripts/ci/check_compliance.py b/scripts/ci/check_compliance.py index 0c4ae7baecc..99343d513e4 100755 --- a/scripts/ci/check_compliance.py +++ b/scripts/ci/check_compliance.py @@ -1303,6 +1303,7 @@ def check_no_undef_outside_kconfig(self, kconf): "GEN_UICR_GENERATE_PERIPHCONF", # Used in specialized build tool, not part of main Kconfig "GEN_UICR_SECONDARY", # Used in specialized build tool, not part of main Kconfig "GEN_UICR_SECONDARY_GENERATE_PERIPHCONF", # Used in specialized build tool, not part of main Kconfig + "GEN_UICR_SECURESTORAGE", # Used in specialized build tool, not part of main Kconfig "HEAP_MEM_POOL_ADD_SIZE_", # Used as an option matching prefix "HUGETLBFS", # Linux, in boards/xtensa/intel_adsp_cavs25/doc "IAR_BUFFERED_WRITE", diff --git a/soc/nordic/common/uicr/gen_uicr.py b/soc/nordic/common/uicr/gen_uicr.py index ce69cd7e80a..b4594d2e272 100644 --- a/soc/nordic/common/uicr/gen_uicr.py +++ b/soc/nordic/common/uicr/gen_uicr.py @@ -8,7 +8,8 @@ import argparse import ctypes as c import sys -from itertools import groupby +from itertools import groupby, pairwise +from typing import NamedTuple from elftools.elf.elffile import ELFFile from intelhex import IntelHex @@ -29,6 +30,14 @@ class ScriptError(RuntimeError): ... +class PartitionInfo(NamedTuple): + """Information about a partition for secure storage validation.""" + + address: int + size: int + name: str + + class PeriphconfEntry(c.LittleEndianStructure): _pack_ = 1 _fields_ = [ @@ -198,6 +207,105 @@ class Uicr(c.LittleEndianStructure): ] +def validate_secure_storage_partitions(args: argparse.Namespace) -> None: + """ + Validate that secure storage partitions are laid out correctly. + + Args: + args: Parsed command line arguments containing partition information + + Raises: + ScriptError: If validation fails + """ + # Expected order: cpuapp_crypto_partition, cpurad_crypto_partition, + # cpuapp_its_partition, cpurad_its_partition + partitions = [ + PartitionInfo( + args.cpuapp_crypto_address, args.cpuapp_crypto_size, "cpuapp_crypto_partition" + ), + PartitionInfo( + args.cpurad_crypto_address, args.cpurad_crypto_size, "cpurad_crypto_partition" + ), + PartitionInfo(args.cpuapp_its_address, args.cpuapp_its_size, "cpuapp_its_partition"), + PartitionInfo(args.cpurad_its_address, args.cpurad_its_size, "cpurad_its_partition"), + ] + + # Filter out zero-sized partitions (missing partitions) + present_partitions = [p for p in partitions if p.size > 0] + + # Require at least one subpartition to be present + if not present_partitions: + raise ScriptError( + "At least one secure storage subpartition must be defined. " + "Define one or more of: cpuapp_crypto_partition, cpurad_crypto_partition, " + "cpuapp_its_partition, cpurad_its_partition" + ) + + # Check 4KB alignment for secure storage start address + if args.securestorage_address % 4096 != 0: + raise ScriptError( + f"Secure storage address {args.securestorage_address:#x} must be aligned to 4KB " + f"(4096 bytes)" + ) + + # Check 4KB alignment for secure storage size + if args.securestorage_size % 4096 != 0: + raise ScriptError( + f"Secure storage size {args.securestorage_size} bytes must be aligned to 4KB " + f"(4096 bytes)" + ) + + # Check that the first present partition starts at the secure storage address + first_partition = present_partitions[0] + if first_partition.address != args.securestorage_address: + raise ScriptError( + f"First partition {first_partition.name} starts at {first_partition.address:#x}, " + f"but must start at secure storage address {args.securestorage_address:#x}" + ) + + # Check that all present partitions have sizes that are multiples of 1KB + for partition in present_partitions: + if partition.size % 1024 != 0: + raise ScriptError( + f"Partition {partition.name} has size {partition.size} bytes, but must be " + f"a multiple of 1024 bytes (1KB)" + ) + + # Check that partitions are in correct order and don't overlap + for curr_partition, next_partition in pairwise(present_partitions): + # Check order - partitions should be in ascending address order + if curr_partition.address >= next_partition.address: + raise ScriptError( + f"Partition {curr_partition.name} (starts at {curr_partition.address:#x}) " + f"must come before {next_partition.name} (starts at {next_partition.address:#x})" + ) + + # Check for overlap + curr_end = curr_partition.address + curr_partition.size + if curr_end > next_partition.address: + raise ScriptError( + f"Partition {curr_partition.name} (ends at {curr_end:#x}) overlaps with " + f"{next_partition.name} (starts at {next_partition.address:#x})" + ) + + # Check for gaps (should be no gaps between consecutive partitions) + if curr_end < next_partition.address: + gap = next_partition.address - curr_end + raise ScriptError( + f"Gap of {gap} bytes between {curr_partition.name} (ends at {curr_end:#x}) and " + f"{next_partition.name} (starts at {next_partition.address:#x})" + ) + + # Check that combined subpartition sizes equal secure_storage_partition size + total_subpartition_size = sum(p.size for p in present_partitions) + if total_subpartition_size != args.securestorage_size: + raise ScriptError( + f"Combined size of subpartitions ({total_subpartition_size} bytes) does not match " + f"secure_storage_partition size ({args.securestorage_size} bytes). " + f"The definition is not coherent." + ) + + def main() -> None: parser = argparse.ArgumentParser( allow_abbrev=False, @@ -255,6 +363,71 @@ def main() -> None: type=lambda s: int(s, 0), help="Absolute flash address of the UICR region (decimal or 0x-prefixed hex)", ) + parser.add_argument( + "--securestorage", + action="store_true", + help="Enable secure storage support in UICR", + ) + parser.add_argument( + "--securestorage-address", + default=None, + type=lambda s: int(s, 0), + help="Absolute flash address of the secure storage partition (decimal or 0x-prefixed hex)", + ) + parser.add_argument( + "--securestorage-size", + default=None, + type=lambda s: int(s, 0), + help="Size in bytes of the secure storage partition (decimal or 0x-prefixed hex)", + ) + parser.add_argument( + "--cpuapp-crypto-address", + default=0, + type=lambda s: int(s, 0), + help="Absolute flash address of cpuapp_crypto_partition (decimal or 0x-prefixed hex)", + ) + parser.add_argument( + "--cpuapp-crypto-size", + default=0, + type=lambda s: int(s, 0), + help="Size in bytes of cpuapp_crypto_partition (decimal or 0x-prefixed hex)", + ) + parser.add_argument( + "--cpurad-crypto-address", + default=0, + type=lambda s: int(s, 0), + help="Absolute flash address of cpurad_crypto_partition (decimal or 0x-prefixed hex)", + ) + parser.add_argument( + "--cpurad-crypto-size", + default=0, + type=lambda s: int(s, 0), + help="Size in bytes of cpurad_crypto_partition (decimal or 0x-prefixed hex)", + ) + parser.add_argument( + "--cpuapp-its-address", + default=0, + type=lambda s: int(s, 0), + help="Absolute flash address of cpuapp_its_partition (decimal or 0x-prefixed hex)", + ) + parser.add_argument( + "--cpuapp-its-size", + default=0, + type=lambda s: int(s, 0), + help="Size in bytes of cpuapp_its_partition (decimal or 0x-prefixed hex)", + ) + parser.add_argument( + "--cpurad-its-address", + default=0, + type=lambda s: int(s, 0), + help="Absolute flash address of cpurad_its_partition (decimal or 0x-prefixed hex)", + ) + parser.add_argument( + "--cpurad-its-size", + default=0, + type=lambda s: int(s, 0), + help="Size in bytes of cpurad_its_partition (decimal or 0x-prefixed hex)", + ) parser.add_argument( "--secondary", action="store_true", @@ -327,12 +500,35 @@ def main() -> None: "--out-secondary-periphconf-hex is used" ) + # Validate secure storage argument dependencies + if args.securestorage: + if args.securestorage_address is None: + raise ScriptError( + "--securestorage-address is required when --securestorage is used" + ) + if args.securestorage_size is None: + raise ScriptError("--securestorage-size is required when --securestorage is used") + + # Validate partition layout + validate_secure_storage_partitions(args) + init_values = DISABLED_VALUE.to_bytes(4, "little") * (c.sizeof(Uicr) // 4) uicr = Uicr.from_buffer_copy(init_values) uicr.VERSION.MAJOR = UICR_FORMAT_VERSION_MAJOR uicr.VERSION.MINOR = UICR_FORMAT_VERSION_MINOR + # Handle secure storage configuration + if args.securestorage: + uicr.SECURESTORAGE.ENABLE = ENABLED_VALUE + uicr.SECURESTORAGE.ADDRESS = args.securestorage_address + + # Set partition sizes in 1KB units + uicr.SECURESTORAGE.CRYPTO.APPLICATIONSIZE1KB = args.cpuapp_crypto_size // 1024 + uicr.SECURESTORAGE.CRYPTO.RADIOCORESIZE1KB = args.cpurad_crypto_size // 1024 + uicr.SECURESTORAGE.ITS.APPLICATIONSIZE1KB = args.cpuapp_its_size // 1024 + uicr.SECURESTORAGE.ITS.RADIOCORESIZE1KB = args.cpurad_its_size // 1024 + # Process periphconf data first and configure UICR completely before creating hex objects periphconf_hex = IntelHex() secondary_periphconf_hex = IntelHex() diff --git a/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt b/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt index 1163ff8e21f..28c6e674909 100644 --- a/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt +++ b/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt @@ -44,6 +44,25 @@ function(compute_partition_address_and_size partition_nodelabel output_address_v set(${output_size_var} ${partition_size} PARENT_SCOPE) endfunction() +# Function to compute optional partition address and size from devicetree +# If partition doesn't exist, sets both address and size to 0 +function(compute_optional_partition_address_and_size partition_nodelabel output_address_var output_size_var) + # Initialize with default values + set(${output_address_var} 0 PARENT_SCOPE) + set(${output_size_var} 0 PARENT_SCOPE) + + # Check if partition exists + dt_nodelabel(partition_path NODELABEL ${partition_nodelabel} QUIET) + if(partition_path) + # Call nested function with different variable names to avoid naming conflicts + compute_partition_address_and_size(${partition_nodelabel} temp_addr temp_size) + + # Copy the results to the output variables in parent scope + set(${output_address_var} ${temp_addr} PARENT_SCOPE) + set(${output_size_var} ${temp_size} PARENT_SCOPE) + endif() +endfunction() + # Use CMAKE_VERBOSE_MAKEFILE to silence an unused-variable warning. if(CMAKE_VERBOSE_MAKEFILE) endif() @@ -60,6 +79,32 @@ set(secondary_periphconf_hex_file ${APPLICATION_BINARY_DIR}/zephyr/secondary_per dt_nodelabel(uicr_path NODELABEL "uicr" REQUIRED) dt_reg_addr(UICR_ADDRESS PATH ${uicr_path} REQUIRED) +# Handle secure storage configuration +set(securestorage_args) +if(CONFIG_GEN_UICR_SECURESTORAGE) + list(APPEND securestorage_args --securestorage) + + # Extract secure storage partition information (required) + compute_partition_address_and_size("secure_storage_partition" SECURE_STORAGE_ADDRESS SECURE_STORAGE_SIZE) + list(APPEND securestorage_args --securestorage-address ${SECURE_STORAGE_ADDRESS}) + list(APPEND securestorage_args --securestorage-size ${SECURE_STORAGE_SIZE}) + + # Extract individual partition information for validation (optional partitions) + compute_optional_partition_address_and_size("cpuapp_crypto_partition" CPUAPP_CRYPTO_ADDRESS CPUAPP_CRYPTO_SIZE) + compute_optional_partition_address_and_size("cpurad_crypto_partition" CPURAD_CRYPTO_ADDRESS CPURAD_CRYPTO_SIZE) + compute_optional_partition_address_and_size("cpuapp_its_partition" CPUAPP_ITS_ADDRESS CPUAPP_ITS_SIZE) + compute_optional_partition_address_and_size("cpurad_its_partition" CPURAD_ITS_ADDRESS CPURAD_ITS_SIZE) + + list(APPEND securestorage_args --cpuapp-crypto-address ${CPUAPP_CRYPTO_ADDRESS}) + list(APPEND securestorage_args --cpuapp-crypto-size ${CPUAPP_CRYPTO_SIZE}) + list(APPEND securestorage_args --cpurad-crypto-address ${CPURAD_CRYPTO_ADDRESS}) + list(APPEND securestorage_args --cpurad-crypto-size ${CPURAD_CRYPTO_SIZE}) + list(APPEND securestorage_args --cpuapp-its-address ${CPUAPP_ITS_ADDRESS}) + list(APPEND securestorage_args --cpuapp-its-size ${CPUAPP_ITS_SIZE}) + list(APPEND securestorage_args --cpurad-its-address ${CPURAD_ITS_ADDRESS}) + list(APPEND securestorage_args --cpurad-its-size ${CPURAD_ITS_SIZE}) +endif(CONFIG_GEN_UICR_SECURESTORAGE) + if(CONFIG_GEN_UICR_GENERATE_PERIPHCONF) # gen_uicr.py parses all zephyr.elf files. To find these files (which # have not been built yet) we scan sibling build directories for @@ -134,6 +179,7 @@ add_custom_command( --out-merged-hex ${merged_hex_file} --out-uicr-hex ${uicr_hex_file} ${periphconf_args} + ${securestorage_args} ${secondary_args} DEPENDS ${periphconf_elfs} ${secondary_periphconf_elfs} WORKING_DIRECTORY ${APPLICATION_BINARY_DIR} diff --git a/soc/nordic/common/uicr/gen_uicr/Kconfig b/soc/nordic/common/uicr/gen_uicr/Kconfig index 41d31db6464..17758597bc3 100644 --- a/soc/nordic/common/uicr/gen_uicr/Kconfig +++ b/soc/nordic/common/uicr/gen_uicr/Kconfig @@ -21,6 +21,28 @@ config GEN_UICR_SECONDARY_GENERATE_PERIPHCONF When enabled, the UICR generator will populate the secondary_periphconf_partition partition. +config GEN_UICR_SECURESTORAGE + bool "Enable UICR.SECURESTORAGE" + default y + depends on $(dt_nodelabel_enabled,secure_storage_partition) + help + When enabled, the UICR generator will configure the + secure storage region based on device tree partitions. + + The following device tree partitions are used: + - secure_storage_partition: Main secure storage partition (required) + - cpuapp_crypto_partition: Application processor crypto storage (optional) + - cpurad_crypto_partition: Radio core crypto storage (optional) + - cpuapp_its_partition: Application processor internal trusted storage (optional) + - cpurad_its_partition: Radio core internal trusted storage (optional) + + Requirements: + - The secure_storage_partition address and size must be aligned to 4KB + - All subpartitions must be multiples of 1KB and laid out contiguously + without gaps + - At least one subpartition must be defined + - Combined subpartition sizes must equal secure_storage_partition size + endmenu source "Kconfig.zephyr" From 3ad22125bbdecf779444170812ab75388de1ec24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=B8e?= Date: Tue, 9 Sep 2025 13:27:39 +0200 Subject: [PATCH 2/5] [nrf fromtree] soc: nordic: uicr: Add support for uicr.SECONDARY.PROCESSOR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for uicr.SECONDARY.PROCESSOR. Signed-off-by: Sebastian Bøe (cherry picked from commit 9f45d2ccd787655efb62fd2a8afac4dd026d0a01) --- scripts/ci/check_compliance.py | 1 + soc/nordic/common/uicr/gen_uicr.py | 7 +++ .../common/uicr/gen_uicr/CMakeLists.txt | 1 + soc/nordic/common/uicr/gen_uicr/Kconfig | 48 ++++++++++++++----- 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/scripts/ci/check_compliance.py b/scripts/ci/check_compliance.py index 99343d513e4..7a27e6dca3d 100755 --- a/scripts/ci/check_compliance.py +++ b/scripts/ci/check_compliance.py @@ -1303,6 +1303,7 @@ def check_no_undef_outside_kconfig(self, kconf): "GEN_UICR_GENERATE_PERIPHCONF", # Used in specialized build tool, not part of main Kconfig "GEN_UICR_SECONDARY", # Used in specialized build tool, not part of main Kconfig "GEN_UICR_SECONDARY_GENERATE_PERIPHCONF", # Used in specialized build tool, not part of main Kconfig + "GEN_UICR_SECONDARY_PROCESSOR_VALUE", # Used in specialized build tool, not part of main Kconfig "GEN_UICR_SECURESTORAGE", # Used in specialized build tool, not part of main Kconfig "HEAP_MEM_POOL_ADD_SIZE_", # Used as an option matching prefix "HUGETLBFS", # Linux, in boards/xtensa/intel_adsp_cavs25/doc diff --git a/soc/nordic/common/uicr/gen_uicr.py b/soc/nordic/common/uicr/gen_uicr.py index b4594d2e272..e074314a1ab 100644 --- a/soc/nordic/common/uicr/gen_uicr.py +++ b/soc/nordic/common/uicr/gen_uicr.py @@ -439,6 +439,12 @@ def main() -> None: type=lambda s: int(s, 0), help="Absolute flash address of the secondary firmware (decimal or 0x-prefixed hex)", ) + parser.add_argument( + "--secondary-processor", + default=0xBD2328A8, + type=lambda s: int(s, 0), + help="Processor to boot for the secondary firmware ", + ) parser.add_argument( "--secondary-periphconf-address", default=None, @@ -562,6 +568,7 @@ def main() -> None: if args.secondary: uicr.SECONDARY.ENABLE = ENABLED_VALUE uicr.SECONDARY.ADDRESS = args.secondary_address + uicr.SECONDARY.PROCESSOR = args.secondary_processor # Handle secondary periphconf if provided if args.out_secondary_periphconf_hex: diff --git a/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt b/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt index 28c6e674909..fe1b0e378cf 100644 --- a/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt +++ b/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt @@ -154,6 +154,7 @@ if(CONFIG_GEN_UICR_SECONDARY) list(APPEND secondary_args --secondary-address ${SECONDARY_ADDRESS} + --secondary-processor ${CONFIG_GEN_UICR_SECONDARY_PROCESSOR_VALUE} ) if(CONFIG_GEN_UICR_SECONDARY_GENERATE_PERIPHCONF) diff --git a/soc/nordic/common/uicr/gen_uicr/Kconfig b/soc/nordic/common/uicr/gen_uicr/Kconfig index 17758597bc3..1c49209592f 100644 --- a/soc/nordic/common/uicr/gen_uicr/Kconfig +++ b/soc/nordic/common/uicr/gen_uicr/Kconfig @@ -10,17 +10,6 @@ config GEN_UICR_GENERATE_PERIPHCONF When enabled, the UICR generator will populate the periphconf_partition partition. -config GEN_UICR_SECONDARY - bool "Enable UICR.SECONDARY.ENABLE" - -config GEN_UICR_SECONDARY_GENERATE_PERIPHCONF - bool "Generate SECONDARY.PERIPHCONF hex alongside UICR" - default y - depends on GEN_UICR_SECONDARY - help - When enabled, the UICR generator will populate the - secondary_periphconf_partition partition. - config GEN_UICR_SECURESTORAGE bool "Enable UICR.SECURESTORAGE" default y @@ -43,6 +32,43 @@ config GEN_UICR_SECURESTORAGE - At least one subpartition must be defined - Combined subpartition sizes must equal secure_storage_partition size +config GEN_UICR_SECONDARY + bool "Enable UICR.SECONDARY.ENABLE" + +if GEN_UICR_SECONDARY + +config GEN_UICR_SECONDARY_GENERATE_PERIPHCONF + bool "Generate SECONDARY.PERIPHCONF hex alongside UICR" + default y + help + When enabled, the UICR generator will populate the + secondary_periphconf_partition partition. + +choice GEN_UICR_SECONDARY_PROCESSOR + prompt "Secondary processor selection" + default GEN_UICR_SECONDARY_PROCESSOR_APPLICATION + help + Processor to boot for the secondary firmware. + +config GEN_UICR_SECONDARY_PROCESSOR_APPLICATION + bool "APPLICATION processor" + help + Boot secondary firmware on the APPLICATION processor. + +config GEN_UICR_SECONDARY_PROCESSOR_RADIOCORE + bool "RADIOCORE processor" + help + Boot secondary firmware on the RADIOCORE processor. + +endchoice + +config GEN_UICR_SECONDARY_PROCESSOR_VALUE + hex + default 0xBD2328A8 if GEN_UICR_SECONDARY_PROCESSOR_APPLICATION + default 0x1730C77F if GEN_UICR_SECONDARY_PROCESSOR_RADIOCORE + +endif # GEN_UICR_SECONDARY + endmenu source "Kconfig.zephyr" From 440ad55343e1c0d366e787b9205066a109eb9b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=B8e?= Date: Tue, 9 Sep 2025 13:46:07 +0200 Subject: [PATCH 3/5] [nrf fromtree] soc: nordic: uicr: Add support for uicr.PROTECTEDMEM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for PROTECTEDMEM. Signed-off-by: Sebastian Bøe (cherry picked from commit 7c9275c8914ab105d8d96ea4096bef4c4ef7d10e) --- scripts/ci/check_compliance.py | 2 ++ soc/nordic/common/uicr/gen_uicr.py | 22 +++++++++++++++++++ .../common/uicr/gen_uicr/CMakeLists.txt | 8 +++++++ soc/nordic/common/uicr/gen_uicr/Kconfig | 14 ++++++++++++ 4 files changed, 46 insertions(+) diff --git a/scripts/ci/check_compliance.py b/scripts/ci/check_compliance.py index 7a27e6dca3d..7102a12049a 100755 --- a/scripts/ci/check_compliance.py +++ b/scripts/ci/check_compliance.py @@ -1301,6 +1301,8 @@ def check_no_undef_outside_kconfig(self, kconf): "FOO_SETTING_1", "FOO_SETTING_2", "GEN_UICR_GENERATE_PERIPHCONF", # Used in specialized build tool, not part of main Kconfig + "GEN_UICR_PROTECTEDMEM", # Used in specialized build tool, not part of main Kconfig + "GEN_UICR_PROTECTEDMEM_SIZE_BYTES", # Used in specialized build tool, not part of main Kconfig "GEN_UICR_SECONDARY", # Used in specialized build tool, not part of main Kconfig "GEN_UICR_SECONDARY_GENERATE_PERIPHCONF", # Used in specialized build tool, not part of main Kconfig "GEN_UICR_SECONDARY_PROCESSOR_VALUE", # Used in specialized build tool, not part of main Kconfig diff --git a/soc/nordic/common/uicr/gen_uicr.py b/soc/nordic/common/uicr/gen_uicr.py index e074314a1ab..bea0ba2a2df 100644 --- a/soc/nordic/common/uicr/gen_uicr.py +++ b/soc/nordic/common/uicr/gen_uicr.py @@ -26,6 +26,8 @@ ENABLED_VALUE = 0xFFFF_FFFF DISABLED_VALUE = 0xBD23_28A8 +KB_4 = 4096 + class ScriptError(RuntimeError): ... @@ -428,6 +430,16 @@ def main() -> None: type=lambda s: int(s, 0), help="Size in bytes of cpurad_its_partition (decimal or 0x-prefixed hex)", ) + parser.add_argument( + "--protectedmem", + action="store_true", + help="Enable protected memory region in UICR", + ) + parser.add_argument( + "--protectedmem-size-bytes", + type=int, + help="Protected memory size in bytes (must be divisible by 4096)", + ) parser.add_argument( "--secondary", action="store_true", @@ -535,6 +547,16 @@ def main() -> None: uicr.SECURESTORAGE.ITS.APPLICATIONSIZE1KB = args.cpuapp_its_size // 1024 uicr.SECURESTORAGE.ITS.RADIOCORESIZE1KB = args.cpurad_its_size // 1024 + # Handle protected memory configuration + if args.protectedmem: + if args.protectedmem_size_bytes % KB_4 != 0: + raise ScriptError( + f"Protected memory size ({args.protectedmem_size_bytes} bytes) " + f"must be divisible by {KB_4}" + ) + uicr.PROTECTEDMEM.ENABLE = ENABLED_VALUE + uicr.PROTECTEDMEM.SIZE4KB = args.protectedmem_size_bytes // KB_4 + # Process periphconf data first and configure UICR completely before creating hex objects periphconf_hex = IntelHex() secondary_periphconf_hex = IntelHex() diff --git a/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt b/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt index fe1b0e378cf..9e37639a82c 100644 --- a/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt +++ b/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt @@ -67,6 +67,7 @@ endfunction() if(CMAKE_VERBOSE_MAKEFILE) endif() +set(protectedmem_args) set(periphconf_args) set(periphconf_elfs) set(merged_hex_file ${APPLICATION_BINARY_DIR}/zephyr/${CONFIG_KERNEL_BIN_NAME}.hex) @@ -105,6 +106,12 @@ if(CONFIG_GEN_UICR_SECURESTORAGE) list(APPEND securestorage_args --cpurad-its-size ${CPURAD_ITS_SIZE}) endif(CONFIG_GEN_UICR_SECURESTORAGE) +# Handle protected memory configuration +if(CONFIG_GEN_UICR_PROTECTEDMEM) + list(APPEND protectedmem_args --protectedmem) + list(APPEND protectedmem_args --protectedmem-size-bytes ${CONFIG_GEN_UICR_PROTECTEDMEM_SIZE_BYTES}) +endif() + if(CONFIG_GEN_UICR_GENERATE_PERIPHCONF) # gen_uicr.py parses all zephyr.elf files. To find these files (which # have not been built yet) we scan sibling build directories for @@ -181,6 +188,7 @@ add_custom_command( --out-uicr-hex ${uicr_hex_file} ${periphconf_args} ${securestorage_args} + ${protectedmem_args} ${secondary_args} DEPENDS ${periphconf_elfs} ${secondary_periphconf_elfs} WORKING_DIRECTORY ${APPLICATION_BINARY_DIR} diff --git a/soc/nordic/common/uicr/gen_uicr/Kconfig b/soc/nordic/common/uicr/gen_uicr/Kconfig index 1c49209592f..fe7413a9d0d 100644 --- a/soc/nordic/common/uicr/gen_uicr/Kconfig +++ b/soc/nordic/common/uicr/gen_uicr/Kconfig @@ -32,6 +32,20 @@ config GEN_UICR_SECURESTORAGE - At least one subpartition must be defined - Combined subpartition sizes must equal secure_storage_partition size +config GEN_UICR_PROTECTEDMEM + bool "Enable UICR.PROTECTEDMEM" + help + When enabled, the UICR generator will configure the + protected memory region. + +config GEN_UICR_PROTECTEDMEM_SIZE_BYTES + int "Protected memory size in bytes" + default 4096 + depends on GEN_UICR_PROTECTEDMEM + help + Size of the protected memory region in bytes. + This value must be divisible by 4096 (4 kiB). + config GEN_UICR_SECONDARY bool "Enable UICR.SECONDARY.ENABLE" From f3c82079c2e3c5b539c524db4b6af13ac64fdf60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=B8e?= Date: Fri, 10 Oct 2025 16:35:28 +0200 Subject: [PATCH 4/5] [nrf fromlist] soc: nordic: uicr: Change how secondary images are detected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Detect secondary images by checking a Kconfig value instead of a marker file. Upstream PR #: 97356 Signed-off-by: Sebastian Bøe --- soc/nordic/common/uicr/Kconfig | 7 +++++++ soc/nordic/common/uicr/gen_uicr/CMakeLists.txt | 13 +++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/soc/nordic/common/uicr/Kconfig b/soc/nordic/common/uicr/Kconfig index 73b5e5cf2d1..37bdd014b7d 100644 --- a/soc/nordic/common/uicr/Kconfig +++ b/soc/nordic/common/uicr/Kconfig @@ -18,3 +18,10 @@ config NRF_PERIPHCONF_GENERATE_ENTRIES help Generate a C file containing PERIPHCONF entries based on the device configuration in the devicetree. + +config IS_IRONSIDE_SE_SECONDARY_IMAGE + bool "Ironside SE secondary image indicator (informative only, do not change)" + help + This Kconfig is set by sysbuild to indicate that this image is a + secondary firmware for Ironside SE. This is used by the UICR generation + system to determine which PERIPHCONF partition to use. diff --git a/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt b/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt index 9e37639a82c..b64b7c0399a 100644 --- a/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt +++ b/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt @@ -23,11 +23,19 @@ project(uicr) function(parse_kconfig_value config_file config_name output_var) file(STRINGS ${config_file} config_lines ENCODING "UTF-8") foreach(line ${config_lines}) + # Match quoted strings like CONFIG_FOO="value" if("${line}" MATCHES "^${config_name}=\"(.*)\"$") set(${output_var} "${CMAKE_MATCH_1}" PARENT_SCOPE) return() endif() + # Match unquoted values like CONFIG_FOO=y or CONFIG_FOO=n + if("${line}" MATCHES "^${config_name}=(.*)$") + set(${output_var} "${CMAKE_MATCH_1}" PARENT_SCOPE) + return() + endif() endforeach() + # If not found, return empty (including "# CONFIG_FOO is not set" case) + set(${output_var} "" PARENT_SCOPE) endfunction() # Function to compute partition absolute address and size from devicetree @@ -131,8 +139,9 @@ if(CONFIG_GEN_UICR_GENERATE_PERIPHCONF) parse_kconfig_value(${_dir}/zephyr/.config CONFIG_KERNEL_BIN_NAME kernel_bin_name) set(kernel_elf_path ${_dir}/zephyr/${kernel_bin_name}.elf) - # Check if this is secondary firmware by looking for the marker file - if(EXISTS ${_dir}/is_secondary_firmware.txt) + # Check if this is secondary firmware by reading the Kconfig from .config + parse_kconfig_value(${_dir}/zephyr/.config CONFIG_IS_IRONSIDE_SE_SECONDARY_IMAGE is_secondary) + if(is_secondary STREQUAL "y") list(APPEND secondary_periphconf_elfs ${kernel_elf_path}) else() list(APPEND periphconf_elfs ${kernel_elf_path}) From ffd450240cdd64ec8e1789b942a321a576638b3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=B8e?= Date: Mon, 13 Oct 2025 10:41:24 +0200 Subject: [PATCH 5/5] [nrf fromlist] soc: nordic: uicr: Add support for UICR.WDTSTART MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for UICR.WDTSTART. UICR.WDTSTART configures the automatic start of a local watchdog timer before the application core is booted. This provides early system protection ensuring that the system can recover from early boot failures. Upstream PR #: 97337 Signed-off-by: Sebastian Bøe --- scripts/ci/check_compliance.py | 18 ++-- soc/nordic/common/uicr/gen_uicr.py | 42 +++++++++ .../common/uicr/gen_uicr/CMakeLists.txt | 16 ++++ soc/nordic/common/uicr/gen_uicr/Kconfig | 85 +++++++++++++++++++ 4 files changed, 155 insertions(+), 6 deletions(-) diff --git a/scripts/ci/check_compliance.py b/scripts/ci/check_compliance.py index 7102a12049a..f5ca11da190 100755 --- a/scripts/ci/check_compliance.py +++ b/scripts/ci/check_compliance.py @@ -1301,12 +1301,18 @@ def check_no_undef_outside_kconfig(self, kconf): "FOO_SETTING_1", "FOO_SETTING_2", "GEN_UICR_GENERATE_PERIPHCONF", # Used in specialized build tool, not part of main Kconfig - "GEN_UICR_PROTECTEDMEM", # Used in specialized build tool, not part of main Kconfig - "GEN_UICR_PROTECTEDMEM_SIZE_BYTES", # Used in specialized build tool, not part of main Kconfig - "GEN_UICR_SECONDARY", # Used in specialized build tool, not part of main Kconfig - "GEN_UICR_SECONDARY_GENERATE_PERIPHCONF", # Used in specialized build tool, not part of main Kconfig - "GEN_UICR_SECONDARY_PROCESSOR_VALUE", # Used in specialized build tool, not part of main Kconfig - "GEN_UICR_SECURESTORAGE", # Used in specialized build tool, not part of main Kconfig + "GEN_UICR_PROTECTEDMEM", + "GEN_UICR_PROTECTEDMEM_SIZE_BYTES", + "GEN_UICR_SECONDARY", + "GEN_UICR_SECONDARY_GENERATE_PERIPHCONF", + "GEN_UICR_SECONDARY_PROCESSOR_VALUE", + "GEN_UICR_SECONDARY_WDTSTART", + "GEN_UICR_SECONDARY_WDTSTART_CRV", + "GEN_UICR_SECONDARY_WDTSTART_INSTANCE_CODE", + "GEN_UICR_SECURESTORAGE", + "GEN_UICR_WDTSTART", + "GEN_UICR_WDTSTART_CRV", + "GEN_UICR_WDTSTART_INSTANCE_CODE", # Used in specialized build tool, not part of main Kconfig "HEAP_MEM_POOL_ADD_SIZE_", # Used as an option matching prefix "HUGETLBFS", # Linux, in boards/xtensa/intel_adsp_cavs25/doc "IAR_BUFFERED_WRITE", diff --git a/soc/nordic/common/uicr/gen_uicr.py b/soc/nordic/common/uicr/gen_uicr.py index bea0ba2a2df..da2938bf05c 100644 --- a/soc/nordic/common/uicr/gen_uicr.py +++ b/soc/nordic/common/uicr/gen_uicr.py @@ -440,6 +440,36 @@ def main() -> None: type=int, help="Protected memory size in bytes (must be divisible by 4096)", ) + parser.add_argument( + "--wdtstart", + action="store_true", + help="Enable watchdog timer start in UICR", + ) + parser.add_argument( + "--wdtstart-instance-code", + type=lambda s: int(s, 0), + help="Watchdog timer instance code (0xBD2328A8 for WDT0, 0x1730C77F for WDT1)", + ) + parser.add_argument( + "--wdtstart-crv", + type=int, + help="Initial Counter Reload Value (CRV) for watchdog timer (minimum: 0xF)", + ) + parser.add_argument( + "--secondary-wdtstart", + action="store_true", + help="Enable watchdog timer start in UICR.SECONDARY", + ) + parser.add_argument( + "--secondary-wdtstart-instance-code", + type=lambda s: int(s, 0), + help="Secondary watchdog timer instance code (0xBD2328A8 for WDT0, 0x1730C77F for WDT1)", + ) + parser.add_argument( + "--secondary-wdtstart-crv", + type=int, + help="Secondary initial Counter Reload Value (CRV) for watchdog timer (minimum: 0xF)", + ) parser.add_argument( "--secondary", action="store_true", @@ -557,6 +587,12 @@ def main() -> None: uicr.PROTECTEDMEM.ENABLE = ENABLED_VALUE uicr.PROTECTEDMEM.SIZE4KB = args.protectedmem_size_bytes // KB_4 + # Handle WDTSTART configuration + if args.wdtstart: + uicr.WDTSTART.ENABLE = ENABLED_VALUE + uicr.WDTSTART.CRV = args.wdtstart_crv + uicr.WDTSTART.INSTANCE = args.wdtstart_instance_code + # Process periphconf data first and configure UICR completely before creating hex objects periphconf_hex = IntelHex() secondary_periphconf_hex = IntelHex() @@ -625,6 +661,12 @@ def main() -> None: uicr.SECONDARY.PERIPHCONF.MAXCOUNT = args.secondary_periphconf_size // 8 + # Handle secondary WDTSTART configuration + if args.secondary_wdtstart: + uicr.SECONDARY.WDTSTART.ENABLE = ENABLED_VALUE + uicr.SECONDARY.WDTSTART.CRV = args.secondary_wdtstart_crv + uicr.SECONDARY.WDTSTART.INSTANCE = args.secondary_wdtstart_instance_code + # Create UICR hex object with final UICR data uicr_hex = IntelHex() uicr_hex.frombytes(bytes(uicr), offset=args.uicr_address) diff --git a/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt b/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt index b64b7c0399a..39737888023 100644 --- a/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt +++ b/soc/nordic/common/uicr/gen_uicr/CMakeLists.txt @@ -77,6 +77,7 @@ endif() set(protectedmem_args) set(periphconf_args) +set(wdtstart_args) set(periphconf_elfs) set(merged_hex_file ${APPLICATION_BINARY_DIR}/zephyr/${CONFIG_KERNEL_BIN_NAME}.hex) set(secondary_periphconf_elfs) @@ -120,6 +121,13 @@ if(CONFIG_GEN_UICR_PROTECTEDMEM) list(APPEND protectedmem_args --protectedmem-size-bytes ${CONFIG_GEN_UICR_PROTECTEDMEM_SIZE_BYTES}) endif() +# Handle WDTSTART configuration +if(CONFIG_GEN_UICR_WDTSTART) + list(APPEND wdtstart_args --wdtstart) + list(APPEND wdtstart_args --wdtstart-instance-code ${CONFIG_GEN_UICR_WDTSTART_INSTANCE_CODE}) + list(APPEND wdtstart_args --wdtstart-crv ${CONFIG_GEN_UICR_WDTSTART_CRV}) +endif() + if(CONFIG_GEN_UICR_GENERATE_PERIPHCONF) # gen_uicr.py parses all zephyr.elf files. To find these files (which # have not been built yet) we scan sibling build directories for @@ -173,6 +181,13 @@ if(CONFIG_GEN_UICR_SECONDARY) --secondary-processor ${CONFIG_GEN_UICR_SECONDARY_PROCESSOR_VALUE} ) + # Handle secondary WDTSTART configuration + if(CONFIG_GEN_UICR_SECONDARY_WDTSTART) + list(APPEND secondary_args --secondary-wdtstart) + list(APPEND secondary_args --secondary-wdtstart-instance-code ${CONFIG_GEN_UICR_SECONDARY_WDTSTART_INSTANCE_CODE}) + list(APPEND secondary_args --secondary-wdtstart-crv ${CONFIG_GEN_UICR_SECONDARY_WDTSTART_CRV}) + endif() + if(CONFIG_GEN_UICR_SECONDARY_GENERATE_PERIPHCONF) # Compute SECONDARY_PERIPHCONF absolute address and size from this image's devicetree compute_partition_address_and_size("secondary_periphconf_partition" SECONDARY_PERIPHCONF_ADDRESS SECONDARY_PERIPHCONF_SIZE) @@ -195,6 +210,7 @@ add_custom_command( --uicr-address ${UICR_ADDRESS} --out-merged-hex ${merged_hex_file} --out-uicr-hex ${uicr_hex_file} + ${wdtstart_args} ${periphconf_args} ${securestorage_args} ${protectedmem_args} diff --git a/soc/nordic/common/uicr/gen_uicr/Kconfig b/soc/nordic/common/uicr/gen_uicr/Kconfig index fe7413a9d0d..9b630139e64 100644 --- a/soc/nordic/common/uicr/gen_uicr/Kconfig +++ b/soc/nordic/common/uicr/gen_uicr/Kconfig @@ -46,6 +46,91 @@ config GEN_UICR_PROTECTEDMEM_SIZE_BYTES Size of the protected memory region in bytes. This value must be divisible by 4096 (4 kiB). +config GEN_UICR_WDTSTART + bool "Enable UICR.WDTSTART" + help + When enabled, the UICR generator will configure the + watchdog timer to start automatically before the + application core is booted. + +choice GEN_UICR_WDTSTART_INSTANCE + prompt "Watchdog timer instance" + depends on GEN_UICR_WDTSTART + help + Select which watchdog timer instance to use. + +config GEN_UICR_WDTSTART_INSTANCE_WDT0 + bool "WDT0" + help + Use watchdog timer instance 0. + +config GEN_UICR_WDTSTART_INSTANCE_WDT1 + bool "WDT1" + help + Use watchdog timer instance 1. + +endchoice + +config GEN_UICR_WDTSTART_INSTANCE_CODE + hex + default 0xBD2328A8 if GEN_UICR_WDTSTART_INSTANCE_WDT0 + default 0x1730C77F if GEN_UICR_WDTSTART_INSTANCE_WDT1 + depends on GEN_UICR_WDTSTART + +config GEN_UICR_WDTSTART_CRV + int "Initial Counter Reload Value (CRV)" + default 65535 + range 15 4294967295 + depends on GEN_UICR_WDTSTART + help + Initial Counter Reload Value (CRV) for the watchdog timer. + This value determines the watchdog timeout period. + Must be at least 15 (0xF) to ensure proper watchdog operation. + Default value 65535 creates a 2-second timeout. + +config GEN_UICR_SECONDARY_WDTSTART + bool "Enable UICR.SECONDARY.WDTSTART" + depends on GEN_UICR_SECONDARY + help + When enabled, the UICR generator will configure the + watchdog timer to start automatically before the + secondary firmware is booted. + +choice GEN_UICR_SECONDARY_WDTSTART_INSTANCE + prompt "Secondary watchdog timer instance" + depends on GEN_UICR_SECONDARY_WDTSTART + help + Select which watchdog timer instance to use for secondary firmware. + +config GEN_UICR_SECONDARY_WDTSTART_INSTANCE_WDT0 + bool "WDT0" + help + Use watchdog timer instance 0 for secondary firmware. + +config GEN_UICR_SECONDARY_WDTSTART_INSTANCE_WDT1 + bool "WDT1" + help + Use watchdog timer instance 1 for secondary firmware. + +endchoice + +config GEN_UICR_SECONDARY_WDTSTART_INSTANCE_CODE + hex + default 0xBD2328A8 if GEN_UICR_SECONDARY_WDTSTART_INSTANCE_WDT0 + default 0x1730C77F if GEN_UICR_SECONDARY_WDTSTART_INSTANCE_WDT1 + depends on GEN_UICR_SECONDARY_WDTSTART + +config GEN_UICR_SECONDARY_WDTSTART_CRV + int "Secondary initial Counter Reload Value (CRV)" + default 65535 + range 15 4294967295 + depends on GEN_UICR_SECONDARY_WDTSTART + help + Initial Counter Reload Value (CRV) for the secondary watchdog timer. + This value determines the watchdog timeout period. + Must be at least 15 (0xF) to ensure proper watchdog operation. + Default value 65535 creates a 2-second timeout. + config GEN_UICR_SECONDARY bool "Enable UICR.SECONDARY.ENABLE"