• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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