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