|
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 | + |
| 36 | + address: int |
| 37 | + size: int |
| 38 | + name: str |
| 39 | + |
| 40 | + |
32 | 41 | class PeriphconfEntry(c.LittleEndianStructure): |
33 | 42 | _pack_ = 1 |
34 | 43 | _fields_ = [ |
@@ -198,6 +207,105 @@ class Uicr(c.LittleEndianStructure): |
198 | 207 | ] |
199 | 208 |
|
200 | 209 |
|
| 210 | +def validate_secure_storage_partitions(args: argparse.Namespace) -> None: |
| 211 | + """ |
| 212 | + Validate that secure storage partitions are laid out correctly. |
| 213 | +
|
| 214 | + Args: |
| 215 | + args: Parsed command line arguments containing partition information |
| 216 | +
|
| 217 | + Raises: |
| 218 | + ScriptError: If validation fails |
| 219 | + """ |
| 220 | + # Expected order: cpuapp_crypto_partition, cpurad_crypto_partition, |
| 221 | + # cpuapp_its_partition, cpurad_its_partition |
| 222 | + partitions = [ |
| 223 | + PartitionInfo( |
| 224 | + args.cpuapp_crypto_address, args.cpuapp_crypto_size, "cpuapp_crypto_partition" |
| 225 | + ), |
| 226 | + PartitionInfo( |
| 227 | + args.cpurad_crypto_address, args.cpurad_crypto_size, "cpurad_crypto_partition" |
| 228 | + ), |
| 229 | + PartitionInfo(args.cpuapp_its_address, args.cpuapp_its_size, "cpuapp_its_partition"), |
| 230 | + PartitionInfo(args.cpurad_its_address, args.cpurad_its_size, "cpurad_its_partition"), |
| 231 | + ] |
| 232 | + |
| 233 | + # Filter out zero-sized partitions (missing partitions) |
| 234 | + present_partitions = [p for p in partitions if p.size > 0] |
| 235 | + |
| 236 | + # Require at least one subpartition to be present |
| 237 | + if not present_partitions: |
| 238 | + raise ScriptError( |
| 239 | + "At least one secure storage subpartition must be defined. " |
| 240 | + "Define one or more of: cpuapp_crypto_partition, cpurad_crypto_partition, " |
| 241 | + "cpuapp_its_partition, cpurad_its_partition" |
| 242 | + ) |
| 243 | + |
| 244 | + # Check 4KB alignment for secure storage start address |
| 245 | + if args.securestorage_address % 4096 != 0: |
| 246 | + raise ScriptError( |
| 247 | + f"Secure storage address {args.securestorage_address:#x} must be aligned to 4KB " |
| 248 | + f"(4096 bytes)" |
| 249 | + ) |
| 250 | + |
| 251 | + # Check 4KB alignment for secure storage size |
| 252 | + if args.securestorage_size % 4096 != 0: |
| 253 | + raise ScriptError( |
| 254 | + f"Secure storage size {args.securestorage_size} bytes must be aligned to 4KB " |
| 255 | + f"(4096 bytes)" |
| 256 | + ) |
| 257 | + |
| 258 | + # Check that the first present partition starts at the secure storage address |
| 259 | + first_partition = present_partitions[0] |
| 260 | + if first_partition.address != args.securestorage_address: |
| 261 | + raise ScriptError( |
| 262 | + f"First partition {first_partition.name} starts at {first_partition.address:#x}, " |
| 263 | + f"but must start at secure storage address {args.securestorage_address:#x}" |
| 264 | + ) |
| 265 | + |
| 266 | + # Check that all present partitions have sizes that are multiples of 1KB |
| 267 | + for partition in present_partitions: |
| 268 | + if partition.size % 1024 != 0: |
| 269 | + raise ScriptError( |
| 270 | + f"Partition {partition.name} has size {partition.size} bytes, but must be " |
| 271 | + f"a multiple of 1024 bytes (1KB)" |
| 272 | + ) |
| 273 | + |
| 274 | + # Check that partitions are in correct order and don't overlap |
| 275 | + for curr_partition, next_partition in pairwise(present_partitions): |
| 276 | + # Check order - partitions should be in ascending address order |
| 277 | + if curr_partition.address >= next_partition.address: |
| 278 | + raise ScriptError( |
| 279 | + f"Partition {curr_partition.name} (starts at {curr_partition.address:#x}) " |
| 280 | + f"must come before {next_partition.name} (starts at {next_partition.address:#x})" |
| 281 | + ) |
| 282 | + |
| 283 | + # Check for overlap |
| 284 | + curr_end = curr_partition.address + curr_partition.size |
| 285 | + if curr_end > next_partition.address: |
| 286 | + raise ScriptError( |
| 287 | + f"Partition {curr_partition.name} (ends at {curr_end:#x}) overlaps with " |
| 288 | + f"{next_partition.name} (starts at {next_partition.address:#x})" |
| 289 | + ) |
| 290 | + |
| 291 | + # Check for gaps (should be no gaps between consecutive partitions) |
| 292 | + if curr_end < next_partition.address: |
| 293 | + gap = next_partition.address - curr_end |
| 294 | + raise ScriptError( |
| 295 | + f"Gap of {gap} bytes between {curr_partition.name} (ends at {curr_end:#x}) and " |
| 296 | + f"{next_partition.name} (starts at {next_partition.address:#x})" |
| 297 | + ) |
| 298 | + |
| 299 | + # Check that combined subpartition sizes equal secure_storage_partition size |
| 300 | + total_subpartition_size = sum(p.size for p in present_partitions) |
| 301 | + if total_subpartition_size != args.securestorage_size: |
| 302 | + raise ScriptError( |
| 303 | + f"Combined size of subpartitions ({total_subpartition_size} bytes) does not match " |
| 304 | + f"secure_storage_partition size ({args.securestorage_size} bytes). " |
| 305 | + f"The definition is not coherent." |
| 306 | + ) |
| 307 | + |
| 308 | + |
201 | 309 | def main() -> None: |
202 | 310 | parser = argparse.ArgumentParser( |
203 | 311 | allow_abbrev=False, |
@@ -255,6 +363,71 @@ def main() -> None: |
255 | 363 | type=lambda s: int(s, 0), |
256 | 364 | help="Absolute flash address of the UICR region (decimal or 0x-prefixed hex)", |
257 | 365 | ) |
| 366 | + parser.add_argument( |
| 367 | + "--securestorage", |
| 368 | + action="store_true", |
| 369 | + help="Enable secure storage support in UICR", |
| 370 | + ) |
| 371 | + parser.add_argument( |
| 372 | + "--securestorage-address", |
| 373 | + default=None, |
| 374 | + type=lambda s: int(s, 0), |
| 375 | + help="Absolute flash address of the secure storage partition (decimal or 0x-prefixed hex)", |
| 376 | + ) |
| 377 | + parser.add_argument( |
| 378 | + "--securestorage-size", |
| 379 | + default=None, |
| 380 | + type=lambda s: int(s, 0), |
| 381 | + help="Size in bytes of the secure storage partition (decimal or 0x-prefixed hex)", |
| 382 | + ) |
| 383 | + parser.add_argument( |
| 384 | + "--cpuapp-crypto-address", |
| 385 | + default=0, |
| 386 | + type=lambda s: int(s, 0), |
| 387 | + help="Absolute flash address of cpuapp_crypto_partition (decimal or 0x-prefixed hex)", |
| 388 | + ) |
| 389 | + parser.add_argument( |
| 390 | + "--cpuapp-crypto-size", |
| 391 | + default=0, |
| 392 | + type=lambda s: int(s, 0), |
| 393 | + help="Size in bytes of cpuapp_crypto_partition (decimal or 0x-prefixed hex)", |
| 394 | + ) |
| 395 | + parser.add_argument( |
| 396 | + "--cpurad-crypto-address", |
| 397 | + default=0, |
| 398 | + type=lambda s: int(s, 0), |
| 399 | + help="Absolute flash address of cpurad_crypto_partition (decimal or 0x-prefixed hex)", |
| 400 | + ) |
| 401 | + parser.add_argument( |
| 402 | + "--cpurad-crypto-size", |
| 403 | + default=0, |
| 404 | + type=lambda s: int(s, 0), |
| 405 | + help="Size in bytes of cpurad_crypto_partition (decimal or 0x-prefixed hex)", |
| 406 | + ) |
| 407 | + parser.add_argument( |
| 408 | + "--cpuapp-its-address", |
| 409 | + default=0, |
| 410 | + type=lambda s: int(s, 0), |
| 411 | + help="Absolute flash address of cpuapp_its_partition (decimal or 0x-prefixed hex)", |
| 412 | + ) |
| 413 | + parser.add_argument( |
| 414 | + "--cpuapp-its-size", |
| 415 | + default=0, |
| 416 | + type=lambda s: int(s, 0), |
| 417 | + help="Size in bytes of cpuapp_its_partition (decimal or 0x-prefixed hex)", |
| 418 | + ) |
| 419 | + parser.add_argument( |
| 420 | + "--cpurad-its-address", |
| 421 | + default=0, |
| 422 | + type=lambda s: int(s, 0), |
| 423 | + help="Absolute flash address of cpurad_its_partition (decimal or 0x-prefixed hex)", |
| 424 | + ) |
| 425 | + parser.add_argument( |
| 426 | + "--cpurad-its-size", |
| 427 | + default=0, |
| 428 | + type=lambda s: int(s, 0), |
| 429 | + help="Size in bytes of cpurad_its_partition (decimal or 0x-prefixed hex)", |
| 430 | + ) |
258 | 431 | parser.add_argument( |
259 | 432 | "--secondary", |
260 | 433 | action="store_true", |
@@ -327,12 +500,35 @@ def main() -> None: |
327 | 500 | "--out-secondary-periphconf-hex is used" |
328 | 501 | ) |
329 | 502 |
|
| 503 | + # Validate secure storage argument dependencies |
| 504 | + if args.securestorage: |
| 505 | + if args.securestorage_address is None: |
| 506 | + raise ScriptError( |
| 507 | + "--securestorage-address is required when --securestorage is used" |
| 508 | + ) |
| 509 | + if args.securestorage_size is None: |
| 510 | + raise ScriptError("--securestorage-size is required when --securestorage is used") |
| 511 | + |
| 512 | + # Validate partition layout |
| 513 | + validate_secure_storage_partitions(args) |
| 514 | + |
330 | 515 | init_values = DISABLED_VALUE.to_bytes(4, "little") * (c.sizeof(Uicr) // 4) |
331 | 516 | uicr = Uicr.from_buffer_copy(init_values) |
332 | 517 |
|
333 | 518 | uicr.VERSION.MAJOR = UICR_FORMAT_VERSION_MAJOR |
334 | 519 | uicr.VERSION.MINOR = UICR_FORMAT_VERSION_MINOR |
335 | 520 |
|
| 521 | + # Handle secure storage configuration |
| 522 | + if args.securestorage: |
| 523 | + uicr.SECURESTORAGE.ENABLE = ENABLED_VALUE |
| 524 | + uicr.SECURESTORAGE.ADDRESS = args.securestorage_address |
| 525 | + |
| 526 | + # Set partition sizes in 1KB units |
| 527 | + uicr.SECURESTORAGE.CRYPTO.APPLICATIONSIZE1KB = args.cpuapp_crypto_size // 1024 |
| 528 | + uicr.SECURESTORAGE.CRYPTO.RADIOCORESIZE1KB = args.cpurad_crypto_size // 1024 |
| 529 | + uicr.SECURESTORAGE.ITS.APPLICATIONSIZE1KB = args.cpuapp_its_size // 1024 |
| 530 | + uicr.SECURESTORAGE.ITS.RADIOCORESIZE1KB = args.cpurad_its_size // 1024 |
| 531 | + |
336 | 532 | # Process periphconf data first and configure UICR completely before creating hex objects |
337 | 533 | periphconf_hex = IntelHex() |
338 | 534 | secondary_periphconf_hex = IntelHex() |
|
0 commit comments