blob: ed8d3fb7d7146744947a31a884806cede4c946fc [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 Pengfb1761b2022-05-12 12:11:31 +080061def parse_configurations(file_paths):
62 """
63 Parses the given config files and return a dict whose key-values are build
64 configurations and their values.
65
66 Valid configurations should be in the format of:
67 "#define VAR [...]" in a single line.
68 The value of the config is optional.
69 """
70 configurations = {}
71
72 lines = []
73 for file in file_paths:
74 with open(file, 'r') as config_file:
75 lines += config_file.readlines()
76
77 for line in lines:
78 if not line.startswith('#define'):
79 continue
80
81 line = line.rstrip('\r\n')
82 line_items = line.split(maxsplit=2)
83 if len(line_items) == 3:
84 configurations[line_items[1]] = line_items[2]
85 elif len(line_items) == 2:
86 configurations[line_items[1]] = ''
87
88 logging.debug(configurations)
89
90 return configurations
91
Kevin Peng5c3fee72022-02-16 22:25:22 +080092def manifest_validation(manifest, pid):
Mingyang Sun294ce2e2021-06-11 11:58:24 +080093 """
94 This function validates FF-M compliance for partition manifest, and sets
95 default values for optional attributes.
Kevin Pengce99e5d2021-11-09 18:06:53 +080096 The validation is skipped for TF-M specific Partitions (PID < TFM_PID_BASE).
Mingyang Sun294ce2e2021-06-11 11:58:24 +080097 """
Kevin Peng5bc82d22021-10-19 11:18:40 +080098
Kevin Peng5c3fee72022-02-16 22:25:22 +080099 service_list = manifest.get('services', [])
100 irq_list = manifest.get('irqs', [])
Mingyang Sun294ce2e2021-06-11 11:58:24 +0800101
Kevin Peng5c3fee72022-02-16 22:25:22 +0800102 # "psa_framework_version" validation
103 if manifest['psa_framework_version'] not in [1.0, 1.1]:
104 raise Exception('Invalid psa_framework_version of {}'.format(manifest['name']))
105
Chris Brandf1d6b6f2022-07-22 11:49:01 -0700106 # "type" validation
Kevin Peng5c3fee72022-02-16 22:25:22 +0800107 if manifest['type'] not in ['PSA-ROT', 'APPLICATION-ROT']:
108 raise Exception('Invalid type of {}'.format(manifest['name']))
109
Chris Brandf1d6b6f2022-07-22 11:49:01 -0700110 # "priority" validation
111 if manifest['priority'] not in ['HIGH', 'NORMAL', 'LOW']:
112 raise Exception('Invalid priority of {}'.format(manifest['name']))
113
Chris Brandc422cdd2022-07-21 13:32:47 -0700114 if 'ns_agent' not in manifest:
115 manifest['ns_agent'] = False
116
Kevin Peng5c3fee72022-02-16 22:25:22 +0800117 # Every PSA Partition must have at least either a secure service or an IRQ
Kevin Pengce99e5d2021-11-09 18:06:53 +0800118 if (pid == None or pid >= TFM_PID_BASE) \
Kevin Peng8849b6a2021-11-09 14:17:35 +0800119 and len(service_list) == 0 and len(irq_list) == 0:
120 raise Exception('{} must declare at least either a secure service or an IRQ!'
Kevin Peng5c3fee72022-02-16 22:25:22 +0800121 .format(manifest['name']))
122
123 if manifest['psa_framework_version'] == 1.0:
124 # For 1.0 Partition, the model is IPC
125 manifest['model'] = 'IPC'
126
127 # "model" validation:
128 model = manifest.get('model', None)
129 if model == None:
130 raise Exception('{} is missing the "model" attribute'.format(manifest['name']))
131
132 # Assign a unified 'entry' for templates
133 if model == 'IPC':
134 # entry_point is mandatory for IPC Partitions
135 if 'entry_point' not in manifest.keys():
136 raise Exception('{} is missing the "entry_point" attribute'.format(manifest['name']))
137 manifest['entry'] = manifest['entry_point']
138 elif model == 'SFN':
139 if 'entry_init' in manifest.keys():
140 manifest['entry'] = manifest['entry_init']
141 else:
142 manifest['entry'] = 0
143 else:
144 raise Exception('Invalid "model" of {}'.format(manifest['name']))
Kevin Peng8849b6a2021-11-09 14:17:35 +0800145
146 # Service FF-M manifest validation
147 for service in service_list:
Kevin Peng5c3fee72022-02-16 22:25:22 +0800148 if manifest['psa_framework_version'] == 1.0:
149 service['connection_based'] = True
150 elif 'connection_based' not in service:
151 raise Exception("'connection_based' is mandatory in FF-M 1.1 service!")
152
Mingyang Sun294ce2e2021-06-11 11:58:24 +0800153 if 'version' not in service.keys():
154 service['version'] = 1
155 if 'version_policy' not in service.keys():
Kevin Peng5bc82d22021-10-19 11:18:40 +0800156 service['version_policy'] = 'STRICT'
Mingyang Sun294ce2e2021-06-11 11:58:24 +0800157
Kevin Peng5bc82d22021-10-19 11:18:40 +0800158 # SID duplication check
159 if service['sid'] in sid_list:
160 raise Exception('Service ID: {} has duplications!'.format(service['sid']))
161 else:
162 sid_list.append(service['sid'])
Ruiqi Jiang71d361c2021-06-23 17:45:55 +0100163
Kevin Peng5c3fee72022-02-16 22:25:22 +0800164 return manifest
Mingyang Sun294ce2e2021-06-11 11:58:24 +0800165
Xinyu Zhang7d0a5c82022-05-16 18:15:11 +0800166def check_circular_dependency(partitions, service_partition_map):
167 """
168 This function detects if there is any circular partition dependency chain.
169 If a circular dependency is detected, the script exits with error.
170
171 Inputs:
172 - partitions: dict of partition manifests
173 - service_partition_map: map between services and their owner Partitions
174 """
175
176 dependency_table = {}
177 for partition in partitions:
178 manifest = partition['manifest']
179 dependencies = manifest['dependencies'].copy() \
180 if 'dependencies' in manifest else []
181 dependencies += manifest['weak_dependencies'].copy() \
182 if 'weak_dependencies' in manifest else []
183 dependency_table[manifest['name']] = {
184 'dependencies': [service_partition_map[dependency]
185 for dependency in dependencies
186 if dependency in service_partition_map],
187 'validated': False
188 }
189
190 for partition in dependency_table.keys():
191 validate_dependency_chain(partition, dependency_table, [])
192
193def validate_dependency_chain(partition,
194 dependency_table,
195 dependency_chain):
196 """
197 Recursively validate if the given partition and its dependencies
198 have a circular dependency with the given dependency_chain.
199 Exit with error code once any circular is detected.
200
201 Inputs:
202 - partition: next partition to be checked
203 - dependency_table: dict of partitions and their dependencies
204 - dependency_chain: list of dependencies in current chain
205 """
206
207 dependency_chain.append(partition)
208 if partition in dependency_chain[:-1]:
209 logging.error(
210 'Circular dependency exists in chain: {}'.format(
211 ', '.join(dependency_chain)))
212 exit(1)
213 for dependency in dependency_table[partition]['dependencies']:
214 if dependency_table[dependency]['validated']:
215 continue
216 validate_dependency_chain(dependency, dependency_table, dependency_chain)
217 dependency_table[partition]['validated'] = True
218
Kevin Pengfb1761b2022-05-12 12:11:31 +0800219def process_partition_manifests(manifest_lists, configs):
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100220 """
Xinyu Zhang7d0a5c82022-05-16 18:15:11 +0800221 Parse the input manifest lists, check if manifest settings are valid,
Chris Brandf1d6b6f2022-07-22 11:49:01 -0700222 generate the data base for generated files
Kevin Peng655f2392019-11-27 16:33:02 +0800223 and generate manifest header files.
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100224
225 Parameters
226 ----------
Kevin Peng65064c52021-10-27 17:12:17 +0800227 manifest_lists:
Kevin Pengfb1761b2022-05-12 12:11:31 +0800228 A list of Secure Partition manifest lists
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100229
230 Returns
231 -------
Kevin Peng5bc82d22021-10-19 11:18:40 +0800232 The manifest data base.
Edison Ai48b2d9e2019-06-24 14:39:45 +0800233 """
Kevin Peng655f2392019-11-27 16:33:02 +0800234
Kevin Peng5bc82d22021-10-19 11:18:40 +0800235 context = {}
236
Ken Liu861b0782021-05-22 13:15:08 +0800237 partition_list = []
Kevin Peng65064c52021-10-27 17:12:17 +0800238 all_manifests = []
Kevin Peng56b0ea62021-10-18 11:32:57 +0800239 pid_list = []
240 no_pid_manifest_idx = []
Xinyu Zhang7d0a5c82022-05-16 18:15:11 +0800241 service_partition_map = {}
Xinyu Zhang90f08dc2022-01-12 15:55:17 +0800242 partition_statistics = {
Xinyu Zhang2bc4d572021-12-27 16:37:46 +0800243 'connection_based_srv_num': 0,
Kevin Peng47c26ec2022-03-11 11:49:52 +0800244 'ipc_partitions': [],
Sherry Zhang2ed48fd2022-09-06 11:33:22 +0800245 'mmio_region_num': 0,
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800246 'flih_num': 0,
247 'slih_num': 0
Xinyu Zhang90f08dc2022-01-12 15:55:17 +0800248 }
Kevin Peng9f1a7542022-02-07 16:32:27 +0800249 config_impl = {
250 'CONFIG_TFM_SPM_BACKEND_SFN' : '0',
251 'CONFIG_TFM_SPM_BACKEND_IPC' : '0',
252 'CONFIG_TFM_PSA_API_SFN_CALL' : '0',
253 'CONFIG_TFM_PSA_API_CROSS_CALL' : '0',
254 'CONFIG_TFM_PSA_API_SUPERVISOR_CALL' : '0',
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800255 'CONFIG_TFM_CONNECTION_BASED_SERVICE_API' : '0',
Sherry Zhang2ed48fd2022-09-06 11:33:22 +0800256 'CONFIG_TFM_MMIO_REGION_ENABLE' : '0',
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800257 'CONFIG_TFM_FLIH_API' : '0',
258 'CONFIG_TFM_SLIH_API' : '0'
Kevin Peng9f1a7542022-02-07 16:32:27 +0800259 }
Kevin Peng655f2392019-11-27 16:33:02 +0800260
Kevin Pengfb1761b2022-05-12 12:11:31 +0800261 isolation_level = int(configs['TFM_ISOLATION_LEVEL'], base = 10)
262 backend = configs['CONFIG_TFM_SPM_BACKEND']
263
Kevin Peng65064c52021-10-27 17:12:17 +0800264 # Get all the manifests information as a dictionary
265 for i, item in enumerate(manifest_lists):
Kevin Pengfb1761b2022-05-12 12:11:31 +0800266 if not os.path.isfile(item):
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500267 logging.error('Manifest list item [{}] must be a file'.format(i))
Kevin Peng65064c52021-10-27 17:12:17 +0800268 exit(1)
Kevin Peng655f2392019-11-27 16:33:02 +0800269
Kevin Peng65064c52021-10-27 17:12:17 +0800270 # The manifest list file generated by configure_file()
271 with open(item) as manifest_list_yaml_file:
272 manifest_dic = yaml.safe_load(manifest_list_yaml_file)['manifest_list']
273 for dict in manifest_dic:
Kevin Pengfb1761b2022-05-12 12:11:31 +0800274 # Replace environment variables in the manifest path and convert to absolute path.
275 # If it's already abspath, the path will not be changed.
276 manifest_path = os.path.join(os.path.dirname(item), # path of manifest list
277 os.path.expandvars(dict['manifest']))\
278 .replace('\\', '/')
279 dict['manifest'] = manifest_path
Kevin Peng65064c52021-10-27 17:12:17 +0800280 all_manifests.append(dict)
281
Chris Brand4b69fcf2022-06-06 09:37:49 -0700282 logging.info("------------ Display partition configuration - start ------------")
283
Kevin Peng65064c52021-10-27 17:12:17 +0800284 # Parse the manifests
285 for i, manifest_item in enumerate(all_manifests):
Kevin Pengfb1761b2022-05-12 12:11:31 +0800286 valid_enabled_conditions = ['1', 'on', 'true', 'enabled']
287 valid_disabled_conditions = ['0', 'off', 'false', 'disabled', '']
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800288 is_enabled = ''
289
290 if 'conditional' in manifest_item.keys():
Kevin Pengfb1761b2022-05-12 12:11:31 +0800291 if manifest_item['conditional'] not in configs.keys():
292 logging.error('Configuration "{}" is not defined!'.format(manifest_item['conditional']))
293 exit(1)
294 is_enabled = configs[manifest_item['conditional']].lower()
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800295 else:
Kevin Pengfb1761b2022-05-12 12:11:31 +0800296 # Partitions without 'conditional' is always on
297 is_enabled = '1'
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800298
299 if is_enabled in valid_disabled_conditions:
BohdanHunko6cea95d2022-08-16 15:33:51 +0300300 logging.info(" {:40s} OFF".format(manifest_item['description']))
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800301 continue
Chris Brand4b69fcf2022-06-06 09:37:49 -0700302 elif is_enabled in valid_enabled_conditions:
BohdanHunko6cea95d2022-08-16 15:33:51 +0300303 logging.info(" {:40s} ON".format(manifest_item['description']))
Chris Brand4b69fcf2022-06-06 09:37:49 -0700304 else:
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800305 raise Exception('Invalid "conditional" attribute: "{}" for {}. '
306 'Please set to one of {} or {}, case-insensitive.'\
307 .format(manifest_item['conditional'],
BohdanHunko6cea95d2022-08-16 15:33:51 +0300308 manifest_item['description'],
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800309 valid_enabled_conditions, valid_disabled_conditions))
310
Xinyu Zhangc46ee1f2021-04-01 10:10:43 +0800311 # Check if partition ID is manually set
312 if 'pid' not in manifest_item.keys():
313 no_pid_manifest_idx.append(i)
Kevin Peng8849b6a2021-11-09 14:17:35 +0800314 pid = None
Kevin Peng56b0ea62021-10-18 11:32:57 +0800315 else:
Kevin Peng8849b6a2021-11-09 14:17:35 +0800316 pid = manifest_item['pid']
317
318 # Check if partition ID is duplicated
319 if pid in pid_list:
Antonio de Angelisbaa27642022-05-25 11:07:12 +0100320 raise Exception('PID No. {} has already been used!'.format(pid))
Kevin Peng8849b6a2021-11-09 14:17:35 +0800321 else:
322 pid_list.append(pid)
Xinyu Zhang19504a52021-03-31 16:26:20 +0800323
Kevin Pengfb1761b2022-05-12 12:11:31 +0800324 manifest_path = manifest_item['manifest']
Kevin Peng5bc82d22021-10-19 11:18:40 +0800325 with open(manifest_path) as manifest_file:
Kevin Pengec0e5ce2022-02-17 13:48:51 +0800326 manifest = yaml.safe_load(manifest_file)
327 if manifest.get('model', None) == 'dual':
328 # If a Partition supports both models, it can set the "model" to "backend".
329 # The actual model used follows the backend being used.
330 manifest['model'] = backend
331 manifest = manifest_validation(manifest, pid)
Kevin Peng655f2392019-11-27 16:33:02 +0800332
Kevin Pengce99e5d2021-11-09 18:06:53 +0800333 if pid == None or pid >= TFM_PID_BASE:
Kevin Pengd08f3ba2021-11-18 15:18:56 +0800334 # Count the number of IPC/SFN partitions
Kevin Peng47c26ec2022-03-11 11:49:52 +0800335 if manifest['model'] == 'IPC':
336 partition_statistics['ipc_partitions'].append(manifest['name'])
Mingyang Suneab7eae2021-09-30 13:06:52 +0800337
Kevin Peng5c3fee72022-02-16 22:25:22 +0800338 # Set initial value to -1 to make (srv_idx + 1) reflect the correct
339 # number (0) when there are no services.
340 srv_idx = -1
341 for srv_idx, service in enumerate(manifest.get('services', [])):
Xinyu Zhang7d0a5c82022-05-16 18:15:11 +0800342 service_partition_map[service['name']] = manifest['name']
Kevin Peng5c3fee72022-02-16 22:25:22 +0800343 if manifest['model'] == 'IPC':
344 # Assign signal value, the first 4 bits are reserved by FF-M
345 service['signal_value'] = (1 << (srv_idx + 4))
346 else:
347 # Signals of SFN Partitions are SPM internal only, does not
348 # need to reserve 4 bits.
349 service['signal_value'] = (1 << srv_idx)
350 if service['connection_based']:
Xinyu Zhang2bc4d572021-12-27 16:37:46 +0800351 partition_statistics['connection_based_srv_num'] += 1
Kevin Peng5c3fee72022-02-16 22:25:22 +0800352 logging.debug('{} has {} services'.format(manifest['name'], srv_idx +1))
Xinyu Zhang2bc4d572021-12-27 16:37:46 +0800353
Sherry Zhang2ed48fd2022-09-06 11:33:22 +0800354 # Calculate the number of mmio region
355 mmio_region_list = manifest.get('mmio_regions', [])
356 partition_statistics['mmio_region_num'] += len(mmio_region_list)
357
Kevin Peng5c3fee72022-02-16 22:25:22 +0800358 # Set initial value to -1 to make (irq + 1) reflect the correct
359 # number (0) when there are no irqs.
360 irq_idx = -1
361 for irq_idx, irq in enumerate(manifest.get('irqs', [])):
362 # Assign signal value, from the most significant bit
363 irq['signal_value'] = (1 << (31 - irq_idx))
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800364 if irq.get('handling', None) == 'FLIH':
365 partition_statistics['flih_num'] += 1
366 else:
367 partition_statistics['slih_num'] += 1
Kevin Peng5c3fee72022-02-16 22:25:22 +0800368 logging.debug('{} has {} IRQS'.format(manifest['name'], irq_idx +1))
369
370 if ((srv_idx + 1) + (irq_idx + 1)) > 28:
371 raise Exception('Total number of Services and IRQs of {} exceeds the limit (28)'
372 .format(manifest['name']))
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800373
Kevin Peng65064c52021-10-27 17:12:17 +0800374 manifest_out_basename = os.path.splitext(os.path.basename(manifest_path))[0]
Kevin Peng655f2392019-11-27 16:33:02 +0800375
Kevin Peng4fade072021-10-26 17:57:50 +0800376 if 'output_path' in manifest_item:
Kevin Peng4fade072021-10-26 17:57:50 +0800377 output_path = os.path.expandvars(manifest_item['output_path'])
Kevin Peng4fade072021-10-26 17:57:50 +0800378 else:
Kevin Peng65064c52021-10-27 17:12:17 +0800379 output_path = ''
David Hub2694202021-07-15 14:58:39 +0800380
Kevin Peng5bc82d22021-10-19 11:18:40 +0800381 manifest_head_file = os.path.join(OUT_DIR, output_path, 'psa_manifest',
382 '{}.h'.format(manifest_out_basename))\
383 .replace('\\', '/')
384 intermedia_file = os.path.join(OUT_DIR, output_path, 'auto_generated',
385 'intermedia_{}.c'.format(manifest_out_basename))\
386 .replace('\\', '/')
387 load_info_file = os.path.join(OUT_DIR, output_path, 'auto_generated',
388 'load_info_{}.c'.format(manifest_out_basename))\
389 .replace('\\', '/')
Jianliang Shen785ed5e2022-02-08 14:16:06 +0800390 output_dir = os.path.join(OUT_DIR, output_path).replace('\\', '/')
Kevin Peng655f2392019-11-27 16:33:02 +0800391
Kevin Peng5bc82d22021-10-19 11:18:40 +0800392 partition_list.append({'manifest': manifest, 'attr': manifest_item,
393 'manifest_out_basename': manifest_out_basename,
394 'header_file': manifest_head_file,
395 'intermedia_file': intermedia_file,
Jianliang Shen785ed5e2022-02-08 14:16:06 +0800396 'loadinfo_file': load_info_file,
397 'output_dir':output_dir})
Ken Liu861b0782021-05-22 13:15:08 +0800398
Chris Brand4b69fcf2022-06-06 09:37:49 -0700399 logging.info("------------ Display partition configuration - end ------------")
400
Xinyu Zhang7d0a5c82022-05-16 18:15:11 +0800401 check_circular_dependency(partition_list, service_partition_map)
402
Kevin Peng56b0ea62021-10-18 11:32:57 +0800403 # Automatically assign PIDs for partitions without 'pid' attribute
Kevin Pengce99e5d2021-11-09 18:06:53 +0800404 pid = max(pid_list, default = TFM_PID_BASE - 1)
Kevin Peng56b0ea62021-10-18 11:32:57 +0800405 for idx in no_pid_manifest_idx:
Kevin Pengc424eec2021-06-25 17:26:11 +0800406 pid += 1
Kevin Peng65064c52021-10-27 17:12:17 +0800407 all_manifests[idx]['pid'] = pid
Kevin Peng56b0ea62021-10-18 11:32:57 +0800408 pid_list.append(pid)
409
Kevin Peng9f1a7542022-02-07 16:32:27 +0800410 # Set up configurations
Kevin Peng76c0c162022-02-09 22:49:06 +0800411 if backend == 'SFN':
Kevin Peng47c26ec2022-03-11 11:49:52 +0800412 if len(partition_statistics['ipc_partitions']) > 0:
413 logging.error('SFN backend does not support IPC Partitions:')
414 logging.error(partition_statistics['ipc_partitions'])
Kevin Peng9f1a7542022-02-07 16:32:27 +0800415 exit(1)
Kevin Peng76c0c162022-02-09 22:49:06 +0800416
417 if isolation_level > 1:
418 logging.error('SFN backend does not support high isolation levels.')
419 exit(1)
420
Kevin Peng9f1a7542022-02-07 16:32:27 +0800421 config_impl['CONFIG_TFM_SPM_BACKEND_SFN'] = '1'
422 config_impl['CONFIG_TFM_PSA_API_SFN_CALL'] = '1'
Kevin Peng76c0c162022-02-09 22:49:06 +0800423 elif backend == 'IPC':
Kevin Peng9f1a7542022-02-07 16:32:27 +0800424 config_impl['CONFIG_TFM_SPM_BACKEND_IPC'] = '1'
425 if isolation_level > 1:
426 config_impl['CONFIG_TFM_PSA_API_SUPERVISOR_CALL'] = '1'
427 else:
428 config_impl['CONFIG_TFM_PSA_API_CROSS_CALL'] = '1'
Kevin Peng9f1a7542022-02-07 16:32:27 +0800429
430 if partition_statistics['connection_based_srv_num'] > 0:
431 config_impl['CONFIG_TFM_CONNECTION_BASED_SERVICE_API'] = 1
432
Sherry Zhang2ed48fd2022-09-06 11:33:22 +0800433 if partition_statistics['mmio_region_num'] > 0:
434 config_impl['CONFIG_TFM_MMIO_REGION_ENABLE'] = 1
435
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800436 if partition_statistics['flih_num'] > 0:
437 config_impl['CONFIG_TFM_FLIH_API'] = 1
Joakim Anderssoneec5cd32022-06-10 12:02:07 +0200438 if partition_statistics['slih_num'] > 0:
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800439 config_impl['CONFIG_TFM_SLIH_API'] = 1
440
Kevin Peng5bc82d22021-10-19 11:18:40 +0800441 context['partitions'] = partition_list
Kevin Peng9f1a7542022-02-07 16:32:27 +0800442 context['config_impl'] = config_impl
Kevin Pengce99e5d2021-11-09 18:06:53 +0800443 context['stateless_services'] = process_stateless_services(partition_list)
Ruiqi Jiang71d361c2021-06-23 17:45:55 +0100444
Kevin Peng5bc82d22021-10-19 11:18:40 +0800445 return context
Ken Liu861b0782021-05-22 13:15:08 +0800446
447def gen_per_partition_files(context):
448 """
449 Generate per-partition files
450
451 Parameters
452 ----------
453 context:
454 context contains partition infos
455 """
456
Kevin Peng5bc82d22021-10-19 11:18:40 +0800457 partition_context = {}
Sherry Zhangf58f2bd2022-01-10 17:21:11 +0800458 partition_context['utilities'] = context['utilities']
Sherry Zhangf865c4e2022-03-23 13:53:38 +0800459 partition_context['config_impl'] = context['config_impl']
Ken Liu861b0782021-05-22 13:15:08 +0800460
Kevin Peng5bc82d22021-10-19 11:18:40 +0800461 manifesttemplate = ENV.get_template(os.path.join(sys.path[0], 'templates/manifestfilename.template'))
462 memorytemplate = ENV.get_template(os.path.join(sys.path[0], 'templates/partition_intermedia.template'))
463 infotemplate = ENV.get_template(os.path.join(sys.path[0], 'templates/partition_load_info.template'))
Ken Liu861b0782021-05-22 13:15:08 +0800464
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500465 logging.info ("Start to generate partition files:")
Ken Liu861b0782021-05-22 13:15:08 +0800466
467 for one_partition in context['partitions']:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800468 partition_context['manifest'] = one_partition['manifest']
469 partition_context['attr'] = one_partition['attr']
470 partition_context['manifest_out_basename'] = one_partition['manifest_out_basename']
Ken Liu861b0782021-05-22 13:15:08 +0800471
BohdanHunko6cea95d2022-08-16 15:33:51 +0300472 logging.info ('Generating {} in {}'.format(one_partition['attr']['description'],
Jianliang Shen785ed5e2022-02-08 14:16:06 +0800473 one_partition['output_dir']))
Ken Liu861b0782021-05-22 13:15:08 +0800474 outfile_path = os.path.dirname(one_partition['header_file'])
Kevin Peng655f2392019-11-27 16:33:02 +0800475 if not os.path.exists(outfile_path):
476 os.makedirs(outfile_path)
477
Kevin Peng5bc82d22021-10-19 11:18:40 +0800478 headerfile = io.open(one_partition['header_file'], 'w', newline=None)
479 headerfile.write(manifesttemplate.render(partition_context))
Ken Liu861b0782021-05-22 13:15:08 +0800480 headerfile.close()
Kevin Peng655f2392019-11-27 16:33:02 +0800481
Ken Liu861b0782021-05-22 13:15:08 +0800482 intermediafile_path = os.path.dirname(one_partition['intermedia_file'])
Mingyang Sund20999f2020-10-15 14:53:12 +0800483 if not os.path.exists(intermediafile_path):
484 os.makedirs(intermediafile_path)
Kevin Peng5bc82d22021-10-19 11:18:40 +0800485 intermediafile = io.open(one_partition['intermedia_file'], 'w', newline=None)
486 intermediafile.write(memorytemplate.render(partition_context))
Ken Liu861b0782021-05-22 13:15:08 +0800487 intermediafile.close()
Mingyang Sund20999f2020-10-15 14:53:12 +0800488
Ken Liu861b0782021-05-22 13:15:08 +0800489 infofile_path = os.path.dirname(one_partition['loadinfo_file'])
Mingyang Sunf6a78572021-04-02 16:51:05 +0800490 if not os.path.exists(infofile_path):
491 os.makedirs(infofile_path)
Kevin Peng5bc82d22021-10-19 11:18:40 +0800492 infooutfile = io.open(one_partition['loadinfo_file'], 'w', newline=None)
493 infooutfile.write(infotemplate.render(partition_context))
Ken Liu861b0782021-05-22 13:15:08 +0800494 infooutfile.close()
Mingyang Sunf6a78572021-04-02 16:51:05 +0800495
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500496 logging.info ("Per-partition files done:")
Mingyang Sunf6a78572021-04-02 16:51:05 +0800497
Ken Liu861b0782021-05-22 13:15:08 +0800498def gen_summary_files(context, gen_file_lists):
Kevin Peng655f2392019-11-27 16:33:02 +0800499 """
500 Generate files according to the gen_file_list
Edison Ai48b2d9e2019-06-24 14:39:45 +0800501
502 Parameters
503 ----------
Raef Colesf42f0882020-07-10 10:01:58 +0100504 gen_file_lists:
505 The lists of files to generate
Edison Ai48b2d9e2019-06-24 14:39:45 +0800506 """
Kevin Peng655f2392019-11-27 16:33:02 +0800507 file_list = []
Shawn Shana9ad1e02019-08-07 15:49:48 +0800508
Raef Colesf42f0882020-07-10 10:01:58 +0100509 for f in gen_file_lists:
510 with open(f) as file_list_yaml_file:
Kevin Peng655f2392019-11-27 16:33:02 +0800511 file_list_yaml = yaml.safe_load(file_list_yaml_file)
Kevin Peng5bc82d22021-10-19 11:18:40 +0800512 file_list.extend(file_list_yaml['file_list'])
Edison Ai48b2d9e2019-06-24 14:39:45 +0800513
Kevin Peng655f2392019-11-27 16:33:02 +0800514 for file in file_list:
Raef Coles558487a2020-10-29 13:09:44 +0000515 # Replace environment variables in the output filepath
Kevin Peng5bc82d22021-10-19 11:18:40 +0800516 manifest_out_file = os.path.expandvars(file['output'])
Raef Coles558487a2020-10-29 13:09:44 +0000517 # Replace environment variables in the template filepath
Kevin Peng5bc82d22021-10-19 11:18:40 +0800518 templatefile_name = os.path.expandvars(file['template'])
Edison Ai48b2d9e2019-06-24 14:39:45 +0800519
Kevin Peng4fade072021-10-26 17:57:50 +0800520 manifest_out_file = os.path.join(OUT_DIR, manifest_out_file)
Edison Ai48b2d9e2019-06-24 14:39:45 +0800521
Ken Liu861b0782021-05-22 13:15:08 +0800522 outfile_path = os.path.dirname(manifest_out_file)
Kevin Peng655f2392019-11-27 16:33:02 +0800523 if not os.path.exists(outfile_path):
524 os.makedirs(outfile_path)
Edison Ai48b2d9e2019-06-24 14:39:45 +0800525
Kevin Peng655f2392019-11-27 16:33:02 +0800526 template = ENV.get_template(templatefile_name)
Edison Ai6e3f2a32019-06-11 15:29:05 +0800527
Kevin Peng5bc82d22021-10-19 11:18:40 +0800528 outfile = io.open(manifest_out_file, 'w', newline=None)
Kevin Peng655f2392019-11-27 16:33:02 +0800529 outfile.write(template.render(context))
530 outfile.close()
Edison Ai48b2d9e2019-06-24 14:39:45 +0800531
Kevin Pengce99e5d2021-11-09 18:06:53 +0800532def process_stateless_services(partitions):
Mingyang Suna1ca6112021-01-11 11:34:59 +0800533 """
534 This function collects all stateless services together, and allocates
Mingyang Sun4ecea992021-03-30 17:56:26 +0800535 stateless handles for them.
Kevin Pengc05319d2021-04-22 22:59:35 +0800536 Valid stateless handle in service will be converted to an index. If the
537 stateless handle is set as "auto", or not set, framework will allocate a
538 valid index for the service.
539 Framework puts each service into a reordered stateless service list at
540 position of "index". Other unused positions are left None.
Ken Liu50948382022-10-27 12:45:53 +0800541
542 Keep the variable names start with upper case 'STATIC_HANDLE_' the same
543 as the preprocessors in C sources. This could easier the upcomping
544 modification when developer searches these definitions for modification.
Mingyang Suna1ca6112021-01-11 11:34:59 +0800545 """
Kevin Pengce99e5d2021-11-09 18:06:53 +0800546
Kevin Pengc05319d2021-04-22 22:59:35 +0800547 collected_stateless_services = []
Ken Liu50948382022-10-27 12:45:53 +0800548 STATIC_HANDLE_NUM_LIMIT = 32
Mingyang Suna1ca6112021-01-11 11:34:59 +0800549
550 # Collect all stateless services first.
551 for partition in partitions:
552 # Skip the FF-M 1.0 partitions
553 if partition['manifest']['psa_framework_version'] < 1.1:
554 continue
Kevin Peng8849b6a2021-11-09 14:17:35 +0800555
556 service_list = partition['manifest'].get('services', [])
557
558 for service in service_list:
Mingyang Suna1ca6112021-01-11 11:34:59 +0800559 if service['connection_based'] is False:
Kevin Pengc05319d2021-04-22 22:59:35 +0800560 collected_stateless_services.append(service)
Mingyang Suna1ca6112021-01-11 11:34:59 +0800561
Kevin Pengc05319d2021-04-22 22:59:35 +0800562 if len(collected_stateless_services) == 0:
Mingyang Suna1ca6112021-01-11 11:34:59 +0800563 return []
564
Ken Liu50948382022-10-27 12:45:53 +0800565 if len(collected_stateless_services) > STATIC_HANDLE_NUM_LIMIT:
566 raise Exception('Stateless service numbers range exceed {number}.'.format(number=STATIC_HANDLE_NUM_LIMIT))
Mingyang Suna1ca6112021-01-11 11:34:59 +0800567
568 """
Kevin Pengc05319d2021-04-22 22:59:35 +0800569 Allocate an empty stateless service list to store services.
570 Use "handle - 1" as the index for service, since handle value starts from
571 1 and list index starts from 0.
Mingyang Suna1ca6112021-01-11 11:34:59 +0800572 """
Ken Liu50948382022-10-27 12:45:53 +0800573 reordered_stateless_services = [None] * STATIC_HANDLE_NUM_LIMIT
Kevin Pengc05319d2021-04-22 22:59:35 +0800574 auto_alloc_services = []
Mingyang Suna1ca6112021-01-11 11:34:59 +0800575
Kevin Pengc05319d2021-04-22 22:59:35 +0800576 for service in collected_stateless_services:
577 # If not set, it is "auto" by default
578 if 'stateless_handle' not in service:
579 auto_alloc_services.append(service)
580 continue
581
Mingyang Sun4ecea992021-03-30 17:56:26 +0800582 service_handle = service['stateless_handle']
Mingyang Suna1ca6112021-01-11 11:34:59 +0800583
Mingyang Sun4ecea992021-03-30 17:56:26 +0800584 # Fill in service list with specified stateless handle, otherwise skip
585 if isinstance(service_handle, int):
Ken Liu50948382022-10-27 12:45:53 +0800586 if service_handle < 1 or service_handle > STATIC_HANDLE_NUM_LIMIT:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800587 raise Exception('Invalid stateless_handle setting: {handle}.'.format(handle=service['stateless_handle']))
Mingyang Sun4ecea992021-03-30 17:56:26 +0800588 # Convert handle index to reordered service list index
589 service_handle = service_handle - 1
590
591 if reordered_stateless_services[service_handle] is not None:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800592 raise Exception('Duplicated stateless_handle setting: {handle}.'.format(handle=service['stateless_handle']))
Mingyang Sun4ecea992021-03-30 17:56:26 +0800593 reordered_stateless_services[service_handle] = service
Kevin Pengc05319d2021-04-22 22:59:35 +0800594 elif service_handle == 'auto':
595 auto_alloc_services.append(service)
596 else:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800597 raise Exception('Invalid stateless_handle setting: {handle}.'.format(handle=service['stateless_handle']))
Mingyang Sun4ecea992021-03-30 17:56:26 +0800598
Xinyu Zhangb2d4ed42023-02-14 11:43:16 +0800599 STATIC_HANDLE_IDX_BIT_WIDTH = 5
Kevin Pengce99e5d2021-11-09 18:06:53 +0800600 STATIC_HANDLE_IDX_MASK = (1 << STATIC_HANDLE_IDX_BIT_WIDTH) - 1
Ken Liu50948382022-10-27 12:45:53 +0800601 STATIC_HANDLE_INDICATOR_OFFSET = 30
602 STATIC_HANDLE_VER_OFFSET = 8
603 STATIC_HANDLE_VER_BIT_WIDTH = 8
Kevin Pengce99e5d2021-11-09 18:06:53 +0800604 STATIC_HANDLE_VER_MASK = (1 << STATIC_HANDLE_VER_BIT_WIDTH) - 1
605
Mingyang Sun4ecea992021-03-30 17:56:26 +0800606 # Auto-allocate stateless handle and encode the stateless handle
Ken Liu50948382022-10-27 12:45:53 +0800607 for i in range(0, STATIC_HANDLE_NUM_LIMIT):
Mingyang Sun4ecea992021-03-30 17:56:26 +0800608 service = reordered_stateless_services[i]
609
Kevin Pengc05319d2021-04-22 22:59:35 +0800610 if service == None and len(auto_alloc_services) > 0:
611 service = auto_alloc_services.pop(0)
Mingyang Sun4ecea992021-03-30 17:56:26 +0800612
Mingyang Sun453ad402021-03-17 17:58:33 +0800613 """
614 Encode stateless flag and version into stateless handle
Mingyang Sun453ad402021-03-17 17:58:33 +0800615 """
Mingyang Sun4ecea992021-03-30 17:56:26 +0800616 stateless_handle_value = 0
617 if service != None:
Kevin Pengce99e5d2021-11-09 18:06:53 +0800618 stateless_index = (i & STATIC_HANDLE_IDX_MASK)
Mingyang Sun4ecea992021-03-30 17:56:26 +0800619 stateless_handle_value |= stateless_index
Kevin Pengce99e5d2021-11-09 18:06:53 +0800620 stateless_handle_value |= (1 << STATIC_HANDLE_INDICATOR_OFFSET)
621 stateless_version = (service['version'] & STATIC_HANDLE_VER_MASK) << STATIC_HANDLE_VER_OFFSET
Mingyang Sun453ad402021-03-17 17:58:33 +0800622 stateless_handle_value |= stateless_version
Mingyang Sun4ecea992021-03-30 17:56:26 +0800623 service['stateless_handle_value'] = '0x{0:08x}'.format(stateless_handle_value)
Ken Liu861b0782021-05-22 13:15:08 +0800624 service['stateless_handle_index'] = stateless_index
Mingyang Suna1ca6112021-01-11 11:34:59 +0800625
Mingyang Sun4ecea992021-03-30 17:56:26 +0800626 reordered_stateless_services[i] = service
627
628 return reordered_stateless_services
Mingyang Suna1ca6112021-01-11 11:34:59 +0800629
Kevin Peng655f2392019-11-27 16:33:02 +0800630def parse_args():
Raef Coles558487a2020-10-29 13:09:44 +0000631 parser = argparse.ArgumentParser(description='Parse secure partition manifest list and generate files listed by the file list',
632 epilog='Note that environment variables in template files will be replaced with their values')
633
Kevin Peng655f2392019-11-27 16:33:02 +0800634 parser.add_argument('-o', '--outdir'
635 , dest='outdir'
Kevin Peng4fade072021-10-26 17:57:50 +0800636 , required=True
Kevin Peng655f2392019-11-27 16:33:02 +0800637 , metavar='out_dir'
Kevin Peng4fade072021-10-26 17:57:50 +0800638 , help='The root directory for generated files')
Shawn Shana9ad1e02019-08-07 15:49:48 +0800639
Kevin Peng5bc82d22021-10-19 11:18:40 +0800640 parser.add_argument('-m', '--manifest-lists'
Raef Colesf42f0882020-07-10 10:01:58 +0100641 , nargs='+'
Kevin Peng5bc82d22021-10-19 11:18:40 +0800642 , dest='manifest_lists'
Raef Colesf42f0882020-07-10 10:01:58 +0100643 , required=True
Kevin Peng65064c52021-10-27 17:12:17 +0800644 , metavar='manifest list'
645 , help='A list of Secure Partition manifest lists and their original paths.\n\
646 The manifest lists might be processed by CMake and\n\
647 the path might be different to the original one\n\
648 The format must be [list A, orignal path A, list B, orignal path B, ...]')
Kevin Peng655f2392019-11-27 16:33:02 +0800649
650 parser.add_argument('-f', '--file-list'
Raef Colesf42f0882020-07-10 10:01:58 +0100651 , nargs='+'
Kevin Peng655f2392019-11-27 16:33:02 +0800652 , dest='gen_file_args'
Raef Colesf42f0882020-07-10 10:01:58 +0100653 , required=True
Kevin Peng655f2392019-11-27 16:33:02 +0800654 , metavar='file-list'
Chris Brandf1d6b6f2022-07-22 11:49:01 -0700655 , help='These files describe the file list to generate')
Kevin Peng9f1a7542022-02-07 16:32:27 +0800656
Kevin Pengfb1761b2022-05-12 12:11:31 +0800657 parser.add_argument('-c', '--config-files'
658 , nargs='+'
659 , dest='config_files'
Kevin Peng9f1a7542022-02-07 16:32:27 +0800660 , required=True
Kevin Pengfb1761b2022-05-12 12:11:31 +0800661 , metavar='config-files'
662 , help='A header file contains build configurations')
Kevin Peng9f1a7542022-02-07 16:32:27 +0800663
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500664 parser.add_argument('-q', '--quiet'
665 , dest='quiet'
666 , required=False
667 , default=False
668 , action='store_true'
669 , help='Reduce log messages')
Kevin Peng655f2392019-11-27 16:33:02 +0800670
671 args = parser.parse_args()
Kevin Peng655f2392019-11-27 16:33:02 +0800672
Kevin Peng655f2392019-11-27 16:33:02 +0800673 return args
674
675ENV = Environment(
676 loader = TemplateLoader(),
677 autoescape = select_autoescape(['html', 'xml']),
678 lstrip_blocks = True,
679 trim_blocks = True,
680 keep_trailing_newline = True
681 )
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100682
Miklos Balint470919c2018-05-22 17:51:29 +0200683def main():
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100684 """
685 The entry point of the script.
686
687 Generates the output files based on the templates and the manifests.
688 """
Shawn Shana9ad1e02019-08-07 15:49:48 +0800689
Kevin Peng655f2392019-11-27 16:33:02 +0800690 global OUT_DIR
Shawn Shana9ad1e02019-08-07 15:49:48 +0800691
Kevin Peng655f2392019-11-27 16:33:02 +0800692 args = parse_args()
Shawn Shana9ad1e02019-08-07 15:49:48 +0800693
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500694 logging.basicConfig(format='%(message)s'
695 , level=logging.WARNING if args.quiet else logging.INFO)
696
Kevin Peng5bc82d22021-10-19 11:18:40 +0800697 OUT_DIR = os.path.abspath(args.outdir)
Kevin Peng655f2392019-11-27 16:33:02 +0800698
Kevin Peng5bc82d22021-10-19 11:18:40 +0800699 manifest_lists = [os.path.abspath(x) for x in args.manifest_lists]
700 gen_file_lists = [os.path.abspath(x) for x in args.gen_file_args]
Shawn Shana9ad1e02019-08-07 15:49:48 +0800701
Shawn Shana9ad1e02019-08-07 15:49:48 +0800702 """
Kevin Peng655f2392019-11-27 16:33:02 +0800703 Relative path to TF-M root folder is supported in the manifests
704 and default value of manifest list and generated file list are relative to TF-M root folder as well,
705 so first change directory to TF-M root folder.
Shawn Shana9ad1e02019-08-07 15:49:48 +0800706 By doing this, the script can be executed anywhere
Kevin Peng655f2392019-11-27 16:33:02 +0800707 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 +0800708 """
Kevin Peng5bc82d22021-10-19 11:18:40 +0800709 os.chdir(os.path.join(sys.path[0], '..'))
Shawn Shana9ad1e02019-08-07 15:49:48 +0800710
Kevin Peng76c0c162022-02-09 22:49:06 +0800711 context = process_partition_manifests(manifest_lists,
Kevin Pengfb1761b2022-05-12 12:11:31 +0800712 parse_configurations(args.config_files))
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100713
Edison Ai6e3f2a32019-06-11 15:29:05 +0800714 utilities = {}
Mingyang Suna1ca6112021-01-11 11:34:59 +0800715 utilities['donotedit_warning'] = donotedit_warning
Miklos Balint470919c2018-05-22 17:51:29 +0200716
Kevin Peng655f2392019-11-27 16:33:02 +0800717 context['utilities'] = utilities
Mingyang Suneab7eae2021-09-30 13:06:52 +0800718
Ken Liu861b0782021-05-22 13:15:08 +0800719 gen_per_partition_files(context)
Kevin Peng5bc82d22021-10-19 11:18:40 +0800720 gen_summary_files(context, gen_file_lists)
Miklos Balint470919c2018-05-22 17:51:29 +0200721
Kevin Peng5bc82d22021-10-19 11:18:40 +0800722if __name__ == '__main__':
Miklos Balint470919c2018-05-22 17:51:29 +0200723 main()