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