From 496106686728aca4a18d7407133126008d48b88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=B8e?= Date: Wed, 10 Sep 2025 16:04:38 +0200 Subject: [PATCH 1/2] boards: nrf54h20dk: Add secure storage partitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the default memory map to include a `secure_storage_partition`, which is divided into at most four subpartitions. These will be used to configure UICR.SECURESTORAGE, if enabled. Signed-off-by: Sebastian Bøe --- .../nrf54h20dk_nrf54h20-memory_map.dtsi | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-memory_map.dtsi b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-memory_map.dtsi index f8557c65ce605..fa1b601974b0d 100644 --- a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-memory_map.dtsi +++ b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-memory_map.dtsi @@ -174,5 +174,34 @@ secondary_periphconf_partition: partition@1c0000 { reg = <0x1c0000 DT_SIZE_K(8)>; }; + + /* NB: A gap has been left here for future partitions */ + + /* 0x1fd000 was chosen for secure_storage_partition such that + * there is no more space after secure_storage_partition. + */ + secure_storage_partition: partition@1fd000 { + compatible = "fixed-subpartitions"; + reg = <0x1fd000 DT_SIZE_K(12)>; + ranges = <0x0 0x1fd000 0x3000>; + #address-cells = <1>; + #size-cells = <1>; + + cpuapp_crypto_partition: partition@0 { + reg = <0x0 DT_SIZE_K(4)>; + }; + + cpurad_crypto_partition: partition@1000 { + reg = <0x1000 DT_SIZE_K(4)>; + }; + + cpuapp_its_partition: partition@2000 { + reg = <0x2000 DT_SIZE_K(2)>; + }; + + cpurad_its_partition: partition@2800 { + reg = <0x2800 DT_SIZE_K(2)>; + }; + }; }; }; From 468cff68e9c8c47037e371afd7ee0637a8b38121 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 2/2] 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 --- 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 540744d7e122d..6ffb0347ddc78 100755 --- a/scripts/ci/check_compliance.py +++ b/scripts/ci/check_compliance.py @@ -1270,6 +1270,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 78422e4312cd4..64a35f0e3ba61 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 1163ff8e21f95..28c6e6749094a 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 41d31db64647c..17758597bc348 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"