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