blob: cfc6bfcbde25e941210b866f8bb0a9bdc497815e [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
edison.ai7b299f52020-07-16 15:44:18 +080079 print("Start to generate PSA manifests:")
Kevin Peng655f2392019-11-27 16:33:02 +080080 for manifest_item in manifest_list:
Raef Coles558487a2020-10-29 13:09:44 +000081 # Replace environment variables in the manifest path
Kevin Peng655f2392019-11-27 16:33:02 +080082 manifest_path = os.path.expandvars(manifest_item['manifest'])
83 file = open(manifest_path)
84 manifest = yaml.safe_load(file)
85
Kevin Peng655f2392019-11-27 16:33:02 +080086 utilities = {}
87 utilities['donotedit_warning']=donotedit_warning
88
89 context = {}
90 context['manifest'] = manifest
91 context['attr'] = manifest_item
92 context['utilities'] = utilities
93
94 manifest_dir, manifest_name = os.path.split(manifest_path)
95 outfile_name = manifest_name.replace('yaml', 'h').replace('json', 'h')
96 context['file_name'] = outfile_name.replace('.h', '')
TTornblom7f92a732020-03-05 13:12:20 +010097 outfile_name = os.path.join(manifest_dir, "psa_manifest", outfile_name).replace('\\', '/')
Mingyang Sund20999f2020-10-15 14:53:12 +080098 intermediafile_name = os.path.join(manifest_dir, "auto_generated", 'intermedia_' + context['file_name'] + '.c').replace('\\', '/')
Kevin Peng655f2392019-11-27 16:33:02 +080099
Mingyang Sun4f012692020-10-16 14:04:49 +0800100 """
101 Remove the `source_path` portion of the filepaths, so that it can be
102 interpreted as a relative path from the OUT_DIR.
103 """
104 if 'source_path' in manifest_item:
Raef Coles558487a2020-10-29 13:09:44 +0000105 # Replace environment variables in the source path
106 source_path = os.path.expandvars(manifest_item['source_path'])
107 outfile_name = os.path.relpath(outfile_name, start = source_path)
Mingyang Sund20999f2020-10-15 14:53:12 +0800108 intermediafile_name = os.path.relpath(intermediafile_name, start = source_path)
Mingyang Sun4f012692020-10-16 14:04:49 +0800109
Kevin Peng578a8492020-12-31 10:22:59 +0800110 partition_db.append({"manifest": manifest, "attr": manifest_item, "header_file": outfile_name})
Kevin Peng655f2392019-11-27 16:33:02 +0800111
112 if OUT_DIR is not None:
113 outfile_name = os.path.join(OUT_DIR, outfile_name)
Mingyang Sund20999f2020-10-15 14:53:12 +0800114 intermediafile_name = os.path.join(OUT_DIR, intermediafile_name)
Kevin Peng655f2392019-11-27 16:33:02 +0800115
116 outfile_path = os.path.dirname(outfile_name)
117 if not os.path.exists(outfile_path):
118 os.makedirs(outfile_path)
119
120 print ("Generating " + outfile_name)
121
TTornblom441a0702020-04-28 12:40:42 +0200122 outfile = io.open(outfile_name, "w", newline=None)
Mingyang Sund20999f2020-10-15 14:53:12 +0800123 outfile.write(manifesttemplate.render(context))
Kevin Peng655f2392019-11-27 16:33:02 +0800124 outfile.close()
125
Mingyang Sund20999f2020-10-15 14:53:12 +0800126 intermediafile_path = os.path.dirname(intermediafile_name)
127 if not os.path.exists(intermediafile_path):
128 os.makedirs(intermediafile_path)
129
130 print ("Generating " + intermediafile_name)
131
132 memoutfile = io.open(intermediafile_name, "w", newline=None)
133 memoutfile.write(memorytemplate.render(context))
134 memoutfile.close()
135
Kevin Peng578a8492020-12-31 10:22:59 +0800136 return partition_db
Kevin Peng655f2392019-11-27 16:33:02 +0800137
Raef Colesf42f0882020-07-10 10:01:58 +0100138def gen_files(context, gen_file_lists):
Kevin Peng655f2392019-11-27 16:33:02 +0800139 """
140 Generate files according to the gen_file_list
Edison Ai48b2d9e2019-06-24 14:39:45 +0800141
142 Parameters
143 ----------
Raef Colesf42f0882020-07-10 10:01:58 +0100144 gen_file_lists:
145 The lists of files to generate
Edison Ai48b2d9e2019-06-24 14:39:45 +0800146 """
Kevin Peng655f2392019-11-27 16:33:02 +0800147 file_list = []
Shawn Shana9ad1e02019-08-07 15:49:48 +0800148
Raef Colesf42f0882020-07-10 10:01:58 +0100149 for f in gen_file_lists:
150 with open(f) as file_list_yaml_file:
Kevin Peng655f2392019-11-27 16:33:02 +0800151 file_list_yaml = yaml.safe_load(file_list_yaml_file)
152 file_list.extend(file_list_yaml["file_list"])
Edison Ai48b2d9e2019-06-24 14:39:45 +0800153
edison.ai7b299f52020-07-16 15:44:18 +0800154 print("Start to generate file from the generated list:")
Kevin Peng655f2392019-11-27 16:33:02 +0800155 for file in file_list:
Raef Coles558487a2020-10-29 13:09:44 +0000156 # Replace environment variables in the output filepath
Kevin Peng655f2392019-11-27 16:33:02 +0800157 outfile_name = os.path.expandvars(file["output"])
Raef Coles558487a2020-10-29 13:09:44 +0000158 # Replace environment variables in the template filepath
Kevin Peng1ec5e7c2019-11-29 10:52:00 +0800159 templatefile_name = os.path.expandvars(file["template"])
Edison Ai48b2d9e2019-06-24 14:39:45 +0800160
Kevin Peng655f2392019-11-27 16:33:02 +0800161 if OUT_DIR is not None:
162 outfile_name = os.path.join(OUT_DIR, outfile_name)
Edison Ai48b2d9e2019-06-24 14:39:45 +0800163
edison.ai7b299f52020-07-16 15:44:18 +0800164 print ("Generating " + outfile_name)
165
Kevin Peng655f2392019-11-27 16:33:02 +0800166 outfile_path = os.path.dirname(outfile_name)
167 if not os.path.exists(outfile_path):
168 os.makedirs(outfile_path)
Edison Ai48b2d9e2019-06-24 14:39:45 +0800169
Kevin Peng655f2392019-11-27 16:33:02 +0800170 template = ENV.get_template(templatefile_name)
Edison Ai6e3f2a32019-06-11 15:29:05 +0800171
TTornblom441a0702020-04-28 12:40:42 +0200172 outfile = io.open(outfile_name, "w", newline=None)
Kevin Peng655f2392019-11-27 16:33:02 +0800173 outfile.write(template.render(context))
174 outfile.close()
Edison Ai48b2d9e2019-06-24 14:39:45 +0800175
Kevin Peng655f2392019-11-27 16:33:02 +0800176 print ("Generation of files done")
Edison Ai48b2d9e2019-06-24 14:39:45 +0800177
Mingyang Suna1ca6112021-01-11 11:34:59 +0800178def process_stateless_services(partitions, static_handle_max_num):
179 """
180 This function collects all stateless services together, and allocates
181 stateless handle for them.
182 If the stateless handle is set to a valid value in yaml file, it is used as
183 the index directly, if the stateless handle is set as "auto" or not set,
184 framework will allocate a valid index for the service.
185 After that, framework puts each service into a stateless service list at
186 position of its "index". Other elements in list are left None.
187 """
188 stateless_services = []
189
190 # Collect all stateless services first.
191 for partition in partitions:
192 # Skip the FF-M 1.0 partitions
193 if partition['manifest']['psa_framework_version'] < 1.1:
194 continue
195 # Skip the Non-IPC partitions
196 if partition['attr']['tfm_partition_ipc'] is not True:
197 continue
198 for service in partition['manifest']['services']:
199 if 'connection_based' not in service:
200 raise Exception("'connection_based' is mandatory in FF-M 1.1 service!")
201 if service['connection_based'] is False:
202 stateless_services.append(service)
203
204 if len(stateless_services) == 0:
205 return []
206
207 if len(stateless_services) > static_handle_max_num:
208 raise Exception("Stateless service numbers range exceed.")
209
210 """
211 Allocate an empty stateless service list to store services and find the
212 service easily by handle.
213 Use service stateless handle values as indexes. Put service in the list
214 at index "handle - 1", since handle value starts from 1 and list index
215 starts from 0.
216 """
217 reordered_stateless_list = [None] * static_handle_max_num
218
219 # Fill in services with specified stateless handle, index is "handle - 1".
220 for service in stateless_services:
221 if service['stateless_handle'] == "auto":
222 continue
223 try:
224 if reordered_stateless_list[service['stateless_handle']-1] is not None:
225 raise Exception("Duplicated stateless service handle.")
226 reordered_stateless_list[service['stateless_handle']-1] = service
227 except IndexError:
228 raise Exception("Stateless service index out of range.")
229 # Remove recorded node from the existing list
230 stateless_services.remove(service)
231
232 # Auto-allocate stateless handle
233 for i in range(0, static_handle_max_num):
Mingyang Sun453ad402021-03-17 17:58:33 +0800234 if reordered_stateless_list[i] == None and len(stateless_services) > 0:
Mingyang Suna1ca6112021-01-11 11:34:59 +0800235 service = stateless_services.pop(0)
236 service['stateless_handle'] = i + 1
237 reordered_stateless_list[i] = service
Mingyang Sun453ad402021-03-17 17:58:33 +0800238 """
239 Encode stateless flag and version into stateless handle
240 bit 30: stateless handle indicator
241 bit 15-8: stateless service version
242 bit 7-0: stateless handle index
243 """
244 if reordered_stateless_list[i] != None:
245 stateless_handle_value = reordered_stateless_list[i]['stateless_handle']
246 stateless_flag = 1 << 30
247 stateless_handle_value |= stateless_flag
248 stateless_version = (reordered_stateless_list[i]['version'] & 0xFF) << 8
249 stateless_handle_value |= stateless_version
250 reordered_stateless_list[i]['stateless_handle'] = '0x%08x' % stateless_handle_value
Mingyang Suna1ca6112021-01-11 11:34:59 +0800251
252 return reordered_stateless_list
253
Kevin Peng655f2392019-11-27 16:33:02 +0800254def parse_args():
Raef Coles558487a2020-10-29 13:09:44 +0000255 parser = argparse.ArgumentParser(description='Parse secure partition manifest list and generate files listed by the file list',
256 epilog='Note that environment variables in template files will be replaced with their values')
257
Kevin Peng655f2392019-11-27 16:33:02 +0800258 parser.add_argument('-o', '--outdir'
259 , dest='outdir'
260 , required=False
261 , default=None
262 , metavar='out_dir'
263 , help='The root directory for generated files, the default is TF-M root folder.')
Shawn Shana9ad1e02019-08-07 15:49:48 +0800264
Kevin Peng655f2392019-11-27 16:33:02 +0800265 parser.add_argument('-m', '--manifest'
Raef Colesf42f0882020-07-10 10:01:58 +0100266 , nargs='+'
Kevin Peng655f2392019-11-27 16:33:02 +0800267 , dest='manifest_args'
Raef Colesf42f0882020-07-10 10:01:58 +0100268 , required=True
Kevin Peng655f2392019-11-27 16:33:02 +0800269 , metavar='manifest'
Raef Colesf42f0882020-07-10 10:01:58 +0100270 , help='A set of secure partition manifest lists to parse')
Kevin Peng655f2392019-11-27 16:33:02 +0800271
272 parser.add_argument('-f', '--file-list'
Raef Colesf42f0882020-07-10 10:01:58 +0100273 , nargs='+'
Kevin Peng655f2392019-11-27 16:33:02 +0800274 , dest='gen_file_args'
Raef Colesf42f0882020-07-10 10:01:58 +0100275 , required=True
Kevin Peng655f2392019-11-27 16:33:02 +0800276 , metavar='file-list'
Raef Colesf42f0882020-07-10 10:01:58 +0100277 , help='These files descripe the file list to generate')
Kevin Peng655f2392019-11-27 16:33:02 +0800278
279 args = parser.parse_args()
280 manifest_args = args.manifest_args
281 gen_file_args = args.gen_file_args
282
Kevin Peng655f2392019-11-27 16:33:02 +0800283 return args
284
285ENV = Environment(
286 loader = TemplateLoader(),
287 autoescape = select_autoescape(['html', 'xml']),
288 lstrip_blocks = True,
289 trim_blocks = True,
290 keep_trailing_newline = True
291 )
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100292
Miklos Balint470919c2018-05-22 17:51:29 +0200293def main():
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100294 """
295 The entry point of the script.
296
297 Generates the output files based on the templates and the manifests.
298 """
Shawn Shana9ad1e02019-08-07 15:49:48 +0800299
Kevin Peng655f2392019-11-27 16:33:02 +0800300 global OUT_DIR
Shawn Shana9ad1e02019-08-07 15:49:48 +0800301
Kevin Peng655f2392019-11-27 16:33:02 +0800302 args = parse_args()
Shawn Shana9ad1e02019-08-07 15:49:48 +0800303
Kevin Peng655f2392019-11-27 16:33:02 +0800304 manifest_args = args.manifest_args
305 gen_file_args = args.gen_file_args
306 OUT_DIR = args.outdir
Kevin Peng655f2392019-11-27 16:33:02 +0800307
Raef Coles558487a2020-10-29 13:09:44 +0000308 manifest_list = [os.path.abspath(x) for x in args.manifest_args]
309 gen_file_list = [os.path.abspath(x) for x in args.gen_file_args]
Shawn Shana9ad1e02019-08-07 15:49:48 +0800310
311 # Arguments could be relative path.
Kevin Peng655f2392019-11-27 16:33:02 +0800312 # Convert to absolute path as we are going to change diretory later
313 if OUT_DIR is not None:
314 OUT_DIR = os.path.abspath(OUT_DIR)
315
Shawn Shana9ad1e02019-08-07 15:49:48 +0800316 """
Kevin Peng655f2392019-11-27 16:33:02 +0800317 Relative path to TF-M root folder is supported in the manifests
318 and default value of manifest list and generated file list are relative to TF-M root folder as well,
319 so first change directory to TF-M root folder.
Shawn Shana9ad1e02019-08-07 15:49:48 +0800320 By doing this, the script can be executed anywhere
Kevin Peng655f2392019-11-27 16:33:02 +0800321 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 +0800322 """
323 os.chdir(os.path.join(sys.path[0], ".."))
324
Kevin Peng578a8492020-12-31 10:22:59 +0800325 partition_db = process_manifest(manifest_list)
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100326
Edison Ai6e3f2a32019-06-11 15:29:05 +0800327 utilities = {}
328 context = {}
329
Mingyang Suna1ca6112021-01-11 11:34:59 +0800330 utilities['donotedit_warning'] = donotedit_warning
Miklos Balint470919c2018-05-22 17:51:29 +0200331
Kevin Peng578a8492020-12-31 10:22:59 +0800332 context['partitions'] = partition_db
Kevin Peng655f2392019-11-27 16:33:02 +0800333 context['utilities'] = utilities
Mingyang Suna1ca6112021-01-11 11:34:59 +0800334 context['stateless_services'] = process_stateless_services(partition_db, 32)
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100335
Raef Colesf42f0882020-07-10 10:01:58 +0100336 gen_files(context, gen_file_list)
Miklos Balint470919c2018-05-22 17:51:29 +0200337
338if __name__ == "__main__":
339 main()