mirror of https://github.com/ARMmbed/mbed-os.git
				
				
				
			Refactor PSA code generators
* Unify PSA generators * Replace scanning for mbed_spm templates with json * Make generate_source_files and parse_manifests common * Make assert_int an internal function * Use parse_manifests in pytests * Update docspull/10364/head
							parent
							
								
									ffe9ddfb2d
								
							
						
					
					
						commit
						ae4341e8db
					
				| 
						 | 
				
			
			@ -5,8 +5,6 @@ This document describes the following scripts:
 | 
			
		|||
* \_\_init\_\_.py
 | 
			
		||||
* generate_partition_code.py
 | 
			
		||||
* mbed_spm_tfm_common.py
 | 
			
		||||
* generate_mbed_spm_partition_code.py
 | 
			
		||||
* generate_tfm_partition_code.py
 | 
			
		||||
* release.py
 | 
			
		||||
 | 
			
		||||
## \_\_init\_\_.py
 | 
			
		||||
| 
						 | 
				
			
			@ -25,8 +23,6 @@ Mbed OS holds two implementations of PSA:
 | 
			
		|||
Each implementation requires a set of auto-generated files describing the secure partitions:
 | 
			
		||||
 | 
			
		||||
* `generate_partition_code.py` - Generate files for both implementations.
 | 
			
		||||
* `generate_mbed_spm_partition_code.py` - Generate files for MBED_SPM.
 | 
			
		||||
* `generate_tfm_partition_code.py` - Generate files for TF-M.
 | 
			
		||||
* `mbed_spm_tfm_common.py` - Holds common functions for both.
 | 
			
		||||
 | 
			
		||||
## Secure image generation
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,214 +0,0 @@
 | 
			
		|||
#!/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 itertools
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
from os.path import join as path_join
 | 
			
		||||
from jinja2 import Environment, FileSystemLoader, StrictUndefined
 | 
			
		||||
 | 
			
		||||
# Be sure that the tools directory is in the search path
 | 
			
		||||
ROOT = os.path.abspath(path_join(os.path.dirname(__file__), os.pardir, os.pardir))
 | 
			
		||||
sys.path.insert(0, ROOT)
 | 
			
		||||
 | 
			
		||||
from tools.psa.mbed_spm_tfm_common import \
 | 
			
		||||
    Manifest, validate_partition_manifests, manifests_discovery, MBED_OS_ROOT
 | 
			
		||||
 | 
			
		||||
__version__ = '1.0'
 | 
			
		||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
 | 
			
		||||
TEMPLATES_DIR = path_join(SCRIPT_DIR, 'mbed_spm', 'templates')
 | 
			
		||||
MANIFEST_TEMPLATES = [filename for filename in
 | 
			
		||||
                      [os.path.join(dp, f) for dp, dn, fn in
 | 
			
		||||
                       os.walk(TEMPLATES_DIR) for f in fn if f.endswith('.tpl')]
 | 
			
		||||
                      if '_NAME_' in filename]
 | 
			
		||||
COMMON_TEMPLATES = [filename for filename in
 | 
			
		||||
                    [os.path.join(dp, f) for dp, dn, fn in
 | 
			
		||||
                     os.walk(TEMPLATES_DIR) for f in fn if f.endswith('.tpl')]
 | 
			
		||||
                    if '_NAME_' not in filename]
 | 
			
		||||
MANIFEST_FILE_PATTERN = '*_psa.json'
 | 
			
		||||
SPM_CORE_ROOT = path_join(MBED_OS_ROOT, 'components', 'TARGET_PSA')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_source_files(
 | 
			
		||||
        templates,
 | 
			
		||||
        render_args,
 | 
			
		||||
        output_folder,
 | 
			
		||||
        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 output_folder: Output directory for file generation
 | 
			
		||||
    :param extra_filters: Dictionary of extra filters to use in the rendering
 | 
			
		||||
           process
 | 
			
		||||
    :return: Path to generated folder containing common generated files
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    rendered_files = []
 | 
			
		||||
    templates_dirs = list(
 | 
			
		||||
        set([os.path.dirname(path) for path in templates])
 | 
			
		||||
    )
 | 
			
		||||
    template_files = {os.path.basename(t): t for t in templates}
 | 
			
		||||
 | 
			
		||||
    # Load templates for the code generation.
 | 
			
		||||
    env = Environment(
 | 
			
		||||
        loader=FileSystemLoader(templates_dirs),
 | 
			
		||||
        lstrip_blocks=True,
 | 
			
		||||
        trim_blocks=True,
 | 
			
		||||
        undefined=StrictUndefined
 | 
			
		||||
    )
 | 
			
		||||
    if extra_filters:
 | 
			
		||||
        env.filters.update(extra_filters)
 | 
			
		||||
 | 
			
		||||
    for tf in template_files:
 | 
			
		||||
        template = env.get_template(tf)
 | 
			
		||||
        rendered_files.append(
 | 
			
		||||
            (templates[template_files[tf]], template.render(**render_args)))
 | 
			
		||||
        rendered_file_dir = os.path.dirname(templates[template_files[tf]])
 | 
			
		||||
        if not os.path.exists(rendered_file_dir):
 | 
			
		||||
            os.makedirs(rendered_file_dir)
 | 
			
		||||
 | 
			
		||||
    if not os.path.exists(output_folder):
 | 
			
		||||
        os.makedirs(output_folder)
 | 
			
		||||
 | 
			
		||||
    for fname, data in rendered_files:
 | 
			
		||||
        with open(fname, 'wt') as fh:
 | 
			
		||||
            fh.write(data)
 | 
			
		||||
 | 
			
		||||
    return output_folder
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_partitions_sources(manifest_files, extra_filters=None):
 | 
			
		||||
    """
 | 
			
		||||
    Process all the given manifest files and generate C code from them
 | 
			
		||||
 | 
			
		||||
    :param manifest_files: List of manifest files
 | 
			
		||||
    :param extra_filters: Dictionary of extra filters to use in the rendering
 | 
			
		||||
           process
 | 
			
		||||
    :return: List of paths to the generated files
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    # Construct a list of all the manifests and sids.
 | 
			
		||||
    manifests = []
 | 
			
		||||
    for manifest_file in manifest_files:
 | 
			
		||||
        manifest = Manifest.from_json(manifest_file)
 | 
			
		||||
        manifests.append(manifest)
 | 
			
		||||
 | 
			
		||||
    generated_folders = set()
 | 
			
		||||
    for manifest in manifests:
 | 
			
		||||
        manifest_output_folder = manifest.autogen_folder
 | 
			
		||||
 | 
			
		||||
        render_args = {
 | 
			
		||||
            'partition': manifest,
 | 
			
		||||
            'dependent_partitions': manifest.find_dependencies(manifests),
 | 
			
		||||
            'script_ver': __version__
 | 
			
		||||
        }
 | 
			
		||||
        manifest_output_folder = generate_source_files(
 | 
			
		||||
            manifest.templates_to_files(MANIFEST_TEMPLATES,
 | 
			
		||||
                                        TEMPLATES_DIR,
 | 
			
		||||
                                        manifest_output_folder),
 | 
			
		||||
            render_args,
 | 
			
		||||
            manifest_output_folder,
 | 
			
		||||
            extra_filters=extra_filters
 | 
			
		||||
        )
 | 
			
		||||
        generated_folders.add(manifest_output_folder)
 | 
			
		||||
 | 
			
		||||
    return list(generated_folders)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_psa_setup(manifest_files, output_dir, weak_setup, 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
 | 
			
		||||
    """
 | 
			
		||||
    autogen_folder = output_dir
 | 
			
		||||
    templates_dict = {
 | 
			
		||||
        t: path_join(autogen_folder,
 | 
			
		||||
                     os.path.relpath(os.path.splitext(t)[0], TEMPLATES_DIR))
 | 
			
		||||
        for t in COMMON_TEMPLATES
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    complete_source_list = list(templates_dict.values())
 | 
			
		||||
 | 
			
		||||
    # 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)
 | 
			
		||||
        complete_source_list.extend(
 | 
			
		||||
            list(manifest_obj.templates_to_files(
 | 
			
		||||
                MANIFEST_TEMPLATES,
 | 
			
		||||
                TEMPLATES_DIR,
 | 
			
		||||
                manifest_obj.autogen_folder).values())
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    # Validate the correctness of the manifest collection.
 | 
			
		||||
    validate_partition_manifests(manifests)
 | 
			
		||||
 | 
			
		||||
    render_args = {
 | 
			
		||||
        'partitions': manifests,
 | 
			
		||||
        'regions': region_list,
 | 
			
		||||
        'region_pair_list': list(itertools.combinations(region_list, 2)),
 | 
			
		||||
        'weak': weak_setup,
 | 
			
		||||
        'script_ver': __version__
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return generate_source_files(
 | 
			
		||||
        templates_dict,
 | 
			
		||||
        render_args,
 | 
			
		||||
        autogen_folder,
 | 
			
		||||
        extra_filters=extra_filters
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_psa_code():
 | 
			
		||||
    # Find all manifest files in the mbed-os tree
 | 
			
		||||
    service_manifest_files, test_manifest_files = manifests_discovery(root_dir=MBED_OS_ROOT)
 | 
			
		||||
 | 
			
		||||
    # Generate partition code for each manifest file
 | 
			
		||||
    generate_partitions_sources(service_manifest_files + test_manifest_files)
 | 
			
		||||
 | 
			
		||||
    # Generate default system psa setup file (only system partitions)
 | 
			
		||||
    generate_psa_setup(service_manifest_files,
 | 
			
		||||
        SPM_CORE_ROOT, weak_setup=True)
 | 
			
		||||
 | 
			
		||||
    tests_dict = {}
 | 
			
		||||
    for test_manifest in test_manifest_files:
 | 
			
		||||
        test_dir = os.path.dirname(test_manifest)
 | 
			
		||||
        if test_dir not in tests_dict:
 | 
			
		||||
            tests_dict[test_dir] = [test_manifest]
 | 
			
		||||
        else:
 | 
			
		||||
            tests_dict[test_dir].append(test_manifest)
 | 
			
		||||
 | 
			
		||||
    for test_dir in tests_dict:
 | 
			
		||||
        generate_psa_setup(service_manifest_files + tests_dict[test_dir],
 | 
			
		||||
                           test_dir, weak_setup=False)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    generate_psa_code()
 | 
			
		||||
| 
						 | 
				
			
			@ -15,8 +15,162 @@
 | 
			
		|||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
 | 
			
		||||
from generate_mbed_spm_partition_code import generate_psa_code
 | 
			
		||||
from generate_tfm_partition_code import generate_tfm_code
 | 
			
		||||
import itertools
 | 
			
		||||
import json
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
from os.path import join as path_join
 | 
			
		||||
 | 
			
		||||
generate_psa_code()
 | 
			
		||||
generate_tfm_code()
 | 
			
		||||
# Be sure that the tools directory is in the search path
 | 
			
		||||
ROOT = os.path.abspath(path_join(os.path.dirname(__file__), os.pardir, os.pardir))
 | 
			
		||||
sys.path.insert(0, ROOT)
 | 
			
		||||
 | 
			
		||||
from tools.psa.mbed_spm_tfm_common import validate_partition_manifests, \
 | 
			
		||||
    manifests_discovery, parse_manifests, generate_source_files, \
 | 
			
		||||
    MBED_OS_ROOT, SERVICES_DIR, TESTS_DIR
 | 
			
		||||
 | 
			
		||||
__version__ = '1.0'
 | 
			
		||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
 | 
			
		||||
MANIFEST_FILE_PATTERN = '*_psa.json'
 | 
			
		||||
PSA_CORE_ROOT = path_join(MBED_OS_ROOT, 'components', 'TARGET_PSA')
 | 
			
		||||
TFM_TEMPLATES_DESC = path_join(SCRIPT_DIR, 'tfm', 'tfm_generated_file_list.json')
 | 
			
		||||
MBED_SPM_TEMPLATES_DESC = path_join(SCRIPT_DIR, 'mbed_spm', 'mbed_spm_generated_file_list.json')
 | 
			
		||||
MBED_SPM_TEMPLATES_DIR = path_join(SCRIPT_DIR, 'mbed_spm', 'templates')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_partitions_sources(manifest_files, extra_filters=None):
 | 
			
		||||
    """
 | 
			
		||||
    Process all the given manifest files and generate C code from them
 | 
			
		||||
 | 
			
		||||
    :param manifest_files: List of manifest files
 | 
			
		||||
    :param extra_filters: Dictionary of extra filters to use in the rendering
 | 
			
		||||
           process
 | 
			
		||||
    :return: List of paths to the generated files
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    # Construct a list of all the manifests and sids.
 | 
			
		||||
    manifests, _ = parse_manifests(manifest_files, 'MBED_SPM')
 | 
			
		||||
 | 
			
		||||
    with open(MBED_SPM_TEMPLATES_DESC, 'r') as fh:
 | 
			
		||||
        template_data = json.load(fh)
 | 
			
		||||
        manifest_template_list = [path_join(MBED_OS_ROOT, t['template'])
 | 
			
		||||
                                  for t in template_data['partition']]
 | 
			
		||||
 | 
			
		||||
    generated_folders = set()
 | 
			
		||||
    for manifest in manifests:
 | 
			
		||||
        manifest_output_folder = manifest.autogen_folder
 | 
			
		||||
        render_args = {
 | 
			
		||||
            'partition': manifest,
 | 
			
		||||
            'dependent_partitions': manifest.find_dependencies(manifests),
 | 
			
		||||
            'script_ver': __version__
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        manifest_output_folder = generate_source_files(
 | 
			
		||||
            manifest.templates_to_files(manifest_template_list,
 | 
			
		||||
                                        MBED_SPM_TEMPLATES_DIR,
 | 
			
		||||
                                        manifest_output_folder),
 | 
			
		||||
            render_args,
 | 
			
		||||
            manifest_output_folder,
 | 
			
		||||
            extra_filters=extra_filters
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        generated_folders.add(manifest_output_folder)
 | 
			
		||||
 | 
			
		||||
    return list(generated_folders)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_psa_setup(manifest_files, output_dir, weak_setup, 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
 | 
			
		||||
    """
 | 
			
		||||
    with open(MBED_SPM_TEMPLATES_DESC, 'r') as fh:
 | 
			
		||||
        template_data = json.load(fh)
 | 
			
		||||
        templates_dict = {
 | 
			
		||||
            path_join(MBED_OS_ROOT, t['template']):
 | 
			
		||||
                path_join(output_dir, t['target'])
 | 
			
		||||
            for t in template_data['common']
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    # Construct lists of all the manifests and mmio_regions.
 | 
			
		||||
    manifests, region_list = parse_manifests(manifest_files, 'MBED_SPM')
 | 
			
		||||
 | 
			
		||||
    # Validate the correctness of the manifest collection.
 | 
			
		||||
    validate_partition_manifests(manifests)
 | 
			
		||||
 | 
			
		||||
    render_args = {
 | 
			
		||||
        'partitions': manifests,
 | 
			
		||||
        'regions': region_list,
 | 
			
		||||
        'region_pair_list': list(itertools.combinations(region_list, 2)),
 | 
			
		||||
        'weak': weak_setup,
 | 
			
		||||
        'script_ver': __version__
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return generate_source_files(
 | 
			
		||||
        templates_dict,
 | 
			
		||||
        render_args,
 | 
			
		||||
        output_dir,
 | 
			
		||||
        extra_filters=extra_filters
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_psa_code(service_files, test_files):
 | 
			
		||||
    # Generate partition code for each manifest file
 | 
			
		||||
    generate_partitions_sources(service_files + test_files)
 | 
			
		||||
 | 
			
		||||
    # Generate default system psa setup file (only system partitions)
 | 
			
		||||
    generate_psa_setup(service_files, PSA_CORE_ROOT, weak_setup=True)
 | 
			
		||||
 | 
			
		||||
    tests_dict = {}
 | 
			
		||||
    for test_manifest in test_files:
 | 
			
		||||
        test_dir = os.path.dirname(test_manifest)
 | 
			
		||||
        if test_dir not in tests_dict:
 | 
			
		||||
            tests_dict[test_dir] = [test_manifest]
 | 
			
		||||
        else:
 | 
			
		||||
            tests_dict[test_dir].append(test_manifest)
 | 
			
		||||
 | 
			
		||||
    for test_dir in tests_dict:
 | 
			
		||||
        generate_psa_setup(service_files + tests_dict[test_dir],
 | 
			
		||||
                           test_dir, weak_setup=False)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_tfm_code(service_files, test_files):
 | 
			
		||||
    # Construct lists of all the manifests and mmio_regions.
 | 
			
		||||
    service_manifests, service_region_list = parse_manifests(
 | 
			
		||||
        service_files, 'TFM')
 | 
			
		||||
    test_manifests, test_region_list = parse_manifests(
 | 
			
		||||
        test_files, 'TFM')
 | 
			
		||||
 | 
			
		||||
    # Validate the correctness of the manifest collection.
 | 
			
		||||
    validate_partition_manifests(service_manifests + test_manifests)
 | 
			
		||||
 | 
			
		||||
    render_args = {
 | 
			
		||||
        'service_partitions': service_manifests,
 | 
			
		||||
        'test_partitions': test_manifests
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    with open(TFM_TEMPLATES_DESC, 'r') as fh:
 | 
			
		||||
        templates_data = json.load(fh)
 | 
			
		||||
        templates_dict = {
 | 
			
		||||
            path_join(MBED_OS_ROOT, t['template']):
 | 
			
		||||
                path_join(MBED_OS_ROOT, t['output']) for t in templates_data
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        generate_source_files(templates_dict, render_args, MBED_OS_ROOT)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    services, _ = manifests_discovery(root_dir=SERVICES_DIR)
 | 
			
		||||
    _, tests = manifests_discovery(root_dir=TESTS_DIR)
 | 
			
		||||
    generate_psa_code(services, tests)
 | 
			
		||||
    generate_tfm_code(services, tests)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    main()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,105 +0,0 @@
 | 
			
		|||
#!/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 json
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
from os.path import join as path_join
 | 
			
		||||
from jinja2 import Environment, FileSystemLoader, StrictUndefined
 | 
			
		||||
 | 
			
		||||
# Be sure that the tools directory is in the search path
 | 
			
		||||
ROOT = os.path.abspath(path_join(os.path.dirname(__file__), os.pardir, os.pardir))
 | 
			
		||||
sys.path.insert(0, ROOT)
 | 
			
		||||
 | 
			
		||||
from tools.psa.mbed_spm_tfm_common import \
 | 
			
		||||
    Manifest, validate_partition_manifests, manifests_discovery, MBED_OS_ROOT
 | 
			
		||||
 | 
			
		||||
__version__ = '1.0'
 | 
			
		||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
 | 
			
		||||
TEMPLATES_LIST_FILE = path_join(SCRIPT_DIR, 'tfm', 'tfm_generated_file_list.json')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_manifests(manifests_files):
 | 
			
		||||
    region_list = []
 | 
			
		||||
    manifests = []
 | 
			
		||||
    for manifest_file in manifests_files:
 | 
			
		||||
        manifest_obj = Manifest.from_json(manifest_file, psa_type='TFM')
 | 
			
		||||
        manifests.append(manifest_obj)
 | 
			
		||||
        for region in manifest_obj.mmio_regions:
 | 
			
		||||
            region_list.append(region)
 | 
			
		||||
 | 
			
		||||
    return manifests, region_list
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_partition_source_files(service_manifest_files, test_manifest_files, extra_filters=None):
 | 
			
		||||
    """
 | 
			
		||||
    Process all the given manifest files and generate C code from them.
 | 
			
		||||
 | 
			
		||||
    :param service_manifest_files: List of PSA serices manifest files
 | 
			
		||||
    :param test_manifest_files: List of tests manifest files
 | 
			
		||||
    :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.
 | 
			
		||||
    service_manifests, service_region_list = parse_manifests(service_manifest_files)
 | 
			
		||||
    test_manifests, test_region_list = parse_manifests(test_manifest_files)
 | 
			
		||||
 | 
			
		||||
    # Validate the correctness of the manifest collection.
 | 
			
		||||
    validate_partition_manifests(service_manifests + test_manifests)
 | 
			
		||||
 | 
			
		||||
    render_args = {
 | 
			
		||||
        'service_partitions': service_manifests,
 | 
			
		||||
        'test_partitions': test_manifests
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    # Load templates for the code generation.
 | 
			
		||||
    with open(TEMPLATES_LIST_FILE, 'r') as fh:
 | 
			
		||||
        templates_data = json.load(fh)
 | 
			
		||||
 | 
			
		||||
    env = Environment(
 | 
			
		||||
        loader=FileSystemLoader(MBED_OS_ROOT),
 | 
			
		||||
        lstrip_blocks=True,
 | 
			
		||||
        trim_blocks=True,
 | 
			
		||||
        undefined=StrictUndefined
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    if extra_filters:
 | 
			
		||||
        env.filters.update(extra_filters)
 | 
			
		||||
 | 
			
		||||
    # Generate code for each template
 | 
			
		||||
    for tpl in templates_data:
 | 
			
		||||
        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_code():
 | 
			
		||||
    service_manifests, tests_manifests = manifests_discovery(MBED_OS_ROOT)
 | 
			
		||||
    generate_partition_source_files(service_manifests, tests_manifests)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    generate_tfm_code()
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,26 @@
 | 
			
		|||
{
 | 
			
		||||
    "common": [
 | 
			
		||||
        {
 | 
			
		||||
            "name": "mbed-SPM database",
 | 
			
		||||
            "template": "tools/psa/mbed_spm/templates/TARGET_MBED_SPM/COMPONENT_SPE/psa_setup.c.tpl",
 | 
			
		||||
            "target": "TARGET_MBED_SPM/COMPONENT_SPE/psa_setup.c"
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "partition": [
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Details partition defines and structures",
 | 
			
		||||
            "template": "tools/psa/mbed_spm/templates/COMPONENT_SPE/psa_NAME_partition.h.tpl",
 | 
			
		||||
            "target": "COMPONENT_SPE/psa_NAME_partition.h"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Details partition structures and init functions",
 | 
			
		||||
            "template": "tools/psa/mbed_spm/templates/TARGET_MBED_SPM/COMPONENT_SPE/psa_NAME_partition.c.tpl",
 | 
			
		||||
            "target": "TARGET_MBED_SPM/COMPONENT_SPE/psa_NAME_partition.c"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Secure Service signals list",
 | 
			
		||||
            "template": "tools/psa/mbed_spm/templates/psa_NAME_ifs.h.tpl",
 | 
			
		||||
            "target": "psa_NAME_ifs.h"
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -21,6 +21,7 @@ import json
 | 
			
		|||
from jsonschema import validate
 | 
			
		||||
import fnmatch
 | 
			
		||||
from six import integer_types, string_types
 | 
			
		||||
from jinja2 import Environment, FileSystemLoader, StrictUndefined
 | 
			
		||||
 | 
			
		||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
 | 
			
		||||
MBED_OS_ROOT = os.path.abspath(path_join(SCRIPT_DIR, os.pardir, os.pardir))
 | 
			
		||||
| 
						 | 
				
			
			@ -29,7 +30,7 @@ TESTS_DIR = path_join(MBED_OS_ROOT, "TESTS", "psa")
 | 
			
		|||
MANIFEST_FILE_PATTERN = '*_psa.json'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def assert_int(num):
 | 
			
		||||
def _assert_int(num):
 | 
			
		||||
    """
 | 
			
		||||
    Tries to parse an integer num from a given string
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -74,13 +75,13 @@ class RotService(object):
 | 
			
		|||
        self.id = identifier
 | 
			
		||||
        self.signal = signal
 | 
			
		||||
 | 
			
		||||
        assert assert_int(identifier)
 | 
			
		||||
        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)
 | 
			
		||||
        self.minor_version = _assert_int(minor_version)
 | 
			
		||||
        assert self.minor_version > 0, 'minor_version parameter is invalid'
 | 
			
		||||
 | 
			
		||||
        assert minor_policy in self.MINOR_POLICIES, \
 | 
			
		||||
| 
						 | 
				
			
			@ -89,7 +90,7 @@ class RotService(object):
 | 
			
		|||
 | 
			
		||||
    @property
 | 
			
		||||
    def numeric_id(self):
 | 
			
		||||
        return assert_int(self.id)
 | 
			
		||||
        return _assert_int(self.id)
 | 
			
		||||
 | 
			
		||||
    def __eq__(self, other):
 | 
			
		||||
        return (
 | 
			
		||||
| 
						 | 
				
			
			@ -131,10 +132,10 @@ class MmioRegion(object):
 | 
			
		|||
            self.size = '(sizeof(*({})))'.format(kwargs['name'])
 | 
			
		||||
        if 'base' in kwargs:
 | 
			
		||||
            self.base = kwargs['base']
 | 
			
		||||
            self.size = assert_int(kwargs['size'])
 | 
			
		||||
            self.size = _assert_int(kwargs['size'])
 | 
			
		||||
 | 
			
		||||
        assert 'partition_id' in kwargs
 | 
			
		||||
        self.partition_id = assert_int(kwargs['partition_id'])
 | 
			
		||||
        self.partition_id = _assert_int(kwargs['partition_id'])
 | 
			
		||||
 | 
			
		||||
        assert hasattr(self, 'base')
 | 
			
		||||
        assert hasattr(self, 'size')
 | 
			
		||||
| 
						 | 
				
			
			@ -157,7 +158,7 @@ class Irq(object):
 | 
			
		|||
        :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)
 | 
			
		||||
        self.line_num = _assert_int(line_num)
 | 
			
		||||
        assert isinstance(signal, string_types)
 | 
			
		||||
        self.signal = signal
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -353,11 +354,11 @@ class Manifest(object):
 | 
			
		|||
            manifest_file=manifest_file,
 | 
			
		||||
            psa_type=psa_type,
 | 
			
		||||
            name=manifest['name'],
 | 
			
		||||
            partition_id=assert_int(manifest['id']),
 | 
			
		||||
            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']),
 | 
			
		||||
            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,
 | 
			
		||||
| 
						 | 
				
			
			@ -616,3 +617,66 @@ def manifests_discovery(root_dir=SERVICES_DIR):
 | 
			
		|||
        test_manifest_files.update(filter(is_test_manifest, to_add))
 | 
			
		||||
 | 
			
		||||
    return sorted(list(service_manifest_files)), sorted(list(test_manifest_files))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_manifests(manifests_files, psa_type):
 | 
			
		||||
    region_list = []
 | 
			
		||||
    manifests = []
 | 
			
		||||
    for manifest_file in manifests_files:
 | 
			
		||||
        manifest_obj = Manifest.from_json(manifest_file, psa_type=psa_type)
 | 
			
		||||
        manifests.append(manifest_obj)
 | 
			
		||||
        for region in manifest_obj.mmio_regions:
 | 
			
		||||
            region_list.append(region)
 | 
			
		||||
 | 
			
		||||
    return manifests, region_list
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_source_files(
 | 
			
		||||
        templates,
 | 
			
		||||
        render_args,
 | 
			
		||||
        output_folder,
 | 
			
		||||
        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 output_folder: Output directory for file generation
 | 
			
		||||
    :param extra_filters: Dictionary of extra filters to use in the rendering
 | 
			
		||||
           process
 | 
			
		||||
    :return: Path to generated folder containing common generated files
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    rendered_files = []
 | 
			
		||||
    templates_dirs = list(
 | 
			
		||||
        set([os.path.dirname(path) for path in templates])
 | 
			
		||||
    )
 | 
			
		||||
    template_files = {os.path.basename(t): t for t in templates}
 | 
			
		||||
 | 
			
		||||
    # Load templates for the code generation.
 | 
			
		||||
    env = Environment(
 | 
			
		||||
        loader=FileSystemLoader(templates_dirs),
 | 
			
		||||
        lstrip_blocks=True,
 | 
			
		||||
        trim_blocks=True,
 | 
			
		||||
        undefined=StrictUndefined
 | 
			
		||||
    )
 | 
			
		||||
    if extra_filters:
 | 
			
		||||
        env.filters.update(extra_filters)
 | 
			
		||||
 | 
			
		||||
    for tf in template_files:
 | 
			
		||||
        template = env.get_template(tf)
 | 
			
		||||
        rendered_files.append(
 | 
			
		||||
            (templates[template_files[tf]], template.render(**render_args)))
 | 
			
		||||
        rendered_file_dir = os.path.dirname(templates[template_files[tf]])
 | 
			
		||||
        if not os.path.exists(rendered_file_dir):
 | 
			
		||||
            os.makedirs(rendered_file_dir)
 | 
			
		||||
 | 
			
		||||
    if not os.path.exists(output_folder):
 | 
			
		||||
        os.makedirs(output_folder)
 | 
			
		||||
 | 
			
		||||
    for fname, data in rendered_files:
 | 
			
		||||
        with open(fname, 'wt') as fh:
 | 
			
		||||
            fh.write(data)
 | 
			
		||||
 | 
			
		||||
    return output_folder
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,17 +18,13 @@ import filecmp
 | 
			
		|||
import re
 | 
			
		||||
import shutil
 | 
			
		||||
import tempfile
 | 
			
		||||
 | 
			
		||||
import jsonschema.exceptions as jexcep
 | 
			
		||||
import pytest
 | 
			
		||||
import jsonschema.exceptions as jexcep
 | 
			
		||||
from jinja2.defaults import DEFAULT_FILTERS
 | 
			
		||||
 | 
			
		||||
from .test_data import *
 | 
			
		||||
from tools.psa.mbed_spm_tfm_common import *
 | 
			
		||||
from tools.psa.generate_mbed_spm_partition_code import *
 | 
			
		||||
from tools.psa.generate_partition_code import *
 | 
			
		||||
from .test_data import *
 | 
			
		||||
 | 
			
		||||
# Imported again as a module for monkey-patching
 | 
			
		||||
import tools.psa.generate_mbed_spm_partition_code as generate_partition_code
 | 
			
		||||
 | 
			
		||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -369,7 +365,7 @@ def test_validate_partition_manifest(request, temp_test_data, manifests, asserti
 | 
			
		|||
    """
 | 
			
		||||
    test_name = extract_test_name(request.node.name)
 | 
			
		||||
    jsons = [dump_manifest_to_json(m, '%s_%d' % (test_name, i), temp_test_data['dir']) for i, m in enumerate(manifests)]
 | 
			
		||||
    created_manifests = [Manifest.from_json(json) for json in jsons]
 | 
			
		||||
    created_manifests, _ = parse_manifests(jsons, 'MBED_SPM')
 | 
			
		||||
 | 
			
		||||
    with pytest.raises(assertion[0], match=assertion[1]):
 | 
			
		||||
        validate_partition_manifests(created_manifests)
 | 
			
		||||
| 
						 | 
				
			
			@ -464,11 +460,9 @@ def test_verify_json(verify_json):
 | 
			
		|||
    :param verify_json: The 'verify_json' fixture.
 | 
			
		||||
    :return:
 | 
			
		||||
    """
 | 
			
		||||
    manifest1 = Manifest.from_json(verify_json['files_list'][0])
 | 
			
		||||
    manifest2 = Manifest.from_json(verify_json['files_list'][1])
 | 
			
		||||
 | 
			
		||||
    validate_partition_manifests([manifest1, manifest2])
 | 
			
		||||
    assert getattr(manifest1, verify_json['field']) == verify_json['expected']
 | 
			
		||||
    test_manifests, _ = parse_manifests(verify_json['files_list'], 'MBED_SPM')
 | 
			
		||||
    validate_partition_manifests(test_manifests)
 | 
			
		||||
    assert getattr(test_manifests[0], verify_json['field']) == verify_json['expected']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture(scope="function")
 | 
			
		||||
| 
						 | 
				
			
			@ -515,13 +509,13 @@ def test_template_setup(tmpdir_factory):
 | 
			
		|||
    manifest_files = [
 | 
			
		||||
        dump_manifest_to_json(manifest, manifest['name'], test_dir) for
 | 
			
		||||
        manifest in manifests]
 | 
			
		||||
    manifest_objects = [Manifest.from_json(_file) for _file in manifest_files]
 | 
			
		||||
    manifest_objects, regions = parse_manifests(manifest_files, 'MBED_SPM')
 | 
			
		||||
    filters = {
 | 
			
		||||
        'basename': os.path.basename,
 | 
			
		||||
        'find_priority_key': find_priority_key,
 | 
			
		||||
        'find_permission_key': find_permission_key
 | 
			
		||||
    }
 | 
			
		||||
    template_files = [test_dir.join('_NAME_.json.tpl'),
 | 
			
		||||
    template_files = [test_dir.join('_NAME_data.json.tpl'),
 | 
			
		||||
                      test_dir.join('common.json.tpl')]
 | 
			
		||||
    for template, _file in [(test_partition_template, template_files[0]),
 | 
			
		||||
                            (test_common_template, template_files[1])]:
 | 
			
		||||
| 
						 | 
				
			
			@ -539,6 +533,7 @@ def test_template_setup(tmpdir_factory):
 | 
			
		|||
        'manifest_files': manifest_files,
 | 
			
		||||
        'common_files': expected_common_files,
 | 
			
		||||
        'manifests': manifest_objects,
 | 
			
		||||
        'region_list': regions,
 | 
			
		||||
        'filters': filters
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -558,8 +553,8 @@ def test_generate_source_files(test_template_setup):
 | 
			
		|||
    common_templates = {
 | 
			
		||||
        t: path_join(test_template_setup['dir'], os.path.basename(os.path.splitext(t)[0])) for t in common_templates
 | 
			
		||||
    }
 | 
			
		||||
    region_list = []
 | 
			
		||||
 | 
			
		||||
    region_pair_list = list(itertools.combinations(test_template_setup['region_list'], 2))
 | 
			
		||||
    for manifest in test_template_setup['manifests']:
 | 
			
		||||
        generate_source_files(
 | 
			
		||||
            templates=manifest.templates_to_files(partition_templates, test_template_setup['dir'], test_template_setup['dir']),
 | 
			
		||||
| 
						 | 
				
			
			@ -570,14 +565,12 @@ def test_generate_source_files(test_template_setup):
 | 
			
		|||
            output_folder=test_template_setup['dir'],
 | 
			
		||||
            extra_filters=test_template_setup['filters']
 | 
			
		||||
        )
 | 
			
		||||
        for region in manifest.mmio_regions:
 | 
			
		||||
            region_list.append(region)
 | 
			
		||||
 | 
			
		||||
    generate_source_files(
 | 
			
		||||
        common_templates,
 | 
			
		||||
        render_args={
 | 
			
		||||
            'partitions': test_template_setup['manifests'],
 | 
			
		||||
            'region_pair_list': list(itertools.combinations(region_list, 2))
 | 
			
		||||
            'region_pair_list': region_pair_list
 | 
			
		||||
        },
 | 
			
		||||
        output_folder=test_template_setup['dir'],
 | 
			
		||||
        extra_filters=test_template_setup['filters']
 | 
			
		||||
| 
						 | 
				
			
			@ -609,15 +602,11 @@ def test_generate_source_files(test_template_setup):
 | 
			
		|||
            with open(input_file) as fh:
 | 
			
		||||
                expected = json.load(fh)
 | 
			
		||||
        else:
 | 
			
		||||
            region_list = [region for manifest in
 | 
			
		||||
                           test_template_setup['manifests'] for region in
 | 
			
		||||
                           manifest.mmio_regions]
 | 
			
		||||
            expected = {
 | 
			
		||||
                'num_of_partitions': len(test_template_setup['manifests']),
 | 
			
		||||
                'partition_names': [manifest.name for manifest in
 | 
			
		||||
                                    test_template_setup['manifests']],
 | 
			
		||||
                'num_of_region_pairs': len(
 | 
			
		||||
                    list(itertools.combinations(region_list, 2)))
 | 
			
		||||
                'num_of_region_pairs': len(region_pair_list)
 | 
			
		||||
            }
 | 
			
		||||
        assert generated == expected
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -759,7 +748,6 @@ def test_check_circular_call_dependencies(circular_dependencies):
 | 
			
		|||
    :param circular_dependencies: the 'circular_dependencies' fixture
 | 
			
		||||
    :return:
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    objects = [Manifest.from_json(_file) for _file in circular_dependencies['files']]
 | 
			
		||||
 | 
			
		||||
    assert check_circular_call_dependencies(objects) == circular_dependencies['result']
 | 
			
		||||
    objects, _ = parse_manifests(circular_dependencies['files'], 'MBED_SPM')
 | 
			
		||||
    assert check_circular_call_dependencies(objects) == circular_dependencies[
 | 
			
		||||
        'result']
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue