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