Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-memory_map.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -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)>;
};
};
};
};
1 change: 1 addition & 0 deletions scripts/ci/check_compliance.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
198 changes: 197 additions & 1 deletion soc/nordic/common/uicr/gen_uicr.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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_ = [
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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()
Expand Down
46 changes: 46 additions & 0 deletions soc/nordic/common/uicr/gen_uicr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand Down Expand Up @@ -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}
Expand Down
22 changes: 22 additions & 0 deletions soc/nordic/common/uicr/gen_uicr/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Loading