blob: 3dd1d4e686bef985f3fe4e13094c527df77f9224 [file] [log] [blame]
Manish Pandey3f90ad72020-01-14 11:52:05 +00001#!/usr/bin/python3
Bence Balogh2e425bb2025-04-25 09:24:15 +00002# Copyright (c) 2020-2025, Arm Limited. All rights reserved.
Manish Pandey3f90ad72020-01-14 11:52:05 +00003#
4# SPDX-License-Identifier: BSD-3-Clause
5
6"""
7This script is invoked by Make system and generates secure partition makefile.
8It expects platform provided secure partition layout file which contains list
9of Secure Partition Images and Partition manifests(PM).
10Layout file can exist outside of TF-A tree and the paths of Image and PM files
11must be relative to it.
12
13This script parses the layout file and generates a make file which updates
Manish Pandeyaaaeb312020-05-26 23:59:36 +010014FDT_SOURCES, FIP_ARGS, CRT_ARGS and SPTOOL_ARGS which are used in later build
15steps.
Imre Kis3f370fd2022-02-08 18:06:18 +010016If the SP entry in the layout file has a "uuid" field the scripts gets the UUID
17from there, otherwise it parses the associated partition manifest and extracts
18the UUID from there.
Manish Pandey3f90ad72020-01-14 11:52:05 +000019
20param1: Generated mk file "sp_gen.mk"
21param2: "SP_LAYOUT_FILE", json file containing platform provided information
22param3: plat out directory
Ruari Phipps571ed6b2020-07-24 16:20:57 +010023param4: CoT parameter
Karl Meakinaba46182023-02-14 11:56:02 +000024param5: Generated dts file "sp_list_fragment.dts"
Manish Pandey3f90ad72020-01-14 11:52:05 +000025
26Generated "sp_gen.mk" file contains triplet of following information for each
27Secure Partition entry
28 FDT_SOURCES += sp1.dts
29 SPTOOL_ARGS += -i sp1.bin:sp1.dtb -o sp1.pkg
30 FIP_ARGS += --blob uuid=XXXXX-XXX...,file=sp1.pkg
Manish Pandeyaaaeb312020-05-26 23:59:36 +010031 CRT_ARGS += --sp-pkg1 sp1.pkg
Manish Pandey3f90ad72020-01-14 11:52:05 +000032
Ben Horganf7deec92024-10-29 17:54:24 +000033It populates the number of SP in the defined macro 'NUM_SP'
34 $(eval $(call add_define_val,NUM_SP,{len(sp_layout.keys())}))
35
Manish Pandey3f90ad72020-01-14 11:52:05 +000036A typical SP_LAYOUT_FILE file will look like
37{
38 "SP1" : {
39 "image": "sp1.bin",
40 "pm": "test/sp1.dts"
41 },
42
43 "SP2" : {
44 "image": "sp2.bin",
Imre Kis3f370fd2022-02-08 18:06:18 +010045 "pm": "test/sp2.dts",
46 "uuid": "1b1820fe-48f7-4175-8999-d51da00b7c9f"
Manish Pandey3f90ad72020-01-14 11:52:05 +000047 }
48
49 ...
50}
51
52"""
Manish Pandey3f90ad72020-01-14 11:52:05 +000053import json
54import os
55import re
56import sys
57import uuid
J-Alvesb5d913e2024-12-16 17:48:52 +000058import fdt
J-Alvesd10fb032022-03-21 14:11:43 +000059from spactions import SpSetupActions
J-Alvesb5d913e2024-12-16 17:48:52 +000060import hob
61import struct
62from hob import HobList
Manish Pandey3f90ad72020-01-14 11:52:05 +000063
Ruari Phipps571ed6b2020-07-24 16:20:57 +010064MAX_SP = 8
J-Alvesd10fb032022-03-21 14:11:43 +000065UUID_LEN = 4
J-Alvesb5d913e2024-12-16 17:48:52 +000066HOB_OFFSET_DEFAULT=0x2000
Manish Pandey3f90ad72020-01-14 11:52:05 +000067
J-Alvesd10fb032022-03-21 14:11:43 +000068# Some helper functions to access args propagated to the action functions in
69# SpSetupActions framework.
J-Alvesd10fb032022-03-21 14:11:43 +000070def check_sp_mk_gen(args :dict):
71 if "sp_gen_mk" not in args.keys():
72 raise Exception(f"Path to file sp_gen.mk needs to be in 'args'.")
Ruari Phipps571ed6b2020-07-24 16:20:57 +010073
J-Alvesd10fb032022-03-21 14:11:43 +000074def check_out_dir(args :dict):
75 if "out_dir" not in args.keys() or not os.path.isdir(args["out_dir"]):
76 raise Exception("Define output folder with \'out_dir\' key.")
Ruari Phipps571ed6b2020-07-24 16:20:57 +010077
J-Alvesd10fb032022-03-21 14:11:43 +000078def check_sp_layout_dir(args :dict):
79 if "sp_layout_dir" not in args.keys() or not os.path.isdir(args["sp_layout_dir"]):
80 raise Exception("Define output folder with \'sp_layout_dir\' key.")
Ruari Phipps571ed6b2020-07-24 16:20:57 +010081
J-Alvesd10fb032022-03-21 14:11:43 +000082def write_to_sp_mk_gen(content, args :dict):
83 check_sp_mk_gen(args)
84 with open(args["sp_gen_mk"], "a") as f:
85 f.write(f"{content}\n")
Manish Pandey3f90ad72020-01-14 11:52:05 +000086
J-Alvesd10fb032022-03-21 14:11:43 +000087def get_sp_manifest_full_path(sp_node, args :dict):
88 check_sp_layout_dir(args)
89 return os.path.join(args["sp_layout_dir"], get_file_from_layout(sp_node["pm"]))
Manish Pandey3f90ad72020-01-14 11:52:05 +000090
J-Alvesd10fb032022-03-21 14:11:43 +000091def get_sp_img_full_path(sp_node, args :dict):
92 check_sp_layout_dir(args)
93 return os.path.join(args["sp_layout_dir"], get_file_from_layout(sp_node["image"]))
94
J-Alvesa62598e2024-11-18 17:49:53 +000095def get_size(sp_node):
96 if not "size" in sp_node:
97 print("WARNING: default image size 0x100000")
98 return 0x100000
99
100 # Try if it was a decimal value.
101 try:
102 return int(sp_node["size"])
103 except ValueError:
104 print("WARNING: trying to parse base 16 size")
105 # Try if it is of base 16
106 return int(sp_node["size"], 16)
107
J-Alvesd10fb032022-03-21 14:11:43 +0000108def get_sp_pkg(sp, args :dict):
109 check_out_dir(args)
110 return os.path.join(args["out_dir"], f"{sp}.pkg")
111
112def is_line_in_sp_gen(line, args :dict):
113 with open(args["sp_gen_mk"], "r") as f:
114 sppkg_rule = [l for l in f if line in l]
J-Alves5b97ec32022-10-07 10:02:33 +0100115 return len(sppkg_rule) != 0
Olivier Deprez18909842021-05-11 09:43:37 +0200116
J-Alvesd10fb032022-03-21 14:11:43 +0000117def get_file_from_layout(node):
118 ''' Helper to fetch a file path from sp_layout.json. '''
119 if type(node) is dict and "file" in node.keys():
120 return node["file"]
121 return node
Manish Pandey3f90ad72020-01-14 11:52:05 +0000122
J-Alvesd10fb032022-03-21 14:11:43 +0000123def get_offset_from_layout(node):
124 ''' Helper to fetch an offset from sp_layout.json. '''
125 if type(node) is dict and "offset" in node.keys():
126 return int(node["offset"], 0)
127 return None
128
129def get_image_offset(node):
130 ''' Helper to fetch image offset from sp_layout.json '''
131 return get_offset_from_layout(node["image"])
132
133def get_pm_offset(node):
134 ''' Helper to fetch pm offset from sp_layout.json '''
135 return get_offset_from_layout(node["pm"])
136
Karl Meakinaba46182023-02-14 11:56:02 +0000137def get_uuid(sp_layout, sp, args :dict):
138 ''' Helper to fetch uuid from pm file listed in sp_layout.json'''
139 if "uuid" in sp_layout[sp]:
140 # Extract the UUID from the JSON file if the SP entry has a 'uuid' field
141 uuid_std = uuid.UUID(sp_layout[sp]['uuid'])
142 else:
143 with open(get_sp_manifest_full_path(sp_layout[sp], args), "r") as pm_f:
144 uuid_lines = [l for l in pm_f if 'uuid' in l]
145 assert(len(uuid_lines) == 1)
146 # The uuid field in SP manifest is the little endian representation
147 # mapped to arguments as described in SMCCC section 5.3.
148 # Convert each unsigned integer value to a big endian representation
149 # required by fiptool.
150 uuid_parsed = re.findall("0x([0-9a-f]+)", uuid_lines[0])
151 y = list(map(bytearray.fromhex, uuid_parsed))
152 z = [int.from_bytes(i, byteorder='little', signed=False) for i in y]
153 uuid_std = uuid.UUID(f'{z[0]:08x}{z[1]:08x}{z[2]:08x}{z[3]:08x}')
154 return uuid_std
155
156def get_load_address(sp_layout, sp, args :dict):
157 ''' Helper to fetch load-address from pm file listed in sp_layout.json'''
158 with open(get_sp_manifest_full_path(sp_layout[sp], args), "r") as pm_f:
Bence Balogh2e425bb2025-04-25 09:24:15 +0000159 load_address_lines = [l for l in pm_f if re.search(r'load-address[^-]', l)]
J-Alvesa890d062024-01-11 13:35:52 +0000160
J-Alves5f10faa2024-01-17 09:15:28 +0000161 if len(load_address_lines) != 1:
J-Alvesa890d062024-01-11 13:35:52 +0000162 return None
163
Karl Meakinaba46182023-02-14 11:56:02 +0000164 load_address_parsed = re.search("(0x[0-9a-f]+)", load_address_lines[0])
165 return load_address_parsed.group(0)
166
J-Alvesd10fb032022-03-21 14:11:43 +0000167@SpSetupActions.sp_action(global_action=True)
168def check_max_sps(sp_layout, _, args :dict):
169 ''' Check validate the maximum number of SPs is respected. '''
170 if len(sp_layout.keys()) > MAX_SP:
171 raise Exception(f"Too many SPs in SP layout file. Max: {MAX_SP}")
172 return args
Manish Pandey3f90ad72020-01-14 11:52:05 +0000173
Ben Horganf7deec92024-10-29 17:54:24 +0000174@SpSetupActions.sp_action(global_action=True)
175def count_sps(sp_layout, _, args :dict):
176 ''' Count number of SP and put in NUM_SP '''
177 write_to_sp_mk_gen(f"$(eval $(call add_define_val,NUM_SP,{len(sp_layout.keys())}))", args)
178 return args
179
J-Alvesd10fb032022-03-21 14:11:43 +0000180@SpSetupActions.sp_action
181def gen_fdt_sources(sp_layout, sp, args :dict):
182 ''' Generate FDT_SOURCES values for a given SP. '''
183 manifest_path = get_sp_manifest_full_path(sp_layout[sp], args)
184 write_to_sp_mk_gen(f"FDT_SOURCES += {manifest_path}", args)
185 return args
Manish Pandeyaaaeb312020-05-26 23:59:36 +0100186
J-Alvesb5d913e2024-12-16 17:48:52 +0000187@SpSetupActions.sp_action(exec_order=1)
188def generate_hob_list(sp_layout, sp, args: dict):
189 '''
190 Generates a HOB file for the partition, if it requested it in its FF-A
191 manifest.
192 '''
193 with open(get_sp_manifest_full_path(sp_layout[sp], args), "r") as f:
194 sp_fdt = fdt.parse_dts(f.read())
195
196 if sp_fdt.exist_property('hob_list', '/boot-info'):
197 sp_hob_name = os.path.basename(sp + ".hob.bin")
198 sp_hob_name = os.path.join(args["out_dir"], f"{sp_hob_name}")
199
200 # Add to the args so it can be consumed by the TL pkg function.
201 sp_layout[sp]["hob_path"] = sp_hob_name
202 hob_list = hob.generate_hob_from_fdt_node(sp_fdt, HOB_OFFSET_DEFAULT)
203 with open(sp_hob_name, "wb") as h:
204 for block in hob_list.get_list():
205 h.write(block.pack())
206
207 return args
208
J-Alvesa62598e2024-11-18 17:49:53 +0000209def generate_sp_pkg(sp_node, pkg, sp_img, sp_dtb):
210 ''' Generates the rule in case SP is to be generated in an SP Pkg. '''
211 pm_offset = get_pm_offset(sp_node)
212 sptool_args = f" --pm-offset {pm_offset}" if pm_offset is not None else ""
213 image_offset = get_image_offset(sp_node)
214 sptool_args += f" --img-offset {image_offset}" if image_offset is not None else ""
215 sptool_args += f" -o {pkg}"
216 return f'''
217{pkg}: {sp_dtb} {sp_img}
218\t$(Q)echo Generating {pkg}
219\t$(Q)$(PYTHON) $(SPTOOL) -i {sp_img}:{sp_dtb} {sptool_args}
220'''
221
222def generate_tl_pkg(sp_node, pkg, sp_img, sp_dtb, hob_path = None):
223 ''' Generate make rules for a Transfer List type package. '''
224 # TE Type for the FF-A manifest.
225 TE_FFA_MANIFEST = 0x106
226 # TE Type for the SP binary.
227 TE_SP_BINARY = 0x103
228 # TE Type for the HOB List.
229 TE_HOB_LIST = 0x3
J-Alves15605bd2024-12-16 17:49:47 +0000230 tlc_add_hob = f"\t$(Q)$(TLCTOOL) add --entry {TE_HOB_LIST} {hob_path} {pkg}" if hob_path is not None else ""
J-Alvesa62598e2024-11-18 17:49:53 +0000231 return f'''
232{pkg}: {sp_dtb} {sp_img}
233\t$(Q)echo Generating {pkg}
234\t$(Q)$(TLCTOOL) create --size {get_size(sp_node)} --entry {TE_FFA_MANIFEST} {sp_dtb} {pkg} --align 12
J-Alves15605bd2024-12-16 17:49:47 +0000235{tlc_add_hob}
J-Alvesa62598e2024-11-18 17:49:53 +0000236\t$(Q)$(TLCTOOL) add --entry {TE_SP_BINARY} {sp_img} {pkg}
237'''
238
J-Alvesd10fb032022-03-21 14:11:43 +0000239@SpSetupActions.sp_action
J-Alvesa62598e2024-11-18 17:49:53 +0000240def gen_partition_pkg(sp_layout, sp, args :dict):
J-Alvesef8e0982022-03-22 16:28:51 +0000241 ''' Generate Sp Pkgs rules. '''
J-Alvesa62598e2024-11-18 17:49:53 +0000242 pkg = get_sp_pkg(sp, args)
243
J-Alvesef8e0982022-03-22 16:28:51 +0000244 sp_dtb_name = os.path.basename(get_file_from_layout(sp_layout[sp]["pm"]))[:-1] + "b"
J-Alvesd10fb032022-03-21 14:11:43 +0000245 sp_dtb = os.path.join(args["out_dir"], f"fdts/{sp_dtb_name}")
Jens Wiklander6b533252022-10-31 09:17:50 +0100246 sp_img = get_sp_img_full_path(sp_layout[sp], args)
J-Alvesef8e0982022-03-22 16:28:51 +0000247
248 # Do not generate rule if already there.
J-Alvesa62598e2024-11-18 17:49:53 +0000249 if is_line_in_sp_gen(f'{pkg}:', args):
J-Alvesef8e0982022-03-22 16:28:51 +0000250 return args
J-Alvesef8e0982022-03-22 16:28:51 +0000251
J-Alvesa62598e2024-11-18 17:49:53 +0000252 # This should include all packages of all kinds.
253 write_to_sp_mk_gen(f"SP_PKGS += {pkg}\n", args)
254 package_type = sp_layout[sp]["package"] if "package" in sp_layout[sp] else "sp_pkg"
255
256 if package_type == "sp_pkg":
257 partition_pkg_rule = generate_sp_pkg(sp_layout[sp], pkg, sp_img, sp_dtb)
258 elif package_type == "tl_pkg":
J-Alves15605bd2024-12-16 17:49:47 +0000259 # Conditionally provide the Hob.
260 hob_path = sp_layout[sp]["hob_path"] if "hob_path" in sp_layout[sp] else None
261 partition_pkg_rule = generate_tl_pkg(
262 sp_layout[sp], pkg, sp_img, sp_dtb, hob_path)
J-Alvesa62598e2024-11-18 17:49:53 +0000263 else:
264 raise ValueError(f"Specified invalid pkg type {package_type}")
265
266 write_to_sp_mk_gen(partition_pkg_rule, args)
J-Alvesd10fb032022-03-21 14:11:43 +0000267 return args
268
269@SpSetupActions.sp_action(global_action=True, exec_order=1)
270def check_dualroot(sp_layout, _, args :dict):
271 ''' Validate the amount of SPs from SiP and Platform owners. '''
272 if not args.get("dualroot"):
273 return args
274 args["split"] = int(MAX_SP / 2)
275 owners = [sp_layout[sp].get("owner") for sp in sp_layout]
276 args["plat_max_count"] = owners.count("Plat")
J-Alvesa62598e2024-11-18 17:49:53 +0000277
J-Alvesd10fb032022-03-21 14:11:43 +0000278 # If it is owned by the platform owner, it is assigned to the SiP.
279 args["sip_max_count"] = len(sp_layout.keys()) - args["plat_max_count"]
280 if args["sip_max_count"] > args["split"] or args["sip_max_count"] > args["split"]:
281 print(f"WARN: SiP Secure Partitions should not be more than {args['split']}")
282 # Counters for gen_crt_args.
283 args["sip_count"] = 1
284 args["plat_count"] = 1
285 return args
286
287@SpSetupActions.sp_action
288def gen_crt_args(sp_layout, sp, args :dict):
289 ''' Append CRT_ARGS. '''
290 # If "dualroot" is configured, 'sp_pkg_idx' depends on whether the SP is owned
291 # by the "SiP" or the "Plat".
292 if args.get("dualroot"):
293 # If the owner is not specified as "Plat", default to "SiP".
294 if sp_layout[sp].get("owner") == "Plat":
295 if args["plat_count"] > args["plat_max_count"]:
296 raise ValueError("plat_count can't surpass plat_max_count in args.")
297 sp_pkg_idx = args["plat_count"] + args["split"]
298 args["plat_count"] += 1
299 else:
300 if args["sip_count"] > args["sip_max_count"]:
301 raise ValueError("sip_count can't surpass sip_max_count in args.")
302 sp_pkg_idx = args["sip_count"]
303 args["sip_count"] += 1
304 else:
305 sp_pkg_idx = [k for k in sp_layout.keys()].index(sp) + 1
306 write_to_sp_mk_gen(f"CRT_ARGS += --sp-pkg{sp_pkg_idx} {get_sp_pkg(sp, args)}\n", args)
307 return args
308
309@SpSetupActions.sp_action
310def gen_fiptool_args(sp_layout, sp, args :dict):
311 ''' Generate arguments for the FIP Tool. '''
Karl Meakinaba46182023-02-14 11:56:02 +0000312 uuid_std = get_uuid(sp_layout, sp, args)
J-Alvesd10fb032022-03-21 14:11:43 +0000313 write_to_sp_mk_gen(f"FIP_ARGS += --blob uuid={str(uuid_std)},file={get_sp_pkg(sp, args)}\n", args)
314 return args
315
Karl Meakinaba46182023-02-14 11:56:02 +0000316@SpSetupActions.sp_action
317def gen_fconf_fragment(sp_layout, sp, args: dict):
318 ''' Generate the fconf fragment file'''
319 with open(args["fconf_fragment"], "a") as f:
320 uuid = get_uuid(sp_layout, sp, args)
321 owner = "Plat" if sp_layout[sp].get("owner") == "Plat" else "SiP"
322
323 if "physical-load-address" in sp_layout[sp].keys():
324 load_address = sp_layout[sp]["physical-load-address"]
325 else:
326 load_address = get_load_address(sp_layout, sp, args)
327
J-Alvesa890d062024-01-11 13:35:52 +0000328 if load_address is not None:
329 f.write(
Karl Meakinaba46182023-02-14 11:56:02 +0000330f'''\
331{sp} {{
332 uuid = "{uuid}";
333 load-address = <{load_address}>;
334 owner = "{owner}";
335}};
336
337''')
J-Alvesa890d062024-01-11 13:35:52 +0000338 else:
339 print("Warning: No load-address was found in the SP manifest.")
340
Karl Meakinaba46182023-02-14 11:56:02 +0000341 return args
342
J-Alvesd10fb032022-03-21 14:11:43 +0000343def init_sp_actions(sys):
J-Alvesd10fb032022-03-21 14:11:43 +0000344 # Initialize arguments for the SP actions framework
345 args = {}
346 args["sp_gen_mk"] = os.path.abspath(sys.argv[1])
Karl Meakinaba46182023-02-14 11:56:02 +0000347 sp_layout_file = os.path.abspath(sys.argv[2])
J-Alvesd10fb032022-03-21 14:11:43 +0000348 args["sp_layout_dir"] = os.path.dirname(sp_layout_file)
349 args["out_dir"] = os.path.abspath(sys.argv[3])
350 args["dualroot"] = sys.argv[4] == "dualroot"
Karl Meakinaba46182023-02-14 11:56:02 +0000351 args["fconf_fragment"] = os.path.abspath(sys.argv[5])
352
353
354 with open(sp_layout_file) as json_file:
355 sp_layout = json.load(json_file)
J-Alvesd10fb032022-03-21 14:11:43 +0000356 #Clear content of file "sp_gen.mk".
357 with open(args["sp_gen_mk"], "w"):
358 None
Karl Meakinaba46182023-02-14 11:56:02 +0000359 #Clear content of file "fconf_fragment".
360 with open(args["fconf_fragment"], "w"):
361 None
362
J-Alvesd10fb032022-03-21 14:11:43 +0000363 return args, sp_layout
Ruari Phipps571ed6b2020-07-24 16:20:57 +0100364
J-Alvesd10fb032022-03-21 14:11:43 +0000365if __name__ == "__main__":
366 args, sp_layout = init_sp_actions(sys)
367 SpSetupActions.run_actions(sp_layout, args)