blob: 73c764139c9e8005de53c82e563b671a542ad537 [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
200 stateless handle for them.
201 If the stateless handle is set to a valid value in yaml file, it is used as
202 the index directly, if the stateless handle is set as "auto" or not set,
203 framework will allocate a valid index for the service.
204 After that, framework puts each service into a stateless service list at
205 position of its "index". Other elements in list are left None.
206 """
207 stateless_services = []
208
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:
221 stateless_services.append(service)
222
223 if len(stateless_services) == 0:
224 return []
225
226 if len(stateless_services) > static_handle_max_num:
227 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 """
236 reordered_stateless_list = [None] * static_handle_max_num
237
238 # Fill in services with specified stateless handle, index is "handle - 1".
239 for service in stateless_services:
240 if service['stateless_handle'] == "auto":
241 continue
242 try:
243 if reordered_stateless_list[service['stateless_handle']-1] is not None:
244 raise Exception("Duplicated stateless service handle.")
245 reordered_stateless_list[service['stateless_handle']-1] = service
246 except IndexError:
247 raise Exception("Stateless service index out of range.")
248 # Remove recorded node from the existing list
249 stateless_services.remove(service)
250
251 # Auto-allocate stateless handle
252 for i in range(0, static_handle_max_num):
Mingyang Sun453ad402021-03-17 17:58:33 +0800253 if reordered_stateless_list[i] == None and len(stateless_services) > 0:
Mingyang Suna1ca6112021-01-11 11:34:59 +0800254 service = stateless_services.pop(0)
255 service['stateless_handle'] = i + 1
256 reordered_stateless_list[i] = service
Mingyang Sun453ad402021-03-17 17:58:33 +0800257 """
258 Encode stateless flag and version into stateless handle
259 bit 30: stateless handle indicator
260 bit 15-8: stateless service version
261 bit 7-0: stateless handle index
262 """
263 if reordered_stateless_list[i] != None:
264 stateless_handle_value = reordered_stateless_list[i]['stateless_handle']
265 stateless_flag = 1 << 30
266 stateless_handle_value |= stateless_flag
267 stateless_version = (reordered_stateless_list[i]['version'] & 0xFF) << 8
268 stateless_handle_value |= stateless_version
269 reordered_stateless_list[i]['stateless_handle'] = '0x%08x' % stateless_handle_value
Mingyang Suna1ca6112021-01-11 11:34:59 +0800270
271 return reordered_stateless_list
272
Kevin Peng655f2392019-11-27 16:33:02 +0800273def parse_args():
Raef Coles558487a2020-10-29 13:09:44 +0000274 parser = argparse.ArgumentParser(description='Parse secure partition manifest list and generate files listed by the file list',
275 epilog='Note that environment variables in template files will be replaced with their values')
276
Kevin Peng655f2392019-11-27 16:33:02 +0800277 parser.add_argument('-o', '--outdir'
278 , dest='outdir'
279 , required=False
280 , default=None
281 , metavar='out_dir'
282 , help='The root directory for generated files, the default is TF-M root folder.')
Shawn Shana9ad1e02019-08-07 15:49:48 +0800283
Kevin Peng655f2392019-11-27 16:33:02 +0800284 parser.add_argument('-m', '--manifest'
Raef Colesf42f0882020-07-10 10:01:58 +0100285 , nargs='+'
Kevin Peng655f2392019-11-27 16:33:02 +0800286 , dest='manifest_args'
Raef Colesf42f0882020-07-10 10:01:58 +0100287 , required=True
Kevin Peng655f2392019-11-27 16:33:02 +0800288 , metavar='manifest'
Raef Colesf42f0882020-07-10 10:01:58 +0100289 , help='A set of secure partition manifest lists to parse')
Kevin Peng655f2392019-11-27 16:33:02 +0800290
291 parser.add_argument('-f', '--file-list'
Raef Colesf42f0882020-07-10 10:01:58 +0100292 , nargs='+'
Kevin Peng655f2392019-11-27 16:33:02 +0800293 , dest='gen_file_args'
Raef Colesf42f0882020-07-10 10:01:58 +0100294 , required=True
Kevin Peng655f2392019-11-27 16:33:02 +0800295 , metavar='file-list'
Raef Colesf42f0882020-07-10 10:01:58 +0100296 , help='These files descripe the file list to generate')
Kevin Peng655f2392019-11-27 16:33:02 +0800297
298 args = parser.parse_args()
299 manifest_args = args.manifest_args
300 gen_file_args = args.gen_file_args
301
Kevin Peng655f2392019-11-27 16:33:02 +0800302 return args
303
304ENV = Environment(
305 loader = TemplateLoader(),
306 autoescape = select_autoescape(['html', 'xml']),
307 lstrip_blocks = True,
308 trim_blocks = True,
309 keep_trailing_newline = True
310 )
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100311
Miklos Balint470919c2018-05-22 17:51:29 +0200312def main():
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100313 """
314 The entry point of the script.
315
316 Generates the output files based on the templates and the manifests.
317 """
Shawn Shana9ad1e02019-08-07 15:49:48 +0800318
Kevin Peng655f2392019-11-27 16:33:02 +0800319 global OUT_DIR
Shawn Shana9ad1e02019-08-07 15:49:48 +0800320
Kevin Peng655f2392019-11-27 16:33:02 +0800321 args = parse_args()
Shawn Shana9ad1e02019-08-07 15:49:48 +0800322
Kevin Peng655f2392019-11-27 16:33:02 +0800323 manifest_args = args.manifest_args
324 gen_file_args = args.gen_file_args
325 OUT_DIR = args.outdir
Kevin Peng655f2392019-11-27 16:33:02 +0800326
Raef Coles558487a2020-10-29 13:09:44 +0000327 manifest_list = [os.path.abspath(x) for x in args.manifest_args]
328 gen_file_list = [os.path.abspath(x) for x in args.gen_file_args]
Shawn Shana9ad1e02019-08-07 15:49:48 +0800329
330 # Arguments could be relative path.
Kevin Peng655f2392019-11-27 16:33:02 +0800331 # Convert to absolute path as we are going to change diretory later
332 if OUT_DIR is not None:
333 OUT_DIR = os.path.abspath(OUT_DIR)
334
Shawn Shana9ad1e02019-08-07 15:49:48 +0800335 """
Kevin Peng655f2392019-11-27 16:33:02 +0800336 Relative path to TF-M root folder is supported in the manifests
337 and default value of manifest list and generated file list are relative to TF-M root folder as well,
338 so first change directory to TF-M root folder.
Shawn Shana9ad1e02019-08-07 15:49:48 +0800339 By doing this, the script can be executed anywhere
Kevin Peng655f2392019-11-27 16:33:02 +0800340 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 +0800341 """
342 os.chdir(os.path.join(sys.path[0], ".."))
343
Kevin Peng578a8492020-12-31 10:22:59 +0800344 partition_db = process_manifest(manifest_list)
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100345
Edison Ai6e3f2a32019-06-11 15:29:05 +0800346 utilities = {}
347 context = {}
348
Mingyang Suna1ca6112021-01-11 11:34:59 +0800349 utilities['donotedit_warning'] = donotedit_warning
Miklos Balint470919c2018-05-22 17:51:29 +0200350
Kevin Peng578a8492020-12-31 10:22:59 +0800351 context['partitions'] = partition_db
Kevin Peng655f2392019-11-27 16:33:02 +0800352 context['utilities'] = utilities
Mingyang Suna1ca6112021-01-11 11:34:59 +0800353 context['stateless_services'] = process_stateless_services(partition_db, 32)
Mate Toth-Pal36f21842018-11-08 16:12:51 +0100354
Raef Colesf42f0882020-07-10 10:01:58 +0100355 gen_files(context, gen_file_list)
Miklos Balint470919c2018-05-22 17:51:29 +0200356
357if __name__ == "__main__":
358 main()