1#!/bin/sh 2""":" # Shell script (in docstring to appease pylint) 3# Find and invoke hermetic python3 interpreter 4. "`dirname $0`/../../../vendor/google/aosp/scripts/envsetup.sh" 5exec "$PY3" "$0" "$@" 6# Shell script end 7 8This program will take trusted application's manifest config JSON file as 9input. Processes the JSON config file and creates packed data 10mapping to C structures and dumps in binary format. 11 12USAGE: 13 manifest_compiler.py \ 14 --input <input_filename_1> \ 15 --input <input_filename_2> \ 16 --output <output_filename> \ 17 --constants <config_constants_file_1> \ 18 --constants <config_constants_file_2> \ 19 --header-dir <header_file_path> 20 21 Arguments: 22 input_filename - Trusted app manifest config file in JSON format. 23 At least one manifest config should be provided. 24 Options from additional configs override scalars 25 and merge to lists of the main manifest config, 26 processing manifests in the order specified on the 27 command line. 28 output_filename - Binary file containing packed manifest config data mapped 29 to C structres. 30 config_constant_file - This is optional 31 Config file with constants in JSON format 32 Corresponding header file will be 33 created with its constants defined in it 34 header_file_path - Directory in which header files to be generated. 35 36 example: 37 manifest_compiler.py \ 38 --input manifest.json \ 39 --input manifest_extra_mem_maps.json \ 40 --output output.bin \ 41 --constants manifest_constants.json \ 42 --header-dir \ 43 <build_dir>/user_tasks/trusty/user/app/sample/hwcrypto/include 44 45 If the output filename is omitted, the compiler will only generate constants 46 headers for the given constants files. 47 48 49 Input sample JSON Manifest config file content - 50 { 51 "uuid": "SECURE_STORAGE_SERVER_APP_UUID", 52 "min_heap": 4096, 53 "min_stack": 4096, 54 "mem_map": [ 55 { 56 "id": 1, 57 "addr": "0x70000000", 58 "size": "0x1000" 59 }, 60 { 61 "id": 2, 62 "addr": "0x70010000", 63 "size": "0x100" 64 }, 65 { 66 "id": 3, 67 "addr": "0x70020000", 68 "size": "0x4", 69 "type": "uncached_device", 70 "non_secure": false 71 } 72 ], 73 "mgmt_flags": { 74 "restart_on_exit": true, 75 "deferred_start": false, 76 "non_critical_app": false 77 }, 78 "start_ports": [ 79 { 80 "name": "LOADABLE_START_PORT", 81 "flags": { 82 "allow_ta_connect": true, 83 "allow_ns_connect": false 84 } 85 } 86 ], 87 "pinned_cpu": 3, 88 "priority" : 10, 89 "version": 1, 90 "min_version": 1, 91 "apploader_flags": { 92 "requires_encryption": false 93 } 94 } 95 96 JSON manifest constant config - 97 { 98 "header": "storage_constants.h", 99 "constants": [ 100 { 101 "name": "LOADABLE_START_PORT", 102 "value": "com.android.trusty.appmgmt.loadable.start", 103 "type": "port" 104 }, 105 { 106 "name": "SECURE_STORAGE_SERVER_APP_UUID", 107 "value": "eca48f94-00aa-560e-8f8c-d94b50d484f3", 108 "type": "uuid" 109 } 110 ] 111 } 112""" 113 114import argparse 115import io 116import json 117import os.path 118import struct 119import sys 120 121assert (sys.version_info.major, sys.version_info.minor) >= (3, 6), ( 122 # pylint: disable-next=consider-using-f-string 123 "Python 3.6 or newer is required; found {}. Did you forget to set PY3?" 124 .format(sys.version)) 125 126# Manifest properties 127UUID = "uuid" 128MIN_HEAP = "min_heap" 129MIN_STACK = "min_stack" 130MIN_SHADOW_STACK = "min_shadow_stack" 131MEM_MAP = "mem_map" 132MEM_MAP_ID = "id" 133MEM_MAP_ADDR = "addr" 134MEM_MAP_SIZE = "size" 135MEM_MAP_TYPE = "type" 136MEM_MAP_TYPE_CACHED = "cached" 137MEM_MAP_TYPE_UNCACHED = "uncached" 138MEM_MAP_TYPE_UNCACHED_DEVICE = "uncached_device" 139MEM_MAP_NON_SECURE = "non_secure" 140MGMT_FLAGS = "mgmt_flags" 141MGMT_FLAG_RESTART_ON_EXIT = "restart_on_exit" 142MGMT_FLAG_DEFERRED_START = "deferred_start" 143MGMT_FLAG_NON_CRITICAL_APP = "non_critical_app" 144START_PORTS = "start_ports" 145START_PORT_FLAGS = "flags" 146START_PORT_NAME = "name" 147START_PORT_ALLOW_TA_CONNECT = "allow_ta_connect" 148START_PORT_ALLOW_NS_CONNECT = "allow_ns_connect" 149APP_NAME = "app_name" 150PINNED_CPU = "pinned_cpu" 151PRIORITY = "priority" 152VERSION = "version" 153MIN_VERSION = "min_version" 154APPLOADER_FLAGS = "apploader_flags" 155APPLOADER_FLAGS_REQUIRES_ENCRYPTION = "requires_encryption" 156 157# constants configs 158CONSTANTS = "constants" 159HEADER = "header" 160CONST_NAME = "name" 161CONST_VALUE = "value" 162CONST_TYPE = "type" 163CONST_UNSIGNED = "unsigned" 164CONST_PORT = "port" 165CONST_UUID = "uuid" 166CONST_INT = "int" 167CONST_BOOL = "bool" 168CONST_ID = "identifier" 169 170# CONFIG TAGS 171# These values need to be kept in sync with lib/app_manifest/app_manifest.h 172TRUSTY_APP_CONFIG_KEY_MIN_STACK_SIZE = 1 173TRUSTY_APP_CONFIG_KEY_MIN_HEAP_SIZE = 2 174TRUSTY_APP_CONFIG_KEY_MAP_MEM = 3 175TRUSTY_APP_CONFIG_KEY_MGMT_FLAGS = 4 176TRUSTY_APP_CONFIG_KEY_START_PORT = 5 177TRUSTY_APP_CONFIG_KEY_PINNED_CPU = 6 178TRUSTY_APP_CONFIG_KEY_VERSION = 7 179TRUSTY_APP_CONFIG_KEY_MIN_SHADOW_STACK_SIZE = 8 180TRUSTY_APP_CONFIG_KEY_APPLOADER_FLAGS = 9 181TRUSTY_APP_CONFIG_KEY_PRIORITY = 10 182TRUSTY_APP_CONFIG_KEY_MIN_VERSION = 11 183 184# MEM_MAP ARCH_MMU_FLAGS 185# These values need to be kept in sync with $LKROOT/include/arch/mmu.h 186ARCH_MMU_FLAG_CACHED = 0 << 0 187ARCH_MMU_FLAG_UNCACHED = 1 << 0 188ARCH_MMU_FLAG_UNCACHED_DEVICE = 2 << 0 189ARCH_MMU_FLAG_CACHE_MASK = 3 << 0 190ARCH_MMU_FLAG_NS = 1 << 5 191 192# MGMT FLAGS 193# These values need to be kept in sync with lib/app_manifest/app_manifest.h 194TRUSTY_APP_MGMT_FLAGS_NONE = 0 195TRUSTY_APP_MGMT_FLAGS_RESTART_ON_EXIT = 1 << 0 196TRUSTY_APP_MGMT_FLAGS_DEFERRED_START = 1 << 1 197TRUSTY_APP_MGMT_FLAGS_NON_CRITICAL_APP = 1 << 2 198 199# APPLOADER FLAGS 200# These values need to be kept in sync with lib/app_manifest/app_manifest.h 201TRUSTY_APP_APPLOADER_FLAGS_NONE = 0 202TRUSTY_APP_APPLOADER_FLAGS_REQUIRES_ENCRYPTION = 1 << 0 203 204# START_PORT flags 205# These values need to be kept in sync with user/base/include/user/trusty_ipc.h 206IPC_PORT_ALLOW_TA_CONNECT = 0x1 207IPC_PORT_ALLOW_NS_CONNECT = 0x2 208 209IPC_PORT_PATH_MAX = 64 210 211 212class Constant(object): 213 def __init__(self, name, value, type_, unsigned=False, hex_num=False): 214 self.name = name 215 self.value = value 216 self.type = type_ 217 self.unsigned = unsigned 218 self.hex_num = hex_num 219 220 221class ConfigConstants(object): 222 def __init__(self, constants, header): 223 self.constants = constants 224 self.header = header 225 226 227class StartPortFlags(object): 228 def __init__(self, allow_ta_connect, allow_ns_connect): 229 self.allow_ta_connect = allow_ta_connect 230 self.allow_ns_connect = allow_ns_connect 231 232 233class StartPort(object): 234 def __init__(self, name, name_size, start_port_flags): 235 self.name = name 236 self.name_size = name_size 237 self.start_port_flags = start_port_flags 238 239 240class MemIOMap(object): 241 def __init__(self, id_, addr, size, type_, non_secure): 242 self.id = id_ 243 self.addr = addr 244 self.size = size 245 self.type = type_ 246 self.non_secure = non_secure 247 248 249class MgmtFlags(object): 250 def __init__(self, restart_on_exit, deferred_start, non_critical_app): 251 self.restart_on_exit = restart_on_exit 252 self.deferred_start = deferred_start 253 self.non_critical_app = non_critical_app 254 255 256class ApploaderFlags(object): 257 def __init__(self, requires_encryption): 258 self.requires_encryption = requires_encryption 259 260 261class Manifest(object): 262 """Holds Manifest data to be used for packing""" 263 264 def __init__( 265 self, 266 uuid, 267 app_name, 268 min_heap, 269 min_stack, 270 min_shadow_stack, 271 mem_io_maps, 272 mgmt_flags, 273 start_ports, 274 pinned_cpu, 275 priority, 276 version, 277 min_version, 278 apploader_flags, 279 ): 280 self.uuid = uuid 281 self.app_name = app_name 282 self.min_heap = min_heap 283 self.min_stack = min_stack 284 self.min_shadow_stack = min_shadow_stack 285 self.mem_io_maps = mem_io_maps 286 self.mgmt_flags = mgmt_flags 287 self.start_ports = start_ports 288 self.pinned_cpu = pinned_cpu 289 self.priority = priority 290 self.version = version 291 self.min_version = min_version 292 self.apploader_flags = apploader_flags 293 294 295class Log(object): 296 """Tracks errors during manifest compilation""" 297 298 def __init__(self): 299 self.error_count = 0 300 301 def error(self, msg): 302 sys.stderr.write(f"Error: {msg}\n") 303 self.error_count += 1 304 305 def error_occurred(self): 306 return self.error_count > 0 307 308 309def get_string_sub_type(field): 310 """For the given manifest JSON field it returns its literal value type 311 mapped. 312 """ 313 if field == UUID: 314 return CONST_UUID 315 if field == START_PORT_NAME: 316 return CONST_PORT 317 # field with string value but doesn't support a constant 318 return None 319 320 321def get_constant(constants, key, type_, log): 322 const = constants.get(key) 323 if const is None: 324 return None 325 326 if const.type != type_: 327 log.error(f"{key} constant type mismatch, expected type is {type_}") 328 return None 329 330 return const.value 331 332 333def get_string(manifest_dict, key, constants, log, optional=False, 334 default=None): 335 """Determines whether the value for the given key in dictionary is of type 336 string and if it is a string then returns the value. 337 """ 338 if key not in manifest_dict: 339 if not optional: 340 log.error(f"Manifest is missing required attribute - {key}") 341 return default 342 343 value = manifest_dict.pop(key) 344 345 # try to check is this field holding a constant 346 type_ = get_string_sub_type(key) 347 if type_: 348 const_value = get_constant(constants, value, type_, log) 349 if const_value is not None: 350 return const_value 351 352 return coerce_to_string(value, key, log) 353 354 355def coerce_to_string(value, key, log): 356 if not isinstance(value, str): 357 log.error( 358 "Invalid value for" + 359 f" {key} - \"{value}\", Valid string value is expected") 360 return None 361 362 return value 363 364 365def get_int(manifest_dict, key, constants, log, optional=False, 366 default=None): 367 """Determines whether the value for the given key in dictionary is of type 368 integer and if it is int then returns the value 369 """ 370 if key not in manifest_dict: 371 if not optional: 372 log.error(f"Manifest is missing required attribute - {key}") 373 return default 374 375 value = manifest_dict.pop(key) 376 const_value = get_constant(constants, value, CONST_INT, log) 377 if const_value is not None: 378 return const_value 379 380 return coerce_to_int(value, key, log) 381 382 383def coerce_to_int(value, key, log): 384 if isinstance(value, int) and not isinstance(value, bool): 385 return value 386 if isinstance(value, str): 387 try: 388 return int(value, 0) 389 except ValueError: 390 log.error(f"Invalid value for {key} - \"{value}\", " + 391 "valid integer or hex string is expected") 392 return None 393 else: 394 log.error("Invalid value for" + 395 f" {key} - \"{value}\", valid integer value is expected") 396 return None 397 398 399def get_list(manifest_dict, key, log, optional=False, default=None): 400 """Determines whether the value for the given key in dictionary is of type 401 List and if it is List then returns the value 402 """ 403 if key not in manifest_dict: 404 if not optional: 405 log.error(f"Manifest is missing required attribute - {key}") 406 return default 407 408 return coerce_to_list(manifest_dict.pop(key), key, log) 409 410 411def coerce_to_list(value, key, log): 412 if not isinstance(value, list): 413 log.error("Invalid value for" + 414 f" {key} - \"{value}\", valid list is expected") 415 return None 416 417 return value 418 419 420def get_dict(manifest_dict, key, log, optional=False, default=None): 421 """Determines whether the value for the given key in dictionary is of type 422 Dictionary and if it is Dictionary then returns the value 423 """ 424 if key not in manifest_dict: 425 if not optional: 426 log.error(f"Manifest is missing required attribute - {key}") 427 return default 428 429 return coerce_to_dict(manifest_dict.pop(key), key, log) 430 431 432def coerce_to_dict(value, key, log): 433 if not isinstance(value, dict): 434 log.error("Invalid value for" + 435 f" {key} - \"{value}\", valid dict is expected") 436 return None 437 438 return value 439 440 441def get_boolean(manifest_dict, key, constants, log, optional=False, 442 default=None): 443 """Determines whether the value for the given key in dictionary is of type 444 boolean and if it is boolean then returns the value 445 """ 446 if key not in manifest_dict: 447 if not optional: 448 log.error(f"Manifest is missing required attribute - {key}") 449 return default 450 451 value = manifest_dict.pop(key) 452 const_value = get_constant(constants, value, CONST_BOOL, log) 453 if const_value is not None: 454 return const_value 455 456 return coerce_to_boolean(value, key, log) 457 458 459def coerce_to_boolean(value, key, log): 460 if not isinstance(value, bool): 461 log.error( 462 "Invalid value for" + 463 f" {key} - \"{value}\", Valid boolean value is expected") 464 return None 465 466 return value 467 468 469def get_uuid(manifest_dict, key, constants, log, optional=False, default=None): 470 if key not in manifest_dict: 471 if not optional: 472 log.error(f"Manifest is missing required attribute - {key}") 473 return default 474 475 uuid = get_string(manifest_dict, key, {}, log, optional, default) 476 const_value = get_constant(constants, uuid, CONST_UUID, log) 477 if const_value is not None: 478 return const_value 479 480 return parse_uuid(uuid, log) 481 482 483def get_port(port, key, constants, log, optional=False, default=None): 484 return get_string(port, key, constants, log, optional, default) 485 486 487def parse_uuid(uuid, log): 488 """Validate and arrange UUID byte order. If it is valid UUID then return 16 489 byte UUID 490 """ 491 if uuid is None: 492 return None 493 494 # Example UUID: "5f902ace-5e5c-4cd8-ae54-87b88c22ddaf" 495 if len(uuid) != 36: 496 log.error(f"Invalid UUID {uuid}. UUID should be 16 bytes long") 497 return None 498 499 uuid_data = uuid.split("-") 500 if len(uuid_data) != 5: 501 log.error( 502 f"Invalid UUID {uuid}. UUID should be 16 hexadecimal numbers" 503 " divided into 5 groups by hyphens (-)" 504 ) 505 return None 506 507 try: 508 uuid_data = [bytearray.fromhex(part) for part in uuid_data] 509 except ValueError: 510 log.error( 511 f"Invalid UUID {uuid}. UUID should only contain hexadecimal" 512 " numbers (separated by hyphens)" 513 ) 514 return None 515 516 if len(uuid_data[0]) != 4 or \ 517 len(uuid_data[1]) != 2 or \ 518 len(uuid_data[2]) != 2 or \ 519 len(uuid_data[3]) != 2 or \ 520 len(uuid_data[4]) != 6: 521 log.error(f"Wrong grouping of UUID {uuid}") 522 return None 523 524 return b"".join(uuid_data) 525 526 527def parse_memory_size(memory_size, memory_kind, log, zero_is_ok=True): 528 """Validate memory size value. If valid, return memory size value else 529 return None 530 """ 531 if memory_size is None: 532 return None 533 534 if memory_size == 0 and not zero_is_ok: 535 log.error(f"{memory_kind}: Minimum memory size cannot be zero.") 536 return None 537 if memory_size < 0 or memory_size % 4096 != 0: 538 log.error(f"{memory_kind}: {memory_size}, Minimum memory size should " + 539 "be a non-negative multiple of 4096") 540 return None 541 542 return memory_size 543 544 545def parse_shadow_stack_size(stack_size, log): 546 """Validate the shadow stack size 547 548 :returns: validated shadow stack size or None 549 """ 550 if stack_size is None: 551 return None 552 553 # shadow call stack is only supported on arm64 where pointers are 8 bytes 554 ptr_size = 8 555 if stack_size < 0 or stack_size % ptr_size != 0: 556 log.error(f"{MIN_SHADOW_STACK}: {stack_size}, Minimum shadow stack " + 557 "size should be a non-negative multiple of the native " + 558 "pointer size") 559 return None 560 561 return stack_size 562 563 564def parse_mem_map_type(mem_map_type, log): 565 if mem_map_type not in {MEM_MAP_TYPE_CACHED, 566 MEM_MAP_TYPE_UNCACHED, 567 MEM_MAP_TYPE_UNCACHED_DEVICE}: 568 log.error(f"Unknown mem_map.type entry in manifest: {mem_map_type}") 569 570 return mem_map_type 571 572 573def parse_mem_map(mem_maps, key, constants, log): 574 if mem_maps is None: 575 return None 576 577 mem_io_maps = [] 578 for mem_map_entry in mem_maps: 579 mem_map_entry = coerce_to_dict(mem_map_entry, key, log) 580 if mem_map_entry is None: 581 continue 582 mem_map = MemIOMap( 583 get_int(mem_map_entry, MEM_MAP_ID, constants, log), 584 get_int(mem_map_entry, MEM_MAP_ADDR, constants, log), 585 get_int(mem_map_entry, MEM_MAP_SIZE, constants, log), 586 parse_mem_map_type( 587 get_string(mem_map_entry, MEM_MAP_TYPE, constants, log, 588 optional=True, 589 default=MEM_MAP_TYPE_UNCACHED_DEVICE), log), 590 get_boolean(mem_map_entry, MEM_MAP_NON_SECURE, constants, log, 591 optional=True) 592 ) 593 if mem_map_entry: 594 log.error("Unknown attributes in mem_map entries in " 595 f"manifest: {mem_map_entry}") 596 if any(item.id == mem_map.id for item in mem_io_maps): 597 log.error(f"Duplicate mem_map ID found: {mem_map.id}") 598 mem_io_maps.append(mem_map) 599 600 return mem_io_maps 601 602 603def parse_mgmt_flags(flags, constants, log): 604 if flags is None: 605 return None 606 607 mgmt_flags = MgmtFlags( 608 get_boolean(flags, MGMT_FLAG_RESTART_ON_EXIT, constants, log, 609 optional=True), 610 get_boolean(flags, MGMT_FLAG_DEFERRED_START, constants, log, 611 optional=True), 612 get_boolean(flags, MGMT_FLAG_NON_CRITICAL_APP, constants, log, 613 optional=True) 614 ) 615 616 if flags: 617 log.error("Unknown attributes in mgmt_flags entries in " + 618 f"manifest: {flags}") 619 620 return mgmt_flags 621 622 623def parse_apploader_flags(flags, constants, log): 624 if flags is None: 625 return None 626 627 apploader_flags = ApploaderFlags( 628 get_boolean(flags, APPLOADER_FLAGS_REQUIRES_ENCRYPTION, constants, log, 629 optional=True) 630 ) 631 632 if flags: 633 log.error("Unknown attributes in apploader_flags entries in " + 634 f"manifest: {flags}") 635 636 return apploader_flags 637 638 639def parse_app_start_ports(start_port_list, key, constants, log): 640 start_ports = [] 641 642 for port_entry in start_port_list: 643 port_entry = coerce_to_dict(port_entry, key, log) 644 if port_entry is None: 645 continue 646 647 name = get_port(port_entry, START_PORT_NAME, constants, log) 648 if len(name) >= IPC_PORT_PATH_MAX: 649 log.error("Length of start port name should be less than " + 650 str(IPC_PORT_PATH_MAX)) 651 652 flags = get_dict(port_entry, START_PORT_FLAGS, log) 653 start_ports_flag = None 654 if flags: 655 start_ports_flag = StartPortFlags( 656 get_boolean(flags, START_PORT_ALLOW_TA_CONNECT, constants, 657 log), 658 get_boolean(flags, START_PORT_ALLOW_NS_CONNECT, constants, 659 log)) 660 661 if port_entry: 662 log.error("Unknown attributes in start_ports entries" + 663 f" in manifest: {port_entry}") 664 if flags: 665 log.error("Unknown attributes in start_ports.flags entries" + 666 f" in manifest: {flags}") 667 668 start_ports.append(StartPort(name, len(name), start_ports_flag)) 669 670 return start_ports 671 672 673def parse_app_name(app_name, log): 674 if app_name is None: 675 return None 676 677 if not app_name: 678 log.error("empty app-name is not allowed in manifest") 679 return None 680 681 return app_name.strip() 682 683 684def parse_manifest_config(manifest_dict, constants, default_app_name, log): 685 """validate the manifest config and extract key, values""" 686 # UUID 687 uuid = get_uuid(manifest_dict, UUID, constants, log) 688 689 # MIN_HEAP 690 min_heap = parse_memory_size(get_int(manifest_dict, MIN_HEAP, constants, 691 log), MIN_HEAP, log) 692 693 # MIN_STACK 694 min_stack = parse_memory_size(get_int(manifest_dict, MIN_STACK, constants, 695 log), MIN_STACK, log, False) 696 697 # MIN_SHADOW_STACK 698 min_shadow_stack = parse_shadow_stack_size(get_int(manifest_dict, 699 MIN_SHADOW_STACK, 700 constants, log, 701 optional=True), log) 702 703 # MEM_MAP 704 mem_io_maps = parse_mem_map( 705 get_list(manifest_dict, MEM_MAP, log, optional=True, default=[]), 706 MEM_MAP, 707 constants, log) 708 709 # MGMT_FLAGS 710 mgmt_flags = parse_mgmt_flags( 711 get_dict(manifest_dict, MGMT_FLAGS, log, optional=True, 712 default={ 713 MGMT_FLAG_RESTART_ON_EXIT: False, 714 MGMT_FLAG_DEFERRED_START: False, 715 MGMT_FLAG_NON_CRITICAL_APP: False}), 716 constants, log) 717 718 # START_PORTS 719 start_ports = parse_app_start_ports( 720 get_list(manifest_dict, START_PORTS, log, 721 optional=True, default=[]), 722 START_PORTS, 723 constants, 724 log) 725 726 # APP_NAME 727 app_name = parse_app_name( 728 get_string(manifest_dict, APP_NAME, constants, log, 729 optional=True, default=default_app_name), log) 730 731 # PINNED_CPU 732 pinned_cpu = get_int(manifest_dict, PINNED_CPU, constants, log, 733 optional=True) 734 735 # PRIORITY 736 priority = get_int(manifest_dict, PRIORITY, constants, log, optional=True) 737 738 # VERSION 739 version = get_int(manifest_dict, VERSION, constants, log, optional=True) 740 741 # MIN_VERSION 742 min_version = get_int( 743 manifest_dict, MIN_VERSION, constants, log, optional=True) 744 745 if min_version is not None: 746 if version is None: 747 log.error("'min_version' cannot be specified without 'version'") 748 elif version < min_version: 749 log.error("'version' cannot be less than 'min_version'") 750 751 # APPLOADER_FLAGS 752 apploader_flags = parse_apploader_flags( 753 get_dict(manifest_dict, APPLOADER_FLAGS, log, optional=True, 754 default={APPLOADER_FLAGS_REQUIRES_ENCRYPTION: False}), 755 constants, log) 756 757 # look for any extra attributes 758 if manifest_dict: 759 log.error(f"Unknown attributes in manifest: {manifest_dict} ") 760 761 if log.error_occurred(): 762 return None 763 764 return Manifest(uuid, app_name, min_heap, min_stack, min_shadow_stack, 765 mem_io_maps, mgmt_flags, start_ports, pinned_cpu, 766 priority, version, min_version, apploader_flags) 767 768 769def swap_uuid_bytes(uuid): 770 """This script represents UUIDs in a purely big endian order. 771 Trusty stores the first three components of the UUID in little endian order. 772 Rearrange the byte order accordingly by doing inverse 773 on first three components of UUID 774 """ 775 return uuid[3::-1] + uuid[5:3:-1] + uuid[7:5:-1] + uuid[8:] 776 777 778def pack_mem_map_arch_mmu_flags(mem_map): 779 arch_mmu_flags = 0 780 781 if mem_map.type == MEM_MAP_TYPE_CACHED: 782 arch_mmu_flags |= ARCH_MMU_FLAG_CACHED 783 elif mem_map.type == MEM_MAP_TYPE_UNCACHED: 784 arch_mmu_flags |= ARCH_MMU_FLAG_UNCACHED 785 elif mem_map.type == MEM_MAP_TYPE_UNCACHED_DEVICE: 786 arch_mmu_flags |= ARCH_MMU_FLAG_UNCACHED_DEVICE 787 788 if mem_map.non_secure: 789 arch_mmu_flags |= ARCH_MMU_FLAG_NS 790 791 return arch_mmu_flags 792 793 794def pack_mgmt_flags(mgmt_flags): 795 flags = TRUSTY_APP_MGMT_FLAGS_NONE 796 if mgmt_flags.restart_on_exit: 797 flags |= TRUSTY_APP_MGMT_FLAGS_RESTART_ON_EXIT 798 if mgmt_flags.deferred_start: 799 flags |= TRUSTY_APP_MGMT_FLAGS_DEFERRED_START 800 if mgmt_flags.non_critical_app: 801 flags |= TRUSTY_APP_MGMT_FLAGS_NON_CRITICAL_APP 802 803 return flags 804 805 806def pack_apploader_flags(apploader_flags): 807 flags = TRUSTY_APP_APPLOADER_FLAGS_NONE 808 if apploader_flags.requires_encryption: 809 flags |= TRUSTY_APP_APPLOADER_FLAGS_REQUIRES_ENCRYPTION 810 811 return flags 812 813 814def pack_start_port_flags(flags): 815 start_port_flags = TRUSTY_APP_MGMT_FLAGS_NONE 816 if flags.allow_ta_connect: 817 start_port_flags |= IPC_PORT_ALLOW_TA_CONNECT 818 if flags.allow_ns_connect: 819 start_port_flags |= IPC_PORT_ALLOW_NS_CONNECT 820 821 return start_port_flags 822 823 824def pack_inline_string(value): 825 """Pack a given string with null padding to make its size 826 multiple of 4. 827 packed data includes length + string + null + padding 828 """ 829 size = len(value) + 1 830 pad_len = 3 - (size + 3) % 4 831 packed = struct.pack("I", size) + value.encode() + b"\0" + pad_len * b"\0" 832 assert len(packed) % 4 == 0 833 return packed 834 835 836def pack_manifest_data(manifest): 837 """Creates Packed data from extracted manifest data. 838 Writes the packed data to binary file 839 """ 840 # PACK { 841 # uuid, app_name_size, app_name, 842 # TRUSTY_APP_CONFIG_KEY_MIN_HEAP_SIZE, min_heap, 843 # TRUSTY_APP_CONFIG_KEY_MIN_STACK_SIZE, min_stack, 844 # TRUSTY_APP_CONFIG_KEY_MAP_MEM, id, addr, size, 845 # TRUSTY_APP_CONFIG_KEY_MGMT_FLAGS, mgmt_flags 846 # TRUSTY_APP_CONFIG_KEY_START_PORT, flag, name_size, name 847 # TRUSTY_APP_CONFIG_KEY_PINNED_CPU, pinned_cpu 848 # TRUSTY_APP_CONFIG_KEY_PRIORITY, priority 849 # TRUSTY_APP_CONFIG_KEY_VERSION, version 850 # TRUSTY_APP_CONFIG_KEY_MIN_VERSION, min_version 851 # TRUSTY_APP_CONFIG_KEY_MIN_SHADOW_STACK_SIZE, min_shadow_stack, 852 # TRUSTY_APP_CONFIG_KEY_APPLOADER_FLAGS, apploader_flags, 853 # } 854 out = io.BytesIO() 855 856 uuid = swap_uuid_bytes(manifest.uuid) 857 out.write(uuid) 858 859 out.write(pack_inline_string(manifest.app_name)) 860 861 if manifest.min_heap is not None: 862 out.write(struct.pack("II", TRUSTY_APP_CONFIG_KEY_MIN_HEAP_SIZE, 863 manifest.min_heap)) 864 865 if manifest.min_stack is not None: 866 out.write(struct.pack("II", TRUSTY_APP_CONFIG_KEY_MIN_STACK_SIZE, 867 manifest.min_stack)) 868 869 for memio_map in manifest.mem_io_maps: 870 out.write(struct.pack("IIQQI", 871 TRUSTY_APP_CONFIG_KEY_MAP_MEM, 872 memio_map.id, 873 memio_map.addr, 874 memio_map.size, 875 pack_mem_map_arch_mmu_flags(memio_map))) 876 877 if manifest.mgmt_flags is not None: 878 out.write(struct.pack("II", 879 TRUSTY_APP_CONFIG_KEY_MGMT_FLAGS, 880 pack_mgmt_flags(manifest.mgmt_flags))) 881 882 for port_entry in manifest.start_ports: 883 out.write(struct.pack("II", 884 TRUSTY_APP_CONFIG_KEY_START_PORT, 885 pack_start_port_flags( 886 port_entry.start_port_flags))) 887 out.write(pack_inline_string(port_entry.name)) 888 889 if manifest.pinned_cpu is not None: 890 out.write(struct.pack("II", 891 TRUSTY_APP_CONFIG_KEY_PINNED_CPU, 892 manifest.pinned_cpu)) 893 894 if manifest.priority is not None: 895 out.write(struct.pack("II", 896 TRUSTY_APP_CONFIG_KEY_PRIORITY, 897 manifest.priority)) 898 899 if manifest.version is not None: 900 out.write(struct.pack("II", 901 TRUSTY_APP_CONFIG_KEY_VERSION, 902 manifest.version)) 903 904 if manifest.min_version is not None: 905 out.write(struct.pack("II", 906 TRUSTY_APP_CONFIG_KEY_MIN_VERSION, 907 manifest.min_version)) 908 909 if manifest.min_shadow_stack is not None: 910 out.write(struct.pack("II", 911 TRUSTY_APP_CONFIG_KEY_MIN_SHADOW_STACK_SIZE, 912 manifest.min_shadow_stack)) 913 914 if manifest.apploader_flags is not None: 915 out.write(struct.pack("II", 916 TRUSTY_APP_CONFIG_KEY_APPLOADER_FLAGS, 917 pack_apploader_flags(manifest.apploader_flags))) 918 919 return out.getvalue() 920 921 922def unpack_binary_manifest_to_json(packed_data): 923 """Creates manifest JSON string from packed manifest data""" 924 return manifest_data_to_json(unpack_binary_manifest_to_data(packed_data)) 925 926 927def manifest_data_to_json(manifest): 928 return json.dumps(manifest, sort_keys=True, indent=4) 929 930 931def unpack_binary_manifest_to_data(packed_data): 932 """This method can be used for extracting manifest data from packed binary. 933 UUID should be present in packed data. 934 """ 935 manifest = {} 936 937 # Extract UUID 938 uuid, packed_data = packed_data[:16], packed_data[16:] 939 uuid = swap_uuid_bytes(uuid) 940 uuid = uuid.hex() 941 uuid = uuid[:8] + "-" \ 942 + uuid[8:12] + "-" \ 943 + uuid[12:16] + "-" \ 944 + uuid[16:20] + "-" \ 945 + uuid[20:] 946 947 manifest[UUID] = uuid 948 949 # Extract APP_NAME 950 # read size of the name, this includes a null character 951 (name_size,), packed_data = struct.unpack( 952 "I", packed_data[:4]), packed_data[4:] 953 # read the name without a trailing null character 954 manifest[APP_NAME], packed_data = \ 955 packed_data[:name_size - 1].decode(), packed_data[name_size - 1:] 956 # discard trailing null characters 957 # it includes trailing null character of a string and null padding 958 pad_len = 1 + 3 - (name_size + 3) % 4 959 packed_data = packed_data[pad_len:] 960 961 # Extract remaining app configurations 962 while len(packed_data) > 0: 963 (tag,), packed_data = struct.unpack( 964 "I", packed_data[:4]), packed_data[4:] 965 966 if tag == TRUSTY_APP_CONFIG_KEY_MIN_HEAP_SIZE: 967 assert MIN_HEAP not in manifest 968 (manifest[MIN_HEAP],), packed_data = struct.unpack( 969 "I", packed_data[:4]), packed_data[4:] 970 elif tag == TRUSTY_APP_CONFIG_KEY_MIN_STACK_SIZE: 971 assert MIN_STACK not in manifest 972 (manifest[MIN_STACK],), packed_data = struct.unpack( 973 "I", packed_data[:4]), packed_data[4:] 974 elif tag == TRUSTY_APP_CONFIG_KEY_MIN_SHADOW_STACK_SIZE: 975 assert MIN_SHADOW_STACK not in manifest 976 (manifest[MIN_SHADOW_STACK],), packed_data = struct.unpack( 977 "I", packed_data[:4]), packed_data[4:] 978 elif tag == TRUSTY_APP_CONFIG_KEY_MAP_MEM: 979 if MEM_MAP not in manifest: 980 manifest[MEM_MAP] = [] 981 mem_map_entry = {} 982 (id_,), packed_data = struct.unpack( 983 "I", packed_data[:4]), packed_data[4:] 984 (addr,), packed_data = struct.unpack( 985 "Q", packed_data[:8]), packed_data[8:] 986 (size,), packed_data = struct.unpack( 987 "Q", packed_data[:8]), packed_data[8:] 988 (arch_mmu_flags,), packed_data = struct.unpack( 989 "I", packed_data[:4]), packed_data[4:] 990 mem_map_entry[MEM_MAP_ID] = id_ 991 mem_map_entry[MEM_MAP_ADDR] = hex(addr) 992 mem_map_entry[MEM_MAP_SIZE] = hex(size) 993 mem_map_entry[MEM_MAP_TYPE] = { 994 ARCH_MMU_FLAG_CACHED: MEM_MAP_TYPE_CACHED, 995 ARCH_MMU_FLAG_UNCACHED: MEM_MAP_TYPE_UNCACHED, 996 ARCH_MMU_FLAG_UNCACHED_DEVICE: MEM_MAP_TYPE_UNCACHED_DEVICE, 997 }[arch_mmu_flags & ARCH_MMU_FLAG_CACHE_MASK] 998 mem_map_entry[MEM_MAP_NON_SECURE] = bool(arch_mmu_flags & 999 ARCH_MMU_FLAG_NS) 1000 manifest[MEM_MAP].append(mem_map_entry) 1001 elif tag == TRUSTY_APP_CONFIG_KEY_MGMT_FLAGS: 1002 (flag,), packed_data = struct.unpack( 1003 "I", packed_data[:4]), packed_data[4:] 1004 mgmt_flag = { 1005 MGMT_FLAG_RESTART_ON_EXIT: False, 1006 MGMT_FLAG_DEFERRED_START: False, 1007 MGMT_FLAG_NON_CRITICAL_APP: False 1008 } 1009 if flag & TRUSTY_APP_MGMT_FLAGS_RESTART_ON_EXIT: 1010 mgmt_flag[MGMT_FLAG_RESTART_ON_EXIT] = True 1011 if flag & TRUSTY_APP_MGMT_FLAGS_DEFERRED_START: 1012 mgmt_flag[MGMT_FLAG_DEFERRED_START] = True 1013 if flag & TRUSTY_APP_MGMT_FLAGS_NON_CRITICAL_APP: 1014 mgmt_flag[MGMT_FLAG_NON_CRITICAL_APP] = True 1015 manifest[MGMT_FLAGS] = mgmt_flag 1016 elif tag == TRUSTY_APP_CONFIG_KEY_START_PORT: 1017 if START_PORTS not in manifest: 1018 manifest[START_PORTS] = [] 1019 start_port_entry = {} 1020 1021 (flag,), packed_data = struct.unpack( 1022 "I", packed_data[:4]), packed_data[4:] 1023 1024 # read size of the name, this includes a null character 1025 (name_size,), packed_data = struct.unpack( 1026 "I", packed_data[:4]), packed_data[4:] 1027 # read the name without a trailing null character 1028 start_port_entry[START_PORT_NAME], packed_data = ( 1029 packed_data[:name_size - 1].decode(), 1030 packed_data[name_size - 1:] 1031 ) 1032 # discard trailing null characters 1033 # it includes trailing null character of a string and null padding 1034 pad_len = 1 + 3 - (name_size + 3) % 4 1035 packed_data = packed_data[pad_len:] 1036 1037 start_port_flags = { 1038 START_PORT_ALLOW_TA_CONNECT: False, 1039 START_PORT_ALLOW_NS_CONNECT: False 1040 } 1041 if flag & IPC_PORT_ALLOW_TA_CONNECT: 1042 start_port_flags[START_PORT_ALLOW_TA_CONNECT] = True 1043 if flag & IPC_PORT_ALLOW_NS_CONNECT: 1044 start_port_flags[IPC_PORT_ALLOW_NS_CONNECT] = True 1045 start_port_entry[START_PORT_FLAGS] = start_port_flags 1046 1047 manifest[START_PORTS].append(start_port_entry) 1048 elif tag == TRUSTY_APP_CONFIG_KEY_PINNED_CPU: 1049 assert PINNED_CPU not in manifest 1050 (pinned_cpu,), packed_data = struct.unpack( 1051 "I", packed_data[:4]), packed_data[4:] 1052 manifest[PINNED_CPU] = pinned_cpu 1053 elif tag == TRUSTY_APP_CONFIG_KEY_PRIORITY: 1054 assert PRIORITY not in manifest 1055 (priority,), packed_data = struct.unpack( 1056 "I", packed_data[:4]), packed_data[4:] 1057 manifest[PRIORITY] = priority 1058 elif tag == TRUSTY_APP_CONFIG_KEY_VERSION: 1059 assert VERSION not in manifest 1060 (version,), packed_data = struct.unpack( 1061 "I", packed_data[:4]), packed_data[4:] 1062 manifest[VERSION] = version 1063 elif tag == TRUSTY_APP_CONFIG_KEY_MIN_VERSION: 1064 assert MIN_VERSION not in manifest 1065 (min_version,), packed_data = struct.unpack( 1066 "I", packed_data[:4]), packed_data[4:] 1067 manifest[MIN_VERSION] = min_version 1068 elif tag == TRUSTY_APP_CONFIG_KEY_APPLOADER_FLAGS: 1069 assert APPLOADER_FLAGS not in manifest 1070 (flag,), packed_data = struct.unpack( 1071 "I", packed_data[:4]), packed_data[4:] 1072 apploader_flag = { 1073 APPLOADER_FLAGS_REQUIRES_ENCRYPTION: False, 1074 } 1075 if flag & TRUSTY_APP_APPLOADER_FLAGS_REQUIRES_ENCRYPTION: 1076 apploader_flag[APPLOADER_FLAGS_REQUIRES_ENCRYPTION] = True 1077 manifest[APPLOADER_FLAGS] = apploader_flag 1078 else: 1079 raise Exception(f"Unknown tag: {tag}") 1080 1081 return manifest 1082 1083 1084def write_packed_data_to_bin_file(packed_data, output_file, log): 1085 """Write packed data to binary file""" 1086 try: 1087 with open(output_file, "wb") as out_file: 1088 out_file.write(packed_data) 1089 out_file.close() 1090 except IOError as ex: 1091 log.error(f"Unable to write to output file: {output_file}\n" + str(ex)) 1092 1093 1094def read_json_config_file(input_file, log): 1095 try: 1096 with open(input_file, "r", encoding="utf-8") as read_file: 1097 manifest_dict = json.load(read_file) 1098 return manifest_dict 1099 except IOError as ex: 1100 log.error(f"{input_file}: unable to open input file: {ex}") 1101 return None 1102 except json.JSONDecodeError as jde: 1103 location = f"{input_file}:{jde.lineno}:{jde.colno}" 1104 log.error(f"{location}: Unable to parse config JSON: {jde.msg}") 1105 return None 1106 except ValueError as ex: 1107 log.error(f"{input_file}: Unexpected error: {ex}") 1108 return None 1109 1110 1111def read_config_constants(const_config_files, log): 1112 const_configs_list = [] 1113 for const_file in const_config_files: 1114 const_configs_list.append(read_json_config_file(const_file, log)) 1115 1116 return const_configs_list 1117 1118 1119def define_integer_const_entry(const): 1120 text = hex(const.value) if const.hex_num else str(const.value) 1121 if const.unsigned: 1122 text += "U" 1123 1124 return f"#define {const.name} ({text})\n" 1125 1126 1127def define_string_const_entry(const): 1128 return f"#define {const.name} {json.dumps(const.value)}\n" 1129 1130def define_identifier_const_entry(const): 1131 return f"#define {const.name} {const.value}\n" 1132 1133def define_bool_const_entry(const): 1134 return f"#define {const.name} ({json.dumps(const.value)})\n" 1135 1136 1137def define_uuid_const_entry(const): 1138 uuid = const.value.hex() 1139 1140 part = ", ".join( 1141 ["0x" + uuid[index:index + 2] for index in range(16, len(uuid), 2)]) 1142 1143 value = f"{{0x{uuid[:8]}, 0x{uuid[8:12]}, 0x{uuid[12:16]}, {{ {part} }}}}\n" 1144 1145 return f"#define {const.name} {value}" 1146 1147 1148def create_header_entry(constant): 1149 if constant.type == CONST_PORT: 1150 return define_string_const_entry(constant) 1151 if constant.type == CONST_UUID: 1152 return define_uuid_const_entry(constant) 1153 if constant.type == CONST_INT: 1154 return define_integer_const_entry(constant) 1155 if constant.type == CONST_BOOL: 1156 return define_bool_const_entry(constant) 1157 if constant.type == CONST_ID: 1158 return define_identifier_const_entry(constant) 1159 1160 raise Exception(f"Unknown tag: {constant.type}") 1161 1162 1163def write_consts_to_header_file(const_config, header_dir, log): 1164 """Writes given constants to header file in given header directory.""" 1165 # Construct header file path 1166 header_file = os.path.join(header_dir, const_config.header) 1167 # Check whether the output directory of header file exist 1168 # If it not exists create it. 1169 dir_name = os.path.dirname(header_file) 1170 if dir_name and not os.path.exists(dir_name): 1171 os.makedirs(dir_name) 1172 1173 try: 1174 with open(header_file, "w", encoding="utf-8") as out_file: 1175 out_file.write("#pragma once\n") 1176 out_file.write("#include <stdbool.h>\n\n") 1177 for const in const_config.constants: 1178 header_entries = create_header_entry(const) 1179 out_file.write(header_entries) 1180 except IOError as ex: 1181 log.error(f"Unable to write to header file: {header_file}\n" + str(ex)) 1182 1183 1184def parse_constant(constant, log): 1185 """Parse a give JSON constant data structure""" 1186 const_type = get_string(constant, CONST_TYPE, {}, log) 1187 if const_type is None: 1188 return None 1189 1190 name = get_string(constant, CONST_NAME, {}, log) 1191 if const_type == CONST_PORT or const_type == CONST_ID: 1192 value = get_string(constant, CONST_VALUE, {}, log) 1193 return Constant(name, value, const_type) 1194 if const_type == CONST_UUID: 1195 value = get_string(constant, CONST_VALUE, {}, log) 1196 return Constant(name, parse_uuid(value, log), const_type) 1197 if const_type == CONST_INT: 1198 unsigned = get_boolean(constant, CONST_UNSIGNED, {}, log) 1199 text_value = constant.get(CONST_VALUE) 1200 hex_num = isinstance(text_value, str) and text_value.startswith("0x") 1201 value = get_int(constant, CONST_VALUE, {}, log) 1202 return Constant(name, value, const_type, unsigned, hex_num) 1203 if const_type == CONST_BOOL: 1204 value = get_boolean(constant, CONST_VALUE, {}, log) 1205 return Constant(name, value, const_type) 1206 1207 log.error(f"Unknown constant type: {const_type}") 1208 return None 1209 1210 1211def parse_config_constant(const_config, log): 1212 """Parse a given JSON constant-config data structure containing a header and 1213 list of constants 1214 """ 1215 header_file = get_string(const_config, HEADER, {}, log) 1216 1217 const_list = get_list(const_config, CONSTANTS, log, optional=False, 1218 default=[]) 1219 1220 constants = [] 1221 for item in const_list: 1222 item = coerce_to_dict(item, CONSTANTS, log) 1223 if item is None: 1224 continue 1225 constants.append(parse_constant(item, log)) 1226 if item: 1227 log.error("Unknown attributes in constant: {item}") 1228 1229 if const_config: 1230 log.error(f"Unknown attributes in constants config: {const_config}") 1231 1232 return ConfigConstants(constants, header_file) 1233 1234 1235def extract_config_constants(config_consts_list, log): 1236 """Collects ConfigConstant(s) from list of JSON config constants data""" 1237 config_constants = [] 1238 1239 for config_const in config_consts_list: 1240 config_constants.append(parse_config_constant(config_const, log)) 1241 1242 return config_constants 1243 1244 1245def process_config_constants(const_config_files, header_dir, log): 1246 """Parse JSON config constants and creates separate header files with 1247 constants for each JSON config 1248 """ 1249 if const_config_files is None: 1250 return [] 1251 1252 config_consts_list = read_config_constants(const_config_files, log) 1253 if log.error_occurred(): 1254 return [] 1255 1256 config_constants = extract_config_constants(config_consts_list, log) 1257 if log.error_occurred(): 1258 return [] 1259 1260 # generate header files 1261 for const_config in config_constants: 1262 write_consts_to_header_file(const_config, header_dir, log) 1263 1264 return config_constants 1265 1266def merge_manifest_dicts(manifests: list, log): 1267 """Merges multiple manifests 1268 """ 1269 def merge(base, overlay, log): 1270 match base, overlay: 1271 case dict(), dict(): 1272 common_keys = base.keys() & overlay.keys() 1273 res = {k: merge(base[k], overlay[k], log) for k in common_keys} 1274 res |= {k: base[k] for k in base.keys() - common_keys} 1275 res |= {k: overlay[k] for k in overlay.keys() - common_keys} 1276 return res 1277 case list(), list(): 1278 res = base.copy() 1279 res.extend(i for i in overlay if i not in base) 1280 return res 1281 case int() | float() | str(), int() | float() | str(): 1282 return overlay # overlay overrides base for scalars 1283 case _: 1284 log.error( 1285 f"Unhandled type pair: {type(base)} and {type(overlay)}") 1286 return base 1287 1288 manifest_dict_merged : dict = {} 1289 1290 for manifest in manifests: 1291 manifest_dict_merged = merge(manifest_dict_merged, manifest, log) 1292 1293 if log.error_occurred(): 1294 return None 1295 1296 return manifest_dict_merged 1297 1298def process_manifest_files(manifest_files, constants, log): 1299 """Parse JSON manifest(s) 1300 """ 1301 assert manifest_files 1302 1303 manifest_dicts = [] 1304 1305 for manifest_file in manifest_files: 1306 if not os.path.exists(manifest_file): 1307 log.error( 1308 f"Manifest config JSON file doesn't exist: {manifest_file}") 1309 return None 1310 1311 manifest_dict = read_json_config_file(manifest_file, log) 1312 if log.error_occurred(): 1313 return None 1314 1315 manifest_dicts.append(manifest_dict) 1316 1317 manifest_dict_merged = merge_manifest_dicts(manifest_dicts, log) 1318 1319 if manifest_dict_merged is None or log.error_occurred(): 1320 return None 1321 1322 default_app_name = os.path.basename(os.path.dirname(manifest_files[0])) 1323 1324 return parse_manifest_config(manifest_dict_merged, constants, 1325 default_app_name, log) 1326 1327 1328def index_constants(config_constants): 1329 constants = {} 1330 for const_config in config_constants: 1331 for const in const_config.constants: 1332 constants[const.name] = const 1333 1334 return constants 1335 1336 1337def main(): 1338 """Handles the command line arguments. Parses the given manifest input file 1339 and creates packed data. Writes the packed data to binary output file. 1340 """ 1341 parser = argparse.ArgumentParser() 1342 parser.add_argument( 1343 "-i", "--input", 1344 dest="input_filenames", 1345 required=False, 1346 action="append", 1347 type=str, 1348 help="Trusty app manifest in JSON format. " 1349 "If the flag is used to provide multiple input files, " 1350 "subsequent files overwrite the values provided in " 1351 "the first manifest file." 1352 ) 1353 parser.add_argument( 1354 "-o", "--output", 1355 dest="output_filename", 1356 required=False, 1357 type=str, 1358 help="It will be binary file with packed manifest data" 1359 ) 1360 parser.add_argument( 1361 "-c", "--constants", 1362 dest="constants", 1363 required=False, 1364 action="append", 1365 help="JSON file with manifest config constants" 1366 ) 1367 parser.add_argument( 1368 "--header-dir", 1369 dest="header_dir", 1370 required=False, 1371 type=str, 1372 help="Directory path for generating headers" 1373 ) 1374 parser.add_argument( 1375 "--enable-shadow-call-stack", 1376 dest="shadow_call_stack", 1377 required=False, 1378 action="store_true", # implies default := False 1379 help="Allow apps to opt into having a shadow call stack. " 1380 "Without this flag, apps will not have shadow stacks " 1381 "even if their manifests define \"min_shadow_stack\"." 1382 ) 1383 parser.add_argument( 1384 "--default-shadow-call-stack-size", 1385 dest="default_shadow_call_stack_size", 1386 required=False, 1387 default=4096, 1388 type=int, 1389 metavar="DEFAULT_SIZE", 1390 help="Controls the size of the default shadow call stack." 1391 "This option has no effect unless shadow call stacks " 1392 "are enabled via the --enable-shadow-call-stack flag." 1393 ) 1394 # Parse the command line arguments 1395 args = parser.parse_args() 1396 if args.constants and not args.header_dir: 1397 parser.error("--header-dir is required if --constants are specified") 1398 1399 if args.input_filenames and not args.output_filename: 1400 parser.error("Input file provided with no manifest output file.") 1401 1402 if args.output_filename and not args.input_filenames: 1403 parser.error("Building a manifest output file requires an input file.") 1404 1405 if args.default_shadow_call_stack_size <= 0: 1406 parser.error( 1407 "--default-shadow-call-stack-size expects a positive integer") 1408 1409 log = Log() 1410 1411 # collect config constants and create header files for each const config 1412 config_constants = process_config_constants(args.constants, 1413 args.header_dir, log) 1414 if log.error_occurred(): 1415 return 1 1416 1417 if not args.output_filename: 1418 return 0 1419 1420 constants = index_constants(config_constants) 1421 1422 manifest = process_manifest_files(args.input_filenames, constants, log) 1423 1424 if manifest is None: 1425 return 1 1426 1427 if log.error_occurred(): 1428 return 1 1429 1430 # Optionally adjust min_shadow_stack based on command line arguments 1431 if args.shadow_call_stack: 1432 # If shadow callstack is enabled but the size is not specified in the 1433 # manifest, set it to the default value. 1434 if manifest.min_shadow_stack is None: 1435 manifest.min_shadow_stack = args.default_shadow_call_stack_size 1436 else: 1437 # If shadow call stack is not enabled, make sure the size is set to 1438 # zero in the binary manifest. In the future, "not present" may 1439 # indicate the binary does not use a shadow callstack, but for now 1440 # we're making sure a value is always present. 1441 manifest.min_shadow_stack = 0 1442 1443 assert (args.shadow_call_stack and manifest.min_shadow_stack > 0) != \ 1444 (manifest.min_shadow_stack == 0) 1445 1446 # Pack the data as per C structures 1447 packed_data = pack_manifest_data(manifest) 1448 if log.error_occurred(): 1449 return 1 1450 1451 # Write to file. 1452 write_packed_data_to_bin_file(packed_data, args.output_filename, log) 1453 if log.error_occurred(): 1454 return 1 1455 1456 return 0 1457 1458 1459if __name__ == "__main__": 1460 sys.exit(main()) 1461