blob: baf136bc7d745b22cb854064eb28bf0c6dcdd74c [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.
Miklos Balint470919c2018-05-22 17:51:29 +02003#
4# SPDX-License-Identifier: BSD-3-Clause
5#
6#-------------------------------------------------------------------------------
7
8import os
Mate Toth-Pal36f21842018-11-08 16:12:51 +01009import io
Kevin Pengce99e5d2021-11-09 18:06:53 +080010import re
Shawn Shana9ad1e02019-08-07 15:49:48 +080011import sys
12import argparse
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -050013import logging
Ken Liu1f345b02020-05-30 21:11:05 +080014from jinja2 import Environment, BaseLoader, select_autoescape, TemplateNotFound
Miklos Balint470919c2018-05-22 17:51:29 +020015
16try:
17 import yaml
18except ImportError as e:
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -050019 logging.error (str(e) + " To install it, type:")
20 logging.error ("pip install PyYAML")
Miklos Balint470919c2018-05-22 17:51:29 +020021 exit(1)
22
Edison Ai48b2d9e2019-06-24 14:39:45 +080023donotedit_warning = \
Sherry Zhangf58f2bd2022-01-10 17:21:11 +080024 ' WARNING: This is an auto-generated file. Do not edit! '
Kevin Peng655f2392019-11-27 16:33:02 +080025
Kevin Pengce99e5d2021-11-09 18:06:53 +080026TFM_ROOT_DIR = os.path.join(sys.path[0], '..')
Kevin Peng655f2392019-11-27 16:33:02 +080027OUT_DIR = None # The root directory that files are generated to
Edison Ai48b2d9e2019-06-24 14:39:45 +080028
Sherry Zhang87e6d2e2021-12-27 14:48:59 +080029# PID[0, TFM_PID_BASE - 1] are reserved for TF-M SPM and test usages
Kevin Pengce99e5d2021-11-09 18:06:53 +080030TFM_PID_BASE = 256
31
Ruiqi Jiang71d361c2021-06-23 17:45:55 +010032# variable for checking for duplicated sid
33sid_list = []
Mate Toth-Pal36f21842018-11-08 16:12:51 +010034class TemplateLoader(BaseLoader):
35 """
36 Template loader class.
Miklos Balint470919c2018-05-22 17:51:29 +020037
Mate Toth-Pal36f21842018-11-08 16:12:51 +010038 An instance of this class is passed to the template engine. It is
39 responsible for reading the template file
40 """
41 def __init__(self):
42 pass
Miklos Balint470919c2018-05-22 17:51:29 +020043
Mate Toth-Pal36f21842018-11-08 16:12:51 +010044 def get_source(self, environment, template):
45 """
46 This function reads the template files.
47 For detailed documentation see:
48 http://jinja.pocoo.org/docs/2.10/api/#jinja2.BaseLoader.get_source
49
50 Please note that this function always return 'false' as 'uptodate'
51 value, so the output file will always be generated.
52 """
53 if not os.path.isfile(template):
54 raise TemplateNotFound(template)
55 with open(template) as f:
56 source = f.read()
57 return source, template, False
58
Kevin Pengce99e5d2021-11-09 18:06:53 +080059def get_single_macro_def_from_file(file_name, macro_name):
60 """
61 This function parses the given file_name to get the definition of the given
62 C Macro (macro_name).
63
64 It assumes that the target Macro has no multiple definitions in different
65 build configurations.
66
67 It supports Macros defined in multi-line, for example:
68 #define SOME_MACRO \
69 the_macro_value
70
71 Inputs:
72 - file_name: the file to get the Macro from
73 - macro_name: the name of the Macro to get
74 Returns:
75 - The Macro definition with '()' stripped, or Exception if not found
76 """
77
78 with open(file_name, 'r') as f:
79 pattern = re.compile(r'#define\s+{}[\\\s]+.*'.format(macro_name))
80 result = pattern.findall(f.read())
81
82 if len(result) != 1:
83 raise Exception('{} not defined or has multiple definitions'.format(macro_name))
84
85 macro_def = result[0].split()[-1].strip('()')
86
87 return macro_def
88
Kevin Peng5c3fee72022-02-16 22:25:22 +080089def manifest_validation(manifest, pid):
Mingyang Sun294ce2e2021-06-11 11:58:24 +080090 """
91 This function validates FF-M compliance for partition manifest, and sets
92 default values for optional attributes.
Kevin Pengce99e5d2021-11-09 18:06:53 +080093 The validation is skipped for TF-M specific Partitions (PID < TFM_PID_BASE).
Mingyang Sun294ce2e2021-06-11 11:58:24 +080094 """
Kevin Peng5bc82d22021-10-19 11:18:40 +080095
Kevin Peng5c3fee72022-02-16 22:25:22 +080096 service_list = manifest.get('services', [])
97 irq_list = manifest.get('irqs', [])
Mingyang Sun294ce2e2021-06-11 11:58:24 +080098
Kevin Peng5c3fee72022-02-16 22:25:22 +080099 # "psa_framework_version" validation
100 if manifest['psa_framework_version'] not in [1.0, 1.1]:
101 raise Exception('Invalid psa_framework_version of {}'.format(manifest['name']))
102
103 # "type" validatoin
104 if manifest['type'] not in ['PSA-ROT', 'APPLICATION-ROT']:
105 raise Exception('Invalid type of {}'.format(manifest['name']))
106
107 # Every PSA Partition must have at least either a secure service or an IRQ
Kevin Pengce99e5d2021-11-09 18:06:53 +0800108 if (pid == None or pid >= TFM_PID_BASE) \
Kevin Peng8849b6a2021-11-09 14:17:35 +0800109 and len(service_list) == 0 and len(irq_list) == 0:
110 raise Exception('{} must declare at least either a secure service or an IRQ!'
Kevin Peng5c3fee72022-02-16 22:25:22 +0800111 .format(manifest['name']))
112
113 if manifest['psa_framework_version'] == 1.0:
114 # For 1.0 Partition, the model is IPC
115 manifest['model'] = 'IPC'
116
117 # "model" validation:
118 model = manifest.get('model', None)
119 if model == None:
120 raise Exception('{} is missing the "model" attribute'.format(manifest['name']))
121
122 # Assign a unified 'entry' for templates
123 if model == 'IPC':
124 # entry_point is mandatory for IPC Partitions
125 if 'entry_point' not in manifest.keys():
126 raise Exception('{} is missing the "entry_point" attribute'.format(manifest['name']))
127 manifest['entry'] = manifest['entry_point']
128 elif model == 'SFN':
129 if 'entry_init' in manifest.keys():
130 manifest['entry'] = manifest['entry_init']
131 else:
132 manifest['entry'] = 0
133 else:
134 raise Exception('Invalid "model" of {}'.format(manifest['name']))
Kevin Peng8849b6a2021-11-09 14:17:35 +0800135
136 # Service FF-M manifest validation
137 for service in service_list:
Kevin Peng5c3fee72022-02-16 22:25:22 +0800138 if manifest['psa_framework_version'] == 1.0:
139 service['connection_based'] = True
140 elif 'connection_based' not in service:
141 raise Exception("'connection_based' is mandatory in FF-M 1.1 service!")
142
Mingyang Sun294ce2e2021-06-11 11:58:24 +0800143 if 'version' not in service.keys():
144 service['version'] = 1
145 if 'version_policy' not in service.keys():
Kevin Peng5bc82d22021-10-19 11:18:40 +0800146 service['version_policy'] = 'STRICT'
Mingyang Sun294ce2e2021-06-11 11:58:24 +0800147
Kevin Peng5bc82d22021-10-19 11:18:40 +0800148 # SID duplication check
149 if service['sid'] in sid_list:
150 raise Exception('Service ID: {} has duplications!'.format(service['sid']))
151 else:
152 sid_list.append(service['sid'])
Ruiqi Jiang71d361c2021-06-23 17:45:55 +0100153
Kevin Peng5c3fee72022-02-16 22:25:22 +0800154 return manifest
Mingyang Sun294ce2e2021-06-11 11:58:24 +0800155
Xinyu Zhang7d0a5c82022-05-16 18:15:11 +0800156def check_circular_dependency(partitions, service_partition_map):
157 """
158 This function detects if there is any circular partition dependency chain.
159 If a circular dependency is detected, the script exits with error.
160
161 Inputs:
162 - partitions: dict of partition manifests
163 - service_partition_map: map between services and their owner Partitions
164 """
165
166 dependency_table = {}
167 for partition in partitions:
168 manifest = partition['manifest']
169 dependencies = manifest['dependencies'].copy() \
170 if 'dependencies' in manifest else []
171 dependencies += manifest['weak_dependencies'].copy() \
172 if 'weak_dependencies' in manifest else []
173 dependency_table[manifest['name']] = {
174 'dependencies': [service_partition_map[dependency]
175 for dependency in dependencies
176 if dependency in service_partition_map],
177 'validated': False
178 }
179
180 for partition in dependency_table.keys():
181 validate_dependency_chain(partition, dependency_table, [])
182
183def validate_dependency_chain(partition,
184 dependency_table,
185 dependency_chain):
186 """
187 Recursively validate if the given partition and its dependencies
188 have a circular dependency with the given dependency_chain.
189 Exit with error code once any circular is detected.
190
191 Inputs:
192 - partition: next partition to be checked
193 - dependency_table: dict of partitions and their dependencies
194 - dependency_chain: list of dependencies in current chain
195 """
196
197 dependency_chain.append(partition)
198 if partition in dependency_chain[:-1]:
199 logging.error(
200 'Circular dependency exists in chain: {}'.format(
201 ', '.join(dependency_chain)))
202 exit(1)
203 for dependency in dependency_table[partition]['dependencies']:
204 if dependency_table[dependency]['validated']:
205 continue
206 validate_dependency_chain(dependency, dependency_table, dependency_chain)
207 dependency_table[partition]['validated'] = True
208
Kevin Peng76c0c162022-02-09 22:49:06 +0800209def process_partition_manifests(manifest_lists, isolation_level, backend):
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100210 """
Xinyu Zhang7d0a5c82022-05-16 18:15:11 +0800211 Parse the input manifest lists, check if manifest settings are valid,
212 generate the data base for genereated files
Kevin Peng655f2392019-11-27 16:33:02 +0800213 and generate manifest header files.
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100214
215 Parameters
216 ----------
Kevin Peng65064c52021-10-27 17:12:17 +0800217 manifest_lists:
218 A list of Secure Partition manifest lists and their original paths.
219 The manifest lists might be processed by CMake and the paths might be
220 different to the original ones. Original paths are needed to handle
221 relative paths in the lists.
222 The format must be [list A, orignal path A, list B, orignal path B, ...]
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100223
224 Returns
225 -------
Kevin Peng5bc82d22021-10-19 11:18:40 +0800226 The manifest data base.
Edison Ai48b2d9e2019-06-24 14:39:45 +0800227 """
Kevin Peng655f2392019-11-27 16:33:02 +0800228
Kevin Peng5bc82d22021-10-19 11:18:40 +0800229 context = {}
230
Ken Liu861b0782021-05-22 13:15:08 +0800231 partition_list = []
Kevin Peng65064c52021-10-27 17:12:17 +0800232 all_manifests = []
Kevin Peng56b0ea62021-10-18 11:32:57 +0800233 pid_list = []
234 no_pid_manifest_idx = []
Xinyu Zhang7d0a5c82022-05-16 18:15:11 +0800235 service_partition_map = {}
Xinyu Zhang90f08dc2022-01-12 15:55:17 +0800236 partition_statistics = {
Xinyu Zhang2bc4d572021-12-27 16:37:46 +0800237 'connection_based_srv_num': 0,
Kevin Peng47c26ec2022-03-11 11:49:52 +0800238 'ipc_partitions': [],
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800239 'flih_num': 0,
240 'slih_num': 0
Xinyu Zhang90f08dc2022-01-12 15:55:17 +0800241 }
Kevin Peng9f1a7542022-02-07 16:32:27 +0800242 config_impl = {
243 'CONFIG_TFM_SPM_BACKEND_SFN' : '0',
244 'CONFIG_TFM_SPM_BACKEND_IPC' : '0',
245 'CONFIG_TFM_PSA_API_SFN_CALL' : '0',
246 'CONFIG_TFM_PSA_API_CROSS_CALL' : '0',
247 'CONFIG_TFM_PSA_API_SUPERVISOR_CALL' : '0',
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800248 'CONFIG_TFM_CONNECTION_BASED_SERVICE_API' : '0',
249 'CONFIG_TFM_FLIH_API' : '0',
250 'CONFIG_TFM_SLIH_API' : '0'
Kevin Peng9f1a7542022-02-07 16:32:27 +0800251 }
Kevin Peng655f2392019-11-27 16:33:02 +0800252
Kevin Peng65064c52021-10-27 17:12:17 +0800253 # Get all the manifests information as a dictionary
254 for i, item in enumerate(manifest_lists):
255 if i % 2 == 0 and not os.path.isfile(item):
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500256 logging.error('Manifest list item [{}] must be a file'.format(i))
Kevin Peng65064c52021-10-27 17:12:17 +0800257 exit(1)
Kevin Peng655f2392019-11-27 16:33:02 +0800258
Kevin Peng65064c52021-10-27 17:12:17 +0800259 if i % 2 == 1:
260 if not os.path.isdir(item):
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500261 logging.error('Manifest list item [{}] must be a directory'.format(i))
Kevin Peng65064c52021-10-27 17:12:17 +0800262 exit(1)
David Hub2694202021-07-15 14:58:39 +0800263
Kevin Peng65064c52021-10-27 17:12:17 +0800264 # Skip original manifest paths
265 continue
David Hub2694202021-07-15 14:58:39 +0800266
Kevin Peng65064c52021-10-27 17:12:17 +0800267 # The manifest list file generated by configure_file()
268 with open(item) as manifest_list_yaml_file:
269 manifest_dic = yaml.safe_load(manifest_list_yaml_file)['manifest_list']
270 for dict in manifest_dic:
271 # Add original path of manifest list.
272 # The validation will be done in the next loop.
273 dict['list_path'] = manifest_lists[i + 1]
274 all_manifests.append(dict)
275
Chris Brand4b69fcf2022-06-06 09:37:49 -0700276 logging.info("------------ Display partition configuration - start ------------")
277
Kevin Peng65064c52021-10-27 17:12:17 +0800278 # Parse the manifests
279 for i, manifest_item in enumerate(all_manifests):
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800280 valid_enabled_conditions = ['on', 'true', 'enabled']
Kevin Peng50f413c2021-11-12 10:31:45 +0800281 valid_disabled_conditions = ['off', 'false', 'disabled', '']
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800282 is_enabled = ''
283
284 if 'conditional' in manifest_item.keys():
Kevin Peng50f413c2021-11-12 10:31:45 +0800285 is_enabled = str(manifest_item['conditional']).lower()
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800286 else:
Chris Brand4b69fcf2022-06-06 09:37:49 -0700287 # Partitions without 'conditional' are always on
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800288 is_enabled = 'on'
289
290 if is_enabled in valid_disabled_conditions:
Chris Brand4b69fcf2022-06-06 09:37:49 -0700291 logging.info("{} partition is disabled".format(manifest_item['name']))
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800292 continue
Chris Brand4b69fcf2022-06-06 09:37:49 -0700293 elif is_enabled in valid_enabled_conditions:
294 logging.info("{} partition is enabled".format(manifest_item['name']))
295 else:
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800296 raise Exception('Invalid "conditional" attribute: "{}" for {}. '
297 'Please set to one of {} or {}, case-insensitive.'\
298 .format(manifest_item['conditional'],
299 manifest_item['name'],
300 valid_enabled_conditions, valid_disabled_conditions))
301
Xinyu Zhangc46ee1f2021-04-01 10:10:43 +0800302 # Check if partition ID is manually set
303 if 'pid' not in manifest_item.keys():
304 no_pid_manifest_idx.append(i)
Kevin Peng8849b6a2021-11-09 14:17:35 +0800305 pid = None
Kevin Peng56b0ea62021-10-18 11:32:57 +0800306 else:
Kevin Peng8849b6a2021-11-09 14:17:35 +0800307 pid = manifest_item['pid']
308
309 # Check if partition ID is duplicated
310 if pid in pid_list:
Antonio de Angelisbaa27642022-05-25 11:07:12 +0100311 raise Exception('PID No. {} has already been used!'.format(pid))
Kevin Peng8849b6a2021-11-09 14:17:35 +0800312 else:
313 pid_list.append(pid)
Xinyu Zhang19504a52021-03-31 16:26:20 +0800314
Raef Coles558487a2020-10-29 13:09:44 +0000315 # Replace environment variables in the manifest path
Kevin Peng655f2392019-11-27 16:33:02 +0800316 manifest_path = os.path.expandvars(manifest_item['manifest'])
Kevin Peng65064c52021-10-27 17:12:17 +0800317 # Convert to absolute path. If it's already abspath, the path will not be changed.
318 manifest_path = os.path.join(manifest_item['list_path'], manifest_path).replace('\\', '/')
David Hub2694202021-07-15 14:58:39 +0800319
Kevin Peng5bc82d22021-10-19 11:18:40 +0800320 with open(manifest_path) as manifest_file:
Kevin Pengec0e5ce2022-02-17 13:48:51 +0800321 manifest = yaml.safe_load(manifest_file)
322 if manifest.get('model', None) == 'dual':
323 # If a Partition supports both models, it can set the "model" to "backend".
324 # The actual model used follows the backend being used.
325 manifest['model'] = backend
326 manifest = manifest_validation(manifest, pid)
Kevin Peng655f2392019-11-27 16:33:02 +0800327
Kevin Pengce99e5d2021-11-09 18:06:53 +0800328 if pid == None or pid >= TFM_PID_BASE:
Kevin Pengd08f3ba2021-11-18 15:18:56 +0800329 # Count the number of IPC/SFN partitions
Kevin Peng47c26ec2022-03-11 11:49:52 +0800330 if manifest['model'] == 'IPC':
331 partition_statistics['ipc_partitions'].append(manifest['name'])
Mingyang Suneab7eae2021-09-30 13:06:52 +0800332
Kevin Peng5c3fee72022-02-16 22:25:22 +0800333 # Set initial value to -1 to make (srv_idx + 1) reflect the correct
334 # number (0) when there are no services.
335 srv_idx = -1
336 for srv_idx, service in enumerate(manifest.get('services', [])):
Xinyu Zhang7d0a5c82022-05-16 18:15:11 +0800337 service_partition_map[service['name']] = manifest['name']
Kevin Peng5c3fee72022-02-16 22:25:22 +0800338 if manifest['model'] == 'IPC':
339 # Assign signal value, the first 4 bits are reserved by FF-M
340 service['signal_value'] = (1 << (srv_idx + 4))
341 else:
342 # Signals of SFN Partitions are SPM internal only, does not
343 # need to reserve 4 bits.
344 service['signal_value'] = (1 << srv_idx)
345 if service['connection_based']:
Xinyu Zhang2bc4d572021-12-27 16:37:46 +0800346 partition_statistics['connection_based_srv_num'] += 1
Kevin Peng5c3fee72022-02-16 22:25:22 +0800347 logging.debug('{} has {} services'.format(manifest['name'], srv_idx +1))
Xinyu Zhang2bc4d572021-12-27 16:37:46 +0800348
Kevin Peng5c3fee72022-02-16 22:25:22 +0800349 # Set initial value to -1 to make (irq + 1) reflect the correct
350 # number (0) when there are no irqs.
351 irq_idx = -1
352 for irq_idx, irq in enumerate(manifest.get('irqs', [])):
353 # Assign signal value, from the most significant bit
354 irq['signal_value'] = (1 << (31 - irq_idx))
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800355 if irq.get('handling', None) == 'FLIH':
356 partition_statistics['flih_num'] += 1
357 else:
358 partition_statistics['slih_num'] += 1
Kevin Peng5c3fee72022-02-16 22:25:22 +0800359 logging.debug('{} has {} IRQS'.format(manifest['name'], irq_idx +1))
360
361 if ((srv_idx + 1) + (irq_idx + 1)) > 28:
362 raise Exception('Total number of Services and IRQs of {} exceeds the limit (28)'
363 .format(manifest['name']))
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800364
Kevin Peng65064c52021-10-27 17:12:17 +0800365 manifest_out_basename = os.path.splitext(os.path.basename(manifest_path))[0]
Kevin Peng655f2392019-11-27 16:33:02 +0800366
Kevin Peng4fade072021-10-26 17:57:50 +0800367 if 'output_path' in manifest_item:
Kevin Peng4fade072021-10-26 17:57:50 +0800368 output_path = os.path.expandvars(manifest_item['output_path'])
Kevin Peng4fade072021-10-26 17:57:50 +0800369 else:
Kevin Peng65064c52021-10-27 17:12:17 +0800370 output_path = ''
David Hub2694202021-07-15 14:58:39 +0800371
Kevin Peng5bc82d22021-10-19 11:18:40 +0800372 manifest_head_file = os.path.join(OUT_DIR, output_path, 'psa_manifest',
373 '{}.h'.format(manifest_out_basename))\
374 .replace('\\', '/')
375 intermedia_file = os.path.join(OUT_DIR, output_path, 'auto_generated',
376 'intermedia_{}.c'.format(manifest_out_basename))\
377 .replace('\\', '/')
378 load_info_file = os.path.join(OUT_DIR, output_path, 'auto_generated',
379 'load_info_{}.c'.format(manifest_out_basename))\
380 .replace('\\', '/')
Jianliang Shen785ed5e2022-02-08 14:16:06 +0800381 output_dir = os.path.join(OUT_DIR, output_path).replace('\\', '/')
Kevin Peng655f2392019-11-27 16:33:02 +0800382
Kevin Peng5bc82d22021-10-19 11:18:40 +0800383 partition_list.append({'manifest': manifest, 'attr': manifest_item,
384 'manifest_out_basename': manifest_out_basename,
385 'header_file': manifest_head_file,
386 'intermedia_file': intermedia_file,
Jianliang Shen785ed5e2022-02-08 14:16:06 +0800387 'loadinfo_file': load_info_file,
388 'output_dir':output_dir})
Ken Liu861b0782021-05-22 13:15:08 +0800389
Chris Brand4b69fcf2022-06-06 09:37:49 -0700390 logging.info("------------ Display partition configuration - end ------------")
391
Xinyu Zhang7d0a5c82022-05-16 18:15:11 +0800392 check_circular_dependency(partition_list, service_partition_map)
393
Kevin Peng56b0ea62021-10-18 11:32:57 +0800394 # Automatically assign PIDs for partitions without 'pid' attribute
Kevin Pengce99e5d2021-11-09 18:06:53 +0800395 pid = max(pid_list, default = TFM_PID_BASE - 1)
Kevin Peng56b0ea62021-10-18 11:32:57 +0800396 for idx in no_pid_manifest_idx:
Kevin Pengc424eec2021-06-25 17:26:11 +0800397 pid += 1
Kevin Peng65064c52021-10-27 17:12:17 +0800398 all_manifests[idx]['pid'] = pid
Kevin Peng56b0ea62021-10-18 11:32:57 +0800399 pid_list.append(pid)
400
Kevin Peng9f1a7542022-02-07 16:32:27 +0800401 # Set up configurations
Kevin Peng76c0c162022-02-09 22:49:06 +0800402 if backend == 'SFN':
Kevin Peng47c26ec2022-03-11 11:49:52 +0800403 if len(partition_statistics['ipc_partitions']) > 0:
404 logging.error('SFN backend does not support IPC Partitions:')
405 logging.error(partition_statistics['ipc_partitions'])
Kevin Peng9f1a7542022-02-07 16:32:27 +0800406 exit(1)
Kevin Peng76c0c162022-02-09 22:49:06 +0800407
408 if isolation_level > 1:
409 logging.error('SFN backend does not support high isolation levels.')
410 exit(1)
411
Kevin Peng9f1a7542022-02-07 16:32:27 +0800412 config_impl['CONFIG_TFM_SPM_BACKEND_SFN'] = '1'
413 config_impl['CONFIG_TFM_PSA_API_SFN_CALL'] = '1'
Kevin Peng76c0c162022-02-09 22:49:06 +0800414 elif backend == 'IPC':
Kevin Peng9f1a7542022-02-07 16:32:27 +0800415 config_impl['CONFIG_TFM_SPM_BACKEND_IPC'] = '1'
416 if isolation_level > 1:
417 config_impl['CONFIG_TFM_PSA_API_SUPERVISOR_CALL'] = '1'
418 else:
419 config_impl['CONFIG_TFM_PSA_API_CROSS_CALL'] = '1'
Kevin Peng9f1a7542022-02-07 16:32:27 +0800420
421 if partition_statistics['connection_based_srv_num'] > 0:
422 config_impl['CONFIG_TFM_CONNECTION_BASED_SERVICE_API'] = 1
423
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800424 if partition_statistics['flih_num'] > 0:
425 config_impl['CONFIG_TFM_FLIH_API'] = 1
426 elif partition_statistics['slih_num'] > 0:
427 config_impl['CONFIG_TFM_SLIH_API'] = 1
428
Kevin Peng5bc82d22021-10-19 11:18:40 +0800429 context['partitions'] = partition_list
Kevin Peng9f1a7542022-02-07 16:32:27 +0800430 context['config_impl'] = config_impl
Kevin Pengce99e5d2021-11-09 18:06:53 +0800431 context['stateless_services'] = process_stateless_services(partition_list)
Ruiqi Jiang71d361c2021-06-23 17:45:55 +0100432
Kevin Peng5bc82d22021-10-19 11:18:40 +0800433 return context
Ken Liu861b0782021-05-22 13:15:08 +0800434
435def gen_per_partition_files(context):
436 """
437 Generate per-partition files
438
439 Parameters
440 ----------
441 context:
442 context contains partition infos
443 """
444
Kevin Peng5bc82d22021-10-19 11:18:40 +0800445 partition_context = {}
Sherry Zhangf58f2bd2022-01-10 17:21:11 +0800446 partition_context['utilities'] = context['utilities']
Sherry Zhangf865c4e2022-03-23 13:53:38 +0800447 partition_context['config_impl'] = context['config_impl']
Ken Liu861b0782021-05-22 13:15:08 +0800448
Kevin Peng5bc82d22021-10-19 11:18:40 +0800449 manifesttemplate = ENV.get_template(os.path.join(sys.path[0], 'templates/manifestfilename.template'))
450 memorytemplate = ENV.get_template(os.path.join(sys.path[0], 'templates/partition_intermedia.template'))
451 infotemplate = ENV.get_template(os.path.join(sys.path[0], 'templates/partition_load_info.template'))
Ken Liu861b0782021-05-22 13:15:08 +0800452
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500453 logging.info ("Start to generate partition files:")
Ken Liu861b0782021-05-22 13:15:08 +0800454
455 for one_partition in context['partitions']:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800456 partition_context['manifest'] = one_partition['manifest']
457 partition_context['attr'] = one_partition['attr']
458 partition_context['manifest_out_basename'] = one_partition['manifest_out_basename']
Ken Liu861b0782021-05-22 13:15:08 +0800459
Jianliang Shen785ed5e2022-02-08 14:16:06 +0800460 logging.info ('Generating {} in {}'.format(one_partition['attr']['name'],
461 one_partition['output_dir']))
Ken Liu861b0782021-05-22 13:15:08 +0800462 outfile_path = os.path.dirname(one_partition['header_file'])
Kevin Peng655f2392019-11-27 16:33:02 +0800463 if not os.path.exists(outfile_path):
464 os.makedirs(outfile_path)
465
Kevin Peng5bc82d22021-10-19 11:18:40 +0800466 headerfile = io.open(one_partition['header_file'], 'w', newline=None)
467 headerfile.write(manifesttemplate.render(partition_context))
Ken Liu861b0782021-05-22 13:15:08 +0800468 headerfile.close()
Kevin Peng655f2392019-11-27 16:33:02 +0800469
Ken Liu861b0782021-05-22 13:15:08 +0800470 intermediafile_path = os.path.dirname(one_partition['intermedia_file'])
Mingyang Sund20999f2020-10-15 14:53:12 +0800471 if not os.path.exists(intermediafile_path):
472 os.makedirs(intermediafile_path)
Kevin Peng5bc82d22021-10-19 11:18:40 +0800473 intermediafile = io.open(one_partition['intermedia_file'], 'w', newline=None)
474 intermediafile.write(memorytemplate.render(partition_context))
Ken Liu861b0782021-05-22 13:15:08 +0800475 intermediafile.close()
Mingyang Sund20999f2020-10-15 14:53:12 +0800476
Ken Liu861b0782021-05-22 13:15:08 +0800477 infofile_path = os.path.dirname(one_partition['loadinfo_file'])
Mingyang Sunf6a78572021-04-02 16:51:05 +0800478 if not os.path.exists(infofile_path):
479 os.makedirs(infofile_path)
Kevin Peng5bc82d22021-10-19 11:18:40 +0800480 infooutfile = io.open(one_partition['loadinfo_file'], 'w', newline=None)
481 infooutfile.write(infotemplate.render(partition_context))
Ken Liu861b0782021-05-22 13:15:08 +0800482 infooutfile.close()
Mingyang Sunf6a78572021-04-02 16:51:05 +0800483
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500484 logging.info ("Per-partition files done:")
Mingyang Sunf6a78572021-04-02 16:51:05 +0800485
Ken Liu861b0782021-05-22 13:15:08 +0800486def gen_summary_files(context, gen_file_lists):
Kevin Peng655f2392019-11-27 16:33:02 +0800487 """
488 Generate files according to the gen_file_list
Edison Ai48b2d9e2019-06-24 14:39:45 +0800489
490 Parameters
491 ----------
Raef Colesf42f0882020-07-10 10:01:58 +0100492 gen_file_lists:
493 The lists of files to generate
Edison Ai48b2d9e2019-06-24 14:39:45 +0800494 """
Kevin Peng655f2392019-11-27 16:33:02 +0800495 file_list = []
Shawn Shana9ad1e02019-08-07 15:49:48 +0800496
Raef Colesf42f0882020-07-10 10:01:58 +0100497 for f in gen_file_lists:
498 with open(f) as file_list_yaml_file:
Kevin Peng655f2392019-11-27 16:33:02 +0800499 file_list_yaml = yaml.safe_load(file_list_yaml_file)
Kevin Peng5bc82d22021-10-19 11:18:40 +0800500 file_list.extend(file_list_yaml['file_list'])
Edison Ai48b2d9e2019-06-24 14:39:45 +0800501
Kevin Peng655f2392019-11-27 16:33:02 +0800502 for file in file_list:
Raef Coles558487a2020-10-29 13:09:44 +0000503 # Replace environment variables in the output filepath
Kevin Peng5bc82d22021-10-19 11:18:40 +0800504 manifest_out_file = os.path.expandvars(file['output'])
Raef Coles558487a2020-10-29 13:09:44 +0000505 # Replace environment variables in the template filepath
Kevin Peng5bc82d22021-10-19 11:18:40 +0800506 templatefile_name = os.path.expandvars(file['template'])
Edison Ai48b2d9e2019-06-24 14:39:45 +0800507
Kevin Peng4fade072021-10-26 17:57:50 +0800508 manifest_out_file = os.path.join(OUT_DIR, manifest_out_file)
Edison Ai48b2d9e2019-06-24 14:39:45 +0800509
Ken Liu861b0782021-05-22 13:15:08 +0800510 outfile_path = os.path.dirname(manifest_out_file)
Kevin Peng655f2392019-11-27 16:33:02 +0800511 if not os.path.exists(outfile_path):
512 os.makedirs(outfile_path)
Edison Ai48b2d9e2019-06-24 14:39:45 +0800513
Kevin Peng655f2392019-11-27 16:33:02 +0800514 template = ENV.get_template(templatefile_name)
Edison Ai6e3f2a32019-06-11 15:29:05 +0800515
Kevin Peng5bc82d22021-10-19 11:18:40 +0800516 outfile = io.open(manifest_out_file, 'w', newline=None)
Kevin Peng655f2392019-11-27 16:33:02 +0800517 outfile.write(template.render(context))
518 outfile.close()
Edison Ai48b2d9e2019-06-24 14:39:45 +0800519
Kevin Pengce99e5d2021-11-09 18:06:53 +0800520def process_stateless_services(partitions):
Mingyang Suna1ca6112021-01-11 11:34:59 +0800521 """
522 This function collects all stateless services together, and allocates
Mingyang Sun4ecea992021-03-30 17:56:26 +0800523 stateless handles for them.
Kevin Pengc05319d2021-04-22 22:59:35 +0800524 Valid stateless handle in service will be converted to an index. If the
525 stateless handle is set as "auto", or not set, framework will allocate a
526 valid index for the service.
527 Framework puts each service into a reordered stateless service list at
528 position of "index". Other unused positions are left None.
Mingyang Suna1ca6112021-01-11 11:34:59 +0800529 """
Kevin Pengce99e5d2021-11-09 18:06:53 +0800530
531 STATIC_HANDLE_CONFIG_FILE = 'secure_fw/spm/cmsis_psa/spm_ipc.h'
532
Kevin Pengc05319d2021-04-22 22:59:35 +0800533 collected_stateless_services = []
Kevin Pengce99e5d2021-11-09 18:06:53 +0800534 stateless_index_max_num = \
535 int(get_single_macro_def_from_file(STATIC_HANDLE_CONFIG_FILE, 'STATIC_HANDLE_NUM_LIMIT'), base = 10)
Mingyang Suna1ca6112021-01-11 11:34:59 +0800536
537 # Collect all stateless services first.
538 for partition in partitions:
539 # Skip the FF-M 1.0 partitions
540 if partition['manifest']['psa_framework_version'] < 1.1:
541 continue
Kevin Peng8849b6a2021-11-09 14:17:35 +0800542
543 service_list = partition['manifest'].get('services', [])
544
545 for service in service_list:
Mingyang Suna1ca6112021-01-11 11:34:59 +0800546 if service['connection_based'] is False:
Kevin Pengc05319d2021-04-22 22:59:35 +0800547 collected_stateless_services.append(service)
Mingyang Suna1ca6112021-01-11 11:34:59 +0800548
Kevin Pengc05319d2021-04-22 22:59:35 +0800549 if len(collected_stateless_services) == 0:
Mingyang Suna1ca6112021-01-11 11:34:59 +0800550 return []
551
Ken Liu861b0782021-05-22 13:15:08 +0800552 if len(collected_stateless_services) > stateless_index_max_num:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800553 raise Exception('Stateless service numbers range exceed {number}.'.format(number=stateless_index_max_num))
Mingyang Suna1ca6112021-01-11 11:34:59 +0800554
555 """
Kevin Pengc05319d2021-04-22 22:59:35 +0800556 Allocate an empty stateless service list to store services.
557 Use "handle - 1" as the index for service, since handle value starts from
558 1 and list index starts from 0.
Mingyang Suna1ca6112021-01-11 11:34:59 +0800559 """
Ken Liu861b0782021-05-22 13:15:08 +0800560 reordered_stateless_services = [None] * stateless_index_max_num
Kevin Pengc05319d2021-04-22 22:59:35 +0800561 auto_alloc_services = []
Mingyang Suna1ca6112021-01-11 11:34:59 +0800562
Kevin Pengc05319d2021-04-22 22:59:35 +0800563 for service in collected_stateless_services:
564 # If not set, it is "auto" by default
565 if 'stateless_handle' not in service:
566 auto_alloc_services.append(service)
567 continue
568
Mingyang Sun4ecea992021-03-30 17:56:26 +0800569 service_handle = service['stateless_handle']
Mingyang Suna1ca6112021-01-11 11:34:59 +0800570
Mingyang Sun4ecea992021-03-30 17:56:26 +0800571 # Fill in service list with specified stateless handle, otherwise skip
572 if isinstance(service_handle, int):
Ken Liu861b0782021-05-22 13:15:08 +0800573 if service_handle < 1 or service_handle > stateless_index_max_num:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800574 raise Exception('Invalid stateless_handle setting: {handle}.'.format(handle=service['stateless_handle']))
Mingyang Sun4ecea992021-03-30 17:56:26 +0800575 # Convert handle index to reordered service list index
576 service_handle = service_handle - 1
577
578 if reordered_stateless_services[service_handle] is not None:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800579 raise Exception('Duplicated stateless_handle setting: {handle}.'.format(handle=service['stateless_handle']))
Mingyang Sun4ecea992021-03-30 17:56:26 +0800580 reordered_stateless_services[service_handle] = service
Kevin Pengc05319d2021-04-22 22:59:35 +0800581 elif service_handle == 'auto':
582 auto_alloc_services.append(service)
583 else:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800584 raise Exception('Invalid stateless_handle setting: {handle}.'.format(handle=service['stateless_handle']))
Mingyang Sun4ecea992021-03-30 17:56:26 +0800585
Kevin Pengce99e5d2021-11-09 18:06:53 +0800586 STATIC_HANDLE_IDX_BIT_WIDTH = \
587 int(get_single_macro_def_from_file(STATIC_HANDLE_CONFIG_FILE, 'STATIC_HANDLE_IDX_BIT_WIDTH'), base = 10)
588 STATIC_HANDLE_IDX_MASK = (1 << STATIC_HANDLE_IDX_BIT_WIDTH) - 1
589
590 STATIC_HANDLE_INDICATOR_OFFSET = \
591 int(get_single_macro_def_from_file(STATIC_HANDLE_CONFIG_FILE, 'STATIC_HANDLE_INDICATOR_OFFSET'), base = 10)
592
593 STATIC_HANDLE_VER_OFFSET = \
594 int(get_single_macro_def_from_file(STATIC_HANDLE_CONFIG_FILE, 'STATIC_HANDLE_VER_OFFSET'), base = 10)
595
596 STATIC_HANDLE_VER_BIT_WIDTH = \
597 int(get_single_macro_def_from_file(STATIC_HANDLE_CONFIG_FILE, 'STATIC_HANDLE_VER_BIT_WIDTH'), base = 10)
598 STATIC_HANDLE_VER_MASK = (1 << STATIC_HANDLE_VER_BIT_WIDTH) - 1
599
Mingyang Sun4ecea992021-03-30 17:56:26 +0800600 # Auto-allocate stateless handle and encode the stateless handle
Ken Liu861b0782021-05-22 13:15:08 +0800601 for i in range(0, stateless_index_max_num):
Mingyang Sun4ecea992021-03-30 17:56:26 +0800602 service = reordered_stateless_services[i]
603
Kevin Pengc05319d2021-04-22 22:59:35 +0800604 if service == None and len(auto_alloc_services) > 0:
605 service = auto_alloc_services.pop(0)
Mingyang Sun4ecea992021-03-30 17:56:26 +0800606
Mingyang Sun453ad402021-03-17 17:58:33 +0800607 """
608 Encode stateless flag and version into stateless handle
Kevin Pengce99e5d2021-11-09 18:06:53 +0800609 Check STATIC_HANDLE_CONFIG_FILE for details
Mingyang Sun453ad402021-03-17 17:58:33 +0800610 """
Mingyang Sun4ecea992021-03-30 17:56:26 +0800611 stateless_handle_value = 0
612 if service != None:
Kevin Pengce99e5d2021-11-09 18:06:53 +0800613 stateless_index = (i & STATIC_HANDLE_IDX_MASK)
Mingyang Sun4ecea992021-03-30 17:56:26 +0800614 stateless_handle_value |= stateless_index
Kevin Pengce99e5d2021-11-09 18:06:53 +0800615 stateless_handle_value |= (1 << STATIC_HANDLE_INDICATOR_OFFSET)
616 stateless_version = (service['version'] & STATIC_HANDLE_VER_MASK) << STATIC_HANDLE_VER_OFFSET
Mingyang Sun453ad402021-03-17 17:58:33 +0800617 stateless_handle_value |= stateless_version
Mingyang Sun4ecea992021-03-30 17:56:26 +0800618 service['stateless_handle_value'] = '0x{0:08x}'.format(stateless_handle_value)
Ken Liu861b0782021-05-22 13:15:08 +0800619 service['stateless_handle_index'] = stateless_index
Mingyang Suna1ca6112021-01-11 11:34:59 +0800620
Mingyang Sun4ecea992021-03-30 17:56:26 +0800621 reordered_stateless_services[i] = service
622
623 return reordered_stateless_services
Mingyang Suna1ca6112021-01-11 11:34:59 +0800624
Kevin Peng655f2392019-11-27 16:33:02 +0800625def parse_args():
Raef Coles558487a2020-10-29 13:09:44 +0000626 parser = argparse.ArgumentParser(description='Parse secure partition manifest list and generate files listed by the file list',
627 epilog='Note that environment variables in template files will be replaced with their values')
628
Kevin Peng655f2392019-11-27 16:33:02 +0800629 parser.add_argument('-o', '--outdir'
630 , dest='outdir'
Kevin Peng4fade072021-10-26 17:57:50 +0800631 , required=True
Kevin Peng655f2392019-11-27 16:33:02 +0800632 , metavar='out_dir'
Kevin Peng4fade072021-10-26 17:57:50 +0800633 , help='The root directory for generated files')
Shawn Shana9ad1e02019-08-07 15:49:48 +0800634
Kevin Peng5bc82d22021-10-19 11:18:40 +0800635 parser.add_argument('-m', '--manifest-lists'
Raef Colesf42f0882020-07-10 10:01:58 +0100636 , nargs='+'
Kevin Peng5bc82d22021-10-19 11:18:40 +0800637 , dest='manifest_lists'
Raef Colesf42f0882020-07-10 10:01:58 +0100638 , required=True
Kevin Peng65064c52021-10-27 17:12:17 +0800639 , metavar='manifest list'
640 , help='A list of Secure Partition manifest lists and their original paths.\n\
641 The manifest lists might be processed by CMake and\n\
642 the path might be different to the original one\n\
643 The format must be [list A, orignal path A, list B, orignal path B, ...]')
Kevin Peng655f2392019-11-27 16:33:02 +0800644
645 parser.add_argument('-f', '--file-list'
Raef Colesf42f0882020-07-10 10:01:58 +0100646 , nargs='+'
Kevin Peng655f2392019-11-27 16:33:02 +0800647 , dest='gen_file_args'
Raef Colesf42f0882020-07-10 10:01:58 +0100648 , required=True
Kevin Peng655f2392019-11-27 16:33:02 +0800649 , metavar='file-list'
Raef Colesf42f0882020-07-10 10:01:58 +0100650 , help='These files descripe the file list to generate')
Kevin Peng9f1a7542022-02-07 16:32:27 +0800651
652 parser.add_argument('-l', '--isolation-level'
653 , dest='isolation_level'
654 , required=True
655 , choices=['1', '2', '3']
Kevin Peng76c0c162022-02-09 22:49:06 +0800656 , metavar='isolation-level')
657
658 parser.add_argument('-b', '--backend'
659 , dest='backend'
660 , required=True
661 , choices=['IPC', 'SFN']
662 , metavar='spm-backend'
Kevin Peng9f1a7542022-02-07 16:32:27 +0800663 , help='The isolation level')
664
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500665 parser.add_argument('-q', '--quiet'
666 , dest='quiet'
667 , required=False
668 , default=False
669 , action='store_true'
670 , help='Reduce log messages')
Kevin Peng655f2392019-11-27 16:33:02 +0800671
672 args = parser.parse_args()
Kevin Peng655f2392019-11-27 16:33:02 +0800673
Kevin Peng65064c52021-10-27 17:12:17 +0800674 if len(args.manifest_lists) % 2 != 0:
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500675 logging.error('Invalid structure in manifest lists.\n'
Kevin Peng65064c52021-10-27 17:12:17 +0800676 'Each element shall consist of a manifest list and its original path')
677 exit(1)
678
Kevin Peng655f2392019-11-27 16:33:02 +0800679 return args
680
681ENV = Environment(
682 loader = TemplateLoader(),
683 autoescape = select_autoescape(['html', 'xml']),
684 lstrip_blocks = True,
685 trim_blocks = True,
686 keep_trailing_newline = True
687 )
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100688
Miklos Balint470919c2018-05-22 17:51:29 +0200689def main():
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100690 """
691 The entry point of the script.
692
693 Generates the output files based on the templates and the manifests.
694 """
Shawn Shana9ad1e02019-08-07 15:49:48 +0800695
Kevin Peng655f2392019-11-27 16:33:02 +0800696 global OUT_DIR
Shawn Shana9ad1e02019-08-07 15:49:48 +0800697
Kevin Peng655f2392019-11-27 16:33:02 +0800698 args = parse_args()
Shawn Shana9ad1e02019-08-07 15:49:48 +0800699
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500700 logging.basicConfig(format='%(message)s'
701 , level=logging.WARNING if args.quiet else logging.INFO)
702
Kevin Peng5bc82d22021-10-19 11:18:40 +0800703 OUT_DIR = os.path.abspath(args.outdir)
Kevin Peng655f2392019-11-27 16:33:02 +0800704
Kevin Peng5bc82d22021-10-19 11:18:40 +0800705 manifest_lists = [os.path.abspath(x) for x in args.manifest_lists]
706 gen_file_lists = [os.path.abspath(x) for x in args.gen_file_args]
Shawn Shana9ad1e02019-08-07 15:49:48 +0800707
Shawn Shana9ad1e02019-08-07 15:49:48 +0800708 """
Kevin Peng655f2392019-11-27 16:33:02 +0800709 Relative path to TF-M root folder is supported in the manifests
710 and default value of manifest list and generated file list are relative to TF-M root folder as well,
711 so first change directory to TF-M root folder.
Shawn Shana9ad1e02019-08-07 15:49:48 +0800712 By doing this, the script can be executed anywhere
Kevin Peng655f2392019-11-27 16:33:02 +0800713 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 +0800714 """
Kevin Peng5bc82d22021-10-19 11:18:40 +0800715 os.chdir(os.path.join(sys.path[0], '..'))
Shawn Shana9ad1e02019-08-07 15:49:48 +0800716
Kevin Peng76c0c162022-02-09 22:49:06 +0800717 context = process_partition_manifests(manifest_lists,
718 int(args.isolation_level),
719 args.backend)
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100720
Edison Ai6e3f2a32019-06-11 15:29:05 +0800721 utilities = {}
Mingyang Suna1ca6112021-01-11 11:34:59 +0800722 utilities['donotedit_warning'] = donotedit_warning
Miklos Balint470919c2018-05-22 17:51:29 +0200723
Kevin Peng655f2392019-11-27 16:33:02 +0800724 context['utilities'] = utilities
Mingyang Suneab7eae2021-09-30 13:06:52 +0800725
Ken Liu861b0782021-05-22 13:15:08 +0800726 gen_per_partition_files(context)
Kevin Peng5bc82d22021-10-19 11:18:40 +0800727 gen_summary_files(context, gen_file_lists)
Miklos Balint470919c2018-05-22 17:51:29 +0200728
Kevin Peng5bc82d22021-10-19 11:18:40 +0800729if __name__ == '__main__':
Miklos Balint470919c2018-05-22 17:51:29 +0200730 main()