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