1#------------------------------------------------------------------------------- 2# Copyright (c) 2018-2022, Arm Limited. All rights reserved. 3# Copyright (c) 2022 Cypress Semiconductor Corporation (an Infineon company) 4# or an affiliate of Cypress Semiconductor Corporation. All rights reserved. 5# 6# SPDX-License-Identifier: BSD-3-Clause 7# 8#------------------------------------------------------------------------------- 9 10import os 11import io 12import re 13import sys 14import argparse 15import logging 16from jinja2 import Environment, BaseLoader, select_autoescape, TemplateNotFound 17 18try: 19 import yaml 20except ImportError as e: 21 logging.error (str(e) + " To install it, type:") 22 logging.error ("pip install PyYAML") 23 exit(1) 24 25donotedit_warning = \ 26 ' WARNING: This is an auto-generated file. Do not edit! ' 27 28TFM_ROOT_DIR = os.path.join(sys.path[0], '..') 29OUT_DIR = None # The root directory that files are generated to 30 31# PID[0, TFM_PID_BASE - 1] are reserved for TF-M SPM and test usages 32TFM_PID_BASE = 256 33 34# variable for checking for duplicated sid 35sid_list = [] 36class TemplateLoader(BaseLoader): 37 """ 38 Template loader class. 39 40 An instance of this class is passed to the template engine. It is 41 responsible for reading the template file 42 """ 43 def __init__(self): 44 pass 45 46 def get_source(self, environment, template): 47 """ 48 This function reads the template files. 49 For detailed documentation see: 50 http://jinja.pocoo.org/docs/2.10/api/#jinja2.BaseLoader.get_source 51 52 Please note that this function always return 'false' as 'uptodate' 53 value, so the output file will always be generated. 54 """ 55 if not os.path.isfile(template): 56 raise TemplateNotFound(template) 57 with open(template) as f: 58 source = f.read() 59 return source, template, False 60 61def parse_configurations(file_paths): 62 """ 63 Parses the given config files and return a dict whose key-values are build 64 configurations and their values. 65 66 Valid configurations should be in the format of: 67 "#define VAR [...]" in a single line. 68 The value of the config is optional. 69 """ 70 configurations = {} 71 72 lines = [] 73 for file in file_paths: 74 with open(file, 'r') as config_file: 75 lines += config_file.readlines() 76 77 for line in lines: 78 if not line.startswith('#define'): 79 continue 80 81 line = line.rstrip('\r\n') 82 line_items = line.split(maxsplit=2) 83 if len(line_items) == 3: 84 configurations[line_items[1]] = line_items[2] 85 elif len(line_items) == 2: 86 configurations[line_items[1]] = '' 87 88 logging.debug(configurations) 89 90 return configurations 91 92def manifest_validation(manifest, pid): 93 """ 94 This function validates FF-M compliance for partition manifest, and sets 95 default values for optional attributes. 96 The validation is skipped for TF-M specific Partitions (PID < TFM_PID_BASE). 97 """ 98 99 service_list = manifest.get('services', []) 100 irq_list = manifest.get('irqs', []) 101 102 # "psa_framework_version" validation 103 if manifest['psa_framework_version'] not in [1.0, 1.1]: 104 raise Exception('Invalid psa_framework_version of {}'.format(manifest['name'])) 105 106 # "type" validation 107 if manifest['type'] not in ['PSA-ROT', 'APPLICATION-ROT']: 108 raise Exception('Invalid type of {}'.format(manifest['name'])) 109 110 # "priority" validation 111 if manifest['priority'] not in ['HIGH', 'NORMAL', 'LOW']: 112 raise Exception('Invalid priority of {}'.format(manifest['name'])) 113 114 if 'ns_agent' not in manifest: 115 manifest['ns_agent'] = False 116 117 # Every PSA Partition must have at least either a secure service or an IRQ 118 if (pid == None or pid >= TFM_PID_BASE) \ 119 and len(service_list) == 0 and len(irq_list) == 0: 120 raise Exception('{} must declare at least either a secure service or an IRQ!' 121 .format(manifest['name'])) 122 123 if manifest['psa_framework_version'] == 1.0: 124 # For 1.0 Partition, the model is IPC 125 manifest['model'] = 'IPC' 126 127 # "model" validation: 128 model = manifest.get('model', None) 129 if model == None: 130 raise Exception('{} is missing the "model" attribute'.format(manifest['name'])) 131 132 # Assign a unified 'entry' for templates 133 if model == 'IPC': 134 # entry_point is mandatory for IPC Partitions 135 if 'entry_point' not in manifest.keys(): 136 raise Exception('{} is missing the "entry_point" attribute'.format(manifest['name'])) 137 manifest['entry'] = manifest['entry_point'] 138 elif model == 'SFN': 139 if 'entry_init' in manifest.keys(): 140 manifest['entry'] = manifest['entry_init'] 141 else: 142 manifest['entry'] = 0 143 else: 144 raise Exception('Invalid "model" of {}'.format(manifest['name'])) 145 146 # Service FF-M manifest validation 147 for service in service_list: 148 if manifest['psa_framework_version'] == 1.0: 149 service['connection_based'] = True 150 elif 'connection_based' not in service: 151 raise Exception("'connection_based' is mandatory in FF-M 1.1 service!") 152 153 if 'version' not in service.keys(): 154 service['version'] = 1 155 if 'version_policy' not in service.keys(): 156 service['version_policy'] = 'STRICT' 157 158 # SID duplication check 159 if service['sid'] in sid_list: 160 raise Exception('Service ID: {} has duplications!'.format(service['sid'])) 161 else: 162 sid_list.append(service['sid']) 163 164 return manifest 165 166def check_circular_dependency(partitions, service_partition_map): 167 """ 168 This function detects if there is any circular partition dependency chain. 169 If a circular dependency is detected, the script exits with error. 170 171 Inputs: 172 - partitions: dict of partition manifests 173 - service_partition_map: map between services and their owner Partitions 174 """ 175 176 dependency_table = {} 177 for partition in partitions: 178 manifest = partition['manifest'] 179 dependencies = manifest['dependencies'].copy() \ 180 if 'dependencies' in manifest else [] 181 dependencies += manifest['weak_dependencies'].copy() \ 182 if 'weak_dependencies' in manifest else [] 183 dependency_table[manifest['name']] = { 184 'dependencies': [service_partition_map[dependency] 185 for dependency in dependencies 186 if dependency in service_partition_map], 187 'validated': False 188 } 189 190 for partition in dependency_table.keys(): 191 validate_dependency_chain(partition, dependency_table, []) 192 193def validate_dependency_chain(partition, 194 dependency_table, 195 dependency_chain): 196 """ 197 Recursively validate if the given partition and its dependencies 198 have a circular dependency with the given dependency_chain. 199 Exit with error code once any circular is detected. 200 201 Inputs: 202 - partition: next partition to be checked 203 - dependency_table: dict of partitions and their dependencies 204 - dependency_chain: list of dependencies in current chain 205 """ 206 207 dependency_chain.append(partition) 208 if partition in dependency_chain[:-1]: 209 logging.error( 210 'Circular dependency exists in chain: {}'.format( 211 ', '.join(dependency_chain))) 212 exit(1) 213 for dependency in dependency_table[partition]['dependencies']: 214 if dependency_table[dependency]['validated']: 215 continue 216 validate_dependency_chain(dependency, dependency_table, dependency_chain) 217 dependency_table[partition]['validated'] = True 218 219def process_partition_manifests(manifest_lists, configs): 220 """ 221 Parse the input manifest lists, check if manifest settings are valid, 222 generate the data base for generated files 223 and generate manifest header files. 224 225 Parameters 226 ---------- 227 manifest_lists: 228 A list of Secure Partition manifest lists 229 230 Returns 231 ------- 232 The manifest data base. 233 """ 234 235 context = {} 236 237 partition_list = [] 238 all_manifests = [] 239 pid_list = [] 240 no_pid_manifest_idx = [] 241 service_partition_map = {} 242 partition_statistics = { 243 'connection_based_srv_num': 0, 244 'ipc_partitions': [], 245 'mmio_region_num': 0, 246 'flih_num': 0, 247 'slih_num': 0 248 } 249 config_impl = { 250 'CONFIG_TFM_SPM_BACKEND_SFN' : '0', 251 'CONFIG_TFM_SPM_BACKEND_IPC' : '0', 252 'CONFIG_TFM_PSA_API_SFN_CALL' : '0', 253 'CONFIG_TFM_PSA_API_CROSS_CALL' : '0', 254 'CONFIG_TFM_PSA_API_SUPERVISOR_CALL' : '0', 255 'CONFIG_TFM_CONNECTION_BASED_SERVICE_API' : '0', 256 'CONFIG_TFM_MMIO_REGION_ENABLE' : '0', 257 'CONFIG_TFM_FLIH_API' : '0', 258 'CONFIG_TFM_SLIH_API' : '0' 259 } 260 261 isolation_level = int(configs['TFM_ISOLATION_LEVEL'], base = 10) 262 backend = configs['CONFIG_TFM_SPM_BACKEND'] 263 264 # Get all the manifests information as a dictionary 265 for i, item in enumerate(manifest_lists): 266 if not os.path.isfile(item): 267 logging.error('Manifest list item [{}] must be a file'.format(i)) 268 exit(1) 269 270 # The manifest list file generated by configure_file() 271 with open(item) as manifest_list_yaml_file: 272 manifest_dic = yaml.safe_load(manifest_list_yaml_file)['manifest_list'] 273 for dict in manifest_dic: 274 # Replace environment variables in the manifest path and convert to absolute path. 275 # If it's already abspath, the path will not be changed. 276 manifest_path = os.path.join(os.path.dirname(item), # path of manifest list 277 os.path.expandvars(dict['manifest']))\ 278 .replace('\\', '/') 279 dict['manifest'] = manifest_path 280 all_manifests.append(dict) 281 282 logging.info("------------ Display partition configuration - start ------------") 283 284 # Parse the manifests 285 for i, manifest_item in enumerate(all_manifests): 286 valid_enabled_conditions = ['1', 'on', 'true', 'enabled'] 287 valid_disabled_conditions = ['0', 'off', 'false', 'disabled', ''] 288 is_enabled = '' 289 290 if 'conditional' in manifest_item.keys(): 291 if manifest_item['conditional'] not in configs.keys(): 292 logging.error('Configuration "{}" is not defined!'.format(manifest_item['conditional'])) 293 exit(1) 294 is_enabled = configs[manifest_item['conditional']].lower() 295 else: 296 # Partitions without 'conditional' is always on 297 is_enabled = '1' 298 299 if is_enabled in valid_disabled_conditions: 300 logging.info(" {:40s} OFF".format(manifest_item['description'])) 301 continue 302 elif is_enabled in valid_enabled_conditions: 303 logging.info(" {:40s} ON".format(manifest_item['description'])) 304 else: 305 raise Exception('Invalid "conditional" attribute: "{}" for {}. ' 306 'Please set to one of {} or {}, case-insensitive.'\ 307 .format(manifest_item['conditional'], 308 manifest_item['description'], 309 valid_enabled_conditions, valid_disabled_conditions)) 310 311 # Check if partition ID is manually set 312 if 'pid' not in manifest_item.keys(): 313 no_pid_manifest_idx.append(i) 314 pid = None 315 else: 316 pid = manifest_item['pid'] 317 318 # Check if partition ID is duplicated 319 if pid in pid_list: 320 raise Exception('PID No. {} has already been used!'.format(pid)) 321 else: 322 pid_list.append(pid) 323 324 manifest_path = manifest_item['manifest'] 325 with open(manifest_path) as manifest_file: 326 manifest = yaml.safe_load(manifest_file) 327 if manifest.get('model', None) == 'dual': 328 # If a Partition supports both models, it can set the "model" to "backend". 329 # The actual model used follows the backend being used. 330 manifest['model'] = backend 331 manifest = manifest_validation(manifest, pid) 332 333 if pid == None or pid >= TFM_PID_BASE: 334 # Count the number of IPC/SFN partitions 335 if manifest['model'] == 'IPC': 336 partition_statistics['ipc_partitions'].append(manifest['name']) 337 338 # Set initial value to -1 to make (srv_idx + 1) reflect the correct 339 # number (0) when there are no services. 340 srv_idx = -1 341 for srv_idx, service in enumerate(manifest.get('services', [])): 342 service_partition_map[service['name']] = manifest['name'] 343 if manifest['model'] == 'IPC': 344 # Assign signal value, the first 4 bits are reserved by FF-M 345 service['signal_value'] = (1 << (srv_idx + 4)) 346 else: 347 # Signals of SFN Partitions are SPM internal only, does not 348 # need to reserve 4 bits. 349 service['signal_value'] = (1 << srv_idx) 350 if service['connection_based']: 351 partition_statistics['connection_based_srv_num'] += 1 352 logging.debug('{} has {} services'.format(manifest['name'], srv_idx +1)) 353 354 # Calculate the number of mmio region 355 mmio_region_list = manifest.get('mmio_regions', []) 356 partition_statistics['mmio_region_num'] += len(mmio_region_list) 357 358 # Set initial value to -1 to make (irq + 1) reflect the correct 359 # number (0) when there are no irqs. 360 irq_idx = -1 361 for irq_idx, irq in enumerate(manifest.get('irqs', [])): 362 # Assign signal value, from the most significant bit 363 irq['signal_value'] = (1 << (31 - irq_idx)) 364 if irq.get('handling', None) == 'FLIH': 365 partition_statistics['flih_num'] += 1 366 else: 367 partition_statistics['slih_num'] += 1 368 logging.debug('{} has {} IRQS'.format(manifest['name'], irq_idx +1)) 369 370 if ((srv_idx + 1) + (irq_idx + 1)) > 28: 371 raise Exception('Total number of Services and IRQs of {} exceeds the limit (28)' 372 .format(manifest['name'])) 373 374 manifest_out_basename = os.path.splitext(os.path.basename(manifest_path))[0] 375 376 if 'output_path' in manifest_item: 377 output_path = os.path.expandvars(manifest_item['output_path']) 378 else: 379 output_path = '' 380 381 manifest_head_file = os.path.join(OUT_DIR, output_path, 'psa_manifest', 382 '{}.h'.format(manifest_out_basename))\ 383 .replace('\\', '/') 384 intermedia_file = os.path.join(OUT_DIR, output_path, 'auto_generated', 385 'intermedia_{}.c'.format(manifest_out_basename))\ 386 .replace('\\', '/') 387 load_info_file = os.path.join(OUT_DIR, output_path, 'auto_generated', 388 'load_info_{}.c'.format(manifest_out_basename))\ 389 .replace('\\', '/') 390 output_dir = os.path.join(OUT_DIR, output_path).replace('\\', '/') 391 392 partition_list.append({'manifest': manifest, 'attr': manifest_item, 393 'manifest_out_basename': manifest_out_basename, 394 'header_file': manifest_head_file, 395 'intermedia_file': intermedia_file, 396 'loadinfo_file': load_info_file, 397 'output_dir':output_dir}) 398 399 logging.info("------------ Display partition configuration - end ------------") 400 401 check_circular_dependency(partition_list, service_partition_map) 402 403 # Automatically assign PIDs for partitions without 'pid' attribute 404 pid = max(pid_list, default = TFM_PID_BASE - 1) 405 for idx in no_pid_manifest_idx: 406 pid += 1 407 all_manifests[idx]['pid'] = pid 408 pid_list.append(pid) 409 410 # Set up configurations 411 if backend == 'SFN': 412 if len(partition_statistics['ipc_partitions']) > 0: 413 logging.error('SFN backend does not support IPC Partitions:') 414 logging.error(partition_statistics['ipc_partitions']) 415 exit(1) 416 417 if isolation_level > 1: 418 logging.error('SFN backend does not support high isolation levels.') 419 exit(1) 420 421 config_impl['CONFIG_TFM_SPM_BACKEND_SFN'] = '1' 422 config_impl['CONFIG_TFM_PSA_API_SFN_CALL'] = '1' 423 elif backend == 'IPC': 424 config_impl['CONFIG_TFM_SPM_BACKEND_IPC'] = '1' 425 if isolation_level > 1: 426 config_impl['CONFIG_TFM_PSA_API_SUPERVISOR_CALL'] = '1' 427 else: 428 config_impl['CONFIG_TFM_PSA_API_CROSS_CALL'] = '1' 429 430 if partition_statistics['connection_based_srv_num'] > 0: 431 config_impl['CONFIG_TFM_CONNECTION_BASED_SERVICE_API'] = 1 432 433 if partition_statistics['mmio_region_num'] > 0: 434 config_impl['CONFIG_TFM_MMIO_REGION_ENABLE'] = 1 435 436 if partition_statistics['flih_num'] > 0: 437 config_impl['CONFIG_TFM_FLIH_API'] = 1 438 if partition_statistics['slih_num'] > 0: 439 config_impl['CONFIG_TFM_SLIH_API'] = 1 440 441 context['partitions'] = partition_list 442 context['config_impl'] = config_impl 443 context['stateless_services'] = process_stateless_services(partition_list) 444 445 return context 446 447def gen_per_partition_files(context): 448 """ 449 Generate per-partition files 450 451 Parameters 452 ---------- 453 context: 454 context contains partition infos 455 """ 456 457 partition_context = {} 458 partition_context['utilities'] = context['utilities'] 459 partition_context['config_impl'] = context['config_impl'] 460 461 manifesttemplate = ENV.get_template(os.path.join(sys.path[0], 'templates/manifestfilename.template')) 462 memorytemplate = ENV.get_template(os.path.join(sys.path[0], 'templates/partition_intermedia.template')) 463 infotemplate = ENV.get_template(os.path.join(sys.path[0], 'templates/partition_load_info.template')) 464 465 logging.info ("Start to generate partition files:") 466 467 for one_partition in context['partitions']: 468 partition_context['manifest'] = one_partition['manifest'] 469 partition_context['attr'] = one_partition['attr'] 470 partition_context['manifest_out_basename'] = one_partition['manifest_out_basename'] 471 472 logging.info ('Generating {} in {}'.format(one_partition['attr']['description'], 473 one_partition['output_dir'])) 474 outfile_path = os.path.dirname(one_partition['header_file']) 475 if not os.path.exists(outfile_path): 476 os.makedirs(outfile_path) 477 478 headerfile = io.open(one_partition['header_file'], 'w', newline=None) 479 headerfile.write(manifesttemplate.render(partition_context)) 480 headerfile.close() 481 482 intermediafile_path = os.path.dirname(one_partition['intermedia_file']) 483 if not os.path.exists(intermediafile_path): 484 os.makedirs(intermediafile_path) 485 intermediafile = io.open(one_partition['intermedia_file'], 'w', newline=None) 486 intermediafile.write(memorytemplate.render(partition_context)) 487 intermediafile.close() 488 489 infofile_path = os.path.dirname(one_partition['loadinfo_file']) 490 if not os.path.exists(infofile_path): 491 os.makedirs(infofile_path) 492 infooutfile = io.open(one_partition['loadinfo_file'], 'w', newline=None) 493 infooutfile.write(infotemplate.render(partition_context)) 494 infooutfile.close() 495 496 logging.info ("Per-partition files done:") 497 498def gen_summary_files(context, gen_file_lists): 499 """ 500 Generate files according to the gen_file_list 501 502 Parameters 503 ---------- 504 gen_file_lists: 505 The lists of files to generate 506 """ 507 file_list = [] 508 509 for f in gen_file_lists: 510 with open(f) as file_list_yaml_file: 511 file_list_yaml = yaml.safe_load(file_list_yaml_file) 512 file_list.extend(file_list_yaml['file_list']) 513 514 for file in file_list: 515 # Replace environment variables in the output filepath 516 manifest_out_file = os.path.expandvars(file['output']) 517 # Replace environment variables in the template filepath 518 templatefile_name = os.path.expandvars(file['template']) 519 520 manifest_out_file = os.path.join(OUT_DIR, manifest_out_file) 521 522 outfile_path = os.path.dirname(manifest_out_file) 523 if not os.path.exists(outfile_path): 524 os.makedirs(outfile_path) 525 526 template = ENV.get_template(templatefile_name) 527 528 outfile = io.open(manifest_out_file, 'w', newline=None) 529 outfile.write(template.render(context)) 530 outfile.close() 531 532def process_stateless_services(partitions): 533 """ 534 This function collects all stateless services together, and allocates 535 stateless handles for them. 536 Valid stateless handle in service will be converted to an index. If the 537 stateless handle is set as "auto", or not set, framework will allocate a 538 valid index for the service. 539 Framework puts each service into a reordered stateless service list at 540 position of "index". Other unused positions are left None. 541 542 Keep the variable names start with upper case 'STATIC_HANDLE_' the same 543 as the preprocessors in C sources. This could easier the upcomping 544 modification when developer searches these definitions for modification. 545 """ 546 547 collected_stateless_services = [] 548 STATIC_HANDLE_NUM_LIMIT = 32 549 550 # Collect all stateless services first. 551 for partition in partitions: 552 # Skip the FF-M 1.0 partitions 553 if partition['manifest']['psa_framework_version'] < 1.1: 554 continue 555 556 service_list = partition['manifest'].get('services', []) 557 558 for service in service_list: 559 if service['connection_based'] is False: 560 collected_stateless_services.append(service) 561 562 if len(collected_stateless_services) == 0: 563 return [] 564 565 if len(collected_stateless_services) > STATIC_HANDLE_NUM_LIMIT: 566 raise Exception('Stateless service numbers range exceed {number}.'.format(number=STATIC_HANDLE_NUM_LIMIT)) 567 568 """ 569 Allocate an empty stateless service list to store services. 570 Use "handle - 1" as the index for service, since handle value starts from 571 1 and list index starts from 0. 572 """ 573 reordered_stateless_services = [None] * STATIC_HANDLE_NUM_LIMIT 574 auto_alloc_services = [] 575 576 for service in collected_stateless_services: 577 # If not set, it is "auto" by default 578 if 'stateless_handle' not in service: 579 auto_alloc_services.append(service) 580 continue 581 582 service_handle = service['stateless_handle'] 583 584 # Fill in service list with specified stateless handle, otherwise skip 585 if isinstance(service_handle, int): 586 if service_handle < 1 or service_handle > STATIC_HANDLE_NUM_LIMIT: 587 raise Exception('Invalid stateless_handle setting: {handle}.'.format(handle=service['stateless_handle'])) 588 # Convert handle index to reordered service list index 589 service_handle = service_handle - 1 590 591 if reordered_stateless_services[service_handle] is not None: 592 raise Exception('Duplicated stateless_handle setting: {handle}.'.format(handle=service['stateless_handle'])) 593 reordered_stateless_services[service_handle] = service 594 elif service_handle == 'auto': 595 auto_alloc_services.append(service) 596 else: 597 raise Exception('Invalid stateless_handle setting: {handle}.'.format(handle=service['stateless_handle'])) 598 599 STATIC_HANDLE_IDX_BIT_WIDTH = 8 600 STATIC_HANDLE_IDX_MASK = (1 << STATIC_HANDLE_IDX_BIT_WIDTH) - 1 601 STATIC_HANDLE_INDICATOR_OFFSET = 30 602 STATIC_HANDLE_VER_OFFSET = 8 603 STATIC_HANDLE_VER_BIT_WIDTH = 8 604 STATIC_HANDLE_VER_MASK = (1 << STATIC_HANDLE_VER_BIT_WIDTH) - 1 605 606 # Auto-allocate stateless handle and encode the stateless handle 607 for i in range(0, STATIC_HANDLE_NUM_LIMIT): 608 service = reordered_stateless_services[i] 609 610 if service == None and len(auto_alloc_services) > 0: 611 service = auto_alloc_services.pop(0) 612 613 """ 614 Encode stateless flag and version into stateless handle 615 """ 616 stateless_handle_value = 0 617 if service != None: 618 stateless_index = (i & STATIC_HANDLE_IDX_MASK) 619 stateless_handle_value |= stateless_index 620 stateless_handle_value |= (1 << STATIC_HANDLE_INDICATOR_OFFSET) 621 stateless_version = (service['version'] & STATIC_HANDLE_VER_MASK) << STATIC_HANDLE_VER_OFFSET 622 stateless_handle_value |= stateless_version 623 service['stateless_handle_value'] = '0x{0:08x}'.format(stateless_handle_value) 624 service['stateless_handle_index'] = stateless_index 625 626 reordered_stateless_services[i] = service 627 628 return reordered_stateless_services 629 630def parse_args(): 631 parser = argparse.ArgumentParser(description='Parse secure partition manifest list and generate files listed by the file list', 632 epilog='Note that environment variables in template files will be replaced with their values') 633 634 parser.add_argument('-o', '--outdir' 635 , dest='outdir' 636 , required=True 637 , metavar='out_dir' 638 , help='The root directory for generated files') 639 640 parser.add_argument('-m', '--manifest-lists' 641 , nargs='+' 642 , dest='manifest_lists' 643 , required=True 644 , metavar='manifest list' 645 , help='A list of Secure Partition manifest lists and their original paths.\n\ 646 The manifest lists might be processed by CMake and\n\ 647 the path might be different to the original one\n\ 648 The format must be [list A, orignal path A, list B, orignal path B, ...]') 649 650 parser.add_argument('-f', '--file-list' 651 , nargs='+' 652 , dest='gen_file_args' 653 , required=True 654 , metavar='file-list' 655 , help='These files describe the file list to generate') 656 657 parser.add_argument('-c', '--config-files' 658 , nargs='+' 659 , dest='config_files' 660 , required=True 661 , metavar='config-files' 662 , help='A header file contains build configurations') 663 664 parser.add_argument('-q', '--quiet' 665 , dest='quiet' 666 , required=False 667 , default=False 668 , action='store_true' 669 , help='Reduce log messages') 670 671 args = parser.parse_args() 672 673 return args 674 675ENV = Environment( 676 loader = TemplateLoader(), 677 autoescape = select_autoescape(['html', 'xml']), 678 lstrip_blocks = True, 679 trim_blocks = True, 680 keep_trailing_newline = True 681 ) 682 683def main(): 684 """ 685 The entry point of the script. 686 687 Generates the output files based on the templates and the manifests. 688 """ 689 690 global OUT_DIR 691 692 args = parse_args() 693 694 logging.basicConfig(format='%(message)s' 695 , level=logging.WARNING if args.quiet else logging.INFO) 696 697 OUT_DIR = os.path.abspath(args.outdir) 698 699 manifest_lists = [os.path.abspath(x) for x in args.manifest_lists] 700 gen_file_lists = [os.path.abspath(x) for x in args.gen_file_args] 701 702 """ 703 Relative path to TF-M root folder is supported in the manifests 704 and default value of manifest list and generated file list are relative to TF-M root folder as well, 705 so first change directory to TF-M root folder. 706 By doing this, the script can be executed anywhere 707 The script is located in <TF-M root folder>/tools, so sys.path[0]<location of the script>/.. is TF-M root folder. 708 """ 709 os.chdir(os.path.join(sys.path[0], '..')) 710 711 context = process_partition_manifests(manifest_lists, 712 parse_configurations(args.config_files)) 713 714 utilities = {} 715 utilities['donotedit_warning'] = donotedit_warning 716 717 context['utilities'] = utilities 718 719 gen_per_partition_files(context) 720 gen_summary_files(context, gen_file_lists) 721 722if __name__ == '__main__': 723 main() 724