|
8 | 8 | import argparse
|
9 | 9 | import ctypes as c
|
10 | 10 | import sys
|
11 |
| -from itertools import groupby |
| 11 | +from itertools import groupby, pairwise |
| 12 | +from typing import NamedTuple |
12 | 13 |
|
13 | 14 | from elftools.elf.elffile import ELFFile
|
14 | 15 | from intelhex import IntelHex
|
|
29 | 30 | class ScriptError(RuntimeError): ...
|
30 | 31 |
|
31 | 32 |
|
| 33 | +class PartitionInfo(NamedTuple): |
| 34 | + """Information about a partition for secure storage validation.""" |
| 35 | + address: int |
| 36 | + size: int |
| 37 | + name: str |
| 38 | + |
| 39 | + |
32 | 40 | class PeriphconfEntry(c.LittleEndianStructure):
|
33 | 41 | _pack_ = 1
|
34 | 42 | _fields_ = [
|
@@ -198,6 +206,74 @@ class Uicr(c.LittleEndianStructure):
|
198 | 206 | ]
|
199 | 207 |
|
200 | 208 |
|
| 209 | +def validate_secure_storage_partitions(args: argparse.Namespace) -> None: |
| 210 | + """ |
| 211 | + Validate that secure storage partitions are laid out correctly. |
| 212 | +
|
| 213 | + Args: |
| 214 | + args: Parsed command line arguments containing partition information |
| 215 | +
|
| 216 | + Raises: |
| 217 | + ScriptError: If validation fails |
| 218 | + """ |
| 219 | + # Expected order: cpuapp_crypto_partition, cpurad_crypto_partition, |
| 220 | + # cpuapp_its_partition, cpurad_its_partition |
| 221 | + partitions = [ |
| 222 | + PartitionInfo(args.cpuapp_crypto_address, args.cpuapp_crypto_size, "cpuapp_crypto_partition"), |
| 223 | + PartitionInfo(args.cpurad_crypto_address, args.cpurad_crypto_size, "cpurad_crypto_partition"), |
| 224 | + PartitionInfo(args.cpuapp_its_address, args.cpuapp_its_size, "cpuapp_its_partition"), |
| 225 | + PartitionInfo(args.cpurad_its_address, args.cpurad_its_size, "cpurad_its_partition"), |
| 226 | + ] |
| 227 | + |
| 228 | + # Filter out zero-sized partitions (missing partitions) |
| 229 | + present_partitions = [p for p in partitions if p.size > 0] |
| 230 | + |
| 231 | + if not present_partitions: |
| 232 | + # No partitions present - this is valid |
| 233 | + return |
| 234 | + |
| 235 | + # Check that the first present partition starts at the secure storage address |
| 236 | + first_partition = present_partitions[0] |
| 237 | + if first_partition.address != args.securestorage_address: |
| 238 | + raise ScriptError( |
| 239 | + f"First partition {first_partition.name} starts at {first_partition.address}, " |
| 240 | + f"but must start at secure storage address {args.securestorage_address}" |
| 241 | + ) |
| 242 | + |
| 243 | + # Check that all present partitions have sizes that are multiples of 1KB |
| 244 | + for partition in present_partitions: |
| 245 | + if partition.size % 1024 != 0: |
| 246 | + raise ScriptError( |
| 247 | + f"Partition {partition.name} has size {partition.size} bytes, but must be " |
| 248 | + f"a multiple of 1024 bytes (1KB)" |
| 249 | + ) |
| 250 | + |
| 251 | + # Check that partitions are in correct order and don't overlap |
| 252 | + for curr_partition, next_partition in pairwise(present_partitions): |
| 253 | + # Check order - partitions should be in ascending address order |
| 254 | + if curr_partition.address >= next_partition.address: |
| 255 | + raise ScriptError( |
| 256 | + f"Partition {curr_partition.name} (starts at {curr_partition.address}) " |
| 257 | + f"must come before {next_partition.name} (starts at {next_partition.address})" |
| 258 | + ) |
| 259 | + |
| 260 | + # Check for overlap |
| 261 | + curr_end = curr_partition.address + curr_partition.size |
| 262 | + if curr_end > next_partition.address: |
| 263 | + raise ScriptError( |
| 264 | + f"Partition {curr_partition.name} (ends at {curr_end}) overlaps with " |
| 265 | + f"{next_partition.name} (starts at {next_partition.address})" |
| 266 | + ) |
| 267 | + |
| 268 | + # Check for gaps (should be no gaps between consecutive partitions) |
| 269 | + if curr_end < next_partition.address: |
| 270 | + gap = next_partition.address - curr_end |
| 271 | + raise ScriptError( |
| 272 | + f"Gap of {gap} bytes between {curr_partition.name} (ends at {curr_end}) and " |
| 273 | + f"{next_partition.name} (starts at {next_partition.address})" |
| 274 | + ) |
| 275 | + |
| 276 | + |
201 | 277 | def main() -> None:
|
202 | 278 | parser = argparse.ArgumentParser(
|
203 | 279 | allow_abbrev=False,
|
@@ -255,6 +331,65 @@ def main() -> None:
|
255 | 331 | type=lambda s: int(s, 0),
|
256 | 332 | help="Absolute flash address of the UICR region (decimal or 0x-prefixed hex)",
|
257 | 333 | )
|
| 334 | + parser.add_argument( |
| 335 | + "--securestorage", |
| 336 | + action="store_true", |
| 337 | + help="Enable secure storage support in UICR", |
| 338 | + ) |
| 339 | + parser.add_argument( |
| 340 | + "--securestorage-address", |
| 341 | + default=None, |
| 342 | + type=lambda s: int(s, 0), |
| 343 | + help="Absolute flash address of the secure storage partition (decimal or 0x-prefixed hex)", |
| 344 | + ) |
| 345 | + parser.add_argument( |
| 346 | + "--cpuapp-crypto-address", |
| 347 | + default=0, |
| 348 | + type=lambda s: int(s, 0), |
| 349 | + help="Absolute flash address of cpuapp_crypto_partition (decimal or 0x-prefixed hex)", |
| 350 | + ) |
| 351 | + parser.add_argument( |
| 352 | + "--cpuapp-crypto-size", |
| 353 | + default=0, |
| 354 | + type=lambda s: int(s, 0), |
| 355 | + help="Size in bytes of cpuapp_crypto_partition (decimal or 0x-prefixed hex)", |
| 356 | + ) |
| 357 | + parser.add_argument( |
| 358 | + "--cpurad-crypto-address", |
| 359 | + default=0, |
| 360 | + type=lambda s: int(s, 0), |
| 361 | + help="Absolute flash address of cpurad_crypto_partition (decimal or 0x-prefixed hex)", |
| 362 | + ) |
| 363 | + parser.add_argument( |
| 364 | + "--cpurad-crypto-size", |
| 365 | + default=0, |
| 366 | + type=lambda s: int(s, 0), |
| 367 | + help="Size in bytes of cpurad_crypto_partition (decimal or 0x-prefixed hex)", |
| 368 | + ) |
| 369 | + parser.add_argument( |
| 370 | + "--cpuapp-its-address", |
| 371 | + default=0, |
| 372 | + type=lambda s: int(s, 0), |
| 373 | + help="Absolute flash address of cpuapp_its_partition (decimal or 0x-prefixed hex)", |
| 374 | + ) |
| 375 | + parser.add_argument( |
| 376 | + "--cpuapp-its-size", |
| 377 | + default=0, |
| 378 | + type=lambda s: int(s, 0), |
| 379 | + help="Size in bytes of cpuapp_its_partition (decimal or 0x-prefixed hex)", |
| 380 | + ) |
| 381 | + parser.add_argument( |
| 382 | + "--cpurad-its-address", |
| 383 | + default=0, |
| 384 | + type=lambda s: int(s, 0), |
| 385 | + help="Absolute flash address of cpurad_its_partition (decimal or 0x-prefixed hex)", |
| 386 | + ) |
| 387 | + parser.add_argument( |
| 388 | + "--cpurad-its-size", |
| 389 | + default=0, |
| 390 | + type=lambda s: int(s, 0), |
| 391 | + help="Size in bytes of cpurad_its_partition (decimal or 0x-prefixed hex)", |
| 392 | + ) |
258 | 393 | parser.add_argument(
|
259 | 394 | "--secondary",
|
260 | 395 | action="store_true",
|
@@ -327,12 +462,33 @@ def main() -> None:
|
327 | 462 | "--out-secondary-periphconf-hex is used"
|
328 | 463 | )
|
329 | 464 |
|
| 465 | + # Validate secure storage argument dependencies |
| 466 | + if args.securestorage: |
| 467 | + if args.securestorage_address is None: |
| 468 | + raise ScriptError( |
| 469 | + "--securestorage-address is required when --securestorage is used" |
| 470 | + ) |
| 471 | + |
| 472 | + # Validate partition layout |
| 473 | + validate_secure_storage_partitions(args) |
| 474 | + |
330 | 475 | init_values = DISABLED_VALUE.to_bytes(4, "little") * (c.sizeof(Uicr) // 4)
|
331 | 476 | uicr = Uicr.from_buffer_copy(init_values)
|
332 | 477 |
|
333 | 478 | uicr.VERSION.MAJOR = UICR_FORMAT_VERSION_MAJOR
|
334 | 479 | uicr.VERSION.MINOR = UICR_FORMAT_VERSION_MINOR
|
335 | 480 |
|
| 481 | + # Handle secure storage configuration |
| 482 | + if args.securestorage: |
| 483 | + uicr.SECURESTORAGE.ENABLE = ENABLED_VALUE |
| 484 | + uicr.SECURESTORAGE.ADDRESS = args.securestorage_address |
| 485 | + |
| 486 | + # Set partition sizes in 1KB units |
| 487 | + uicr.SECURESTORAGE.CRYPTO.APPLICATIONSIZE1KB = args.cpuapp_crypto_size // 1024 |
| 488 | + uicr.SECURESTORAGE.CRYPTO.RADIOCORESIZE1KB = args.cpurad_crypto_size // 1024 |
| 489 | + uicr.SECURESTORAGE.ITS.APPLICATIONSIZE1KB = args.cpuapp_its_size // 1024 |
| 490 | + uicr.SECURESTORAGE.ITS.RADIOCORESIZE1KB = args.cpurad_its_size // 1024 |
| 491 | + |
336 | 492 | # Process periphconf data first and configure UICR completely before creating hex objects
|
337 | 493 | periphconf_hex = IntelHex()
|
338 | 494 | secondary_periphconf_hex = IntelHex()
|
|
0 commit comments