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