1#!/usr/bin/python3 2# Copyright (c) 2020-2023, 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 assert(len(load_address_lines) == 1) 140 load_address_parsed = re.search("(0x[0-9a-f]+)", load_address_lines[0]) 141 return load_address_parsed.group(0) 142 143 144@SpSetupActions.sp_action(global_action=True) 145def check_max_sps(sp_layout, _, args :dict): 146 ''' Check validate the maximum number of SPs is respected. ''' 147 if len(sp_layout.keys()) > MAX_SP: 148 raise Exception(f"Too many SPs in SP layout file. Max: {MAX_SP}") 149 return args 150 151@SpSetupActions.sp_action 152def gen_fdt_sources(sp_layout, sp, args :dict): 153 ''' Generate FDT_SOURCES values for a given SP. ''' 154 manifest_path = get_sp_manifest_full_path(sp_layout[sp], args) 155 write_to_sp_mk_gen(f"FDT_SOURCES += {manifest_path}", args) 156 return args 157 158@SpSetupActions.sp_action 159def gen_sptool_args(sp_layout, sp, args :dict): 160 ''' Generate Sp Pkgs rules. ''' 161 sp_pkg = get_sp_pkg(sp, args) 162 sp_dtb_name = os.path.basename(get_file_from_layout(sp_layout[sp]["pm"]))[:-1] + "b" 163 sp_dtb = os.path.join(args["out_dir"], f"fdts/{sp_dtb_name}") 164 sp_img = get_sp_img_full_path(sp_layout[sp], args) 165 166 # Do not generate rule if already there. 167 if is_line_in_sp_gen(f'{sp_pkg}:', args): 168 return args 169 write_to_sp_mk_gen(f"SP_PKGS += {sp_pkg}\n", args) 170 171 sptool_args = f" -i {sp_img}:{sp_dtb}" 172 pm_offset = get_pm_offset(sp_layout[sp]) 173 sptool_args += f" --pm-offset {pm_offset}" if pm_offset is not None else "" 174 image_offset = get_image_offset(sp_layout[sp]) 175 sptool_args += f" --img-offset {image_offset}" if image_offset is not None else "" 176 sptool_args += f" -o {sp_pkg}" 177 sppkg_rule = f''' 178{sp_pkg}: {sp_dtb} {sp_img} 179\t$(Q)echo Generating {sp_pkg} 180\t$(Q)$(PYTHON) $(SPTOOL) {sptool_args} 181''' 182 write_to_sp_mk_gen(sppkg_rule, args) 183 return args 184 185@SpSetupActions.sp_action(global_action=True, exec_order=1) 186def check_dualroot(sp_layout, _, args :dict): 187 ''' Validate the amount of SPs from SiP and Platform owners. ''' 188 if not args.get("dualroot"): 189 return args 190 args["split"] = int(MAX_SP / 2) 191 owners = [sp_layout[sp].get("owner") for sp in sp_layout] 192 args["plat_max_count"] = owners.count("Plat") 193 # If it is owned by the platform owner, it is assigned to the SiP. 194 args["sip_max_count"] = len(sp_layout.keys()) - args["plat_max_count"] 195 if args["sip_max_count"] > args["split"] or args["sip_max_count"] > args["split"]: 196 print(f"WARN: SiP Secure Partitions should not be more than {args['split']}") 197 # Counters for gen_crt_args. 198 args["sip_count"] = 1 199 args["plat_count"] = 1 200 return args 201 202@SpSetupActions.sp_action 203def gen_crt_args(sp_layout, sp, args :dict): 204 ''' Append CRT_ARGS. ''' 205 # If "dualroot" is configured, 'sp_pkg_idx' depends on whether the SP is owned 206 # by the "SiP" or the "Plat". 207 if args.get("dualroot"): 208 # If the owner is not specified as "Plat", default to "SiP". 209 if sp_layout[sp].get("owner") == "Plat": 210 if args["plat_count"] > args["plat_max_count"]: 211 raise ValueError("plat_count can't surpass plat_max_count in args.") 212 sp_pkg_idx = args["plat_count"] + args["split"] 213 args["plat_count"] += 1 214 else: 215 if args["sip_count"] > args["sip_max_count"]: 216 raise ValueError("sip_count can't surpass sip_max_count in args.") 217 sp_pkg_idx = args["sip_count"] 218 args["sip_count"] += 1 219 else: 220 sp_pkg_idx = [k for k in sp_layout.keys()].index(sp) + 1 221 write_to_sp_mk_gen(f"CRT_ARGS += --sp-pkg{sp_pkg_idx} {get_sp_pkg(sp, args)}\n", args) 222 return args 223 224@SpSetupActions.sp_action 225def gen_fiptool_args(sp_layout, sp, args :dict): 226 ''' Generate arguments for the FIP Tool. ''' 227 uuid_std = get_uuid(sp_layout, sp, args) 228 write_to_sp_mk_gen(f"FIP_ARGS += --blob uuid={str(uuid_std)},file={get_sp_pkg(sp, args)}\n", args) 229 return args 230 231@SpSetupActions.sp_action 232def gen_fconf_fragment(sp_layout, sp, args: dict): 233 ''' Generate the fconf fragment file''' 234 with open(args["fconf_fragment"], "a") as f: 235 uuid = get_uuid(sp_layout, sp, args) 236 owner = "Plat" if sp_layout[sp].get("owner") == "Plat" else "SiP" 237 238 if "physical-load-address" in sp_layout[sp].keys(): 239 load_address = sp_layout[sp]["physical-load-address"] 240 else: 241 load_address = get_load_address(sp_layout, sp, args) 242 243 f.write( 244f'''\ 245{sp} {{ 246 uuid = "{uuid}"; 247 load-address = <{load_address}>; 248 owner = "{owner}"; 249}}; 250 251''') 252 return args 253 254def init_sp_actions(sys): 255 # Initialize arguments for the SP actions framework 256 args = {} 257 args["sp_gen_mk"] = os.path.abspath(sys.argv[1]) 258 sp_layout_file = os.path.abspath(sys.argv[2]) 259 args["sp_layout_dir"] = os.path.dirname(sp_layout_file) 260 args["out_dir"] = os.path.abspath(sys.argv[3]) 261 args["dualroot"] = sys.argv[4] == "dualroot" 262 args["fconf_fragment"] = os.path.abspath(sys.argv[5]) 263 264 265 with open(sp_layout_file) as json_file: 266 sp_layout = json.load(json_file) 267 #Clear content of file "sp_gen.mk". 268 with open(args["sp_gen_mk"], "w"): 269 None 270 #Clear content of file "fconf_fragment". 271 with open(args["fconf_fragment"], "w"): 272 None 273 274 return args, sp_layout 275 276if __name__ == "__main__": 277 args, sp_layout = init_sp_actions(sys) 278 SpSetupActions.run_actions(sp_layout, args) 279