diff --git a/tools/tfm/__init__.py b/tools/tfm/__init__.py new file mode 100644 index 0000000000..5305b8db92 --- /dev/null +++ b/tools/tfm/__init__.py @@ -0,0 +1,23 @@ +# Copyright (c) 2017-2018 ARM Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .generate_partition_code import \ + generate_partitions_sources, generate_psa_setup + +__all__ = [ + 'generate_partitions_sources', + 'generate_psa_setup', +] diff --git a/tools/tfm/generate_partition_code.py b/tools/tfm/generate_partition_code.py new file mode 100644 index 0000000000..1f92841a2c --- /dev/null +++ b/tools/tfm/generate_partition_code.py @@ -0,0 +1,689 @@ +#!/usr/bin/python +# Copyright (c) 2017-2018 ARM Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import fnmatch +import itertools +import json +import os +from os.path import join as path_join +from six import integer_types, string_types + +from jinja2 import Environment, FileSystemLoader, StrictUndefined +from jsonschema import validate + +__version__ = '1.0' +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +TEMPLATES_LIST_FILE = path_join(SCRIPT_DIR, 'tfm_generated_file_list.json') +MANIFEST_FILE_PATTERN = '*_psa.json' +MBED_OS_ROOT = os.path.abspath(path_join(SCRIPT_DIR, os.pardir, os.pardir)) +SERVICES_DIR = os.path.join(MBED_OS_ROOT, "components", "TARGET_PSA", "services") + + +def assert_int(num): + """ + Tries to parse an integer num from a given string + + :param num: Number in int/string type + :return: Numeric value + """ + if isinstance(num, int): + return num + num_str = str(num) + radix = 16 if num_str.lower().startswith('0x') else 10 + res = int(num_str, radix) + # Python converts str to int as a signed integer + if res > 0x7FFFFFFF: + res -= 0x100000000 + return res + + +class RotService(object): + MINOR_POLICIES = ['STRICT', 'RELAXED'] + + def __init__( + self, + name, + identifier, + signal, + non_secure_clients, + minor_version=1, + minor_policy='STRICT' + ): + """ + Root of Trust Service C'tor (Aligned with json schema) + + :param name: Root of Trust Service identifier (available to user) + :param identifier: Root of Trust Service numeric enumeration. + :param signal: Root of Trust Service identifier inside the partition + :param non_secure_clients: True to allow connections from non-secure + partitions + :param minor_version: Root of Trust Service version + :param minor_policy: Enforcement level of minor version + """ + self.name = name + self.id = identifier + self.signal = signal + + assert assert_int(identifier) + + assert isinstance(non_secure_clients, bool), \ + 'non_secure_clients parameter must be of boolean type' + self.nspe_callable = non_secure_clients + + self.minor_version = assert_int(minor_version) + assert self.minor_version > 0, 'minor_version parameter is invalid' + + assert minor_policy in self.MINOR_POLICIES, \ + 'minor_policy parameter is invalid' + self.minor_policy = minor_policy + + @property + def numeric_id(self): + return assert_int(self.id) + + def __eq__(self, other): + return ( + (self.name == other.name) and + (self.id == other.id) and + (self.signal == other.signal) and + (self.nspe_callable == other.nspe_callable) and + (self.minor_version == other.minor_version) and + (self.minor_policy == other.minor_policy) + ) + + +class MmioRegion(object): + MMIO_PERMISSIONS = { + 'READ-ONLY': 'PSA_MMIO_PERM_READ_ONLY', + 'READ-WRITE': 'PSA_MMIO_PERM_READ_WRITE' + } + + def __init__(self, **kwargs): + """ + MMIO Region C'tor (Aligned with json schema) + + Supports both named and numeric regions + In case of named region the acceptable params are name and permission + In case of numeric region the acceptable params are name, size and + permission + + :param name: C definition name of the region (size will be + auto-generated) + :param base: C hex string defining a memory address (must be 32bit) + :param size: size of a region (Applicable only for numbered regions) + :param permission: Access permissions to the described region (R/RW) + + """ + assert 'permission' in kwargs + self.permission = self.MMIO_PERMISSIONS[kwargs['permission']] + if 'name' in kwargs: + self.base = kwargs['name'] + self.size = '(sizeof(*({})))'.format(kwargs['name']) + if 'base' in kwargs: + self.base = kwargs['base'] + self.size = assert_int(kwargs['size']) + + assert 'partition_id' in kwargs + self.partition_id = assert_int(kwargs['partition_id']) + + assert hasattr(self, 'base') + assert hasattr(self, 'size') + assert hasattr(self, 'permission') + assert hasattr(self, 'partition_id') + + def __eq__(self, other): + return ( + (self.base == other.base) and + (self.size == other.size) and + (self.permission == other.permission) + ) + + +class Irq(object): + def __init__(self, line_num, signal): + """ + IRQ line C'tor (Aligned with json schema) + + :param line_num: number of interrupt used by the partition + :param signal: IRQ line identifier inside the partition + """ + self.line_num = assert_int(line_num) + assert isinstance(signal, string_types) + self.signal = signal + + def __eq__(self, other): + return (self.line_num == other.line_num) and \ + (self.signal == other.signal) + + +class Manifest(object): + PRIORITY = ['LOW', 'NORMAL', 'HIGH'] + PARTITION_TYPES = ['APPLICATION-ROT', 'PSA-ROT'] + # The following signal bits cannot be used: + # bit[0-2] | Reserved + # bit[3] | PSA Doorbell + # bit[31] | RTX error bit + RESERVED_SIGNALS = 5 + + def __init__( + self, + manifest_file, + name, + partition_id, + partition_type, + priority, + entry_point, + heap_size, + stack_size, + source_files, + mmio_regions=None, + rot_services=None, + extern_sids=None, + irqs=None + ): + """ + Manifest C'tor (Aligned with json schema) + + :param manifest_file: Path to json manifest + :param name: Partition unique name + :param partition_id: Partition identifier + :param partition_type: Whether the partition is unprivileged or part + of the trusted computing base + :param priority: Priority of the partition's thread + :param entry_point: C symbol name of the partition's main function + :param heap_size: Size of heap required for the partition + :param stack_size: Size of stack required for the partition + :param source_files: List of files assembling the partition + (relative paths) + :param mmio_regions: List of MMIO regions used by the partition + :param rot_services: List of Root of Trust Services declared by the + partition + :param extern_sids: List of Root of Trust Services the partition can call + :param irqs: List of interrupts the partition can handle + """ + assert manifest_file is not None + assert name is not None + assert partition_id is not None + assert partition_type is not None + assert entry_point is not None + assert priority is not None + assert heap_size is not None + assert stack_size is not None + assert source_files is not None + + mmio_regions = [] if mmio_regions is None else mmio_regions + rot_services = [] if rot_services is None else rot_services + extern_sids = [] if extern_sids is None else extern_sids + irqs = [] if irqs is None else irqs + + assert os.path.isfile(manifest_file) + assert isinstance(partition_id, integer_types) + assert isinstance(heap_size, int) + assert isinstance(stack_size, int) + assert isinstance(entry_point, string_types) + assert partition_type in self.PARTITION_TYPES + assert priority in self.PRIORITY + assert partition_id > 0 + + self.file = manifest_file + self.name = name + self.id = partition_id + self.type = partition_type + self.priority = priority + self.heap_size = heap_size + self.stack_size = stack_size + self.entry_point = entry_point + if isinstance(source_files, list): + self.source_files = source_files + else: + self.source_files = [source_files] + + self.mmio_regions = mmio_regions + self.rot_services = rot_services + self.extern_sids = extern_sids + self.irqs = irqs + + for src_file in self.source_files: + assert os.path.isfile(src_file), \ + "The source file {} mentioned in {} doesn't exist.".format( + src_file, self.file + ) + + for rot_srv in self.rot_services: + assert isinstance(rot_srv, RotService) + + for extern_sid in self.extern_sids: + assert isinstance(extern_sid, string_types) + + assert len(self.extern_sids) == len(set(self.extern_sids)), \ + 'Detected duplicates external SIDs in {}'.format(self.file) + + for irq in self.irqs: + assert isinstance(irq, Irq) + + total_signals = len(self.rot_services) + len(self.irqs) + assert total_signals <= 32 - self.RESERVED_SIGNALS, \ + 'Manifest {} - {} exceeds limit of RoT services and IRQs allowed ' \ + '({}).'.format( + self.name, self.file, 32 - self.RESERVED_SIGNALS + ) + + def __eq__(self, other): + return ( + (self.file == other.file) and + (self.name == other.name) and + (self.id == other.id) and + (self.type == other.type) and + (self.priority == other.priority) and + (self.heap_size == other.heap_size) and + (self.stack_size == other.stack_size) and + (self.entry_point == other.entry_point) and + (self.source_files == other.source_files) and + (self.mmio_regions == other.mmio_regions) and + (self.rot_services == other.rot_services) and + (self.extern_sids == other.extern_sids) and + (self.irqs == other.irqs) + ) + + @classmethod + def from_json(cls, manifest_file, skip_src=False): + """ + Load a partition manifest file + + :param manifest_file: Manifest file path + :param skip_src: Ignore the `source_files` entry + :return: Manifest object + """ + + partition_schema_path = path_join( + SCRIPT_DIR, + 'partition_description_schema.json' + ) + with open(partition_schema_path) as schema_fh: + partition_schema = json.load(schema_fh) + + # Load partition manifest file. + with open(manifest_file) as fh: + manifest = json.load(fh) + + validate(manifest, partition_schema) + manifest_dir = os.path.dirname(manifest_file) + + source_files = [] + if not skip_src: + for src_file in manifest['source_files']: + source_files.append( + os.path.normpath(path_join(manifest_dir, src_file))) + + mmio_regions = [] + for mmio_region in manifest.get('mmio_regions', []): + mmio_regions.append( + MmioRegion(partition_id=manifest['id'], **mmio_region)) + + rot_services = [] + for rot_srv in manifest.get('services', []): + rot_services.append(RotService(**rot_srv)) + + irqs = [] + for irq in manifest.get('irqs', []): + irqs.append(Irq(**irq)) + + return Manifest( + manifest_file=manifest_file, + name=manifest['name'], + partition_id=assert_int(manifest['id']), + partition_type=manifest['type'], + priority=manifest['priority'], + heap_size=assert_int(manifest['heap_size']), + stack_size=assert_int(manifest['stack_size']), + entry_point=manifest['entry_point'], + source_files=source_files, + mmio_regions=mmio_regions, + rot_services=rot_services, + extern_sids=manifest.get('extern_sids', []), + irqs=irqs + ) + + @property + def sids(self): + return [rot_srv.name for rot_srv in self.rot_services] + + @property + def autogen_folder(self): + return os.path.abspath(os.path.dirname(self.file)) + + def find_dependencies(self, manifests): + """ + Find other manifests which holds Root of Trust Services that + are declared as extern in this manifest + + :param manifests: list of manifests to filter + :return: list of manifest's names that holds current + extern Root of Trust Services + """ + + manifests = [man for man in manifests if man != self] + extern_sids_set = set(self.extern_sids) + return [manifest.name for manifest in manifests + if extern_sids_set.intersection(set(manifest.sids))] + + def templates_to_files(self, templates, templates_base, output_dir): + """ + Translates a list of partition templates to file names + + :param templates: List of partition templates + :param output_dir: Output directory (Default is autogen folder property) + :return: Dictionary of template to output file translation + """ + + generated_files = {} + for t in templates: + fname = os.path.relpath(t, templates_base) + _tpl = fname.replace('NAME', self.name.lower()) + full_path = path_join( + output_dir, + os.path.splitext(_tpl)[0] + ) + generated_files[t] = full_path + + return generated_files + + +def check_circular_call_dependencies(manifests): + """ + Check if there is a circular dependency between the partitions + described by the manifests. + A circular dependency might happen if there is a scenario in which a + partition calls a Root of Trust Service in another partition which than + calls another Root of Trust Service which resides in the + originating partition. + For example: Partition A has a Root of Trust Service A1 and extern sid B1, + partition B has a Root of Trust Service B1 and extern sid A1. + + :param manifests: List of the partition manifests. + :return: True if a circular dependency exists, false otherwise. + """ + + # Construct a call graph. + call_graph = {} + for manifest in manifests: + call_graph[manifest.name] = { + 'calls': manifest.find_dependencies(manifests), + 'called_by': set() + } + for manifest_name in call_graph: + for called in call_graph[manifest_name]['calls']: + call_graph[called]['called_by'].add(manifest_name) + + # Run topological sort on the call graph. + while len(call_graph) > 0: + # Find all the nodes that aren't called by anyone and + # therefore can be removed. + nodes_to_remove = [x for x in list(call_graph.keys()) if + len(call_graph[x]['called_by']) == 0] + + # If no node can be removed we have a circle. + if not nodes_to_remove: + return True + + # Remove the nodes. + for node in nodes_to_remove: + for called in call_graph[node]['calls']: + call_graph[called]['called_by'].remove(node) + call_graph.pop(node) + + return False + + +def validate_partition_manifests(manifests): + """ + Check the correctness of the manifests list + (no conflicts, no missing elements, etc.) + + :param manifests: List of the partition manifests + """ + for manifest in manifests: + assert isinstance(manifest, Manifest) + + partitions_names = {} + partitions_ids = {} + rot_service_ids = {} + rot_service_names = {} + rot_service_signals = {} + irq_signals = {} + irq_numbers = {} + all_extern_sids = set() + spe_contained_manifests = [] + + for manifest in manifests: + # Make sure the partition names are unique. + if manifest.name in partitions_names: + raise ValueError( + 'Partition name {} is not unique, ' + 'found in both {} and {}.'.format( + manifest.name, + partitions_names[manifest.name], + manifest.file + ) + ) + partitions_names[manifest.name] = manifest.file + + # Make sure the partition ID's are unique. + if manifest.id in partitions_ids: + raise ValueError( + 'Partition id {} is not unique, ' + 'found in both {} and {}.'.format( + manifest.id, + partitions_ids[manifest.id], + manifest.file + ) + ) + partitions_ids[manifest.id] = manifest.file + + is_nspe_callabale = False + + # Make sure all the Root of Trust Service IDs and signals are unique. + for rot_service in manifest.rot_services: + if rot_service.name in rot_service_names: + raise ValueError( + 'Root of Trust Service name {} is found ' + 'in both {} and {}.'.format( + rot_service.name, + rot_service_names[rot_service.name], + manifest.file + ) + ) + rot_service_names[rot_service.name] = manifest.file + + if rot_service.signal in rot_service_signals: + raise ValueError( + 'Root of Trust Service signal {} is found ' + 'in both {} and {}.'.format( + rot_service.signal, + rot_service_signals[rot_service.signal], + manifest.file + ) + ) + rot_service_signals[rot_service.signal] = manifest.file + + if rot_service.numeric_id in rot_service_ids: + raise ValueError( + 'Root of Trust Service identifier {} is found ' + 'in both {} and {}.'.format( + rot_service.numeric_id, + rot_service_ids[rot_service.numeric_id], + manifest.file + ) + ) + rot_service_ids[rot_service.numeric_id] = manifest.file + is_nspe_callabale |= rot_service.nspe_callable + + if not is_nspe_callabale: + spe_contained_manifests.append(manifest) + + # Make sure all the IRQ signals and line-numbers are unique. + for irq in manifest.irqs: + if irq.signal in irq_signals: + raise ValueError( + 'IRQ signal {} is found in both {} and {}.'.format( + irq.signal, + irq_signals[irq.signal], + manifest.file + ) + ) + irq_signals[irq.signal] = manifest.file + + if irq.line_num in irq_numbers: + raise ValueError( + 'IRQ line number {} is found in both {} and {}.'.format( + irq.line_num, + irq_numbers[irq.line_num], + manifest.file + ) + ) + irq_numbers[irq.line_num] = manifest.file + + all_extern_sids.update(manifest.extern_sids) + + # Check that all the external SIDs can be found. + declared_sids = set(rot_service_names.keys()) + for manifest in manifests: + extern_sids = set(manifest.extern_sids) + if not extern_sids.issubset(declared_sids): + missing_sids = extern_sids.difference(declared_sids) + raise ValueError( + "External SID(s) {} required by {} can't be found in " + "any partition manifest.".format( + ', '.join(missing_sids), manifest.file) + ) + + if check_circular_call_dependencies(manifests): + raise ValueError( + "Detected a circular call dependency between the partitions.") + + for manifest in spe_contained_manifests: + rot_services = set([service.name for service in manifest.rot_services]) + if not rot_services.intersection(all_extern_sids) and len( + manifest.irqs) == 0: + raise ValueError( + 'Partition {} (defined by {}) is not accessible from NSPE ' + 'and not referenced by any other partition.'.format( + manifest.name, + manifest.file + ) + ) + + +def generate_source_files( + templates_dict, + render_args, + extra_filters=None +): + """ + Generate SPM common C code from manifests using given templates + + :param templates: Dictionary of template and their auto-generated products + :param render_args: Dictionary of arguments that should be passed to render + :param extra_filters: Dictionary of extra filters to use in the rendering + process + """ + + # Load templates for the code generation. + env = Environment( + loader=FileSystemLoader(MBED_OS_ROOT), + lstrip_blocks=True, + trim_blocks=True, + undefined=StrictUndefined + ) + + if extra_filters: + env.filters.update(extra_filters) + + for tpl in templates_dict: + template = env.get_template(tpl['template']) + data = template.render(**render_args) + output_path = os.path.join(MBED_OS_ROOT, tpl['output']) + output_folder = os.path.dirname(output_path) + + if not os.path.exists(output_folder): + os.makedirs(output_folder) + + with open(output_path, 'wt') as fh: + fh.write(data) + + +def generate_tfm_setup(manifest_files, extra_filters=None): + """ + Process all the given manifest files and generate C setup code from them + :param manifest_files: List of manifest files + :param output_dir: Output directory for the generated files + :param weak_setup: Is the functions/data in the setup file weak + (can be overridden by another setup file) + :param extra_filters: Dictionary of extra filters to use in the rendering + process + :return: path to the setup generated files + """ + + # Construct lists of all the manifests and mmio_regions. + region_list = [] + manifests = [] + for manifest_file in manifest_files: + manifest_obj = Manifest.from_json(manifest_file) + manifests.append(manifest_obj) + for region in manifest_obj.mmio_regions: + region_list.append(region) + + # Validate the correctness of the manifest collection. + validate_partition_manifests(manifests) + + with open(TEMPLATES_LIST_FILE, 'r') as fh: + templates_data = json.load(fh) + + render_args = { + 'partitions': manifests, + } + + return generate_source_files( + templates_data, + render_args, + extra_filters=extra_filters + ) + + +def manifests_discovery(root_dir): + manifest_files = set() + + for root, dirs, files in os.walk(root_dir): + to_add = [path_join(root, f) for f in + fnmatch.filter(files, MANIFEST_FILE_PATTERN) if + 'TARGET_IGNORE' not in root] + manifest_files.update(to_add) + + return list(manifest_files) + + +def generate_tfm_code(): + # Find all manifest files in the mbed-os tree + manifest_files = manifests_discovery(SERVICES_DIR) + + # Generate default system psa setup file (only system partitions) + generate_tfm_setup(manifest_files) + + + +if __name__ == '__main__': + generate_tfm_code() diff --git a/tools/tfm/partition_description_schema.json b/tools/tfm/partition_description_schema.json new file mode 100644 index 0000000000..1089c8dca6 --- /dev/null +++ b/tools/tfm/partition_description_schema.json @@ -0,0 +1,196 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "schema for a partition description.", + "type": "object", + "required": ["name", "type", "priority", "id", "entry_point", "stack_size", "heap_size", "source_files"], + "anyOf": [ + {"required" : ["services"]}, + {"required" : ["irqs"]} + ], + "properties": { + "name": { + "description": "Alphanumeric C macro for referring to a partition. (all capital)", + "$ref": "#/definitions/c_macro" + }, + "type": { + "description": "Whether the partition is unprivileged or part of the trusted computing base.", + "enum": ["APPLICATION-ROT", "PSA-ROT"] + }, + "priority": { + "description": "Partition task priority.", + "enum": ["LOW", "NORMAL", "HIGH"] + }, + "id": { + "description": "Partition numeric unique positive identifier. (must be a positive 8 bytes hex string)", + "type": "string", + "pattern": "^0x[0-7][0-9a-fA-F]{7}$" + }, + "entry_point": { + "description": "C symbol name of the partition's entry point. (unmangled, use extern C if needed)", + "$ref": "#/definitions/c_symbol" + }, + "stack_size": { + "description": "Partition's task stack size in bytes.", + "$ref": "#/definitions/positive_integer_or_hex_string" + }, + "heap_size": { + "description": "Partition's task heap size in bytes.", + "$ref": "#/definitions/positive_integer_or_hex_string" + }, + "mmio_regions": { + "description": "List of Memory-Mapped IO region objects which the partition has access to.", + "type": "array", + "items": { + "anyOf": [{ + "$ref": "#/definitions/named_region" + }, + { + "$ref": "#/definitions/numbered_region" + } + ] + }, + "uniqueItems": true + }, + "services": { + "description": "List of RoT Service objects which the partition implements.", + "type": "array", + "items": { + "$ref": "#/definitions/service" + }, + "uniqueItems": true + }, + "extern_sids": { + "description": "List of SID which the partition code depends on and allowed to access.", + "type": "array", + "items": { + "$ref": "#/definitions/c_macro" + }, + "uniqueItems": true + }, + "source_files": { + "description": "List of source files relative to PSA Manifest file. A Secure Partition is built from explicit file list.", + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-zA-Z0-9-_./]+$" + }, + "minItems": 1, + "uniqueItems": true + }, + "irqs": { + "description": "List of IRQ objects which the partition implements.", + "type": "array", + "items": { + "$ref": "#/definitions/irq" + }, + "uniqueItems": true + } + }, + "definitions": { + "c_macro": { + "type": "string", + "pattern": "^[A-Z_][A-Z0-9_]*$" + }, + "c_symbol": { + "type": "string", + "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$" + }, + "hex_string": { + "type": "string", + "pattern": "^0x(0*[1-9a-fA-F][0-9a-fA-F]*)$", + "minLength": 3, + "maxLength": 10 + }, + "positive_integer": { + "type": "integer", + "exclusiveMinimum": true, + "minimum": 0 + }, + "positive_integer_or_hex_string": { + "oneOf": [{ + "$ref": "#/definitions/positive_integer" + }, + { + "$ref": "#/definitions/hex_string" + } + ] + }, + "named_region": { + "description": "MMIO region which is described by it's C macro name and access permissions.", + "required": ["name", "permission"], + "properties": { + "name": { + "description": "Alphanumeric C macro for referring to the region.", + "$ref": "#/definitions/c_macro" + }, + "permission": { + "description": "Access permissions for the region.", + "enum": ["READ-ONLY", "READ-WRITE"] + } + } + }, + "numbered_region": { + "description": "MMIO region which is described by it's base address, size and access permissions.", + "required": ["base", "size", "permission"], + "properties": { + "base": { + "description": "The base address of the region.", + "$ref": "#/definitions/hex_string" + }, + "size": { + "description": "Size in bytes of the region.", + "$ref": "#/definitions/positive_integer_or_hex_string" + }, + "permission": { + "description": "Access permissions for the region.", + "enum": ["READ-ONLY", "READ-WRITE"] + } + } + }, + "service": { + "required": ["name", "identifier", "non_secure_clients", "signal"], + "properties": { + "name": { + "description": "Alphanumeric C macro for referring to a RoT Service from source code (all capital)", + "$ref": "#/definitions/c_macro" + }, + "identifier": { + "description": "The integer value of the NAME field", + "$ref": "#/definitions/positive_integer_or_hex_string" + }, + "non_secure_clients": { + "description": "Denote whether the RoT Service is exposed to non-secure clients.", + "type": "boolean" + }, + "signal": { + "description": "Alphanumeric C macro for referring to the RoT Service's signal value. (all capital)", + "$ref": "#/definitions/c_macro" + }, + "minor_version": { + "description": "Optional: Minor version number of the RoT Service's interface.", + "$ref": "#/definitions/positive_integer", + "default": 1 + }, + "minor_policy": { + "description": "Optional: Minor version policy to apply on connections to the RoT Service.", + "enum": ["STRICT", "RELAXED"], + "default": "STRICT" + } + } + }, + "irq": { + "required": ["line_num", "signal"], + "properties": { + "line_num": { + "description": "Interrupt line number for registering to ISR table entry and enable/disable the specific IRQ once received.", + "type": "integer", + "minimum": 0 + }, + "signal": { + "description": "Alphanumeric C macro for referring to the IRQ's signal value. (all capital)", + "$ref": "#/definitions/c_macro" + } + } + } + } +} diff --git a/tools/tfm/templates/tfm_partition_defs.inc.tpl b/tools/tfm/templates/tfm_partition_defs.inc.tpl new file mode 100644 index 0000000000..c72345dfaa --- /dev/null +++ b/tools/tfm/templates/tfm_partition_defs.inc.tpl @@ -0,0 +1,32 @@ +/* Copyright (c) 2017-2019 ARM Limited + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*********** WARNING: This is an auto-generated file. Do not edit! ***********/ + +#ifndef __TFM_PARTITION_DEFS_INC__ +#define __TFM_PARTITION_DEFS_INC__ + +{% for partition in partitions %} +{% set partition_loop = loop %} +#ifdef TFM_PSA_API +#define {{partition.name|upper}}_ID (TFM_SP_BASE + {{ partition_loop.index0 }}) +#endif + +{% endfor %} +#define TFM_MAX_USER_PARTITIONS ({{partitions|count}}) + +#endif /* __TFM_PARTITION_DEFS_INC__ */ diff --git a/tools/tfm/templates/tfm_partition_list.inc.tpl b/tools/tfm/templates/tfm_partition_list.inc.tpl new file mode 100644 index 0000000000..ce3d39fa0e --- /dev/null +++ b/tools/tfm/templates/tfm_partition_list.inc.tpl @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2019, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +/*********** WARNING: This is an auto-generated file. Do not edit! ***********/ + +#ifndef __TFM_PARTITION_LIST_INC__ +#define __TFM_PARTITION_LIST_INC__ + +{% for partition in partitions %} +#ifdef TFM_PSA_API +/******** {{partition.name|upper}} ********/ +PARTITION_DECLARE({{partition.name|upper}}, 0 + | SPM_PART_FLAG_IPC + , "{{partition.type}}", {{partition.id}}, {{partition.priority}}, {{partition.stack_size}}); +PARTITION_ADD_INIT_FUNC({{partition.name|upper}}, {{partition.entry_point}}); +#endif /* TFM_PSA_API */ + +{% endfor %} +#endif /* __TFM_PARTITION_LIST_INC__ */ diff --git a/tools/tfm/templates/tfm_service_list.inc.tpl b/tools/tfm/templates/tfm_service_list.inc.tpl new file mode 100644 index 0000000000..cef215943e --- /dev/null +++ b/tools/tfm/templates/tfm_service_list.inc.tpl @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018-2019, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +/*********** WARNING: This is an auto-generated file. Do not edit! ***********/ + +#ifndef __TFM_SERVICE_LIST_INC__ +#define __TFM_SERVICE_LIST_INC__ + +{% for partition in partitions %} +#ifdef TFM_PSA_API +/******** {{partition.name|upper}} ********/ +{% for rot_srv in partition.rot_services %} +{"{{rot_srv.name|upper}}", {{partition.name|upper}}_ID, {{rot_srv.signal|upper}}, {{rot_srv.id}}, {% if rot_srv.nspe_callable %}true{% else %}false{% endif %}, {{rot_srv.minor_version}}, TFM_VERSION_POLICY_{{rot_srv.minor_policy|upper}}}, +{% endfor %} +#endif /* TFM_PSA_API */ + +{% endfor %} +#endif /* __TFM_SERVICE_LIST_INC__ */ diff --git a/tools/tfm/templates/tfm_spm_signal_defs.h.tpl b/tools/tfm/templates/tfm_spm_signal_defs.h.tpl new file mode 100644 index 0000000000..c554a026f1 --- /dev/null +++ b/tools/tfm/templates/tfm_spm_signal_defs.h.tpl @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2018, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#ifndef __TFM_SPM_SIGNAL_DEFS_H__ +#define __TFM_SPM_SIGNAL_DEFS_H__ + +{% for partition in partitions %} +{% for rot_srv in partition.rot_services %} +#define {{rot_srv.signal|upper}}_POS ({{loop.index + 3}}UL) +#define {{rot_srv.signal|upper}} (1UL << {{rot_srv.signal|upper}}_POS) +{% endfor %} + +{% endfor %} + +#endif diff --git a/tools/tfm/tfm_generated_file_list.json b/tools/tfm/tfm_generated_file_list.json new file mode 100644 index 0000000000..ef69fc064a --- /dev/null +++ b/tools/tfm/tfm_generated_file_list.json @@ -0,0 +1,22 @@ +[ + { + "name": "Secure Partition ID definitions", + "template": "tools/tfm/templates/tfm_partition_defs.inc.tpl", + "output": "components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/autogen/tfm_partition_defs.inc" + }, + { + "name": "Secure Partition declarations", + "template": "tools/tfm/templates/tfm_partition_list.inc.tpl", + "output": "components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/autogen/tfm_partition_list.inc" + }, + { + "name": "Secure Service list", + "template": "tools/tfm/templates/tfm_service_list.inc.tpl", + "output": "components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/autogen/tfm_service_list.inc" + }, + { + "name": "Secure Service siganls list", + "template": "tools/tfm/templates/tfm_spm_signal_defs.h.tpl", + "output": "components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/autogen/tfm_spm_signal_defs.h" + } +]