1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
|
#!/usr/bin/python3
# Copyright (c) 2020-2023, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
"""
This script is invoked by Make system and generates secure partition makefile.
It expects platform provided secure partition layout file which contains list
of Secure Partition Images and Partition manifests(PM).
Layout file can exist outside of TF-A tree and the paths of Image and PM files
must be relative to it.
This script parses the layout file and generates a make file which updates
FDT_SOURCES, FIP_ARGS, CRT_ARGS and SPTOOL_ARGS which are used in later build
steps.
If the SP entry in the layout file has a "uuid" field the scripts gets the UUID
from there, otherwise it parses the associated partition manifest and extracts
the UUID from there.
param1: Generated mk file "sp_gen.mk"
param2: "SP_LAYOUT_FILE", json file containing platform provided information
param3: plat out directory
param4: CoT parameter
param5: Generated dts file "sp_list_fragment.dts"
Generated "sp_gen.mk" file contains triplet of following information for each
Secure Partition entry
FDT_SOURCES += sp1.dts
SPTOOL_ARGS += -i sp1.bin:sp1.dtb -o sp1.pkg
FIP_ARGS += --blob uuid=XXXXX-XXX...,file=sp1.pkg
CRT_ARGS += --sp-pkg1 sp1.pkg
A typical SP_LAYOUT_FILE file will look like
{
"SP1" : {
"image": "sp1.bin",
"pm": "test/sp1.dts"
},
"SP2" : {
"image": "sp2.bin",
"pm": "test/sp2.dts",
"uuid": "1b1820fe-48f7-4175-8999-d51da00b7c9f"
}
...
}
"""
import json
import os
import re
import sys
import uuid
from spactions import SpSetupActions
MAX_SP = 8
UUID_LEN = 4
# Some helper functions to access args propagated to the action functions in
# SpSetupActions framework.
def check_sp_mk_gen(args :dict):
if "sp_gen_mk" not in args.keys():
raise Exception(f"Path to file sp_gen.mk needs to be in 'args'.")
def check_out_dir(args :dict):
if "out_dir" not in args.keys() or not os.path.isdir(args["out_dir"]):
raise Exception("Define output folder with \'out_dir\' key.")
def check_sp_layout_dir(args :dict):
if "sp_layout_dir" not in args.keys() or not os.path.isdir(args["sp_layout_dir"]):
raise Exception("Define output folder with \'sp_layout_dir\' key.")
def write_to_sp_mk_gen(content, args :dict):
check_sp_mk_gen(args)
with open(args["sp_gen_mk"], "a") as f:
f.write(f"{content}\n")
def get_sp_manifest_full_path(sp_node, args :dict):
check_sp_layout_dir(args)
return os.path.join(args["sp_layout_dir"], get_file_from_layout(sp_node["pm"]))
def get_sp_img_full_path(sp_node, args :dict):
check_sp_layout_dir(args)
return os.path.join(args["sp_layout_dir"], get_file_from_layout(sp_node["image"]))
def get_sp_pkg(sp, args :dict):
check_out_dir(args)
return os.path.join(args["out_dir"], f"{sp}.pkg")
def is_line_in_sp_gen(line, args :dict):
with open(args["sp_gen_mk"], "r") as f:
sppkg_rule = [l for l in f if line in l]
return len(sppkg_rule) != 0
def get_file_from_layout(node):
''' Helper to fetch a file path from sp_layout.json. '''
if type(node) is dict and "file" in node.keys():
return node["file"]
return node
def get_offset_from_layout(node):
''' Helper to fetch an offset from sp_layout.json. '''
if type(node) is dict and "offset" in node.keys():
return int(node["offset"], 0)
return None
def get_image_offset(node):
''' Helper to fetch image offset from sp_layout.json '''
return get_offset_from_layout(node["image"])
def get_pm_offset(node):
''' Helper to fetch pm offset from sp_layout.json '''
return get_offset_from_layout(node["pm"])
def get_uuid(sp_layout, sp, args :dict):
''' Helper to fetch uuid from pm file listed in sp_layout.json'''
if "uuid" in sp_layout[sp]:
# Extract the UUID from the JSON file if the SP entry has a 'uuid' field
uuid_std = uuid.UUID(sp_layout[sp]['uuid'])
else:
with open(get_sp_manifest_full_path(sp_layout[sp], args), "r") as pm_f:
uuid_lines = [l for l in pm_f if 'uuid' in l]
assert(len(uuid_lines) == 1)
# The uuid field in SP manifest is the little endian representation
# mapped to arguments as described in SMCCC section 5.3.
# Convert each unsigned integer value to a big endian representation
# required by fiptool.
uuid_parsed = re.findall("0x([0-9a-f]+)", uuid_lines[0])
y = list(map(bytearray.fromhex, uuid_parsed))
z = [int.from_bytes(i, byteorder='little', signed=False) for i in y]
uuid_std = uuid.UUID(f'{z[0]:08x}{z[1]:08x}{z[2]:08x}{z[3]:08x}')
return uuid_std
def get_load_address(sp_layout, sp, args :dict):
''' Helper to fetch load-address from pm file listed in sp_layout.json'''
with open(get_sp_manifest_full_path(sp_layout[sp], args), "r") as pm_f:
load_address_lines = [l for l in pm_f if 'load-address' in l]
assert(len(load_address_lines) == 1)
load_address_parsed = re.search("(0x[0-9a-f]+)", load_address_lines[0])
return load_address_parsed.group(0)
@SpSetupActions.sp_action(global_action=True)
def check_max_sps(sp_layout, _, args :dict):
''' Check validate the maximum number of SPs is respected. '''
if len(sp_layout.keys()) > MAX_SP:
raise Exception(f"Too many SPs in SP layout file. Max: {MAX_SP}")
return args
@SpSetupActions.sp_action
def gen_fdt_sources(sp_layout, sp, args :dict):
''' Generate FDT_SOURCES values for a given SP. '''
manifest_path = get_sp_manifest_full_path(sp_layout[sp], args)
write_to_sp_mk_gen(f"FDT_SOURCES += {manifest_path}", args)
return args
@SpSetupActions.sp_action
def gen_sptool_args(sp_layout, sp, args :dict):
''' Generate Sp Pkgs rules. '''
sp_pkg = get_sp_pkg(sp, args)
sp_dtb_name = os.path.basename(get_file_from_layout(sp_layout[sp]["pm"]))[:-1] + "b"
sp_dtb = os.path.join(args["out_dir"], f"fdts/{sp_dtb_name}")
sp_img = get_sp_img_full_path(sp_layout[sp], args)
# Do not generate rule if already there.
if is_line_in_sp_gen(f'{sp_pkg}:', args):
return args
write_to_sp_mk_gen(f"SP_PKGS += {sp_pkg}\n", args)
sptool_args = f" -i {sp_img}:{sp_dtb}"
pm_offset = get_pm_offset(sp_layout[sp])
sptool_args += f" --pm-offset {pm_offset}" if pm_offset is not None else ""
image_offset = get_image_offset(sp_layout[sp])
sptool_args += f" --img-offset {image_offset}" if image_offset is not None else ""
sptool_args += f" -o {sp_pkg}"
sppkg_rule = f'''
{sp_pkg}: {sp_dtb} {sp_img}
\t$(Q)echo Generating {sp_pkg}
\t$(Q)$(PYTHON) $(SPTOOL) {sptool_args}
'''
write_to_sp_mk_gen(sppkg_rule, args)
return args
@SpSetupActions.sp_action(global_action=True, exec_order=1)
def check_dualroot(sp_layout, _, args :dict):
''' Validate the amount of SPs from SiP and Platform owners. '''
if not args.get("dualroot"):
return args
args["split"] = int(MAX_SP / 2)
owners = [sp_layout[sp].get("owner") for sp in sp_layout]
args["plat_max_count"] = owners.count("Plat")
# If it is owned by the platform owner, it is assigned to the SiP.
args["sip_max_count"] = len(sp_layout.keys()) - args["plat_max_count"]
if args["sip_max_count"] > args["split"] or args["sip_max_count"] > args["split"]:
print(f"WARN: SiP Secure Partitions should not be more than {args['split']}")
# Counters for gen_crt_args.
args["sip_count"] = 1
args["plat_count"] = 1
return args
@SpSetupActions.sp_action
def gen_crt_args(sp_layout, sp, args :dict):
''' Append CRT_ARGS. '''
# If "dualroot" is configured, 'sp_pkg_idx' depends on whether the SP is owned
# by the "SiP" or the "Plat".
if args.get("dualroot"):
# If the owner is not specified as "Plat", default to "SiP".
if sp_layout[sp].get("owner") == "Plat":
if args["plat_count"] > args["plat_max_count"]:
raise ValueError("plat_count can't surpass plat_max_count in args.")
sp_pkg_idx = args["plat_count"] + args["split"]
args["plat_count"] += 1
else:
if args["sip_count"] > args["sip_max_count"]:
raise ValueError("sip_count can't surpass sip_max_count in args.")
sp_pkg_idx = args["sip_count"]
args["sip_count"] += 1
else:
sp_pkg_idx = [k for k in sp_layout.keys()].index(sp) + 1
write_to_sp_mk_gen(f"CRT_ARGS += --sp-pkg{sp_pkg_idx} {get_sp_pkg(sp, args)}\n", args)
return args
@SpSetupActions.sp_action
def gen_fiptool_args(sp_layout, sp, args :dict):
''' Generate arguments for the FIP Tool. '''
uuid_std = get_uuid(sp_layout, sp, args)
write_to_sp_mk_gen(f"FIP_ARGS += --blob uuid={str(uuid_std)},file={get_sp_pkg(sp, args)}\n", args)
return args
@SpSetupActions.sp_action
def gen_fconf_fragment(sp_layout, sp, args: dict):
''' Generate the fconf fragment file'''
with open(args["fconf_fragment"], "a") as f:
uuid = get_uuid(sp_layout, sp, args)
owner = "Plat" if sp_layout[sp].get("owner") == "Plat" else "SiP"
if "physical-load-address" in sp_layout[sp].keys():
load_address = sp_layout[sp]["physical-load-address"]
else:
load_address = get_load_address(sp_layout, sp, args)
f.write(
f'''\
{sp} {{
uuid = "{uuid}";
load-address = <{load_address}>;
owner = "{owner}";
}};
''')
return args
def init_sp_actions(sys):
# Initialize arguments for the SP actions framework
args = {}
args["sp_gen_mk"] = os.path.abspath(sys.argv[1])
sp_layout_file = os.path.abspath(sys.argv[2])
args["sp_layout_dir"] = os.path.dirname(sp_layout_file)
args["out_dir"] = os.path.abspath(sys.argv[3])
args["dualroot"] = sys.argv[4] == "dualroot"
args["fconf_fragment"] = os.path.abspath(sys.argv[5])
with open(sp_layout_file) as json_file:
sp_layout = json.load(json_file)
#Clear content of file "sp_gen.mk".
with open(args["sp_gen_mk"], "w"):
None
#Clear content of file "fconf_fragment".
with open(args["fconf_fragment"], "w"):
None
return args, sp_layout
if __name__ == "__main__":
args, sp_layout = init_sp_actions(sys)
SpSetupActions.run_actions(sp_layout, args)
|