blob: 0d7588dc5259e5291bdffa400ee2dc702d47e959 [file] [log] [blame]
Miklos Balint470919c2018-05-22 17:51:29 +02001#-------------------------------------------------------------------------------
Xinyu Zhang90f08dc2022-01-12 15:55:17 +08002# Copyright (c) 2018-2022, Arm Limited. All rights reserved.
Chris Brandf1d6b6f2022-07-22 11:49:01 -07003# Copyright (c) 2022 Cypress Semiconductor Corporation (an Infineon company)
4# or an affiliate of Cypress Semiconductor Corporation. All rights reserved.
Miklos Balint470919c2018-05-22 17:51:29 +02005#
6# SPDX-License-Identifier: BSD-3-Clause
7#
8#-------------------------------------------------------------------------------
9
10import os
Mate Toth-Pal36f21842018-11-08 16:12:51 +010011import io
Kevin Pengce99e5d2021-11-09 18:06:53 +080012import re
Shawn Shana9ad1e02019-08-07 15:49:48 +080013import sys
14import argparse
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -050015import logging
Ken Liu1f345b02020-05-30 21:11:05 +080016from jinja2 import Environment, BaseLoader, select_autoescape, TemplateNotFound
Miklos Balint470919c2018-05-22 17:51:29 +020017
18try:
19 import yaml
20except ImportError as e:
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -050021 logging.error (str(e) + " To install it, type:")
22 logging.error ("pip install PyYAML")
Miklos Balint470919c2018-05-22 17:51:29 +020023 exit(1)
24
Edison Ai48b2d9e2019-06-24 14:39:45 +080025donotedit_warning = \
Sherry Zhangf58f2bd2022-01-10 17:21:11 +080026 ' WARNING: This is an auto-generated file. Do not edit! '
Kevin Peng655f2392019-11-27 16:33:02 +080027
Kevin Pengce99e5d2021-11-09 18:06:53 +080028TFM_ROOT_DIR = os.path.join(sys.path[0], '..')
Kevin Peng655f2392019-11-27 16:33:02 +080029OUT_DIR = None # The root directory that files are generated to
Edison Ai48b2d9e2019-06-24 14:39:45 +080030
Sherry Zhang87e6d2e2021-12-27 14:48:59 +080031# PID[0, TFM_PID_BASE - 1] are reserved for TF-M SPM and test usages
Kevin Pengce99e5d2021-11-09 18:06:53 +080032TFM_PID_BASE = 256
33
Ruiqi Jiang71d361c2021-06-23 17:45:55 +010034# variable for checking for duplicated sid
35sid_list = []
Mate Toth-Pal36f21842018-11-08 16:12:51 +010036class TemplateLoader(BaseLoader):
37 """
38 Template loader class.
Miklos Balint470919c2018-05-22 17:51:29 +020039
Mate Toth-Pal36f21842018-11-08 16:12:51 +010040 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
Miklos Balint470919c2018-05-22 17:51:29 +020045
Mate Toth-Pal36f21842018-11-08 16:12:51 +010046 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
Kevin Pengce99e5d2021-11-09 18:06:53 +080061def get_single_macro_def_from_file(file_name, macro_name):
62 """
63 This function parses the given file_name to get the definition of the given
64 C Macro (macro_name).
65
66 It assumes that the target Macro has no multiple definitions in different
67 build configurations.
68
69 It supports Macros defined in multi-line, for example:
70 #define SOME_MACRO \
71 the_macro_value
72
73 Inputs:
74 - file_name: the file to get the Macro from
75 - macro_name: the name of the Macro to get
76 Returns:
77 - The Macro definition with '()' stripped, or Exception if not found
78 """
79
80 with open(file_name, 'r') as f:
81 pattern = re.compile(r'#define\s+{}[\\\s]+.*'.format(macro_name))
82 result = pattern.findall(f.read())
83
84 if len(result) != 1:
85 raise Exception('{} not defined or has multiple definitions'.format(macro_name))
86
87 macro_def = result[0].split()[-1].strip('()')
88
89 return macro_def
90
Kevin Peng56979ee2022-05-12 12:11:31 +080091def parse_configurations(file_paths):
92 """
93 Parses the given config files and return a dict whose key-values are build
94 configurations and their values.
95
96 Valid configurations should be in the format of:
97 "#define VAR [...]" in a single line.
98 The value of the config is optional.
99 """
100 configurations = {}
101
102 lines = []
103 for file in file_paths:
104 with open(file, 'r') as config_file:
105 lines += config_file.readlines()
106
107 for line in lines:
108 if not line.startswith('#define'):
109 continue
110
111 line = line.rstrip('\r\n')
112 line_items = line.split(maxsplit=2)
113 if len(line_items) == 3:
114 configurations[line_items[1]] = line_items[2]
115 elif len(line_items) == 2:
116 configurations[line_items[1]] = ''
117
118 logging.debug(configurations)
119
120 return configurations
121
Kevin Peng5c3fee72022-02-16 22:25:22 +0800122def manifest_validation(manifest, pid):
Mingyang Sun294ce2e2021-06-11 11:58:24 +0800123 """
124 This function validates FF-M compliance for partition manifest, and sets
125 default values for optional attributes.
Kevin Pengce99e5d2021-11-09 18:06:53 +0800126 The validation is skipped for TF-M specific Partitions (PID < TFM_PID_BASE).
Mingyang Sun294ce2e2021-06-11 11:58:24 +0800127 """
Kevin Peng5bc82d22021-10-19 11:18:40 +0800128
Kevin Peng5c3fee72022-02-16 22:25:22 +0800129 service_list = manifest.get('services', [])
130 irq_list = manifest.get('irqs', [])
Mingyang Sun294ce2e2021-06-11 11:58:24 +0800131
Kevin Peng5c3fee72022-02-16 22:25:22 +0800132 # "psa_framework_version" validation
133 if manifest['psa_framework_version'] not in [1.0, 1.1]:
134 raise Exception('Invalid psa_framework_version of {}'.format(manifest['name']))
135
Chris Brandf1d6b6f2022-07-22 11:49:01 -0700136 # "type" validation
Kevin Peng5c3fee72022-02-16 22:25:22 +0800137 if manifest['type'] not in ['PSA-ROT', 'APPLICATION-ROT']:
138 raise Exception('Invalid type of {}'.format(manifest['name']))
139
Chris Brandf1d6b6f2022-07-22 11:49:01 -0700140 # "priority" validation
141 if manifest['priority'] not in ['HIGH', 'NORMAL', 'LOW']:
142 raise Exception('Invalid priority of {}'.format(manifest['name']))
143
Kevin Peng5c3fee72022-02-16 22:25:22 +0800144 # Every PSA Partition must have at least either a secure service or an IRQ
Kevin Pengce99e5d2021-11-09 18:06:53 +0800145 if (pid == None or pid >= TFM_PID_BASE) \
Kevin Peng8849b6a2021-11-09 14:17:35 +0800146 and len(service_list) == 0 and len(irq_list) == 0:
147 raise Exception('{} must declare at least either a secure service or an IRQ!'
Kevin Peng5c3fee72022-02-16 22:25:22 +0800148 .format(manifest['name']))
149
150 if manifest['psa_framework_version'] == 1.0:
151 # For 1.0 Partition, the model is IPC
152 manifest['model'] = 'IPC'
153
154 # "model" validation:
155 model = manifest.get('model', None)
156 if model == None:
157 raise Exception('{} is missing the "model" attribute'.format(manifest['name']))
158
159 # Assign a unified 'entry' for templates
160 if model == 'IPC':
161 # entry_point is mandatory for IPC Partitions
162 if 'entry_point' not in manifest.keys():
163 raise Exception('{} is missing the "entry_point" attribute'.format(manifest['name']))
164 manifest['entry'] = manifest['entry_point']
165 elif model == 'SFN':
166 if 'entry_init' in manifest.keys():
167 manifest['entry'] = manifest['entry_init']
168 else:
169 manifest['entry'] = 0
170 else:
171 raise Exception('Invalid "model" of {}'.format(manifest['name']))
Kevin Peng8849b6a2021-11-09 14:17:35 +0800172
173 # Service FF-M manifest validation
174 for service in service_list:
Kevin Peng5c3fee72022-02-16 22:25:22 +0800175 if manifest['psa_framework_version'] == 1.0:
176 service['connection_based'] = True
177 elif 'connection_based' not in service:
178 raise Exception("'connection_based' is mandatory in FF-M 1.1 service!")
179
Mingyang Sun294ce2e2021-06-11 11:58:24 +0800180 if 'version' not in service.keys():
181 service['version'] = 1
182 if 'version_policy' not in service.keys():
Kevin Peng5bc82d22021-10-19 11:18:40 +0800183 service['version_policy'] = 'STRICT'
Mingyang Sun294ce2e2021-06-11 11:58:24 +0800184
Kevin Peng5bc82d22021-10-19 11:18:40 +0800185 # SID duplication check
186 if service['sid'] in sid_list:
187 raise Exception('Service ID: {} has duplications!'.format(service['sid']))
188 else:
189 sid_list.append(service['sid'])
Ruiqi Jiang71d361c2021-06-23 17:45:55 +0100190
Kevin Peng5c3fee72022-02-16 22:25:22 +0800191 return manifest
Mingyang Sun294ce2e2021-06-11 11:58:24 +0800192
Xinyu Zhang7d0a5c82022-05-16 18:15:11 +0800193def check_circular_dependency(partitions, service_partition_map):
194 """
195 This function detects if there is any circular partition dependency chain.
196 If a circular dependency is detected, the script exits with error.
197
198 Inputs:
199 - partitions: dict of partition manifests
200 - service_partition_map: map between services and their owner Partitions
201 """
202
203 dependency_table = {}
204 for partition in partitions:
205 manifest = partition['manifest']
206 dependencies = manifest['dependencies'].copy() \
207 if 'dependencies' in manifest else []
208 dependencies += manifest['weak_dependencies'].copy() \
209 if 'weak_dependencies' in manifest else []
210 dependency_table[manifest['name']] = {
211 'dependencies': [service_partition_map[dependency]
212 for dependency in dependencies
213 if dependency in service_partition_map],
214 'validated': False
215 }
216
217 for partition in dependency_table.keys():
218 validate_dependency_chain(partition, dependency_table, [])
219
220def validate_dependency_chain(partition,
221 dependency_table,
222 dependency_chain):
223 """
224 Recursively validate if the given partition and its dependencies
225 have a circular dependency with the given dependency_chain.
226 Exit with error code once any circular is detected.
227
228 Inputs:
229 - partition: next partition to be checked
230 - dependency_table: dict of partitions and their dependencies
231 - dependency_chain: list of dependencies in current chain
232 """
233
234 dependency_chain.append(partition)
235 if partition in dependency_chain[:-1]:
236 logging.error(
237 'Circular dependency exists in chain: {}'.format(
238 ', '.join(dependency_chain)))
239 exit(1)
240 for dependency in dependency_table[partition]['dependencies']:
241 if dependency_table[dependency]['validated']:
242 continue
243 validate_dependency_chain(dependency, dependency_table, dependency_chain)
244 dependency_table[partition]['validated'] = True
245
Kevin Peng56979ee2022-05-12 12:11:31 +0800246def process_partition_manifests(manifest_lists, configs):
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100247 """
Xinyu Zhang7d0a5c82022-05-16 18:15:11 +0800248 Parse the input manifest lists, check if manifest settings are valid,
Chris Brandf1d6b6f2022-07-22 11:49:01 -0700249 generate the data base for generated files
Kevin Peng655f2392019-11-27 16:33:02 +0800250 and generate manifest header files.
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100251
252 Parameters
253 ----------
Kevin Peng65064c52021-10-27 17:12:17 +0800254 manifest_lists:
Kevin Peng56979ee2022-05-12 12:11:31 +0800255 A list of Secure Partition manifest lists
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100256
257 Returns
258 -------
Kevin Peng5bc82d22021-10-19 11:18:40 +0800259 The manifest data base.
Edison Ai48b2d9e2019-06-24 14:39:45 +0800260 """
Kevin Peng655f2392019-11-27 16:33:02 +0800261
Kevin Peng5bc82d22021-10-19 11:18:40 +0800262 context = {}
263
Ken Liu861b0782021-05-22 13:15:08 +0800264 partition_list = []
Kevin Peng65064c52021-10-27 17:12:17 +0800265 all_manifests = []
Kevin Peng56b0ea62021-10-18 11:32:57 +0800266 pid_list = []
267 no_pid_manifest_idx = []
Xinyu Zhang7d0a5c82022-05-16 18:15:11 +0800268 service_partition_map = {}
Xinyu Zhang90f08dc2022-01-12 15:55:17 +0800269 partition_statistics = {
Xinyu Zhang2bc4d572021-12-27 16:37:46 +0800270 'connection_based_srv_num': 0,
Kevin Peng47c26ec2022-03-11 11:49:52 +0800271 'ipc_partitions': [],
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800272 'flih_num': 0,
273 'slih_num': 0
Xinyu Zhang90f08dc2022-01-12 15:55:17 +0800274 }
Kevin Peng9f1a7542022-02-07 16:32:27 +0800275 config_impl = {
276 'CONFIG_TFM_SPM_BACKEND_SFN' : '0',
277 'CONFIG_TFM_SPM_BACKEND_IPC' : '0',
278 'CONFIG_TFM_PSA_API_SFN_CALL' : '0',
279 'CONFIG_TFM_PSA_API_CROSS_CALL' : '0',
280 'CONFIG_TFM_PSA_API_SUPERVISOR_CALL' : '0',
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800281 'CONFIG_TFM_CONNECTION_BASED_SERVICE_API' : '0',
282 'CONFIG_TFM_FLIH_API' : '0',
283 'CONFIG_TFM_SLIH_API' : '0'
Kevin Peng9f1a7542022-02-07 16:32:27 +0800284 }
Kevin Peng655f2392019-11-27 16:33:02 +0800285
Kevin Peng56979ee2022-05-12 12:11:31 +0800286 isolation_level = int(configs['TFM_ISOLATION_LEVEL'], base = 10)
287 backend = configs['CONFIG_TFM_SPM_BACKEND']
288
Kevin Peng65064c52021-10-27 17:12:17 +0800289 # Get all the manifests information as a dictionary
290 for i, item in enumerate(manifest_lists):
Kevin Peng56979ee2022-05-12 12:11:31 +0800291 if not os.path.isfile(item):
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500292 logging.error('Manifest list item [{}] must be a file'.format(i))
Kevin Peng65064c52021-10-27 17:12:17 +0800293 exit(1)
Kevin Peng655f2392019-11-27 16:33:02 +0800294
Kevin Peng65064c52021-10-27 17:12:17 +0800295 # The manifest list file generated by configure_file()
296 with open(item) as manifest_list_yaml_file:
297 manifest_dic = yaml.safe_load(manifest_list_yaml_file)['manifest_list']
298 for dict in manifest_dic:
Kevin Peng56979ee2022-05-12 12:11:31 +0800299 # Replace environment variables in the manifest path and convert to absolute path.
300 # If it's already abspath, the path will not be changed.
301 manifest_path = os.path.join(os.path.dirname(item), # path of manifest list
302 os.path.expandvars(dict['manifest']))\
303 .replace('\\', '/')
304 dict['manifest'] = manifest_path
Kevin Peng65064c52021-10-27 17:12:17 +0800305 all_manifests.append(dict)
306
Chris Brand4b69fcf2022-06-06 09:37:49 -0700307 logging.info("------------ Display partition configuration - start ------------")
308
Kevin Peng65064c52021-10-27 17:12:17 +0800309 # Parse the manifests
310 for i, manifest_item in enumerate(all_manifests):
Kevin Peng56979ee2022-05-12 12:11:31 +0800311 valid_enabled_conditions = ['1', 'on', 'true', 'enabled']
312 valid_disabled_conditions = ['0', 'off', 'false', 'disabled', '']
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800313 is_enabled = ''
314
315 if 'conditional' in manifest_item.keys():
Kevin Peng56979ee2022-05-12 12:11:31 +0800316 is_enabled = configs[manifest_item['conditional']].lower()
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800317 else:
Kevin Peng56979ee2022-05-12 12:11:31 +0800318 # Partitions without 'conditional' is always on
319 is_enabled = '1'
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800320
321 if is_enabled in valid_disabled_conditions:
Anton Komlev58dde462022-07-07 13:50:02 +0100322 logging.info(" {:40s} OFF".format(manifest_item['name']))
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800323 continue
Chris Brand4b69fcf2022-06-06 09:37:49 -0700324 elif is_enabled in valid_enabled_conditions:
Anton Komlev58dde462022-07-07 13:50:02 +0100325 logging.info(" {:40s} ON".format(manifest_item['name']))
Chris Brand4b69fcf2022-06-06 09:37:49 -0700326 else:
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800327 raise Exception('Invalid "conditional" attribute: "{}" for {}. '
328 'Please set to one of {} or {}, case-insensitive.'\
329 .format(manifest_item['conditional'],
330 manifest_item['name'],
331 valid_enabled_conditions, valid_disabled_conditions))
332
Xinyu Zhangc46ee1f2021-04-01 10:10:43 +0800333 # Check if partition ID is manually set
334 if 'pid' not in manifest_item.keys():
335 no_pid_manifest_idx.append(i)
Kevin Peng8849b6a2021-11-09 14:17:35 +0800336 pid = None
Kevin Peng56b0ea62021-10-18 11:32:57 +0800337 else:
Kevin Peng8849b6a2021-11-09 14:17:35 +0800338 pid = manifest_item['pid']
339
340 # Check if partition ID is duplicated
341 if pid in pid_list:
Antonio de Angelisbaa27642022-05-25 11:07:12 +0100342 raise Exception('PID No. {} has already been used!'.format(pid))
Kevin Peng8849b6a2021-11-09 14:17:35 +0800343 else:
344 pid_list.append(pid)
Xinyu Zhang19504a52021-03-31 16:26:20 +0800345
Kevin Peng56979ee2022-05-12 12:11:31 +0800346 manifest_path = manifest_item['manifest']
Kevin Peng5bc82d22021-10-19 11:18:40 +0800347 with open(manifest_path) as manifest_file:
Kevin Pengec0e5ce2022-02-17 13:48:51 +0800348 manifest = yaml.safe_load(manifest_file)
349 if manifest.get('model', None) == 'dual':
350 # If a Partition supports both models, it can set the "model" to "backend".
351 # The actual model used follows the backend being used.
352 manifest['model'] = backend
353 manifest = manifest_validation(manifest, pid)
Kevin Peng655f2392019-11-27 16:33:02 +0800354
Kevin Pengce99e5d2021-11-09 18:06:53 +0800355 if pid == None or pid >= TFM_PID_BASE:
Kevin Pengd08f3ba2021-11-18 15:18:56 +0800356 # Count the number of IPC/SFN partitions
Kevin Peng47c26ec2022-03-11 11:49:52 +0800357 if manifest['model'] == 'IPC':
358 partition_statistics['ipc_partitions'].append(manifest['name'])
Mingyang Suneab7eae2021-09-30 13:06:52 +0800359
Kevin Peng5c3fee72022-02-16 22:25:22 +0800360 # Set initial value to -1 to make (srv_idx + 1) reflect the correct
361 # number (0) when there are no services.
362 srv_idx = -1
363 for srv_idx, service in enumerate(manifest.get('services', [])):
Xinyu Zhang7d0a5c82022-05-16 18:15:11 +0800364 service_partition_map[service['name']] = manifest['name']
Kevin Peng5c3fee72022-02-16 22:25:22 +0800365 if manifest['model'] == 'IPC':
366 # Assign signal value, the first 4 bits are reserved by FF-M
367 service['signal_value'] = (1 << (srv_idx + 4))
368 else:
369 # Signals of SFN Partitions are SPM internal only, does not
370 # need to reserve 4 bits.
371 service['signal_value'] = (1 << srv_idx)
372 if service['connection_based']:
Xinyu Zhang2bc4d572021-12-27 16:37:46 +0800373 partition_statistics['connection_based_srv_num'] += 1
Kevin Peng5c3fee72022-02-16 22:25:22 +0800374 logging.debug('{} has {} services'.format(manifest['name'], srv_idx +1))
Xinyu Zhang2bc4d572021-12-27 16:37:46 +0800375
Kevin Peng5c3fee72022-02-16 22:25:22 +0800376 # Set initial value to -1 to make (irq + 1) reflect the correct
377 # number (0) when there are no irqs.
378 irq_idx = -1
379 for irq_idx, irq in enumerate(manifest.get('irqs', [])):
380 # Assign signal value, from the most significant bit
381 irq['signal_value'] = (1 << (31 - irq_idx))
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800382 if irq.get('handling', None) == 'FLIH':
383 partition_statistics['flih_num'] += 1
384 else:
385 partition_statistics['slih_num'] += 1
Kevin Peng5c3fee72022-02-16 22:25:22 +0800386 logging.debug('{} has {} IRQS'.format(manifest['name'], irq_idx +1))
387
388 if ((srv_idx + 1) + (irq_idx + 1)) > 28:
389 raise Exception('Total number of Services and IRQs of {} exceeds the limit (28)'
390 .format(manifest['name']))
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800391
Kevin Peng65064c52021-10-27 17:12:17 +0800392 manifest_out_basename = os.path.splitext(os.path.basename(manifest_path))[0]
Kevin Peng655f2392019-11-27 16:33:02 +0800393
Kevin Peng4fade072021-10-26 17:57:50 +0800394 if 'output_path' in manifest_item:
Kevin Peng4fade072021-10-26 17:57:50 +0800395 output_path = os.path.expandvars(manifest_item['output_path'])
Kevin Peng4fade072021-10-26 17:57:50 +0800396 else:
Kevin Peng65064c52021-10-27 17:12:17 +0800397 output_path = ''
David Hub2694202021-07-15 14:58:39 +0800398
Kevin Peng5bc82d22021-10-19 11:18:40 +0800399 manifest_head_file = os.path.join(OUT_DIR, output_path, 'psa_manifest',
400 '{}.h'.format(manifest_out_basename))\
401 .replace('\\', '/')
402 intermedia_file = os.path.join(OUT_DIR, output_path, 'auto_generated',
403 'intermedia_{}.c'.format(manifest_out_basename))\
404 .replace('\\', '/')
405 load_info_file = os.path.join(OUT_DIR, output_path, 'auto_generated',
406 'load_info_{}.c'.format(manifest_out_basename))\
407 .replace('\\', '/')
Jianliang Shen785ed5e2022-02-08 14:16:06 +0800408 output_dir = os.path.join(OUT_DIR, output_path).replace('\\', '/')
Kevin Peng655f2392019-11-27 16:33:02 +0800409
Kevin Peng5bc82d22021-10-19 11:18:40 +0800410 partition_list.append({'manifest': manifest, 'attr': manifest_item,
411 'manifest_out_basename': manifest_out_basename,
412 'header_file': manifest_head_file,
413 'intermedia_file': intermedia_file,
Jianliang Shen785ed5e2022-02-08 14:16:06 +0800414 'loadinfo_file': load_info_file,
415 'output_dir':output_dir})
Ken Liu861b0782021-05-22 13:15:08 +0800416
Chris Brand4b69fcf2022-06-06 09:37:49 -0700417 logging.info("------------ Display partition configuration - end ------------")
418
Xinyu Zhang7d0a5c82022-05-16 18:15:11 +0800419 check_circular_dependency(partition_list, service_partition_map)
420
Kevin Peng56b0ea62021-10-18 11:32:57 +0800421 # Automatically assign PIDs for partitions without 'pid' attribute
Kevin Pengce99e5d2021-11-09 18:06:53 +0800422 pid = max(pid_list, default = TFM_PID_BASE - 1)
Kevin Peng56b0ea62021-10-18 11:32:57 +0800423 for idx in no_pid_manifest_idx:
Kevin Pengc424eec2021-06-25 17:26:11 +0800424 pid += 1
Kevin Peng65064c52021-10-27 17:12:17 +0800425 all_manifests[idx]['pid'] = pid
Kevin Peng56b0ea62021-10-18 11:32:57 +0800426 pid_list.append(pid)
427
Kevin Peng9f1a7542022-02-07 16:32:27 +0800428 # Set up configurations
Kevin Peng76c0c162022-02-09 22:49:06 +0800429 if backend == 'SFN':
Kevin Peng47c26ec2022-03-11 11:49:52 +0800430 if len(partition_statistics['ipc_partitions']) > 0:
431 logging.error('SFN backend does not support IPC Partitions:')
432 logging.error(partition_statistics['ipc_partitions'])
Kevin Peng9f1a7542022-02-07 16:32:27 +0800433 exit(1)
Kevin Peng76c0c162022-02-09 22:49:06 +0800434
435 if isolation_level > 1:
436 logging.error('SFN backend does not support high isolation levels.')
437 exit(1)
438
Kevin Peng9f1a7542022-02-07 16:32:27 +0800439 config_impl['CONFIG_TFM_SPM_BACKEND_SFN'] = '1'
440 config_impl['CONFIG_TFM_PSA_API_SFN_CALL'] = '1'
Kevin Peng76c0c162022-02-09 22:49:06 +0800441 elif backend == 'IPC':
Kevin Peng9f1a7542022-02-07 16:32:27 +0800442 config_impl['CONFIG_TFM_SPM_BACKEND_IPC'] = '1'
443 if isolation_level > 1:
444 config_impl['CONFIG_TFM_PSA_API_SUPERVISOR_CALL'] = '1'
445 else:
446 config_impl['CONFIG_TFM_PSA_API_CROSS_CALL'] = '1'
Kevin Peng9f1a7542022-02-07 16:32:27 +0800447
448 if partition_statistics['connection_based_srv_num'] > 0:
449 config_impl['CONFIG_TFM_CONNECTION_BASED_SERVICE_API'] = 1
450
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800451 if partition_statistics['flih_num'] > 0:
452 config_impl['CONFIG_TFM_FLIH_API'] = 1
Joakim Anderssoneec5cd32022-06-10 12:02:07 +0200453 if partition_statistics['slih_num'] > 0:
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800454 config_impl['CONFIG_TFM_SLIH_API'] = 1
455
Kevin Peng5bc82d22021-10-19 11:18:40 +0800456 context['partitions'] = partition_list
Kevin Peng9f1a7542022-02-07 16:32:27 +0800457 context['config_impl'] = config_impl
Kevin Pengce99e5d2021-11-09 18:06:53 +0800458 context['stateless_services'] = process_stateless_services(partition_list)
Ruiqi Jiang71d361c2021-06-23 17:45:55 +0100459
Kevin Peng5bc82d22021-10-19 11:18:40 +0800460 return context
Ken Liu861b0782021-05-22 13:15:08 +0800461
462def gen_per_partition_files(context):
463 """
464 Generate per-partition files
465
466 Parameters
467 ----------
468 context:
469 context contains partition infos
470 """
471
Kevin Peng5bc82d22021-10-19 11:18:40 +0800472 partition_context = {}
Sherry Zhangf58f2bd2022-01-10 17:21:11 +0800473 partition_context['utilities'] = context['utilities']
Sherry Zhangf865c4e2022-03-23 13:53:38 +0800474 partition_context['config_impl'] = context['config_impl']
Ken Liu861b0782021-05-22 13:15:08 +0800475
Kevin Peng5bc82d22021-10-19 11:18:40 +0800476 manifesttemplate = ENV.get_template(os.path.join(sys.path[0], 'templates/manifestfilename.template'))
477 memorytemplate = ENV.get_template(os.path.join(sys.path[0], 'templates/partition_intermedia.template'))
478 infotemplate = ENV.get_template(os.path.join(sys.path[0], 'templates/partition_load_info.template'))
Ken Liu861b0782021-05-22 13:15:08 +0800479
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500480 logging.info ("Start to generate partition files:")
Ken Liu861b0782021-05-22 13:15:08 +0800481
482 for one_partition in context['partitions']:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800483 partition_context['manifest'] = one_partition['manifest']
484 partition_context['attr'] = one_partition['attr']
485 partition_context['manifest_out_basename'] = one_partition['manifest_out_basename']
Ken Liu861b0782021-05-22 13:15:08 +0800486
Jianliang Shen785ed5e2022-02-08 14:16:06 +0800487 logging.info ('Generating {} in {}'.format(one_partition['attr']['name'],
488 one_partition['output_dir']))
Ken Liu861b0782021-05-22 13:15:08 +0800489 outfile_path = os.path.dirname(one_partition['header_file'])
Kevin Peng655f2392019-11-27 16:33:02 +0800490 if not os.path.exists(outfile_path):
491 os.makedirs(outfile_path)
492
Kevin Peng5bc82d22021-10-19 11:18:40 +0800493 headerfile = io.open(one_partition['header_file'], 'w', newline=None)
494 headerfile.write(manifesttemplate.render(partition_context))
Ken Liu861b0782021-05-22 13:15:08 +0800495 headerfile.close()
Kevin Peng655f2392019-11-27 16:33:02 +0800496
Ken Liu861b0782021-05-22 13:15:08 +0800497 intermediafile_path = os.path.dirname(one_partition['intermedia_file'])
Mingyang Sund20999f2020-10-15 14:53:12 +0800498 if not os.path.exists(intermediafile_path):
499 os.makedirs(intermediafile_path)
Kevin Peng5bc82d22021-10-19 11:18:40 +0800500 intermediafile = io.open(one_partition['intermedia_file'], 'w', newline=None)
501 intermediafile.write(memorytemplate.render(partition_context))
Ken Liu861b0782021-05-22 13:15:08 +0800502 intermediafile.close()
Mingyang Sund20999f2020-10-15 14:53:12 +0800503
Ken Liu861b0782021-05-22 13:15:08 +0800504 infofile_path = os.path.dirname(one_partition['loadinfo_file'])
Mingyang Sunf6a78572021-04-02 16:51:05 +0800505 if not os.path.exists(infofile_path):
506 os.makedirs(infofile_path)
Kevin Peng5bc82d22021-10-19 11:18:40 +0800507 infooutfile = io.open(one_partition['loadinfo_file'], 'w', newline=None)
508 infooutfile.write(infotemplate.render(partition_context))
Ken Liu861b0782021-05-22 13:15:08 +0800509 infooutfile.close()
Mingyang Sunf6a78572021-04-02 16:51:05 +0800510
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500511 logging.info ("Per-partition files done:")
Mingyang Sunf6a78572021-04-02 16:51:05 +0800512
Ken Liu861b0782021-05-22 13:15:08 +0800513def gen_summary_files(context, gen_file_lists):
Kevin Peng655f2392019-11-27 16:33:02 +0800514 """
515 Generate files according to the gen_file_list
Edison Ai48b2d9e2019-06-24 14:39:45 +0800516
517 Parameters
518 ----------
Raef Colesf42f0882020-07-10 10:01:58 +0100519 gen_file_lists:
520 The lists of files to generate
Edison Ai48b2d9e2019-06-24 14:39:45 +0800521 """
Kevin Peng655f2392019-11-27 16:33:02 +0800522 file_list = []
Shawn Shana9ad1e02019-08-07 15:49:48 +0800523
Raef Colesf42f0882020-07-10 10:01:58 +0100524 for f in gen_file_lists:
525 with open(f) as file_list_yaml_file:
Kevin Peng655f2392019-11-27 16:33:02 +0800526 file_list_yaml = yaml.safe_load(file_list_yaml_file)
Kevin Peng5bc82d22021-10-19 11:18:40 +0800527 file_list.extend(file_list_yaml['file_list'])
Edison Ai48b2d9e2019-06-24 14:39:45 +0800528
Kevin Peng655f2392019-11-27 16:33:02 +0800529 for file in file_list:
Raef Coles558487a2020-10-29 13:09:44 +0000530 # Replace environment variables in the output filepath
Kevin Peng5bc82d22021-10-19 11:18:40 +0800531 manifest_out_file = os.path.expandvars(file['output'])
Raef Coles558487a2020-10-29 13:09:44 +0000532 # Replace environment variables in the template filepath
Kevin Peng5bc82d22021-10-19 11:18:40 +0800533 templatefile_name = os.path.expandvars(file['template'])
Edison Ai48b2d9e2019-06-24 14:39:45 +0800534
Kevin Peng4fade072021-10-26 17:57:50 +0800535 manifest_out_file = os.path.join(OUT_DIR, manifest_out_file)
Edison Ai48b2d9e2019-06-24 14:39:45 +0800536
Ken Liu861b0782021-05-22 13:15:08 +0800537 outfile_path = os.path.dirname(manifest_out_file)
Kevin Peng655f2392019-11-27 16:33:02 +0800538 if not os.path.exists(outfile_path):
539 os.makedirs(outfile_path)
Edison Ai48b2d9e2019-06-24 14:39:45 +0800540
Kevin Peng655f2392019-11-27 16:33:02 +0800541 template = ENV.get_template(templatefile_name)
Edison Ai6e3f2a32019-06-11 15:29:05 +0800542
Kevin Peng5bc82d22021-10-19 11:18:40 +0800543 outfile = io.open(manifest_out_file, 'w', newline=None)
Kevin Peng655f2392019-11-27 16:33:02 +0800544 outfile.write(template.render(context))
545 outfile.close()
Edison Ai48b2d9e2019-06-24 14:39:45 +0800546
Kevin Pengce99e5d2021-11-09 18:06:53 +0800547def process_stateless_services(partitions):
Mingyang Suna1ca6112021-01-11 11:34:59 +0800548 """
549 This function collects all stateless services together, and allocates
Mingyang Sun4ecea992021-03-30 17:56:26 +0800550 stateless handles for them.
Kevin Pengc05319d2021-04-22 22:59:35 +0800551 Valid stateless handle in service will be converted to an index. If the
552 stateless handle is set as "auto", or not set, framework will allocate a
553 valid index for the service.
554 Framework puts each service into a reordered stateless service list at
555 position of "index". Other unused positions are left None.
Mingyang Suna1ca6112021-01-11 11:34:59 +0800556 """
Kevin Pengce99e5d2021-11-09 18:06:53 +0800557
558 STATIC_HANDLE_CONFIG_FILE = 'secure_fw/spm/cmsis_psa/spm_ipc.h'
559
Kevin Pengc05319d2021-04-22 22:59:35 +0800560 collected_stateless_services = []
Kevin Pengce99e5d2021-11-09 18:06:53 +0800561 stateless_index_max_num = \
562 int(get_single_macro_def_from_file(STATIC_HANDLE_CONFIG_FILE, 'STATIC_HANDLE_NUM_LIMIT'), base = 10)
Mingyang Suna1ca6112021-01-11 11:34:59 +0800563
564 # Collect all stateless services first.
565 for partition in partitions:
566 # Skip the FF-M 1.0 partitions
567 if partition['manifest']['psa_framework_version'] < 1.1:
568 continue
Kevin Peng8849b6a2021-11-09 14:17:35 +0800569
570 service_list = partition['manifest'].get('services', [])
571
572 for service in service_list:
Mingyang Suna1ca6112021-01-11 11:34:59 +0800573 if service['connection_based'] is False:
Kevin Pengc05319d2021-04-22 22:59:35 +0800574 collected_stateless_services.append(service)
Mingyang Suna1ca6112021-01-11 11:34:59 +0800575
Kevin Pengc05319d2021-04-22 22:59:35 +0800576 if len(collected_stateless_services) == 0:
Mingyang Suna1ca6112021-01-11 11:34:59 +0800577 return []
578
Ken Liu861b0782021-05-22 13:15:08 +0800579 if len(collected_stateless_services) > stateless_index_max_num:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800580 raise Exception('Stateless service numbers range exceed {number}.'.format(number=stateless_index_max_num))
Mingyang Suna1ca6112021-01-11 11:34:59 +0800581
582 """
Kevin Pengc05319d2021-04-22 22:59:35 +0800583 Allocate an empty stateless service list to store services.
584 Use "handle - 1" as the index for service, since handle value starts from
585 1 and list index starts from 0.
Mingyang Suna1ca6112021-01-11 11:34:59 +0800586 """
Ken Liu861b0782021-05-22 13:15:08 +0800587 reordered_stateless_services = [None] * stateless_index_max_num
Kevin Pengc05319d2021-04-22 22:59:35 +0800588 auto_alloc_services = []
Mingyang Suna1ca6112021-01-11 11:34:59 +0800589
Kevin Pengc05319d2021-04-22 22:59:35 +0800590 for service in collected_stateless_services:
591 # If not set, it is "auto" by default
592 if 'stateless_handle' not in service:
593 auto_alloc_services.append(service)
594 continue
595
Mingyang Sun4ecea992021-03-30 17:56:26 +0800596 service_handle = service['stateless_handle']
Mingyang Suna1ca6112021-01-11 11:34:59 +0800597
Mingyang Sun4ecea992021-03-30 17:56:26 +0800598 # Fill in service list with specified stateless handle, otherwise skip
599 if isinstance(service_handle, int):
Ken Liu861b0782021-05-22 13:15:08 +0800600 if service_handle < 1 or service_handle > stateless_index_max_num:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800601 raise Exception('Invalid stateless_handle setting: {handle}.'.format(handle=service['stateless_handle']))
Mingyang Sun4ecea992021-03-30 17:56:26 +0800602 # Convert handle index to reordered service list index
603 service_handle = service_handle - 1
604
605 if reordered_stateless_services[service_handle] is not None:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800606 raise Exception('Duplicated stateless_handle setting: {handle}.'.format(handle=service['stateless_handle']))
Mingyang Sun4ecea992021-03-30 17:56:26 +0800607 reordered_stateless_services[service_handle] = service
Kevin Pengc05319d2021-04-22 22:59:35 +0800608 elif service_handle == 'auto':
609 auto_alloc_services.append(service)
610 else:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800611 raise Exception('Invalid stateless_handle setting: {handle}.'.format(handle=service['stateless_handle']))
Mingyang Sun4ecea992021-03-30 17:56:26 +0800612
Kevin Pengce99e5d2021-11-09 18:06:53 +0800613 STATIC_HANDLE_IDX_BIT_WIDTH = \
614 int(get_single_macro_def_from_file(STATIC_HANDLE_CONFIG_FILE, 'STATIC_HANDLE_IDX_BIT_WIDTH'), base = 10)
615 STATIC_HANDLE_IDX_MASK = (1 << STATIC_HANDLE_IDX_BIT_WIDTH) - 1
616
617 STATIC_HANDLE_INDICATOR_OFFSET = \
618 int(get_single_macro_def_from_file(STATIC_HANDLE_CONFIG_FILE, 'STATIC_HANDLE_INDICATOR_OFFSET'), base = 10)
619
620 STATIC_HANDLE_VER_OFFSET = \
621 int(get_single_macro_def_from_file(STATIC_HANDLE_CONFIG_FILE, 'STATIC_HANDLE_VER_OFFSET'), base = 10)
622
623 STATIC_HANDLE_VER_BIT_WIDTH = \
624 int(get_single_macro_def_from_file(STATIC_HANDLE_CONFIG_FILE, 'STATIC_HANDLE_VER_BIT_WIDTH'), base = 10)
625 STATIC_HANDLE_VER_MASK = (1 << STATIC_HANDLE_VER_BIT_WIDTH) - 1
626
Mingyang Sun4ecea992021-03-30 17:56:26 +0800627 # Auto-allocate stateless handle and encode the stateless handle
Ken Liu861b0782021-05-22 13:15:08 +0800628 for i in range(0, stateless_index_max_num):
Mingyang Sun4ecea992021-03-30 17:56:26 +0800629 service = reordered_stateless_services[i]
630
Kevin Pengc05319d2021-04-22 22:59:35 +0800631 if service == None and len(auto_alloc_services) > 0:
632 service = auto_alloc_services.pop(0)
Mingyang Sun4ecea992021-03-30 17:56:26 +0800633
Mingyang Sun453ad402021-03-17 17:58:33 +0800634 """
635 Encode stateless flag and version into stateless handle
Kevin Pengce99e5d2021-11-09 18:06:53 +0800636 Check STATIC_HANDLE_CONFIG_FILE for details
Mingyang Sun453ad402021-03-17 17:58:33 +0800637 """
Mingyang Sun4ecea992021-03-30 17:56:26 +0800638 stateless_handle_value = 0
639 if service != None:
Kevin Pengce99e5d2021-11-09 18:06:53 +0800640 stateless_index = (i & STATIC_HANDLE_IDX_MASK)
Mingyang Sun4ecea992021-03-30 17:56:26 +0800641 stateless_handle_value |= stateless_index
Kevin Pengce99e5d2021-11-09 18:06:53 +0800642 stateless_handle_value |= (1 << STATIC_HANDLE_INDICATOR_OFFSET)
643 stateless_version = (service['version'] & STATIC_HANDLE_VER_MASK) << STATIC_HANDLE_VER_OFFSET
Mingyang Sun453ad402021-03-17 17:58:33 +0800644 stateless_handle_value |= stateless_version
Mingyang Sun4ecea992021-03-30 17:56:26 +0800645 service['stateless_handle_value'] = '0x{0:08x}'.format(stateless_handle_value)
Ken Liu861b0782021-05-22 13:15:08 +0800646 service['stateless_handle_index'] = stateless_index
Mingyang Suna1ca6112021-01-11 11:34:59 +0800647
Mingyang Sun4ecea992021-03-30 17:56:26 +0800648 reordered_stateless_services[i] = service
649
650 return reordered_stateless_services
Mingyang Suna1ca6112021-01-11 11:34:59 +0800651
Kevin Peng655f2392019-11-27 16:33:02 +0800652def parse_args():
Raef Coles558487a2020-10-29 13:09:44 +0000653 parser = argparse.ArgumentParser(description='Parse secure partition manifest list and generate files listed by the file list',
654 epilog='Note that environment variables in template files will be replaced with their values')
655
Kevin Peng655f2392019-11-27 16:33:02 +0800656 parser.add_argument('-o', '--outdir'
657 , dest='outdir'
Kevin Peng4fade072021-10-26 17:57:50 +0800658 , required=True
Kevin Peng655f2392019-11-27 16:33:02 +0800659 , metavar='out_dir'
Kevin Peng4fade072021-10-26 17:57:50 +0800660 , help='The root directory for generated files')
Shawn Shana9ad1e02019-08-07 15:49:48 +0800661
Kevin Peng5bc82d22021-10-19 11:18:40 +0800662 parser.add_argument('-m', '--manifest-lists'
Raef Colesf42f0882020-07-10 10:01:58 +0100663 , nargs='+'
Kevin Peng5bc82d22021-10-19 11:18:40 +0800664 , dest='manifest_lists'
Raef Colesf42f0882020-07-10 10:01:58 +0100665 , required=True
Kevin Peng65064c52021-10-27 17:12:17 +0800666 , metavar='manifest list'
667 , help='A list of Secure Partition manifest lists and their original paths.\n\
668 The manifest lists might be processed by CMake and\n\
669 the path might be different to the original one\n\
670 The format must be [list A, orignal path A, list B, orignal path B, ...]')
Kevin Peng655f2392019-11-27 16:33:02 +0800671
672 parser.add_argument('-f', '--file-list'
Raef Colesf42f0882020-07-10 10:01:58 +0100673 , nargs='+'
Kevin Peng655f2392019-11-27 16:33:02 +0800674 , dest='gen_file_args'
Raef Colesf42f0882020-07-10 10:01:58 +0100675 , required=True
Kevin Peng655f2392019-11-27 16:33:02 +0800676 , metavar='file-list'
Chris Brandf1d6b6f2022-07-22 11:49:01 -0700677 , help='These files describe the file list to generate')
Kevin Peng9f1a7542022-02-07 16:32:27 +0800678
Kevin Peng56979ee2022-05-12 12:11:31 +0800679 parser.add_argument('-c', '--config-files'
680 , nargs='+'
681 , dest='config_files'
Kevin Peng9f1a7542022-02-07 16:32:27 +0800682 , required=True
Kevin Peng56979ee2022-05-12 12:11:31 +0800683 , metavar='config-files'
684 , help='A header file contains build configurations')
Kevin Peng9f1a7542022-02-07 16:32:27 +0800685
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500686 parser.add_argument('-q', '--quiet'
687 , dest='quiet'
688 , required=False
689 , default=False
690 , action='store_true'
691 , help='Reduce log messages')
Kevin Peng655f2392019-11-27 16:33:02 +0800692
693 args = parser.parse_args()
Kevin Peng655f2392019-11-27 16:33:02 +0800694
Kevin Peng655f2392019-11-27 16:33:02 +0800695 return args
696
697ENV = Environment(
698 loader = TemplateLoader(),
699 autoescape = select_autoescape(['html', 'xml']),
700 lstrip_blocks = True,
701 trim_blocks = True,
702 keep_trailing_newline = True
703 )
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100704
Miklos Balint470919c2018-05-22 17:51:29 +0200705def main():
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100706 """
707 The entry point of the script.
708
709 Generates the output files based on the templates and the manifests.
710 """
Shawn Shana9ad1e02019-08-07 15:49:48 +0800711
Kevin Peng655f2392019-11-27 16:33:02 +0800712 global OUT_DIR
Shawn Shana9ad1e02019-08-07 15:49:48 +0800713
Kevin Peng655f2392019-11-27 16:33:02 +0800714 args = parse_args()
Shawn Shana9ad1e02019-08-07 15:49:48 +0800715
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500716 logging.basicConfig(format='%(message)s'
717 , level=logging.WARNING if args.quiet else logging.INFO)
718
Kevin Peng5bc82d22021-10-19 11:18:40 +0800719 OUT_DIR = os.path.abspath(args.outdir)
Kevin Peng655f2392019-11-27 16:33:02 +0800720
Kevin Peng5bc82d22021-10-19 11:18:40 +0800721 manifest_lists = [os.path.abspath(x) for x in args.manifest_lists]
722 gen_file_lists = [os.path.abspath(x) for x in args.gen_file_args]
Shawn Shana9ad1e02019-08-07 15:49:48 +0800723
Shawn Shana9ad1e02019-08-07 15:49:48 +0800724 """
Kevin Peng655f2392019-11-27 16:33:02 +0800725 Relative path to TF-M root folder is supported in the manifests
726 and default value of manifest list and generated file list are relative to TF-M root folder as well,
727 so first change directory to TF-M root folder.
Shawn Shana9ad1e02019-08-07 15:49:48 +0800728 By doing this, the script can be executed anywhere
Kevin Peng655f2392019-11-27 16:33:02 +0800729 The script is located in <TF-M root folder>/tools, so sys.path[0]<location of the script>/.. is TF-M root folder.
Shawn Shana9ad1e02019-08-07 15:49:48 +0800730 """
Kevin Peng5bc82d22021-10-19 11:18:40 +0800731 os.chdir(os.path.join(sys.path[0], '..'))
Shawn Shana9ad1e02019-08-07 15:49:48 +0800732
Kevin Peng76c0c162022-02-09 22:49:06 +0800733 context = process_partition_manifests(manifest_lists,
Kevin Peng56979ee2022-05-12 12:11:31 +0800734 parse_configurations(args.config_files))
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100735
Edison Ai6e3f2a32019-06-11 15:29:05 +0800736 utilities = {}
Mingyang Suna1ca6112021-01-11 11:34:59 +0800737 utilities['donotedit_warning'] = donotedit_warning
Miklos Balint470919c2018-05-22 17:51:29 +0200738
Kevin Peng655f2392019-11-27 16:33:02 +0800739 context['utilities'] = utilities
Mingyang Suneab7eae2021-09-30 13:06:52 +0800740
Ken Liu861b0782021-05-22 13:15:08 +0800741 gen_per_partition_files(context)
Kevin Peng5bc82d22021-10-19 11:18:40 +0800742 gen_summary_files(context, gen_file_lists)
Miklos Balint470919c2018-05-22 17:51:29 +0200743
Kevin Peng5bc82d22021-10-19 11:18:40 +0800744if __name__ == '__main__':
Miklos Balint470919c2018-05-22 17:51:29 +0200745 main()