blob: e4468a5018c0c9152c3863b357feb898d946a0b1 [file] [log] [blame]
#!/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:]))