| #!/usr/bin/env python2.7 |
| |
| ''' |
| This program will take truseted application's manifest config JSON file as |
| input. Processes the JSON config file and creates packed data |
| mapping to C structures and dumps in binary format. |
| |
| USAGE: |
| manifest_compiler.py --input <input_filename> --output <output_filename> \ |
| --constants <config_constants_file_1> \ |
| --constants <config_constants_file_2> \ |
| --header-dir <header_file_path> |
| |
| Arguments: |
| input_filename - Trusted app manifest config file in JSON format. |
| output_filename - Binary file containing packed manifest config data mapped |
| to C structres. |
| config_constant_file - This is optional |
| Config file with constants in JSON format |
| Corresponding header file will be |
| created with its constants defined in it |
| header_file_path - Directory in which header files to be generated. |
| |
| example: |
| manifest_compiler.py --input manifest.json --output output.bin \ |
| --constants manifest_constants.json \ |
| --header-dir \ |
| <build_dir>/user_tasks/trusty/user/app/sample/hwcrypto/include |
| |
| |
| Input sample JSON Manifest config file content - |
| { |
| "uuid": "SECURE_STORAGE_SERVER_APP_UUID", |
| "min_heap": 4096, |
| "min_stack": 4096, |
| "mem_map": [{"id": 1, "addr": "0x70000000", "size": "0x1000"}, \ |
| {"id": 2, "addr": "0x70010000", "size": "0x100"}, \ |
| {"id": 3, "addr": "0x70020000", "size": "0x4", |
| "type": "uncached_device", "non_secure": false}], |
| "mgmt_flags": {"restart_on_exit": true, \ |
| "deferred_start": false, \ |
| "non_critical_app": false}, |
| "start_ports": [{"name": "LOADABLE_START_PORT", \ |
| "flags": {"allow_ta_connect": true, "allow_ns_connect": false}}] |
| } |
| |
| JSON manifest constant config - |
| { |
| "header": "storage_constants.h", |
| "constants": [{ |
| "name": "LOADABLE_START_PORT", |
| "value": "com.android.trusty.appmgmt.loadable.start", |
| "type": "port" |
| }, |
| { |
| "name": "SECURE_STORAGE_SERVER_APP_UUID", |
| "value": "eca48f94-00aa-560e-8f8c-d94b50d484f3", |
| "type": "uuid" |
| }] |
| } |
| ''' |
| |
| import argparse |
| import cStringIO |
| import json |
| import optparse |
| import os.path |
| import struct |
| import sys |
| |
| |
| # Manifest properties |
| UUID = "uuid" |
| MIN_HEAP = "min_heap" |
| MIN_STACK = "min_stack" |
| MEM_MAP = "mem_map" |
| MEM_MAP_ID = "id" |
| MEM_MAP_ADDR = "addr" |
| MEM_MAP_SIZE = "size" |
| MEM_MAP_TYPE = "type" |
| MEM_MAP_TYPE_CACHED = "cached" |
| MEM_MAP_TYPE_UNCACHED = "uncached" |
| MEM_MAP_TYPE_UNCACHED_DEVICE = "uncached_device" |
| MEM_MAP_NON_SECURE = "non_secure" |
| MGMT_FLAGS = "mgmt_flags" |
| MGMT_FLAG_RESTART_ON_EXIT = "restart_on_exit" |
| MGMT_FLAG_DEFERRED_START = "deferred_start" |
| MGMT_FLAG_NON_CRITICAL_APP = "non_critical_app" |
| START_PORTS = "start_ports" |
| START_PORT_FLAGS = "flags" |
| START_PORT_NAME = "name" |
| START_PORT_ALLOW_TA_CONNECT = "allow_ta_connect" |
| START_PORT_ALLOW_NS_CONNECT = "allow_ns_connect" |
| APP_NAME = "app_name" |
| |
| # constants configs |
| CONSTANTS = "constants" |
| HEADER = "header" |
| CONST_NAME = "name" |
| CONST_VALUE = "value" |
| CONST_TYPE = "type" |
| CONST_UNSIGNED = "unsigned" |
| CONST_PORT = "port" |
| CONST_UUID = "uuid" |
| CONST_INT = "int" |
| CONST_BOOL = "bool" |
| |
| # CONFIG TAGS |
| # These values need to be kept in sync with uapi/trusty_app_manifest_types.h |
| TRUSTY_APP_CONFIG_KEY_MIN_STACK_SIZE = 1 |
| TRUSTY_APP_CONFIG_KEY_MIN_HEAP_SIZE = 2 |
| TRUSTY_APP_CONFIG_KEY_MAP_MEM = 3 |
| TRUSTY_APP_CONFIG_KEY_MGMT_FLAGS = 4 |
| TRUSTY_APP_CONFIG_KEY_START_PORT = 5 |
| |
| # MEM_MAP ARCH_MMU_FLAGS |
| # These values need to be kept in sync with external/lk/include/arch/mmu.h |
| ARCH_MMU_FLAG_CACHED = 0 << 0 |
| ARCH_MMU_FLAG_UNCACHED = 1 << 0 |
| ARCH_MMU_FLAG_UNCACHED_DEVICE = 2 << 0 |
| ARCH_MMU_FLAG_CACHE_MASK = 3 << 0 |
| ARCH_MMU_FLAG_NS = 1 << 5 |
| |
| # MGMT FLAGS |
| # These values need to be kept in sync with uapi/trusty_app_manifest_types.h |
| TRUSTY_APP_MGMT_FLAGS_NONE = 0 |
| TRUSTY_APP_MGMT_FLAGS_RESTART_ON_EXIT = 1 << 0 |
| TRUSTY_APP_MGMT_FLAGS_DEFERRED_START = 1 << 1 |
| TRUSTY_APP_MGMT_FLAGS_NON_CRITICAL_APP = 1 << 2 |
| |
| # START_PORT flags |
| # These values need to be kept in sync with user/base/include/user/trusty_ipc.h |
| IPC_PORT_ALLOW_TA_CONNECT = 0x1 |
| IPC_PORT_ALLOW_NS_CONNECT = 0x2 |
| |
| IPC_PORT_PATH_MAX = 64 |
| |
| |
| class Constant(object): |
| def __init__(self, name, value, type_, unsigned=False, hex_num=False): |
| self.name = name |
| self.value = value |
| self.type = type_ |
| self.unsigned = unsigned |
| self.hex_num = hex_num |
| |
| |
| class ConfigConstants(object): |
| def __init__(self, constants, header): |
| self.constants = constants |
| self.header = header |
| |
| |
| class StartPortFlags(object): |
| def __init__(self, allow_ta_connect, allow_ns_connect): |
| self.allow_ta_connect = allow_ta_connect |
| self.allow_ns_connect = allow_ns_connect |
| |
| |
| class StartPort(object): |
| def __init__(self, name, name_size, start_port_flags): |
| self.name = name |
| self.name_size = name_size |
| self.start_port_flags = start_port_flags |
| |
| |
| class MemIOMap(object): |
| def __init__(self, id_, addr, size, type, non_secure): |
| self.id = id_ |
| self.addr = addr |
| self.size = size |
| self.type = type |
| self.non_secure = non_secure |
| |
| |
| class MgmtFlags(object): |
| def __init__(self, restart_on_exit, deferred_start, non_critical_app): |
| self.restart_on_exit = restart_on_exit |
| self.deferred_start = deferred_start |
| self.non_critical_app = non_critical_app |
| |
| |
| ''' |
| Holds Manifest data to be used for packing |
| ''' |
| class Manifest(object): |
| def __init__( |
| self, |
| uuid, |
| app_name, |
| min_heap, |
| min_stack, |
| mem_io_maps, |
| mgmt_flags, |
| start_ports |
| ): |
| self.uuid = uuid |
| self.app_name = app_name |
| self.min_heap = min_heap |
| self.min_stack = min_stack |
| self.mem_io_maps = mem_io_maps |
| self.mgmt_flags = mgmt_flags |
| self.start_ports = start_ports |
| |
| |
| ''' |
| Tracks errors during manifest compilation |
| ''' |
| class Log(object): |
| def __init__(self): |
| self.error_count = 0 |
| |
| def error(self, msg): |
| sys.stderr.write("Error: {}\n".format(msg)) |
| self.error_count += 1 |
| |
| def error_occurred(self): |
| return self.error_count > 0 |
| |
| |
| ''' |
| For the given manifest JSON field it returns its literal value type mapped. |
| ''' |
| def get_string_sub_type(field): |
| if field == UUID: |
| return CONST_UUID |
| elif field == START_PORT_NAME: |
| return CONST_PORT |
| else: |
| # field with string value but doesn't support a constant |
| return None |
| |
| |
| def get_constant(constants, key, type_, log): |
| const = constants.get(key) |
| if const is None: |
| return None |
| |
| if const.type != type_: |
| log.error("{} constant type mismatch, expected type is {}" |
| .format(key, type_)) |
| return None |
| |
| return const.value |
| |
| |
| ''' |
| Determines whether the value for the given key in dictionary is of type string |
| and if it is a string then returns the value. |
| ''' |
| def get_string(manifest_dict, key, constants, log, optional=False, |
| default=None): |
| if key not in manifest_dict: |
| if not optional: |
| log.error("Manifest is missing required attribute - {}" |
| .format(key)) |
| return default |
| |
| value = manifest_dict.pop(key) |
| |
| # try to check is this field holding a constant |
| type_ = get_string_sub_type(key) |
| if type_: |
| const_value = get_constant(constants, value, type_, log) |
| if const_value is not None: |
| return const_value |
| |
| return coerce_to_string(value, key, log) |
| |
| |
| def coerce_to_string(value, key, log): |
| if not isinstance(value, str) and \ |
| not isinstance(value, unicode): |
| log.error( |
| "Invalid value for" + |
| " {} - \"{}\", Valid string value is expected" |
| .format(key, value)) |
| return None |
| |
| return value |
| |
| |
| ''' |
| Determines whether the value for the given key in dictionary is of type integer |
| and if it is int then returns the value |
| ''' |
| def get_int(manifest_dict, key, constants, log, optional=False, |
| default=None): |
| if key not in manifest_dict: |
| if not optional: |
| log.error("Manifest is missing required attribute - {}" |
| .format(key)) |
| return default |
| |
| value = manifest_dict.pop(key) |
| const_value = get_constant(constants, value, CONST_INT, log) |
| if const_value is not None: |
| return const_value |
| |
| return coerce_to_int(value, key,log) |
| |
| |
| def coerce_to_int(value, key, log): |
| if isinstance(value, int) and \ |
| not isinstance(value, bool): |
| return value |
| elif isinstance(value, basestring): |
| try: |
| return int(value, 0) |
| except ValueError as ex: |
| log.error("Invalid value for" + |
| " {} - \"{}\", valid integer or hex string is expected" |
| .format(key, value)) |
| return None |
| else: |
| log.error("Invalid value for" + |
| " {} - \"{}\", valid integer value is expected" |
| .format(key, value)) |
| return None |
| |
| |
| ''' |
| Determines whether the value for the given key in dictionary is of type List |
| and if it is List then returns the value |
| ''' |
| def get_list(manifest_dict, key, log, optional=False, default=None): |
| if key not in manifest_dict: |
| if not optional: |
| log.error("Manifest is missing required attribute - {}" |
| .format(key)) |
| return default |
| |
| return coerce_to_list(manifest_dict.pop(key), key, log) |
| |
| |
| def coerce_to_list(value, key, log): |
| if not isinstance(value, list): |
| log.error("Invalid value for" + |
| " {} - \"{}\", valid list is expected" |
| .format(key, value)) |
| return None |
| |
| return value |
| |
| |
| ''' |
| Determines whether the value for the given |
| key in dictionary is of type Dictionary |
| and if it is Dictionary then returns the value |
| ''' |
| def get_dict(manifest_dict, key, log, optional=False, default=None): |
| if key not in manifest_dict: |
| if not optional: |
| log.error("Manifest is missing required attribute - {}" |
| .format(key)) |
| return default |
| |
| return coerce_to_dict(manifest_dict.pop(key), key, log) |
| |
| |
| def coerce_to_dict(value, key, log): |
| if not isinstance(value, dict): |
| log.error("Invalid value for" + |
| " {} - \"{}\", valid dict is expected" |
| .format(key, value)) |
| return None |
| |
| return value |
| |
| |
| ''' |
| Determines whether the value for the given key in dictionary is of type boolean |
| and if it is boolean then returns the value |
| ''' |
| def get_boolean(manifest_dict, key, constants, log, optional=False, |
| default=None): |
| if key not in manifest_dict: |
| if not optional: |
| log.error("Manifest is missing required attribute - {}" |
| .format(key)) |
| return default |
| |
| value = manifest_dict.pop(key) |
| const_value = get_constant(constants, value, CONST_BOOL, log) |
| if const_value is not None: |
| return const_value |
| |
| return coerce_to_boolean(value, key, log) |
| |
| |
| def coerce_to_boolean(value, key, log): |
| if not isinstance(value, bool): |
| log.error( |
| "Invalid value for" + |
| " {} - \"{}\", Valid boolean value is expected" |
| .format(key, value)) |
| return None |
| |
| return value |
| |
| |
| def get_uuid(manifest_dict, key, constants, log, optional=False, default=None): |
| if key not in manifest_dict: |
| if not optional: |
| log.error("Manifest is missing required attribute - {}" |
| .format(key)) |
| return default |
| |
| uuid = get_string(manifest_dict, key, {}, log, optional, default) |
| const_value = get_constant(constants, uuid, CONST_UUID, log) |
| if const_value is not None: |
| return const_value |
| |
| return parse_uuid(uuid, log) |
| |
| |
| def get_port(port, key, constants, log, optional=False, default=None): |
| return get_string(port, key, constants, log, optional, default) |
| |
| |
| ''' |
| Validate and arrange UUID byte order |
| If its valid UUID then returns 16 byte UUID |
| ''' |
| def parse_uuid(uuid, log): |
| if uuid is None: |
| return None |
| |
| # Example UUID: "5f902ace-5e5c-4cd8-ae54-87b88c22ddaf" |
| if len(uuid) != 36: |
| log.error( |
| "Invalid UUID " + |
| "{}, uuid should be of length 16 bytes of hex values" |
| .format(uuid)) |
| return None |
| |
| uuid_data = uuid.split("-") |
| if len(uuid_data) != 5: |
| log.error( |
| "Invalid UUID {}".format(uuid) + |
| "uuid should be of length 16 bytes of hex divided into 5 groups" |
| ) |
| return None |
| |
| try: |
| uuid_data = [part.decode("hex") for part in uuid_data] |
| except TypeError as ex: |
| log.error("Invalid UUID {}, {}".format(uuid, ex)) |
| return None |
| |
| if len(uuid_data[0]) != 4 or \ |
| len(uuid_data[1]) != 2 or \ |
| len(uuid_data[2]) != 2 or \ |
| len(uuid_data[3]) != 2 or \ |
| len(uuid_data[4]) != 6: |
| log.error("Wrong grouping of UUID - {}".format(uuid)) |
| return None |
| |
| return "".join(uuid_data) |
| |
| |
| ''' |
| Validate memory size value. |
| if success return memory size value else return None |
| ''' |
| def parse_memory_size(memory_size, log): |
| if memory_size is None: |
| return None |
| |
| if memory_size <= 0 or memory_size % 4096 != 0: |
| log.error( |
| "{}: {}, Minimum memory size should be " |
| .format(MIN_STACK, memory_size) + |
| "non-negative multiple of 4096") |
| return None |
| |
| return memory_size |
| |
| |
| def parse_mem_map_type(mem_map_type, log): |
| if mem_map_type not in {MEM_MAP_TYPE_CACHED, |
| MEM_MAP_TYPE_UNCACHED, |
| MEM_MAP_TYPE_UNCACHED_DEVICE}: |
| log.error("Unknown mem_map.type entry in manifest: {} " |
| .format(mem_map_type)) |
| |
| return mem_map_type |
| |
| |
| def parse_mem_map(mem_maps, key, constants, log): |
| if mem_maps is None: |
| return None |
| |
| mem_io_maps = [] |
| for mem_map_entry in mem_maps: |
| mem_map_entry = coerce_to_dict(mem_map_entry, key, log) |
| if mem_map_entry is None: |
| continue |
| mem_map = MemIOMap( |
| get_int(mem_map_entry, MEM_MAP_ID, constants, log), |
| get_int(mem_map_entry, MEM_MAP_ADDR, constants, log), |
| get_int(mem_map_entry, MEM_MAP_SIZE, constants, log), |
| parse_mem_map_type( |
| get_string(mem_map_entry, MEM_MAP_TYPE, constants, log, |
| optional=True, |
| default=MEM_MAP_TYPE_UNCACHED_DEVICE), log), |
| get_boolean(mem_map_entry, MEM_MAP_NON_SECURE, constants, log, |
| optional=True) |
| ) |
| if mem_map_entry: |
| log.error("Unknown atributes in mem_map entries in manifest: {} " |
| .format(mem_map_entry)) |
| mem_io_maps.append(mem_map) |
| |
| return mem_io_maps |
| |
| |
| def parse_mgmt_flags(flags, constants, log): |
| if flags is None: |
| return None |
| |
| mgmt_flags = MgmtFlags( |
| get_boolean(flags, MGMT_FLAG_RESTART_ON_EXIT, constants, log, optional=True), |
| get_boolean(flags, MGMT_FLAG_DEFERRED_START, constants, log, optional=True), |
| get_boolean(flags, MGMT_FLAG_NON_CRITICAL_APP, constants, log, optional=True)) |
| |
| if flags: |
| log.error("Unknown atributes in mgmt_flags entries in manifest: {} " |
| .format(flags)) |
| |
| return mgmt_flags |
| |
| |
| def parse_app_start_ports(start_port_list, key, constants, log): |
| start_ports = [] |
| |
| for port_entry in start_port_list: |
| port_entry = coerce_to_dict(port_entry, key, log) |
| if port_entry is None: |
| continue |
| |
| name = get_port(port_entry, START_PORT_NAME, constants, log) |
| if len(name) >= IPC_PORT_PATH_MAX: |
| log.error("Length of start port name should be less than {}" |
| .format(IPC_PORT_PATH_MAX)) |
| |
| flags = get_dict(port_entry, START_PORT_FLAGS, log) |
| start_ports_flag = None |
| if flags: |
| start_ports_flag = StartPortFlags( |
| get_boolean(flags, START_PORT_ALLOW_TA_CONNECT, constants, |
| log), |
| get_boolean(flags, START_PORT_ALLOW_NS_CONNECT, constants, |
| log)) |
| |
| if port_entry: |
| log.error("Unknown atributes in start_ports entries" + |
| " in manifest: {} ".format(port_entry)) |
| if flags: |
| log.error("Unknown atributes in start_ports.flags entries" + |
| " in manifest: {} ".format(flags)) |
| |
| start_ports.append(StartPort(name, len(name), start_ports_flag)) |
| |
| return start_ports |
| |
| |
| def parse_app_name(app_name, log): |
| if app_name is None: |
| return None |
| |
| if not app_name: |
| log.error("empty app-name is not allowed in manifest") |
| return None |
| |
| return app_name.strip() |
| |
| |
| ''' |
| validate the manifest config and extract key, values |
| ''' |
| def parse_manifest_config(manifest_dict, constants, default_app_name, log): |
| # UUID |
| uuid = get_uuid(manifest_dict, UUID, constants, log) |
| |
| # MIN_HEAP |
| min_heap = parse_memory_size(get_int(manifest_dict, MIN_HEAP, constants, |
| log), log) |
| |
| # MIN_STACK |
| min_stack = parse_memory_size(get_int(manifest_dict, MIN_STACK, constants, |
| log), log) |
| |
| # MEM_MAP |
| mem_io_maps = parse_mem_map( |
| get_list(manifest_dict, MEM_MAP, log, optional=True, default=[]), |
| MEM_MAP, |
| constants, log) |
| |
| # MGMT_FLAGS |
| mgmt_flags = parse_mgmt_flags( |
| get_dict(manifest_dict, MGMT_FLAGS, log, optional=True, |
| default={ |
| MGMT_FLAG_RESTART_ON_EXIT: False, |
| MGMT_FLAG_DEFERRED_START: False, |
| MGMT_FLAG_NON_CRITICAL_APP: False}), |
| constants, log) |
| |
| # START_PORTS |
| start_ports = parse_app_start_ports( |
| get_list(manifest_dict, START_PORTS, log, |
| optional=True, default=[]), |
| START_PORTS, |
| constants, |
| log) |
| |
| #APP_NAME |
| app_name = parse_app_name( |
| get_string(manifest_dict, APP_NAME, constants, log, |
| optional=True, default=default_app_name), log) |
| |
| # look for any extra attributes |
| if manifest_dict: |
| log.error("Unknown atributes in manifest: {} ".format(manifest_dict)) |
| |
| if log.error_occurred(): |
| return None |
| |
| return Manifest(uuid, app_name, min_heap, min_stack, mem_io_maps, mgmt_flags, |
| start_ports) |
| |
| |
| ''' |
| This script represents UUIDs in a purely big endian order. |
| Trusty stores the first three components of the UUID in little endian order. |
| Rearrange the byte order accordingly by doing inverse |
| on first three components of UUID |
| ''' |
| def swap_uuid_bytes(uuid): |
| return uuid[3::-1] + uuid[5:3:-1] + uuid[7:5:-1] + uuid[8:] |
| |
| |
| def pack_mem_map_arch_mmu_flags(mem_map): |
| arch_mmu_flags = 0 |
| |
| if mem_map.type == MEM_MAP_TYPE_CACHED: |
| arch_mmu_flags |= ARCH_MMU_FLAG_CACHED |
| elif mem_map.type == MEM_MAP_TYPE_UNCACHED: |
| arch_mmu_flags |= ARCH_MMU_FLAG_UNCACHED |
| elif mem_map.type == MEM_MAP_TYPE_UNCACHED_DEVICE: |
| arch_mmu_flags |= ARCH_MMU_FLAG_UNCACHED_DEVICE |
| |
| if mem_map.non_secure: |
| arch_mmu_flags |= ARCH_MMU_FLAG_NS |
| |
| return arch_mmu_flags |
| |
| |
| def pack_mgmt_flags(mgmt_flags): |
| flags = TRUSTY_APP_MGMT_FLAGS_NONE |
| if mgmt_flags.restart_on_exit: |
| flags |= TRUSTY_APP_MGMT_FLAGS_RESTART_ON_EXIT |
| if mgmt_flags.deferred_start: |
| flags |= TRUSTY_APP_MGMT_FLAGS_DEFERRED_START |
| if mgmt_flags.non_critical_app: |
| flags |= TRUSTY_APP_MGMT_FLAGS_NON_CRITICAL_APP |
| |
| return flags |
| |
| |
| def pack_start_port_flags(flags): |
| start_port_flags = TRUSTY_APP_MGMT_FLAGS_NONE |
| if flags.allow_ta_connect: |
| start_port_flags |= IPC_PORT_ALLOW_TA_CONNECT |
| if flags.allow_ns_connect: |
| start_port_flags |= IPC_PORT_ALLOW_NS_CONNECT |
| |
| return start_port_flags |
| |
| |
| ''' |
| Pack a given string with null padding to make its size |
| multiple of 4. |
| packed data includes length + string + null + padding |
| ''' |
| def pack_inline_string(value): |
| size = len(value) + 1 |
| pad_len = 3 - (size + 3) % 4 |
| packed = struct.pack("I", size) + value + '\0' + pad_len * '\0' |
| assert len(packed) % 4 == 0 |
| return packed |
| |
| |
| ''' |
| Creates Packed data from extracted manifest data |
| Writes the packed data to binary file |
| ''' |
| def pack_manifest_data(manifest, log): |
| # PACK { |
| # uuid, app_name_size, app_name, |
| # TRUSTY_APP_CONFIG_KEY_MIN_HEAP_SIZE, min_heap, |
| # TRUSTY_APP_CONFIG_KEY_MIN_STACK_SIZE, min_stack, |
| # TRUSTY_APP_CONFIG_KEY_MAP_MEM, id, addr, size, |
| # TRUSTY_APP_CONFIG_KEY_MAP_MEM, id, addr, size, |
| # TRUSTY_APP_CONFIG_KEY_MGMT_FLAGS, mgmt_flags |
| # TRUSTY_APP_CONFIG_KEY_START_PORT, flag, name_size, name |
| # } |
| out = cStringIO.StringIO() |
| |
| uuid = swap_uuid_bytes(manifest.uuid) |
| out.write(uuid) |
| |
| out.write(pack_inline_string(manifest.app_name)) |
| |
| if manifest.min_heap is not None: |
| out.write(struct.pack("II",TRUSTY_APP_CONFIG_KEY_MIN_HEAP_SIZE, |
| manifest.min_heap)) |
| |
| if manifest.min_stack is not None: |
| out.write(struct.pack("II", TRUSTY_APP_CONFIG_KEY_MIN_STACK_SIZE, |
| manifest.min_stack)) |
| |
| for memio_map in manifest.mem_io_maps: |
| out.write(struct.pack("IIQQI", |
| TRUSTY_APP_CONFIG_KEY_MAP_MEM, |
| memio_map.id, |
| memio_map.addr, |
| memio_map.size, |
| pack_mem_map_arch_mmu_flags(memio_map))) |
| |
| if manifest.mgmt_flags is not None: |
| out.write(struct.pack("II", |
| TRUSTY_APP_CONFIG_KEY_MGMT_FLAGS, |
| pack_mgmt_flags(manifest.mgmt_flags))) |
| |
| for port_entry in manifest.start_ports: |
| out.write(struct.pack("II", |
| TRUSTY_APP_CONFIG_KEY_START_PORT, |
| pack_start_port_flags( |
| port_entry.start_port_flags))) |
| out.write(pack_inline_string(port_entry.name)) |
| |
| return out.getvalue() |
| |
| |
| ''' |
| Creates manifest JSON string from packed manifest data |
| ''' |
| def unpack_binary_manifest_to_json(packed_data): |
| return manifest_data_to_json(unpack_binary_manifest_to_data(packed_data)) |
| |
| |
| def manifest_data_to_json(manifest): |
| return json.dumps(manifest, sort_keys=True, indent=4) |
| |
| |
| ''' |
| This method can be used for extracting manifest data from packed binary. |
| UUID should be present in packed data. |
| ''' |
| def unpack_binary_manifest_to_data(packed_data): |
| manifest = {} |
| |
| # Extract UUID |
| uuid, packed_data = packed_data[:16], packed_data[16:] |
| uuid = swap_uuid_bytes(uuid) |
| uuid = uuid.encode("hex") |
| uuid = uuid[:8] + "-" \ |
| + uuid[8:12] + "-" \ |
| + uuid[12:16] + "-" \ |
| + uuid[16:20] + "-" \ |
| + uuid[20:] |
| |
| manifest[UUID] = uuid |
| |
| # Extract APP_NAME |
| # read size of the name, this includes a null character |
| (name_size,), packed_data = struct.unpack( |
| "I", packed_data[:4]), packed_data[4:] |
| # read the name without a trailing null character |
| manifest[APP_NAME], packed_data = \ |
| packed_data[:name_size-1], packed_data[name_size-1:] |
| # discard trailing null characters |
| # it includes trailing null character of a string and null padding |
| pad_len = 1 + 3 - (name_size + 3) % 4 |
| packed_data = packed_data[pad_len:] |
| |
| # Extract remaining app configurations |
| while len(packed_data) > 0: |
| (tag,), packed_data = struct.unpack( |
| "I", packed_data[:4]), packed_data[4:] |
| |
| if tag == TRUSTY_APP_CONFIG_KEY_MIN_HEAP_SIZE: |
| assert MIN_HEAP not in manifest |
| (manifest[MIN_HEAP],), packed_data = struct.unpack( |
| "I", packed_data[:4]), packed_data[4:] |
| elif tag == TRUSTY_APP_CONFIG_KEY_MIN_STACK_SIZE: |
| assert MIN_STACK not in manifest |
| (manifest[MIN_STACK],), packed_data = struct.unpack( |
| "I", packed_data[:4]), packed_data[4:] |
| elif tag == TRUSTY_APP_CONFIG_KEY_MAP_MEM: |
| if MEM_MAP not in manifest: |
| manifest[MEM_MAP] = [] |
| mem_map_entry = {} |
| (id_,), packed_data = struct.unpack( |
| "I", packed_data[:4]), packed_data[4:] |
| (addr,), packed_data = struct.unpack( |
| "Q", packed_data[:8]), packed_data[8:] |
| (size,), packed_data = struct.unpack( |
| "Q", packed_data[:8]), packed_data[8:] |
| (arch_mmu_flags,), packed_data = struct.unpack( |
| "I", packed_data[:4]), packed_data[4:] |
| mem_map_entry[MEM_MAP_ID] = id_ |
| mem_map_entry[MEM_MAP_ADDR] = hex(addr) |
| mem_map_entry[MEM_MAP_SIZE] = hex(size) |
| mem_map_entry[MEM_MAP_TYPE] = { |
| ARCH_MMU_FLAG_CACHED: MEM_MAP_TYPE_CACHED, |
| ARCH_MMU_FLAG_UNCACHED: MEM_MAP_TYPE_UNCACHED, |
| ARCH_MMU_FLAG_UNCACHED_DEVICE: MEM_MAP_TYPE_UNCACHED_DEVICE, |
| }[arch_mmu_flags & ARCH_MMU_FLAG_CACHE_MASK] |
| mem_map_entry[MEM_MAP_NON_SECURE] = bool(arch_mmu_flags & |
| ARCH_MMU_FLAG_NS) |
| manifest[MEM_MAP].append(mem_map_entry) |
| elif tag == TRUSTY_APP_CONFIG_KEY_MGMT_FLAGS: |
| (flag,), packed_data = struct.unpack( |
| "I", packed_data[:4]), packed_data[4:] |
| mgmt_flag = { |
| MGMT_FLAG_RESTART_ON_EXIT: False, |
| MGMT_FLAG_DEFERRED_START: False, |
| MGMT_FLAG_NON_CRITICAL_APP: False |
| } |
| if flag & TRUSTY_APP_MGMT_FLAGS_RESTART_ON_EXIT: |
| mgmt_flag[MGMT_FLAG_RESTART_ON_EXIT] = True |
| if flag & TRUSTY_APP_MGMT_FLAGS_DEFERRED_START: |
| mgmt_flag[MGMT_FLAG_DEFERRED_START] = True |
| if flag & TRUSTY_APP_MGMT_FLAGS_NON_CRITICAL_APP: |
| mgmt_flag[MGMT_FLAG_NON_CRITICAL_APP] = True |
| manifest[MGMT_FLAGS] = mgmt_flag |
| elif tag == TRUSTY_APP_CONFIG_KEY_START_PORT: |
| if START_PORTS not in manifest: |
| manifest[START_PORTS] = [] |
| start_port_entry = {} |
| |
| (flag,), packed_data = struct.unpack( |
| "I", packed_data[:4]), packed_data[4:] |
| |
| # read size of the name, this includes a null character |
| (name_size,), packed_data = struct.unpack( |
| "I", packed_data[:4]), packed_data[4:] |
| # read the name without a trailing null character |
| start_port_entry[START_PORT_NAME], packed_data = \ |
| packed_data[:name_size-1], packed_data[name_size-1:] |
| # discard trailing null characters |
| # it includes trailing null character of a string and null padding |
| pad_len = 1 + 3 - (name_size + 3) % 4 |
| packed_data = packed_data[pad_len:] |
| |
| start_port_flags = { |
| START_PORT_ALLOW_TA_CONNECT: False, |
| START_PORT_ALLOW_NS_CONNECT: False |
| } |
| if flag & IPC_PORT_ALLOW_TA_CONNECT: |
| start_port_flags[START_PORT_ALLOW_TA_CONNECT] = True |
| if flag & IPC_PORT_ALLOW_NS_CONNECT: |
| start_port_flags[IPC_PORT_ALLOW_NS_CONNECT] = True |
| start_port_entry[START_PORT_FLAGS] = start_port_flags |
| |
| manifest[START_PORTS].append(start_port_entry) |
| else: |
| raise Exception("Unknown tag: {}".format(tag)) |
| |
| return manifest |
| |
| |
| def write_packed_data_to_bin_file(packed_data, output_file, log): |
| # Write packed data to binary file |
| try: |
| with open(output_file, "wb") as out_file: |
| out_file.write(packed_data) |
| out_file.close() |
| except IOError as ex: |
| log.error( |
| "Unable to write to output file: {}" |
| .format(output_file) + "\n" + str(ex)) |
| |
| |
| def read_json_config_file(input_file, log): |
| try: |
| read_file = open(input_file, "r") |
| except IOError as ex: |
| log.error( |
| "Unable to open input file: {}" |
| .format(input_file) + "\n" + str(ex)) |
| return None |
| |
| try: |
| manifest_dict = json.load(read_file) |
| return manifest_dict |
| except ValueError as ex: |
| log.error( |
| "Unable to parse config JSON - {}" |
| .format(str(ex))) |
| return None |
| |
| |
| def read_config_constants(const_config_files, log): |
| const_configs_list = [] |
| for const_file in const_config_files: |
| const_configs_list.append(read_json_config_file(const_file, log)) |
| |
| return const_configs_list |
| |
| |
| def define_integer_const_entry(const, log): |
| text = hex(const.value) if const.hex_num else str(const.value) |
| if const.unsigned: |
| text += "U" |
| |
| return "#define {} ({})\n".format(const.name, text) |
| |
| |
| def define_string_const_entry(const, log): |
| return "#define {} {}\n".format(const.name, json.dumps(const.value)) |
| |
| |
| def define_bool_const_entry(const, log): |
| return "#define {} ({})\n".format(const.name, json.dumps(const.value)) |
| |
| |
| def define_uuid_const_entry(const, log): |
| uuid = const.value.encode("hex") |
| |
| part = ", ".join( |
| ["0x" + uuid[index:index+2] for index in range(16, len(uuid), 2)]) |
| |
| value = "{{0x{}, 0x{}, 0x{}, {{ {} }}}}\n".format( |
| uuid[:8], uuid[8:12], uuid[12:16], part) |
| |
| return "#define {} {}".format(const.name, value) |
| |
| |
| def create_header_entry(constant, log): |
| if constant.type == CONST_PORT: |
| return define_string_const_entry(constant, log) |
| elif constant.type == CONST_UUID: |
| return define_uuid_const_entry(constant, log) |
| elif constant.type == CONST_INT: |
| return define_integer_const_entry(constant, log) |
| elif constant.type == CONST_BOOL: |
| return define_bool_const_entry(constant, log) |
| else: |
| raise Exception("Unknown tag: {}".format(const_type)) |
| |
| |
| ''' |
| Writes given constants to header file in given header directory. |
| ''' |
| def write_consts_to_header_file(const_config, header_dir, log): |
| # Construct header file path |
| header_file = os.path.join(header_dir, const_config.header) |
| # Check whether the output directory of header file exist |
| # If it not exists create it. |
| dir_name = os.path.dirname(header_file) |
| if dir_name and not os.path.exists(dir_name): |
| os.makedirs(dir_name) |
| |
| try: |
| with open(header_file, "w") as out_file: |
| out_file.write("#pragma once\n") |
| out_file.write("#include <stdbool.h>\n\n") |
| for const in const_config.constants: |
| header_entries = create_header_entry(const, log) |
| out_file.write(header_entries) |
| except IOError as ex: |
| log.error( |
| "Unable to write to header file: {}" |
| .format(header_file) + "\n" + str(ex)) |
| |
| |
| ''' |
| Parse a give JSON constant data structure |
| ''' |
| def parse_constant(constant, log): |
| const_type = get_string(constant, CONST_TYPE, {}, log) |
| if const_type is None: |
| return None |
| |
| name = get_string(constant, CONST_NAME, {}, log) |
| if const_type == CONST_PORT: |
| value = get_string(constant, CONST_VALUE, {}, log) |
| return Constant(name, value, const_type) |
| elif const_type == CONST_UUID: |
| value = get_string(constant, CONST_VALUE, {}, log) |
| return Constant(name, parse_uuid(value, log), const_type) |
| elif const_type == CONST_INT: |
| unsigned = get_boolean(constant, CONST_UNSIGNED, {}, log) |
| text_value = constant.get(CONST_VALUE) |
| hex_num = isinstance(text_value, basestring) and \ |
| text_value.startswith("0x") |
| value = get_int(constant, CONST_VALUE, {}, log) |
| return Constant(name, value, const_type, unsigned, hex_num) |
| elif const_type == CONST_BOOL: |
| value = get_boolean(constant, CONST_VALUE, {}, log) |
| return Constant(name, value, const_type) |
| else: |
| log.error("Unknown constant type: {}".format(const_type)) |
| |
| |
| ''' |
| Parse a given JSON constant-config data structure containing |
| a header and list of constants |
| ''' |
| def parse_config_constant(const_config, log): |
| header_file = get_string(const_config, HEADER, {}, log) |
| |
| const_list = get_list(const_config, CONSTANTS, log, optional=False, |
| default=[]) |
| |
| constants = [] |
| for item in const_list: |
| item = coerce_to_dict(item, CONSTANTS, log) |
| if item is None: |
| continue |
| constants.append(parse_constant(item, log)) |
| if item: |
| log.error("Unknown atributes in constant: {} " |
| .format(item)) |
| |
| if const_config: |
| log.error("Unknown atributes in constants config: {} " |
| .format(const_config)) |
| |
| return ConfigConstants(constants, header_file) |
| |
| |
| ''' |
| Collects ConfigConstant(s) from list of JSON config constants data |
| ''' |
| def extract_config_constants(config_consts_list, log): |
| config_constants = [] |
| |
| for config_const in config_consts_list: |
| config_constants.append(parse_config_constant(config_const, log)) |
| |
| return config_constants |
| |
| |
| ''' |
| Parse JSON config constants and creates separate header files with constants |
| for each JSON config |
| ''' |
| def process_config_constants(const_config_files, header_dir, log): |
| if const_config_files is None: |
| return [] |
| |
| config_consts_list = read_config_constants(const_config_files, log) |
| if log.error_occurred(): |
| return [] |
| |
| config_constants = extract_config_constants(config_consts_list, log) |
| if log.error_occurred(): |
| return [] |
| |
| # generate header files |
| for const_config in config_constants: |
| write_consts_to_header_file(const_config, header_dir, log) |
| |
| return config_constants |
| |
| |
| def index_constants(config_constants, log): |
| constants = {} |
| for const_config in config_constants: |
| for const in const_config.constants: |
| constants[const.name] = const |
| |
| return constants |
| |
| |
| ''' |
| START OF THE PROGRAM |
| Handles the command line arguments |
| Parses the given manifest input file and creates packed data |
| Writes the packed data to binary output file. |
| ''' |
| def main(argv): |
| parser = argparse.ArgumentParser(); |
| parser.add_argument( |
| "-i", "--input", |
| dest="input_filename", |
| required=True, |
| type=str, |
| help="It should be trust app manifest config JSON file" |
| ) |
| parser.add_argument( |
| "-o", "--output", |
| dest="output_filename", |
| required=True, |
| type=str, |
| help="It will be binary file with packed manifest data" |
| ) |
| parser.add_argument( |
| "-c", "--constants", |
| dest="constants", |
| required=False, |
| action="append", |
| help="JSON file with manifest config constants" |
| ) |
| parser.add_argument( |
| "--header-dir", |
| dest="header_dir", |
| required=False, |
| type=str, |
| help="Directory path for generating headers" |
| ) |
| # Parse the command line arguments |
| args = parser.parse_args() |
| if args.constants and not args.header_dir: |
| args.error("--header-dir is required if --constants are specified") |
| |
| log = Log() |
| |
| # collect config constants and create header files for each const config |
| config_constants = process_config_constants(args.constants, |
| args.header_dir, log) |
| if log.error_occurred(): |
| return 1 |
| constants = index_constants(config_constants, log) |
| |
| if not os.path.exists(args.input_filename): |
| log.error( |
| "Manifest config JSON file doesn't exist: {}" |
| .format(args.input_filename)) |
| return 1 |
| |
| manifest_dict = read_json_config_file(args.input_filename, log) |
| if log.error_occurred(): |
| return 1 |
| |
| # By default app directory name will be used as app-name |
| default_app_name = os.path.basename(os.path.dirname(args.input_filename)) |
| |
| # parse the manifest config |
| manifest = parse_manifest_config(manifest_dict, constants, default_app_name, |
| log) |
| if log.error_occurred(): |
| return 1 |
| |
| # Pack the data as per c structures |
| packed_data = pack_manifest_data(manifest, log) |
| if log.error_occurred(): |
| return 1 |
| |
| # Write to file. |
| write_packed_data_to_bin_file(packed_data, args.output_filename, log) |
| if log.error_occurred(): |
| return 1 |
| |
| return 0 |
| |
| |
| if __name__ == "__main__": |
| sys.exit(main(sys.argv[1:])) |