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