|
| 1 | +#!/usr/bin/python |
| 2 | +# Copyright (c) 2017-2018 ARM Limited |
| 3 | +# |
| 4 | +# SPDX-License-Identifier: Apache-2.0 |
| 5 | +# |
| 6 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 7 | +# you may not use this file except in compliance with the License. |
| 8 | +# You may obtain a copy of the License at |
| 9 | +# |
| 10 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | +# |
| 12 | +# Unless required by applicable law or agreed to in writing, software |
| 13 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 14 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 15 | +# See the License for the specific language governing permissions and |
| 16 | +# limitations under the License. |
| 17 | + |
| 18 | +import itertools |
| 19 | +import os |
| 20 | +import sys |
| 21 | +from os.path import join as path_join |
| 22 | +from jinja2 import Environment, FileSystemLoader, StrictUndefined |
| 23 | + |
| 24 | +# Be sure that the tools directory is in the search path |
| 25 | +ROOT = os.path.abspath(path_join(os.path.dirname(__file__), os.pardir, os.pardir)) |
| 26 | +sys.path.insert(0, ROOT) |
| 27 | + |
| 28 | +from tools.psa.mbed_spm_tfm_common import Manifest, validate_partition_manifests, manifests_discovery |
| 29 | + |
| 30 | +__version__ = '1.0' |
| 31 | +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) |
| 32 | +TEMPLATES_DIR = path_join(SCRIPT_DIR, 'mbed_spm', 'templates') |
| 33 | +MANIFEST_TEMPLATES = [filename for filename in |
| 34 | + [os.path.join(dp, f) for dp, dn, fn in |
| 35 | + os.walk(TEMPLATES_DIR) for f in fn if f.endswith('.tpl')] |
| 36 | + if '_NAME_' in filename] |
| 37 | +COMMON_TEMPLATES = [filename for filename in |
| 38 | + [os.path.join(dp, f) for dp, dn, fn in |
| 39 | + os.walk(TEMPLATES_DIR) for f in fn if f.endswith('.tpl')] |
| 40 | + if '_NAME_' not in filename] |
| 41 | +MANIFEST_FILE_PATTERN = '*_psa.json' |
| 42 | +MBED_OS_ROOT = os.path.abspath(path_join(SCRIPT_DIR, os.pardir, os.pardir)) |
| 43 | +SPM_CORE_ROOT = path_join(MBED_OS_ROOT, 'components', 'TARGET_PSA', 'TARGET_MBED_SPM') |
| 44 | +SPM_TESTS_ROOT = path_join(MBED_OS_ROOT, 'TESTS', 'psa') |
| 45 | + |
| 46 | + |
| 47 | +def generate_source_files( |
| 48 | + templates, |
| 49 | + render_args, |
| 50 | + output_folder, |
| 51 | + extra_filters=None |
| 52 | +): |
| 53 | + """ |
| 54 | + Generate SPM common C code from manifests using given templates |
| 55 | +
|
| 56 | + :param templates: Dictionary of template and their auto-generated products |
| 57 | + :param render_args: Dictionary of arguments that should be passed to render |
| 58 | + :param output_folder: Output directory for file generation |
| 59 | + :param extra_filters: Dictionary of extra filters to use in the rendering |
| 60 | + process |
| 61 | + :return: Path to generated folder containing common generated files |
| 62 | + """ |
| 63 | + |
| 64 | + rendered_files = [] |
| 65 | + templates_dirs = list( |
| 66 | + set([os.path.dirname(path) for path in templates]) |
| 67 | + ) |
| 68 | + template_files = {os.path.basename(t): t for t in templates} |
| 69 | + |
| 70 | + # Load templates for the code generation. |
| 71 | + env = Environment( |
| 72 | + loader=FileSystemLoader(templates_dirs), |
| 73 | + lstrip_blocks=True, |
| 74 | + trim_blocks=True, |
| 75 | + undefined=StrictUndefined |
| 76 | + ) |
| 77 | + if extra_filters: |
| 78 | + env.filters.update(extra_filters) |
| 79 | + |
| 80 | + for tf in template_files: |
| 81 | + template = env.get_template(tf) |
| 82 | + rendered_files.append( |
| 83 | + (templates[template_files[tf]], template.render(**render_args))) |
| 84 | + rendered_file_dir = os.path.dirname(templates[template_files[tf]]) |
| 85 | + if not os.path.exists(rendered_file_dir): |
| 86 | + os.makedirs(rendered_file_dir) |
| 87 | + |
| 88 | + if not os.path.exists(output_folder): |
| 89 | + os.makedirs(output_folder) |
| 90 | + |
| 91 | + for fname, data in rendered_files: |
| 92 | + with open(fname, 'wt') as fh: |
| 93 | + fh.write(data) |
| 94 | + |
| 95 | + return output_folder |
| 96 | + |
| 97 | + |
| 98 | +def generate_partitions_sources(manifest_files, extra_filters=None): |
| 99 | + """ |
| 100 | + Process all the given manifest files and generate C code from them |
| 101 | +
|
| 102 | + :param manifest_files: List of manifest files |
| 103 | + :param extra_filters: Dictionary of extra filters to use in the rendering |
| 104 | + process |
| 105 | + :return: List of paths to the generated files |
| 106 | + """ |
| 107 | + |
| 108 | + # Construct a list of all the manifests and sids. |
| 109 | + manifests = [] |
| 110 | + for manifest_file in manifest_files: |
| 111 | + manifest = Manifest.from_json(manifest_file) |
| 112 | + manifests.append(manifest) |
| 113 | + |
| 114 | + generated_folders = set() |
| 115 | + for manifest in manifests: |
| 116 | + manifest_output_folder = manifest.autogen_folder |
| 117 | + |
| 118 | + render_args = { |
| 119 | + 'partition': manifest, |
| 120 | + 'dependent_partitions': manifest.find_dependencies(manifests), |
| 121 | + 'script_ver': __version__ |
| 122 | + } |
| 123 | + manifest_output_folder = generate_source_files( |
| 124 | + manifest.templates_to_files(MANIFEST_TEMPLATES, |
| 125 | + TEMPLATES_DIR, |
| 126 | + manifest_output_folder), |
| 127 | + render_args, |
| 128 | + manifest_output_folder, |
| 129 | + extra_filters=extra_filters |
| 130 | + ) |
| 131 | + generated_folders.add(manifest_output_folder) |
| 132 | + |
| 133 | + return list(generated_folders) |
| 134 | + |
| 135 | + |
| 136 | +def generate_psa_setup(manifest_files, output_dir, weak_setup, |
| 137 | + extra_filters=None): |
| 138 | + """ |
| 139 | +Process all the given manifest files and generate C setup code from them |
| 140 | + :param manifest_files: List of manifest files |
| 141 | + :param output_dir: Output directory for the generated files |
| 142 | + :param weak_setup: Is the functions/data in the setup file weak |
| 143 | + (can be overridden by another setup file) |
| 144 | + :param extra_filters: Dictionary of extra filters to use in the rendering |
| 145 | + process |
| 146 | + :return: path to the setup generated files |
| 147 | + """ |
| 148 | + autogen_folder = output_dir |
| 149 | + templates_dict = { |
| 150 | + t: path_join(autogen_folder, |
| 151 | + os.path.relpath(os.path.splitext(t)[0], TEMPLATES_DIR)) |
| 152 | + for t in COMMON_TEMPLATES |
| 153 | + } |
| 154 | + |
| 155 | + complete_source_list = list(templates_dict.values()) |
| 156 | + |
| 157 | + # Construct lists of all the manifests and mmio_regions. |
| 158 | + region_list = [] |
| 159 | + manifests = [] |
| 160 | + for manifest_file in manifest_files: |
| 161 | + manifest_obj = Manifest.from_json(manifest_file) |
| 162 | + manifests.append(manifest_obj) |
| 163 | + for region in manifest_obj.mmio_regions: |
| 164 | + region_list.append(region) |
| 165 | + complete_source_list.extend( |
| 166 | + list(manifest_obj.templates_to_files( |
| 167 | + MANIFEST_TEMPLATES, |
| 168 | + TEMPLATES_DIR, |
| 169 | + manifest_obj.autogen_folder).values()) |
| 170 | + ) |
| 171 | + |
| 172 | + # Validate the correctness of the manifest collection. |
| 173 | + validate_partition_manifests(manifests) |
| 174 | + |
| 175 | + render_args = { |
| 176 | + 'partitions': manifests, |
| 177 | + 'regions': region_list, |
| 178 | + 'region_pair_list': list(itertools.combinations(region_list, 2)), |
| 179 | + 'weak': weak_setup, |
| 180 | + 'script_ver': __version__ |
| 181 | + } |
| 182 | + |
| 183 | + return generate_source_files( |
| 184 | + templates_dict, |
| 185 | + render_args, |
| 186 | + autogen_folder, |
| 187 | + extra_filters=extra_filters |
| 188 | + ) |
| 189 | + |
| 190 | + |
| 191 | +def generate_psa_code(): |
| 192 | + # Find all manifest files in the mbed-os tree |
| 193 | + manifest_files = manifests_discovery(MBED_OS_ROOT) |
| 194 | + |
| 195 | + # Generate partition code for each manifest file |
| 196 | + generate_partitions_sources(manifest_files) |
| 197 | + |
| 198 | + test_manifest_files = sorted( |
| 199 | + [path for path in manifest_files if 'TESTS' in path]) |
| 200 | + system_manifest_files = sorted( |
| 201 | + list(set(manifest_files) - set(test_manifest_files))) |
| 202 | + |
| 203 | + # Generate default system psa setup file (only system partitions) |
| 204 | + generate_psa_setup(system_manifest_files, SPM_CORE_ROOT, weak_setup=True) |
| 205 | + |
| 206 | + tests_dir_content = [path_join(SPM_TESTS_ROOT, f) for f in |
| 207 | + os.listdir(SPM_TESTS_ROOT)] |
| 208 | + spm_tests = [path for path in tests_dir_content if os.path.isdir(path)] |
| 209 | + |
| 210 | + # Build a dictionary for test partition in the form of: |
| 211 | + # { test_root: manifest_list } |
| 212 | + # For each test generate specific psa setup file (system + test partitions) |
| 213 | + tests_dict = {test_root: [] for test_root in spm_tests} |
| 214 | + for test_root in spm_tests: |
| 215 | + tests_dict[test_root] = [manifest_path for manifest_path in |
| 216 | + test_manifest_files if |
| 217 | + test_root in manifest_path] |
| 218 | + |
| 219 | + if not tests_dict[test_root]: |
| 220 | + continue |
| 221 | + tests_dict[test_root] += system_manifest_files |
| 222 | + generate_psa_setup(sorted(tests_dict[test_root]), test_root, |
| 223 | + weak_setup=False) |
| 224 | + |
| 225 | + |
| 226 | +if __name__ == '__main__': |
| 227 | + generate_psa_code() |
0 commit comments