blob: d54ebdd8c5477a462684066b1938f18f2fdf0bae [file] [log] [blame]
Miklos Balint470919c2018-05-22 17:51:29 +02001#-------------------------------------------------------------------------------
Kevin Peng578a8492020-12-31 10:22:59 +08002# Copyright (c) 2018-2021, 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
Shawn Shana9ad1e02019-08-07 15:49:48 +080010import sys
11import argparse
Ken Liu1f345b02020-05-30 21:11:05 +080012from jinja2 import Environment, BaseLoader, select_autoescape, TemplateNotFound
Miklos Balint470919c2018-05-22 17:51:29 +020013
14try:
15 import yaml
16except ImportError as e:
Mate Toth-Pala99ec6b2019-05-07 11:00:56 +020017 print (str(e) + " To install it, type:")
Mate Toth-Pal36f21842018-11-08 16:12:51 +010018 print ("pip install PyYAML")
Miklos Balint470919c2018-05-22 17:51:29 +020019 exit(1)
20
Edison Ai48b2d9e2019-06-24 14:39:45 +080021donotedit_warning = \
22 "/*********** " + \
23 "WARNING: This is an auto-generated file. Do not edit!" + \
24 " ***********/"
Kevin Peng655f2392019-11-27 16:33:02 +080025
Kevin Peng655f2392019-11-27 16:33:02 +080026OUT_DIR = None # The root directory that files are generated to
Edison Ai48b2d9e2019-06-24 14:39:45 +080027
Mate Toth-Pal36f21842018-11-08 16:12:51 +010028class TemplateLoader(BaseLoader):
29 """
30 Template loader class.
Miklos Balint470919c2018-05-22 17:51:29 +020031
Mate Toth-Pal36f21842018-11-08 16:12:51 +010032 An instance of this class is passed to the template engine. It is
33 responsible for reading the template file
34 """
35 def __init__(self):
36 pass
Miklos Balint470919c2018-05-22 17:51:29 +020037
Mate Toth-Pal36f21842018-11-08 16:12:51 +010038 def get_source(self, environment, template):
39 """
40 This function reads the template files.
41 For detailed documentation see:
42 http://jinja.pocoo.org/docs/2.10/api/#jinja2.BaseLoader.get_source
43
44 Please note that this function always return 'false' as 'uptodate'
45 value, so the output file will always be generated.
46 """
47 if not os.path.isfile(template):
48 raise TemplateNotFound(template)
49 with open(template) as f:
50 source = f.read()
51 return source, template, False
52
Raef Colesf42f0882020-07-10 10:01:58 +010053def process_manifest(manifest_list_files):
Mate Toth-Pal36f21842018-11-08 16:12:51 +010054 """
Kevin Peng655f2392019-11-27 16:33:02 +080055 Parse the input manifest, generate the data base for genereated files
56 and generate manifest header files.
Mate Toth-Pal36f21842018-11-08 16:12:51 +010057
58 Parameters
59 ----------
Raef Colesf42f0882020-07-10 10:01:58 +010060 manifest_list_files:
61 The manifest lists to parse.
Mate Toth-Pal36f21842018-11-08 16:12:51 +010062
63 Returns
64 -------
Kevin Peng578a8492020-12-31 10:22:59 +080065 The partition data base.
Edison Ai48b2d9e2019-06-24 14:39:45 +080066 """
Kevin Peng655f2392019-11-27 16:33:02 +080067
Kevin Peng578a8492020-12-31 10:22:59 +080068 partition_db = []
Kevin Peng655f2392019-11-27 16:33:02 +080069 manifest_list = []
70
Raef Colesf42f0882020-07-10 10:01:58 +010071 for f in manifest_list_files:
72 with open(f) as manifest_list_yaml_file:
73 manifest_dic = yaml.safe_load(manifest_list_yaml_file)
Kevin Peng655f2392019-11-27 16:33:02 +080074 manifest_list.extend(manifest_dic["manifest_list"])
75
Mingyang Sund20999f2020-10-15 14:53:12 +080076 manifesttemplate = ENV.get_template('secure_fw/partitions/manifestfilename.template')
77 memorytemplate = ENV.get_template('secure_fw/partitions/partition_intermedia.template')
Kevin Peng655f2392019-11-27 16:33:02 +080078
Xinyu Zhang19504a52021-03-31 16:26:20 +080079 pid_list = []
Xinyu Zhangc46ee1f2021-04-01 10:10:43 +080080 no_pid_manifest_idx = []
81 for i, manifest_item in enumerate(manifest_list):
82 # Check if partition ID is manually set
83 if 'pid' not in manifest_item.keys():
84 no_pid_manifest_idx.append(i)
85 continue
Xinyu Zhang19504a52021-03-31 16:26:20 +080086 # Check if partition ID is duplicated
87 if manifest_item['pid'] in pid_list:
88 raise Exception("PID No. {pid} has already been used!".format(pid=manifest_item['pid']))
89 pid_list.append(manifest_item['pid'])
Xinyu Zhangc46ee1f2021-04-01 10:10:43 +080090 # Automatically generate PIDs for partitions without PID
91 pid = 256
92 for idx in no_pid_manifest_idx:
93 while pid in pid_list:
94 pid += 1
95 manifest_list[idx]['pid'] = pid
96 pid_list.append(pid)
Xinyu Zhang19504a52021-03-31 16:26:20 +080097
Xinyu Zhangc46ee1f2021-04-01 10:10:43 +080098 print("Start to generate PSA manifests:")
99 for manifest_item in manifest_list:
Raef Coles558487a2020-10-29 13:09:44 +0000100 # Replace environment variables in the manifest path
Kevin Peng655f2392019-11-27 16:33:02 +0800101 manifest_path = os.path.expandvars(manifest_item['manifest'])
102 file = open(manifest_path)
103 manifest = yaml.safe_load(file)
104
Kevin Peng655f2392019-11-27 16:33:02 +0800105 utilities = {}
106 utilities['donotedit_warning']=donotedit_warning
107
108 context = {}
109 context['manifest'] = manifest
110 context['attr'] = manifest_item
111 context['utilities'] = utilities
112
113 manifest_dir, manifest_name = os.path.split(manifest_path)
114 outfile_name = manifest_name.replace('yaml', 'h').replace('json', 'h')
115 context['file_name'] = outfile_name.replace('.h', '')
TTornblom7f92a732020-03-05 13:12:20 +0100116 outfile_name = os.path.join(manifest_dir, "psa_manifest", outfile_name).replace('\\', '/')
Mingyang Sund20999f2020-10-15 14:53:12 +0800117 intermediafile_name = os.path.join(manifest_dir, "auto_generated", 'intermedia_' + context['file_name'] + '.c').replace('\\', '/')
Kevin Peng655f2392019-11-27 16:33:02 +0800118
Mingyang Sun4f012692020-10-16 14:04:49 +0800119 """
120 Remove the `source_path` portion of the filepaths, so that it can be
121 interpreted as a relative path from the OUT_DIR.
122 """
123 if 'source_path' in manifest_item:
Raef Coles558487a2020-10-29 13:09:44 +0000124 # Replace environment variables in the source path
125 source_path = os.path.expandvars(manifest_item['source_path'])
126 outfile_name = os.path.relpath(outfile_name, start = source_path)
Mingyang Sund20999f2020-10-15 14:53:12 +0800127 intermediafile_name = os.path.relpath(intermediafile_name, start = source_path)
Mingyang Sun4f012692020-10-16 14:04:49 +0800128
Kevin Peng578a8492020-12-31 10:22:59 +0800129 partition_db.append({"manifest": manifest, "attr": manifest_item, "header_file": outfile_name})
Kevin Peng655f2392019-11-27 16:33:02 +0800130
131 if OUT_DIR is not None:
132 outfile_name = os.path.join(OUT_DIR, outfile_name)
Mingyang Sund20999f2020-10-15 14:53:12 +0800133 intermediafile_name = os.path.join(OUT_DIR, intermediafile_name)
Kevin Peng655f2392019-11-27 16:33:02 +0800134
135 outfile_path = os.path.dirname(outfile_name)
136 if not os.path.exists(outfile_path):
137 os.makedirs(outfile_path)
138
139 print ("Generating " + outfile_name)
140
TTornblom441a0702020-04-28 12:40:42 +0200141 outfile = io.open(outfile_name, "w", newline=None)
Mingyang Sund20999f2020-10-15 14:53:12 +0800142 outfile.write(manifesttemplate.render(context))
Kevin Peng655f2392019-11-27 16:33:02 +0800143 outfile.close()
144
Mingyang Sund20999f2020-10-15 14:53:12 +0800145 intermediafile_path = os.path.dirname(intermediafile_name)
146 if not os.path.exists(intermediafile_path):
147 os.makedirs(intermediafile_path)
148
149 print ("Generating " + intermediafile_name)
150
151 memoutfile = io.open(intermediafile_name, "w", newline=None)
152 memoutfile.write(memorytemplate.render(context))
153 memoutfile.close()
154
Kevin Peng578a8492020-12-31 10:22:59 +0800155 return partition_db
Kevin Peng655f2392019-11-27 16:33:02 +0800156
Raef Colesf42f0882020-07-10 10:01:58 +0100157def gen_files(context, gen_file_lists):
Kevin Peng655f2392019-11-27 16:33:02 +0800158 """
159 Generate files according to the gen_file_list
Edison Ai48b2d9e2019-06-24 14:39:45 +0800160
161 Parameters
162 ----------
Raef Colesf42f0882020-07-10 10:01:58 +0100163 gen_file_lists:
164 The lists of files to generate
Edison Ai48b2d9e2019-06-24 14:39:45 +0800165 """
Kevin Peng655f2392019-11-27 16:33:02 +0800166 file_list = []
Shawn Shana9ad1e02019-08-07 15:49:48 +0800167
Raef Colesf42f0882020-07-10 10:01:58 +0100168 for f in gen_file_lists:
169 with open(f) as file_list_yaml_file:
Kevin Peng655f2392019-11-27 16:33:02 +0800170 file_list_yaml = yaml.safe_load(file_list_yaml_file)
171 file_list.extend(file_list_yaml["file_list"])
Edison Ai48b2d9e2019-06-24 14:39:45 +0800172
edison.ai7b299f52020-07-16 15:44:18 +0800173 print("Start to generate file from the generated list:")
Kevin Peng655f2392019-11-27 16:33:02 +0800174 for file in file_list:
Raef Coles558487a2020-10-29 13:09:44 +0000175 # Replace environment variables in the output filepath
Kevin Peng655f2392019-11-27 16:33:02 +0800176 outfile_name = os.path.expandvars(file["output"])
Raef Coles558487a2020-10-29 13:09:44 +0000177 # Replace environment variables in the template filepath
Kevin Peng1ec5e7c2019-11-29 10:52:00 +0800178 templatefile_name = os.path.expandvars(file["template"])
Edison Ai48b2d9e2019-06-24 14:39:45 +0800179
Kevin Peng655f2392019-11-27 16:33:02 +0800180 if OUT_DIR is not None:
181 outfile_name = os.path.join(OUT_DIR, outfile_name)
Edison Ai48b2d9e2019-06-24 14:39:45 +0800182
edison.ai7b299f52020-07-16 15:44:18 +0800183 print ("Generating " + outfile_name)
184
Kevin Peng655f2392019-11-27 16:33:02 +0800185 outfile_path = os.path.dirname(outfile_name)
186 if not os.path.exists(outfile_path):
187 os.makedirs(outfile_path)
Edison Ai48b2d9e2019-06-24 14:39:45 +0800188
Kevin Peng655f2392019-11-27 16:33:02 +0800189 template = ENV.get_template(templatefile_name)
Edison Ai6e3f2a32019-06-11 15:29:05 +0800190
TTornblom441a0702020-04-28 12:40:42 +0200191 outfile = io.open(outfile_name, "w", newline=None)
Kevin Peng655f2392019-11-27 16:33:02 +0800192 outfile.write(template.render(context))
193 outfile.close()
Edison Ai48b2d9e2019-06-24 14:39:45 +0800194
Kevin Peng655f2392019-11-27 16:33:02 +0800195 print ("Generation of files done")
Edison Ai48b2d9e2019-06-24 14:39:45 +0800196
Mingyang Suna1ca6112021-01-11 11:34:59 +0800197def process_stateless_services(partitions, static_handle_max_num):
198 """
199 This function collects all stateless services together, and allocates
Mingyang Sun4ecea992021-03-30 17:56:26 +0800200 stateless handles for them.
201 If the stateless handle is set to a valid value in yaml/json file, it is
202 converted to a index directly, if the stateless handle is set as "auto",
203 or not set, framework will allocate a valid index for the service.
204 After that, framework puts each service into a reordered stateless service
205 list at position of "index". Other positions in list are left "None".
Mingyang Suna1ca6112021-01-11 11:34:59 +0800206 """
Mingyang Sun4ecea992021-03-30 17:56:26 +0800207 raw_stateless_services = []
Mingyang Suna1ca6112021-01-11 11:34:59 +0800208
209 # Collect all stateless services first.
210 for partition in partitions:
211 # Skip the FF-M 1.0 partitions
212 if partition['manifest']['psa_framework_version'] < 1.1:
213 continue
214 # Skip the Non-IPC partitions
215 if partition['attr']['tfm_partition_ipc'] is not True:
216 continue
217 for service in partition['manifest']['services']:
218 if 'connection_based' not in service:
219 raise Exception("'connection_based' is mandatory in FF-M 1.1 service!")
220 if service['connection_based'] is False:
Mingyang Sun4ecea992021-03-30 17:56:26 +0800221 raw_stateless_services.append(service)
Mingyang Suna1ca6112021-01-11 11:34:59 +0800222
Mingyang Sun4ecea992021-03-30 17:56:26 +0800223 if len(raw_stateless_services) == 0:
Mingyang Suna1ca6112021-01-11 11:34:59 +0800224 return []
225
Mingyang Sun4ecea992021-03-30 17:56:26 +0800226 if len(raw_stateless_services) > static_handle_max_num:
Mingyang Suna1ca6112021-01-11 11:34:59 +0800227 raise Exception("Stateless service numbers range exceed.")
228
229 """
230 Allocate an empty stateless service list to store services and find the
231 service easily by handle.
232 Use service stateless handle values as indexes. Put service in the list
233 at index "handle - 1", since handle value starts from 1 and list index
234 starts from 0.
235 """
Mingyang Sun4ecea992021-03-30 17:56:26 +0800236 reordered_stateless_services = [None] * static_handle_max_num
Mingyang Suna1ca6112021-01-11 11:34:59 +0800237
Mingyang Sun4ecea992021-03-30 17:56:26 +0800238 for service in raw_stateless_services:
239 service_handle = service['stateless_handle']
Mingyang Suna1ca6112021-01-11 11:34:59 +0800240
Mingyang Sun4ecea992021-03-30 17:56:26 +0800241 # Fill in service list with specified stateless handle, otherwise skip
242 if isinstance(service_handle, int):
243 if service_handle < 1 or service_handle > static_handle_max_num:
244 raise Exception("Invalid stateless_handle setting.")
245 # Convert handle index to reordered service list index
246 service_handle = service_handle - 1
247
248 if reordered_stateless_services[service_handle] is not None:
249 raise Exception("Duplicated stateless_handle setting.")
250 reordered_stateless_services[service_handle] = service
251 # Remove recorded node from the existing list
252 raw_stateless_services.remove(service)
253
254 # Auto-allocate stateless handle and encode the stateless handle
Mingyang Suna1ca6112021-01-11 11:34:59 +0800255 for i in range(0, static_handle_max_num):
Mingyang Sun4ecea992021-03-30 17:56:26 +0800256 service = reordered_stateless_services[i]
257
258 if service == None and len(raw_stateless_services) > 0:
259 service = raw_stateless_services.pop(0)
260
Mingyang Sun453ad402021-03-17 17:58:33 +0800261 """
262 Encode stateless flag and version into stateless handle
263 bit 30: stateless handle indicator
264 bit 15-8: stateless service version
265 bit 7-0: stateless handle index
266 """
Mingyang Sun4ecea992021-03-30 17:56:26 +0800267 stateless_handle_value = 0
268 if service != None:
269 stateless_index = (i & 0xFF)
270 stateless_handle_value |= stateless_index
Mingyang Sun453ad402021-03-17 17:58:33 +0800271 stateless_flag = 1 << 30
272 stateless_handle_value |= stateless_flag
Mingyang Sun4ecea992021-03-30 17:56:26 +0800273 stateless_version = (service['version'] & 0xFF) << 8
Mingyang Sun453ad402021-03-17 17:58:33 +0800274 stateless_handle_value |= stateless_version
Mingyang Sun4ecea992021-03-30 17:56:26 +0800275 service['stateless_handle_value'] = '0x{0:08x}'.format(stateless_handle_value)
Mingyang Suna1ca6112021-01-11 11:34:59 +0800276
Mingyang Sun4ecea992021-03-30 17:56:26 +0800277 reordered_stateless_services[i] = service
278
279 return reordered_stateless_services
Mingyang Suna1ca6112021-01-11 11:34:59 +0800280
Kevin Peng655f2392019-11-27 16:33:02 +0800281def parse_args():
Raef Coles558487a2020-10-29 13:09:44 +0000282 parser = argparse.ArgumentParser(description='Parse secure partition manifest list and generate files listed by the file list',
283 epilog='Note that environment variables in template files will be replaced with their values')
284
Kevin Peng655f2392019-11-27 16:33:02 +0800285 parser.add_argument('-o', '--outdir'
286 , dest='outdir'
287 , required=False
288 , default=None
289 , metavar='out_dir'
290 , help='The root directory for generated files, the default is TF-M root folder.')
Shawn Shana9ad1e02019-08-07 15:49:48 +0800291
Kevin Peng655f2392019-11-27 16:33:02 +0800292 parser.add_argument('-m', '--manifest'
Raef Colesf42f0882020-07-10 10:01:58 +0100293 , nargs='+'
Kevin Peng655f2392019-11-27 16:33:02 +0800294 , dest='manifest_args'
Raef Colesf42f0882020-07-10 10:01:58 +0100295 , required=True
Kevin Peng655f2392019-11-27 16:33:02 +0800296 , metavar='manifest'
Raef Colesf42f0882020-07-10 10:01:58 +0100297 , help='A set of secure partition manifest lists to parse')
Kevin Peng655f2392019-11-27 16:33:02 +0800298
299 parser.add_argument('-f', '--file-list'
Raef Colesf42f0882020-07-10 10:01:58 +0100300 , nargs='+'
Kevin Peng655f2392019-11-27 16:33:02 +0800301 , dest='gen_file_args'
Raef Colesf42f0882020-07-10 10:01:58 +0100302 , required=True
Kevin Peng655f2392019-11-27 16:33:02 +0800303 , metavar='file-list'
Raef Colesf42f0882020-07-10 10:01:58 +0100304 , help='These files descripe the file list to generate')
Kevin Peng655f2392019-11-27 16:33:02 +0800305
306 args = parser.parse_args()
307 manifest_args = args.manifest_args
308 gen_file_args = args.gen_file_args
309
Kevin Peng655f2392019-11-27 16:33:02 +0800310 return args
311
312ENV = Environment(
313 loader = TemplateLoader(),
314 autoescape = select_autoescape(['html', 'xml']),
315 lstrip_blocks = True,
316 trim_blocks = True,
317 keep_trailing_newline = True
318 )
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100319
Miklos Balint470919c2018-05-22 17:51:29 +0200320def main():
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100321 """
322 The entry point of the script.
323
324 Generates the output files based on the templates and the manifests.
325 """
Shawn Shana9ad1e02019-08-07 15:49:48 +0800326
Kevin Peng655f2392019-11-27 16:33:02 +0800327 global OUT_DIR
Shawn Shana9ad1e02019-08-07 15:49:48 +0800328
Kevin Peng655f2392019-11-27 16:33:02 +0800329 args = parse_args()
Shawn Shana9ad1e02019-08-07 15:49:48 +0800330
Kevin Peng655f2392019-11-27 16:33:02 +0800331 manifest_args = args.manifest_args
332 gen_file_args = args.gen_file_args
333 OUT_DIR = args.outdir
Kevin Peng655f2392019-11-27 16:33:02 +0800334
Raef Coles558487a2020-10-29 13:09:44 +0000335 manifest_list = [os.path.abspath(x) for x in args.manifest_args]
336 gen_file_list = [os.path.abspath(x) for x in args.gen_file_args]
Shawn Shana9ad1e02019-08-07 15:49:48 +0800337
338 # Arguments could be relative path.
Kevin Peng655f2392019-11-27 16:33:02 +0800339 # Convert to absolute path as we are going to change diretory later
340 if OUT_DIR is not None:
341 OUT_DIR = os.path.abspath(OUT_DIR)
342
Shawn Shana9ad1e02019-08-07 15:49:48 +0800343 """
Kevin Peng655f2392019-11-27 16:33:02 +0800344 Relative path to TF-M root folder is supported in the manifests
345 and default value of manifest list and generated file list are relative to TF-M root folder as well,
346 so first change directory to TF-M root folder.
Shawn Shana9ad1e02019-08-07 15:49:48 +0800347 By doing this, the script can be executed anywhere
Kevin Peng655f2392019-11-27 16:33:02 +0800348 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 +0800349 """
350 os.chdir(os.path.join(sys.path[0], ".."))
351
Kevin Peng578a8492020-12-31 10:22:59 +0800352 partition_db = process_manifest(manifest_list)
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100353
Edison Ai6e3f2a32019-06-11 15:29:05 +0800354 utilities = {}
355 context = {}
356
Mingyang Suna1ca6112021-01-11 11:34:59 +0800357 utilities['donotedit_warning'] = donotedit_warning
Miklos Balint470919c2018-05-22 17:51:29 +0200358
Kevin Peng578a8492020-12-31 10:22:59 +0800359 context['partitions'] = partition_db
Kevin Peng655f2392019-11-27 16:33:02 +0800360 context['utilities'] = utilities
Mingyang Suna1ca6112021-01-11 11:34:59 +0800361 context['stateless_services'] = process_stateless_services(partition_db, 32)
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100362
Raef Colesf42f0882020-07-10 10:01:58 +0100363 gen_files(context, gen_file_list)
Miklos Balint470919c2018-05-22 17:51:29 +0200364
365if __name__ == "__main__":
366 main()