blob: f1b5367a1156a474f504bd691e23e4f1da923abb [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
276 # Parse the manifests
277 for i, manifest_item in enumerate(all_manifests):
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800278 valid_enabled_conditions = ['on', 'true', 'enabled']
Kevin Peng50f413c2021-11-12 10:31:45 +0800279 valid_disabled_conditions = ['off', 'false', 'disabled', '']
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800280 is_enabled = ''
281
282 if 'conditional' in manifest_item.keys():
Kevin Peng50f413c2021-11-12 10:31:45 +0800283 is_enabled = str(manifest_item['conditional']).lower()
Kevin Peng68d9a3a2021-10-18 11:39:54 +0800284 else:
285 # Partitions without 'conditional' is alwasy on
286 is_enabled = 'on'
287
288 if is_enabled in valid_disabled_conditions:
289 continue
290 elif is_enabled not in valid_enabled_conditions:
291 raise Exception('Invalid "conditional" attribute: "{}" for {}. '
292 'Please set to one of {} or {}, case-insensitive.'\
293 .format(manifest_item['conditional'],
294 manifest_item['name'],
295 valid_enabled_conditions, valid_disabled_conditions))
296
Xinyu Zhangc46ee1f2021-04-01 10:10:43 +0800297 # Check if partition ID is manually set
298 if 'pid' not in manifest_item.keys():
299 no_pid_manifest_idx.append(i)
Kevin Peng8849b6a2021-11-09 14:17:35 +0800300 pid = None
Kevin Peng56b0ea62021-10-18 11:32:57 +0800301 else:
Kevin Peng8849b6a2021-11-09 14:17:35 +0800302 pid = manifest_item['pid']
303
304 # Check if partition ID is duplicated
305 if pid in pid_list:
Antonio de Angelisbaa27642022-05-25 11:07:12 +0100306 raise Exception('PID No. {} has already been used!'.format(pid))
Kevin Peng8849b6a2021-11-09 14:17:35 +0800307 else:
308 pid_list.append(pid)
Xinyu Zhang19504a52021-03-31 16:26:20 +0800309
Raef Coles558487a2020-10-29 13:09:44 +0000310 # Replace environment variables in the manifest path
Kevin Peng655f2392019-11-27 16:33:02 +0800311 manifest_path = os.path.expandvars(manifest_item['manifest'])
Kevin Peng65064c52021-10-27 17:12:17 +0800312 # Convert to absolute path. If it's already abspath, the path will not be changed.
313 manifest_path = os.path.join(manifest_item['list_path'], manifest_path).replace('\\', '/')
David Hub2694202021-07-15 14:58:39 +0800314
Kevin Peng5bc82d22021-10-19 11:18:40 +0800315 with open(manifest_path) as manifest_file:
Kevin Pengec0e5ce2022-02-17 13:48:51 +0800316 manifest = yaml.safe_load(manifest_file)
317 if manifest.get('model', None) == 'dual':
318 # If a Partition supports both models, it can set the "model" to "backend".
319 # The actual model used follows the backend being used.
320 manifest['model'] = backend
321 manifest = manifest_validation(manifest, pid)
Kevin Peng655f2392019-11-27 16:33:02 +0800322
Kevin Pengce99e5d2021-11-09 18:06:53 +0800323 if pid == None or pid >= TFM_PID_BASE:
Kevin Pengd08f3ba2021-11-18 15:18:56 +0800324 # Count the number of IPC/SFN partitions
Kevin Peng47c26ec2022-03-11 11:49:52 +0800325 if manifest['model'] == 'IPC':
326 partition_statistics['ipc_partitions'].append(manifest['name'])
Mingyang Suneab7eae2021-09-30 13:06:52 +0800327
Kevin Peng5c3fee72022-02-16 22:25:22 +0800328 # Set initial value to -1 to make (srv_idx + 1) reflect the correct
329 # number (0) when there are no services.
330 srv_idx = -1
331 for srv_idx, service in enumerate(manifest.get('services', [])):
Xinyu Zhang7d0a5c82022-05-16 18:15:11 +0800332 service_partition_map[service['name']] = manifest['name']
Kevin Peng5c3fee72022-02-16 22:25:22 +0800333 if manifest['model'] == 'IPC':
334 # Assign signal value, the first 4 bits are reserved by FF-M
335 service['signal_value'] = (1 << (srv_idx + 4))
336 else:
337 # Signals of SFN Partitions are SPM internal only, does not
338 # need to reserve 4 bits.
339 service['signal_value'] = (1 << srv_idx)
340 if service['connection_based']:
Xinyu Zhang2bc4d572021-12-27 16:37:46 +0800341 partition_statistics['connection_based_srv_num'] += 1
Kevin Peng5c3fee72022-02-16 22:25:22 +0800342 logging.debug('{} has {} services'.format(manifest['name'], srv_idx +1))
Xinyu Zhang2bc4d572021-12-27 16:37:46 +0800343
Kevin Peng5c3fee72022-02-16 22:25:22 +0800344 # Set initial value to -1 to make (irq + 1) reflect the correct
345 # number (0) when there are no irqs.
346 irq_idx = -1
347 for irq_idx, irq in enumerate(manifest.get('irqs', [])):
348 # Assign signal value, from the most significant bit
349 irq['signal_value'] = (1 << (31 - irq_idx))
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800350 if irq.get('handling', None) == 'FLIH':
351 partition_statistics['flih_num'] += 1
352 else:
353 partition_statistics['slih_num'] += 1
Kevin Peng5c3fee72022-02-16 22:25:22 +0800354 logging.debug('{} has {} IRQS'.format(manifest['name'], irq_idx +1))
355
356 if ((srv_idx + 1) + (irq_idx + 1)) > 28:
357 raise Exception('Total number of Services and IRQs of {} exceeds the limit (28)'
358 .format(manifest['name']))
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800359
Kevin Peng65064c52021-10-27 17:12:17 +0800360 manifest_out_basename = os.path.splitext(os.path.basename(manifest_path))[0]
Kevin Peng655f2392019-11-27 16:33:02 +0800361
Kevin Peng4fade072021-10-26 17:57:50 +0800362 if 'output_path' in manifest_item:
Kevin Peng4fade072021-10-26 17:57:50 +0800363 output_path = os.path.expandvars(manifest_item['output_path'])
Kevin Peng4fade072021-10-26 17:57:50 +0800364 else:
Kevin Peng65064c52021-10-27 17:12:17 +0800365 output_path = ''
David Hub2694202021-07-15 14:58:39 +0800366
Kevin Peng5bc82d22021-10-19 11:18:40 +0800367 manifest_head_file = os.path.join(OUT_DIR, output_path, 'psa_manifest',
368 '{}.h'.format(manifest_out_basename))\
369 .replace('\\', '/')
370 intermedia_file = os.path.join(OUT_DIR, output_path, 'auto_generated',
371 'intermedia_{}.c'.format(manifest_out_basename))\
372 .replace('\\', '/')
373 load_info_file = os.path.join(OUT_DIR, output_path, 'auto_generated',
374 'load_info_{}.c'.format(manifest_out_basename))\
375 .replace('\\', '/')
Jianliang Shen785ed5e2022-02-08 14:16:06 +0800376 output_dir = os.path.join(OUT_DIR, output_path).replace('\\', '/')
Kevin Peng655f2392019-11-27 16:33:02 +0800377
Kevin Peng5bc82d22021-10-19 11:18:40 +0800378 partition_list.append({'manifest': manifest, 'attr': manifest_item,
379 'manifest_out_basename': manifest_out_basename,
380 'header_file': manifest_head_file,
381 'intermedia_file': intermedia_file,
Jianliang Shen785ed5e2022-02-08 14:16:06 +0800382 'loadinfo_file': load_info_file,
383 'output_dir':output_dir})
Ken Liu861b0782021-05-22 13:15:08 +0800384
Xinyu Zhang7d0a5c82022-05-16 18:15:11 +0800385 check_circular_dependency(partition_list, service_partition_map)
386
Kevin Peng56b0ea62021-10-18 11:32:57 +0800387 # Automatically assign PIDs for partitions without 'pid' attribute
Kevin Pengce99e5d2021-11-09 18:06:53 +0800388 pid = max(pid_list, default = TFM_PID_BASE - 1)
Kevin Peng56b0ea62021-10-18 11:32:57 +0800389 for idx in no_pid_manifest_idx:
Kevin Pengc424eec2021-06-25 17:26:11 +0800390 pid += 1
Kevin Peng65064c52021-10-27 17:12:17 +0800391 all_manifests[idx]['pid'] = pid
Kevin Peng56b0ea62021-10-18 11:32:57 +0800392 pid_list.append(pid)
393
Kevin Peng9f1a7542022-02-07 16:32:27 +0800394 # Set up configurations
Kevin Peng76c0c162022-02-09 22:49:06 +0800395 if backend == 'SFN':
Kevin Peng47c26ec2022-03-11 11:49:52 +0800396 if len(partition_statistics['ipc_partitions']) > 0:
397 logging.error('SFN backend does not support IPC Partitions:')
398 logging.error(partition_statistics['ipc_partitions'])
Kevin Peng9f1a7542022-02-07 16:32:27 +0800399 exit(1)
Kevin Peng76c0c162022-02-09 22:49:06 +0800400
401 if isolation_level > 1:
402 logging.error('SFN backend does not support high isolation levels.')
403 exit(1)
404
Kevin Peng9f1a7542022-02-07 16:32:27 +0800405 config_impl['CONFIG_TFM_SPM_BACKEND_SFN'] = '1'
406 config_impl['CONFIG_TFM_PSA_API_SFN_CALL'] = '1'
Kevin Peng76c0c162022-02-09 22:49:06 +0800407 elif backend == 'IPC':
Kevin Peng9f1a7542022-02-07 16:32:27 +0800408 config_impl['CONFIG_TFM_SPM_BACKEND_IPC'] = '1'
409 if isolation_level > 1:
410 config_impl['CONFIG_TFM_PSA_API_SUPERVISOR_CALL'] = '1'
411 else:
412 config_impl['CONFIG_TFM_PSA_API_CROSS_CALL'] = '1'
Kevin Peng9f1a7542022-02-07 16:32:27 +0800413
414 if partition_statistics['connection_based_srv_num'] > 0:
415 config_impl['CONFIG_TFM_CONNECTION_BASED_SERVICE_API'] = 1
416
Mingyang Suned5fe7b2022-02-10 17:33:21 +0800417 if partition_statistics['flih_num'] > 0:
418 config_impl['CONFIG_TFM_FLIH_API'] = 1
419 elif partition_statistics['slih_num'] > 0:
420 config_impl['CONFIG_TFM_SLIH_API'] = 1
421
Kevin Peng5bc82d22021-10-19 11:18:40 +0800422 context['partitions'] = partition_list
Kevin Peng9f1a7542022-02-07 16:32:27 +0800423 context['config_impl'] = config_impl
Kevin Pengce99e5d2021-11-09 18:06:53 +0800424 context['stateless_services'] = process_stateless_services(partition_list)
Ruiqi Jiang71d361c2021-06-23 17:45:55 +0100425
Kevin Peng5bc82d22021-10-19 11:18:40 +0800426 return context
Ken Liu861b0782021-05-22 13:15:08 +0800427
428def gen_per_partition_files(context):
429 """
430 Generate per-partition files
431
432 Parameters
433 ----------
434 context:
435 context contains partition infos
436 """
437
Kevin Peng5bc82d22021-10-19 11:18:40 +0800438 partition_context = {}
Sherry Zhangf58f2bd2022-01-10 17:21:11 +0800439 partition_context['utilities'] = context['utilities']
Sherry Zhangf865c4e2022-03-23 13:53:38 +0800440 partition_context['config_impl'] = context['config_impl']
Ken Liu861b0782021-05-22 13:15:08 +0800441
Kevin Peng5bc82d22021-10-19 11:18:40 +0800442 manifesttemplate = ENV.get_template(os.path.join(sys.path[0], 'templates/manifestfilename.template'))
443 memorytemplate = ENV.get_template(os.path.join(sys.path[0], 'templates/partition_intermedia.template'))
444 infotemplate = ENV.get_template(os.path.join(sys.path[0], 'templates/partition_load_info.template'))
Ken Liu861b0782021-05-22 13:15:08 +0800445
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500446 logging.info ("Start to generate partition files:")
Ken Liu861b0782021-05-22 13:15:08 +0800447
448 for one_partition in context['partitions']:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800449 partition_context['manifest'] = one_partition['manifest']
450 partition_context['attr'] = one_partition['attr']
451 partition_context['manifest_out_basename'] = one_partition['manifest_out_basename']
Ken Liu861b0782021-05-22 13:15:08 +0800452
Jianliang Shen785ed5e2022-02-08 14:16:06 +0800453 logging.info ('Generating {} in {}'.format(one_partition['attr']['name'],
454 one_partition['output_dir']))
Ken Liu861b0782021-05-22 13:15:08 +0800455 outfile_path = os.path.dirname(one_partition['header_file'])
Kevin Peng655f2392019-11-27 16:33:02 +0800456 if not os.path.exists(outfile_path):
457 os.makedirs(outfile_path)
458
Kevin Peng5bc82d22021-10-19 11:18:40 +0800459 headerfile = io.open(one_partition['header_file'], 'w', newline=None)
460 headerfile.write(manifesttemplate.render(partition_context))
Ken Liu861b0782021-05-22 13:15:08 +0800461 headerfile.close()
Kevin Peng655f2392019-11-27 16:33:02 +0800462
Ken Liu861b0782021-05-22 13:15:08 +0800463 intermediafile_path = os.path.dirname(one_partition['intermedia_file'])
Mingyang Sund20999f2020-10-15 14:53:12 +0800464 if not os.path.exists(intermediafile_path):
465 os.makedirs(intermediafile_path)
Kevin Peng5bc82d22021-10-19 11:18:40 +0800466 intermediafile = io.open(one_partition['intermedia_file'], 'w', newline=None)
467 intermediafile.write(memorytemplate.render(partition_context))
Ken Liu861b0782021-05-22 13:15:08 +0800468 intermediafile.close()
Mingyang Sund20999f2020-10-15 14:53:12 +0800469
Ken Liu861b0782021-05-22 13:15:08 +0800470 infofile_path = os.path.dirname(one_partition['loadinfo_file'])
Mingyang Sunf6a78572021-04-02 16:51:05 +0800471 if not os.path.exists(infofile_path):
472 os.makedirs(infofile_path)
Kevin Peng5bc82d22021-10-19 11:18:40 +0800473 infooutfile = io.open(one_partition['loadinfo_file'], 'w', newline=None)
474 infooutfile.write(infotemplate.render(partition_context))
Ken Liu861b0782021-05-22 13:15:08 +0800475 infooutfile.close()
Mingyang Sunf6a78572021-04-02 16:51:05 +0800476
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500477 logging.info ("Per-partition files done:")
Mingyang Sunf6a78572021-04-02 16:51:05 +0800478
Ken Liu861b0782021-05-22 13:15:08 +0800479def gen_summary_files(context, gen_file_lists):
Kevin Peng655f2392019-11-27 16:33:02 +0800480 """
481 Generate files according to the gen_file_list
Edison Ai48b2d9e2019-06-24 14:39:45 +0800482
483 Parameters
484 ----------
Raef Colesf42f0882020-07-10 10:01:58 +0100485 gen_file_lists:
486 The lists of files to generate
Edison Ai48b2d9e2019-06-24 14:39:45 +0800487 """
Kevin Peng655f2392019-11-27 16:33:02 +0800488 file_list = []
Shawn Shana9ad1e02019-08-07 15:49:48 +0800489
Raef Colesf42f0882020-07-10 10:01:58 +0100490 for f in gen_file_lists:
491 with open(f) as file_list_yaml_file:
Kevin Peng655f2392019-11-27 16:33:02 +0800492 file_list_yaml = yaml.safe_load(file_list_yaml_file)
Kevin Peng5bc82d22021-10-19 11:18:40 +0800493 file_list.extend(file_list_yaml['file_list'])
Edison Ai48b2d9e2019-06-24 14:39:45 +0800494
Kevin Peng655f2392019-11-27 16:33:02 +0800495 for file in file_list:
Raef Coles558487a2020-10-29 13:09:44 +0000496 # Replace environment variables in the output filepath
Kevin Peng5bc82d22021-10-19 11:18:40 +0800497 manifest_out_file = os.path.expandvars(file['output'])
Raef Coles558487a2020-10-29 13:09:44 +0000498 # Replace environment variables in the template filepath
Kevin Peng5bc82d22021-10-19 11:18:40 +0800499 templatefile_name = os.path.expandvars(file['template'])
Edison Ai48b2d9e2019-06-24 14:39:45 +0800500
Kevin Peng4fade072021-10-26 17:57:50 +0800501 manifest_out_file = os.path.join(OUT_DIR, manifest_out_file)
Edison Ai48b2d9e2019-06-24 14:39:45 +0800502
Ken Liu861b0782021-05-22 13:15:08 +0800503 outfile_path = os.path.dirname(manifest_out_file)
Kevin Peng655f2392019-11-27 16:33:02 +0800504 if not os.path.exists(outfile_path):
505 os.makedirs(outfile_path)
Edison Ai48b2d9e2019-06-24 14:39:45 +0800506
Kevin Peng655f2392019-11-27 16:33:02 +0800507 template = ENV.get_template(templatefile_name)
Edison Ai6e3f2a32019-06-11 15:29:05 +0800508
Kevin Peng5bc82d22021-10-19 11:18:40 +0800509 outfile = io.open(manifest_out_file, 'w', newline=None)
Kevin Peng655f2392019-11-27 16:33:02 +0800510 outfile.write(template.render(context))
511 outfile.close()
Edison Ai48b2d9e2019-06-24 14:39:45 +0800512
Kevin Pengce99e5d2021-11-09 18:06:53 +0800513def process_stateless_services(partitions):
Mingyang Suna1ca6112021-01-11 11:34:59 +0800514 """
515 This function collects all stateless services together, and allocates
Mingyang Sun4ecea992021-03-30 17:56:26 +0800516 stateless handles for them.
Kevin Pengc05319d2021-04-22 22:59:35 +0800517 Valid stateless handle in service will be converted to an index. If the
518 stateless handle is set as "auto", or not set, framework will allocate a
519 valid index for the service.
520 Framework puts each service into a reordered stateless service list at
521 position of "index". Other unused positions are left None.
Mingyang Suna1ca6112021-01-11 11:34:59 +0800522 """
Kevin Pengce99e5d2021-11-09 18:06:53 +0800523
524 STATIC_HANDLE_CONFIG_FILE = 'secure_fw/spm/cmsis_psa/spm_ipc.h'
525
Kevin Pengc05319d2021-04-22 22:59:35 +0800526 collected_stateless_services = []
Kevin Pengce99e5d2021-11-09 18:06:53 +0800527 stateless_index_max_num = \
528 int(get_single_macro_def_from_file(STATIC_HANDLE_CONFIG_FILE, 'STATIC_HANDLE_NUM_LIMIT'), base = 10)
Mingyang Suna1ca6112021-01-11 11:34:59 +0800529
530 # Collect all stateless services first.
531 for partition in partitions:
532 # Skip the FF-M 1.0 partitions
533 if partition['manifest']['psa_framework_version'] < 1.1:
534 continue
Kevin Peng8849b6a2021-11-09 14:17:35 +0800535
536 service_list = partition['manifest'].get('services', [])
537
538 for service in service_list:
Mingyang Suna1ca6112021-01-11 11:34:59 +0800539 if service['connection_based'] is False:
Kevin Pengc05319d2021-04-22 22:59:35 +0800540 collected_stateless_services.append(service)
Mingyang Suna1ca6112021-01-11 11:34:59 +0800541
Kevin Pengc05319d2021-04-22 22:59:35 +0800542 if len(collected_stateless_services) == 0:
Mingyang Suna1ca6112021-01-11 11:34:59 +0800543 return []
544
Ken Liu861b0782021-05-22 13:15:08 +0800545 if len(collected_stateless_services) > stateless_index_max_num:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800546 raise Exception('Stateless service numbers range exceed {number}.'.format(number=stateless_index_max_num))
Mingyang Suna1ca6112021-01-11 11:34:59 +0800547
548 """
Kevin Pengc05319d2021-04-22 22:59:35 +0800549 Allocate an empty stateless service list to store services.
550 Use "handle - 1" as the index for service, since handle value starts from
551 1 and list index starts from 0.
Mingyang Suna1ca6112021-01-11 11:34:59 +0800552 """
Ken Liu861b0782021-05-22 13:15:08 +0800553 reordered_stateless_services = [None] * stateless_index_max_num
Kevin Pengc05319d2021-04-22 22:59:35 +0800554 auto_alloc_services = []
Mingyang Suna1ca6112021-01-11 11:34:59 +0800555
Kevin Pengc05319d2021-04-22 22:59:35 +0800556 for service in collected_stateless_services:
557 # If not set, it is "auto" by default
558 if 'stateless_handle' not in service:
559 auto_alloc_services.append(service)
560 continue
561
Mingyang Sun4ecea992021-03-30 17:56:26 +0800562 service_handle = service['stateless_handle']
Mingyang Suna1ca6112021-01-11 11:34:59 +0800563
Mingyang Sun4ecea992021-03-30 17:56:26 +0800564 # Fill in service list with specified stateless handle, otherwise skip
565 if isinstance(service_handle, int):
Ken Liu861b0782021-05-22 13:15:08 +0800566 if service_handle < 1 or service_handle > stateless_index_max_num:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800567 raise Exception('Invalid stateless_handle setting: {handle}.'.format(handle=service['stateless_handle']))
Mingyang Sun4ecea992021-03-30 17:56:26 +0800568 # Convert handle index to reordered service list index
569 service_handle = service_handle - 1
570
571 if reordered_stateless_services[service_handle] is not None:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800572 raise Exception('Duplicated stateless_handle setting: {handle}.'.format(handle=service['stateless_handle']))
Mingyang Sun4ecea992021-03-30 17:56:26 +0800573 reordered_stateless_services[service_handle] = service
Kevin Pengc05319d2021-04-22 22:59:35 +0800574 elif service_handle == 'auto':
575 auto_alloc_services.append(service)
576 else:
Kevin Peng5bc82d22021-10-19 11:18:40 +0800577 raise Exception('Invalid stateless_handle setting: {handle}.'.format(handle=service['stateless_handle']))
Mingyang Sun4ecea992021-03-30 17:56:26 +0800578
Kevin Pengce99e5d2021-11-09 18:06:53 +0800579 STATIC_HANDLE_IDX_BIT_WIDTH = \
580 int(get_single_macro_def_from_file(STATIC_HANDLE_CONFIG_FILE, 'STATIC_HANDLE_IDX_BIT_WIDTH'), base = 10)
581 STATIC_HANDLE_IDX_MASK = (1 << STATIC_HANDLE_IDX_BIT_WIDTH) - 1
582
583 STATIC_HANDLE_INDICATOR_OFFSET = \
584 int(get_single_macro_def_from_file(STATIC_HANDLE_CONFIG_FILE, 'STATIC_HANDLE_INDICATOR_OFFSET'), base = 10)
585
586 STATIC_HANDLE_VER_OFFSET = \
587 int(get_single_macro_def_from_file(STATIC_HANDLE_CONFIG_FILE, 'STATIC_HANDLE_VER_OFFSET'), base = 10)
588
589 STATIC_HANDLE_VER_BIT_WIDTH = \
590 int(get_single_macro_def_from_file(STATIC_HANDLE_CONFIG_FILE, 'STATIC_HANDLE_VER_BIT_WIDTH'), base = 10)
591 STATIC_HANDLE_VER_MASK = (1 << STATIC_HANDLE_VER_BIT_WIDTH) - 1
592
Mingyang Sun4ecea992021-03-30 17:56:26 +0800593 # Auto-allocate stateless handle and encode the stateless handle
Ken Liu861b0782021-05-22 13:15:08 +0800594 for i in range(0, stateless_index_max_num):
Mingyang Sun4ecea992021-03-30 17:56:26 +0800595 service = reordered_stateless_services[i]
596
Kevin Pengc05319d2021-04-22 22:59:35 +0800597 if service == None and len(auto_alloc_services) > 0:
598 service = auto_alloc_services.pop(0)
Mingyang Sun4ecea992021-03-30 17:56:26 +0800599
Mingyang Sun453ad402021-03-17 17:58:33 +0800600 """
601 Encode stateless flag and version into stateless handle
Kevin Pengce99e5d2021-11-09 18:06:53 +0800602 Check STATIC_HANDLE_CONFIG_FILE for details
Mingyang Sun453ad402021-03-17 17:58:33 +0800603 """
Mingyang Sun4ecea992021-03-30 17:56:26 +0800604 stateless_handle_value = 0
605 if service != None:
Kevin Pengce99e5d2021-11-09 18:06:53 +0800606 stateless_index = (i & STATIC_HANDLE_IDX_MASK)
Mingyang Sun4ecea992021-03-30 17:56:26 +0800607 stateless_handle_value |= stateless_index
Kevin Pengce99e5d2021-11-09 18:06:53 +0800608 stateless_handle_value |= (1 << STATIC_HANDLE_INDICATOR_OFFSET)
609 stateless_version = (service['version'] & STATIC_HANDLE_VER_MASK) << STATIC_HANDLE_VER_OFFSET
Mingyang Sun453ad402021-03-17 17:58:33 +0800610 stateless_handle_value |= stateless_version
Mingyang Sun4ecea992021-03-30 17:56:26 +0800611 service['stateless_handle_value'] = '0x{0:08x}'.format(stateless_handle_value)
Ken Liu861b0782021-05-22 13:15:08 +0800612 service['stateless_handle_index'] = stateless_index
Mingyang Suna1ca6112021-01-11 11:34:59 +0800613
Mingyang Sun4ecea992021-03-30 17:56:26 +0800614 reordered_stateless_services[i] = service
615
616 return reordered_stateless_services
Mingyang Suna1ca6112021-01-11 11:34:59 +0800617
Kevin Peng655f2392019-11-27 16:33:02 +0800618def parse_args():
Raef Coles558487a2020-10-29 13:09:44 +0000619 parser = argparse.ArgumentParser(description='Parse secure partition manifest list and generate files listed by the file list',
620 epilog='Note that environment variables in template files will be replaced with their values')
621
Kevin Peng655f2392019-11-27 16:33:02 +0800622 parser.add_argument('-o', '--outdir'
623 , dest='outdir'
Kevin Peng4fade072021-10-26 17:57:50 +0800624 , required=True
Kevin Peng655f2392019-11-27 16:33:02 +0800625 , metavar='out_dir'
Kevin Peng4fade072021-10-26 17:57:50 +0800626 , help='The root directory for generated files')
Shawn Shana9ad1e02019-08-07 15:49:48 +0800627
Kevin Peng5bc82d22021-10-19 11:18:40 +0800628 parser.add_argument('-m', '--manifest-lists'
Raef Colesf42f0882020-07-10 10:01:58 +0100629 , nargs='+'
Kevin Peng5bc82d22021-10-19 11:18:40 +0800630 , dest='manifest_lists'
Raef Colesf42f0882020-07-10 10:01:58 +0100631 , required=True
Kevin Peng65064c52021-10-27 17:12:17 +0800632 , metavar='manifest list'
633 , help='A list of Secure Partition manifest lists and their original paths.\n\
634 The manifest lists might be processed by CMake and\n\
635 the path might be different to the original one\n\
636 The format must be [list A, orignal path A, list B, orignal path B, ...]')
Kevin Peng655f2392019-11-27 16:33:02 +0800637
638 parser.add_argument('-f', '--file-list'
Raef Colesf42f0882020-07-10 10:01:58 +0100639 , nargs='+'
Kevin Peng655f2392019-11-27 16:33:02 +0800640 , dest='gen_file_args'
Raef Colesf42f0882020-07-10 10:01:58 +0100641 , required=True
Kevin Peng655f2392019-11-27 16:33:02 +0800642 , metavar='file-list'
Raef Colesf42f0882020-07-10 10:01:58 +0100643 , help='These files descripe the file list to generate')
Kevin Peng9f1a7542022-02-07 16:32:27 +0800644
645 parser.add_argument('-l', '--isolation-level'
646 , dest='isolation_level'
647 , required=True
648 , choices=['1', '2', '3']
Kevin Peng76c0c162022-02-09 22:49:06 +0800649 , metavar='isolation-level')
650
651 parser.add_argument('-b', '--backend'
652 , dest='backend'
653 , required=True
654 , choices=['IPC', 'SFN']
655 , metavar='spm-backend'
Kevin Peng9f1a7542022-02-07 16:32:27 +0800656 , help='The isolation level')
657
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500658 parser.add_argument('-q', '--quiet'
659 , dest='quiet'
660 , required=False
661 , default=False
662 , action='store_true'
663 , help='Reduce log messages')
Kevin Peng655f2392019-11-27 16:33:02 +0800664
665 args = parser.parse_args()
Kevin Peng655f2392019-11-27 16:33:02 +0800666
Kevin Peng65064c52021-10-27 17:12:17 +0800667 if len(args.manifest_lists) % 2 != 0:
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500668 logging.error('Invalid structure in manifest lists.\n'
Kevin Peng65064c52021-10-27 17:12:17 +0800669 'Each element shall consist of a manifest list and its original path')
670 exit(1)
671
Kevin Peng655f2392019-11-27 16:33:02 +0800672 return args
673
674ENV = Environment(
675 loader = TemplateLoader(),
676 autoescape = select_autoescape(['html', 'xml']),
677 lstrip_blocks = True,
678 trim_blocks = True,
679 keep_trailing_newline = True
680 )
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100681
Miklos Balint470919c2018-05-22 17:51:29 +0200682def main():
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100683 """
684 The entry point of the script.
685
686 Generates the output files based on the templates and the manifests.
687 """
Shawn Shana9ad1e02019-08-07 15:49:48 +0800688
Kevin Peng655f2392019-11-27 16:33:02 +0800689 global OUT_DIR
Shawn Shana9ad1e02019-08-07 15:49:48 +0800690
Kevin Peng655f2392019-11-27 16:33:02 +0800691 args = parse_args()
Shawn Shana9ad1e02019-08-07 15:49:48 +0800692
Jimmy Brisson89d4f8d2021-06-23 10:17:36 -0500693 logging.basicConfig(format='%(message)s'
694 , level=logging.WARNING if args.quiet else logging.INFO)
695
Kevin Peng5bc82d22021-10-19 11:18:40 +0800696 OUT_DIR = os.path.abspath(args.outdir)
Kevin Peng655f2392019-11-27 16:33:02 +0800697
Kevin Peng5bc82d22021-10-19 11:18:40 +0800698 manifest_lists = [os.path.abspath(x) for x in args.manifest_lists]
699 gen_file_lists = [os.path.abspath(x) for x in args.gen_file_args]
Shawn Shana9ad1e02019-08-07 15:49:48 +0800700
Shawn Shana9ad1e02019-08-07 15:49:48 +0800701 """
Kevin Peng655f2392019-11-27 16:33:02 +0800702 Relative path to TF-M root folder is supported in the manifests
703 and default value of manifest list and generated file list are relative to TF-M root folder as well,
704 so first change directory to TF-M root folder.
Shawn Shana9ad1e02019-08-07 15:49:48 +0800705 By doing this, the script can be executed anywhere
Kevin Peng655f2392019-11-27 16:33:02 +0800706 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 +0800707 """
Kevin Peng5bc82d22021-10-19 11:18:40 +0800708 os.chdir(os.path.join(sys.path[0], '..'))
Shawn Shana9ad1e02019-08-07 15:49:48 +0800709
Kevin Peng76c0c162022-02-09 22:49:06 +0800710 context = process_partition_manifests(manifest_lists,
711 int(args.isolation_level),
712 args.backend)
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()