xref: /rk3399_ARM-atf/tools/sptool/sp_mk_generator.py (revision a96a07bfb66b7d38fe3da824e8ba183967659008)
1#!/usr/bin/python3
2# Copyright (c) 2020-2022, 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
24
25Generated "sp_gen.mk" file contains triplet of following information for each
26Secure Partition entry
27    FDT_SOURCES +=  sp1.dts
28    SPTOOL_ARGS += -i sp1.bin:sp1.dtb -o sp1.pkg
29    FIP_ARGS += --blob uuid=XXXXX-XXX...,file=sp1.pkg
30    CRT_ARGS += --sp-pkg1 sp1.pkg
31
32A typical SP_LAYOUT_FILE file will look like
33{
34        "SP1" : {
35                "image": "sp1.bin",
36                "pm": "test/sp1.dts"
37        },
38
39        "SP2" : {
40                "image": "sp2.bin",
41                "pm": "test/sp2.dts",
42                "uuid": "1b1820fe-48f7-4175-8999-d51da00b7c9f"
43        }
44
45        ...
46}
47
48"""
49
50import json
51import os
52import re
53import sys
54import uuid
55from spactions import SpSetupActions
56
57MAX_SP = 8
58UUID_LEN = 4
59
60# Some helper functions to access args propagated to the action functions in
61# SpSetupActions framework.
62
63def check_sp_mk_gen(args :dict):
64    if "sp_gen_mk" not in args.keys():
65        raise Exception(f"Path to file sp_gen.mk needs to be in 'args'.")
66
67def check_out_dir(args :dict):
68    if "out_dir" not in args.keys() or not os.path.isdir(args["out_dir"]):
69        raise Exception("Define output folder with \'out_dir\' key.")
70
71def check_sp_layout_dir(args :dict):
72    if "sp_layout_dir" not in args.keys() or not os.path.isdir(args["sp_layout_dir"]):
73        raise Exception("Define output folder with \'sp_layout_dir\' key.")
74
75def write_to_sp_mk_gen(content, args :dict):
76    check_sp_mk_gen(args)
77    with open(args["sp_gen_mk"], "a") as f:
78        f.write(f"{content}\n")
79
80def get_sp_manifest_full_path(sp_node, args :dict):
81    check_sp_layout_dir(args)
82    return os.path.join(args["sp_layout_dir"], get_file_from_layout(sp_node["pm"]))
83
84def get_sp_img_full_path(sp_node, args :dict):
85    check_sp_layout_dir(args)
86    return os.path.join(args["sp_layout_dir"], get_file_from_layout(sp_node["image"]))
87
88def get_sp_pkg(sp, args :dict):
89    check_out_dir(args)
90    return os.path.join(args["out_dir"], f"{sp}.pkg")
91
92def is_line_in_sp_gen(line, args :dict):
93    with open(args["sp_gen_mk"], "r") as f:
94        sppkg_rule = [l for l in f if line in l]
95    return len(sppkg_rule) is not 0
96
97def get_file_from_layout(node):
98    ''' Helper to fetch a file path from sp_layout.json. '''
99    if type(node) is dict and "file" in node.keys():
100        return node["file"]
101    return node
102
103def get_offset_from_layout(node):
104    ''' Helper to fetch an offset from sp_layout.json. '''
105    if type(node) is dict and "offset" in node.keys():
106        return int(node["offset"], 0)
107    return None
108
109def get_image_offset(node):
110    ''' Helper to fetch image offset from sp_layout.json '''
111    return get_offset_from_layout(node["image"])
112
113def get_pm_offset(node):
114    ''' Helper to fetch pm offset from sp_layout.json '''
115    return get_offset_from_layout(node["pm"])
116
117@SpSetupActions.sp_action(global_action=True)
118def check_max_sps(sp_layout, _, args :dict):
119    ''' Check validate the maximum number of SPs is respected. '''
120    if len(sp_layout.keys()) > MAX_SP:
121        raise Exception(f"Too many SPs in SP layout file. Max: {MAX_SP}")
122    return args
123
124@SpSetupActions.sp_action
125def gen_fdt_sources(sp_layout, sp, args :dict):
126    ''' Generate FDT_SOURCES values for a given SP. '''
127    manifest_path = get_sp_manifest_full_path(sp_layout[sp], args)
128    write_to_sp_mk_gen(f"FDT_SOURCES += {manifest_path}", args)
129    return args
130
131@SpSetupActions.sp_action
132def gen_sptool_args(sp_layout, sp, args):
133    ''' Generate sptool arguments to generate SP Pkg for a given SP. '''
134    check_out_dir(args)
135    check_sp_layout_dir(args)
136    sp_pkg = get_sp_pkg(sp, args)
137    sp_dtb_name = os.path.basename(sp_layout[sp]["pm"])[:-1] + "b"
138    sp_dtb = os.path.join(args["out_dir"], f"fdts/{sp_dtb_name}")
139    sp_bin = os.path.join(args["sp_layout_dir"], sp_layout[sp]["image"])
140    write_to_sp_mk_gen(f"SPTOOL_ARGS += -i {sp_bin}:{sp_dtb} -o {sp_pkg}\n", args)
141    return args
142
143@SpSetupActions.sp_action(global_action=True, exec_order=1)
144def check_dualroot(sp_layout, _, args :dict):
145    ''' Validate the amount of SPs from SiP and Platform owners. '''
146    if not args.get("dualroot"):
147        return args
148    args["split"] =  int(MAX_SP / 2)
149    owners = [sp_layout[sp].get("owner") for sp in sp_layout]
150    args["plat_max_count"] = owners.count("Plat")
151    # If it is owned by the platform owner, it is assigned to the SiP.
152    args["sip_max_count"] = len(sp_layout.keys()) - args["plat_max_count"]
153    if  args["sip_max_count"] > args["split"] or args["sip_max_count"] > args["split"]:
154        print(f"WARN: SiP Secure Partitions should not be more than {args['split']}")
155    # Counters for gen_crt_args.
156    args["sip_count"] = 1
157    args["plat_count"] = 1
158    return args
159
160@SpSetupActions.sp_action
161def gen_crt_args(sp_layout, sp, args :dict):
162    ''' Append CRT_ARGS. '''
163    # If "dualroot" is configured, 'sp_pkg_idx' depends on whether the SP is owned
164    # by the "SiP" or the "Plat".
165    if args.get("dualroot"):
166        # If the owner is not specified as "Plat", default to "SiP".
167        if sp_layout[sp].get("owner") == "Plat":
168            if args["plat_count"] > args["plat_max_count"]:
169                raise ValueError("plat_count can't surpass plat_max_count in args.")
170            sp_pkg_idx = args["plat_count"] + args["split"]
171            args["plat_count"] += 1
172        else:
173            if args["sip_count"] > args["sip_max_count"]:
174                raise ValueError("sip_count can't surpass sip_max_count in args.")
175            sp_pkg_idx = args["sip_count"]
176            args["sip_count"] += 1
177    else:
178        sp_pkg_idx = [k for k in sp_layout.keys()].index(sp) + 1
179    write_to_sp_mk_gen(f"CRT_ARGS += --sp-pkg{sp_pkg_idx} {get_sp_pkg(sp, args)}\n", args)
180    return args
181
182@SpSetupActions.sp_action
183def gen_fiptool_args(sp_layout, sp, args :dict):
184    ''' Generate arguments for the FIP Tool. '''
185    if "uuid" in sp_layout[sp]:
186        # Extract the UUID from the JSON file if the SP entry has a 'uuid' field
187        uuid_std = uuid.UUID(data[key]['uuid'])
188    else:
189        with open(get_sp_manifest_full_path(sp_layout[sp], args), "r") as pm_f:
190            uuid_lines = [l for l in pm_f if 'uuid' in l]
191        assert(len(uuid_lines) is 1)
192        # The uuid field in SP manifest is the little endian representation
193        # mapped to arguments as described in SMCCC section 5.3.
194        # Convert each unsigned integer value to a big endian representation
195        # required by fiptool.
196        uuid_parsed = re.findall("0x([0-9a-f]+)", uuid_lines[0])
197        y = list(map(bytearray.fromhex, uuid_parsed))
198        z = [int.from_bytes(i, byteorder='little', signed=False) for i in y]
199        uuid_std = uuid.UUID(f'{z[0]:08x}{z[1]:08x}{z[2]:08x}{z[3]:08x}')
200    write_to_sp_mk_gen(f"FIP_ARGS += --blob uuid={str(uuid_std)},file={get_sp_pkg(sp, args)}\n", args)
201    return args
202
203def init_sp_actions(sys):
204    sp_layout_file = os.path.abspath(sys.argv[2])
205    with open(sp_layout_file) as json_file:
206        sp_layout = json.load(json_file)
207    # Initialize arguments for the SP actions framework
208    args = {}
209    args["sp_gen_mk"] = os.path.abspath(sys.argv[1])
210    args["sp_layout_dir"] = os.path.dirname(sp_layout_file)
211    args["out_dir"] = os.path.abspath(sys.argv[3])
212    args["dualroot"] = sys.argv[4] == "dualroot"
213    #Clear content of file "sp_gen.mk".
214    with open(args["sp_gen_mk"], "w"):
215        None
216    return args, sp_layout
217
218if __name__ == "__main__":
219    args, sp_layout = init_sp_actions(sys)
220    SpSetupActions.run_actions(sp_layout, args)
221