refactor(sptool): use SpSetupActions in sp_mk_generator.py

The "sp_mk_generator.py" is responsible for processing the SP layout
file, which contains information about the SPs to be deployed on top of
the SPM, to generate the "sp_gen.mk" file which appends information
specific to each SP that shall help with packing all SPs into a fip
binary.
Before this patch the "sp_mk_generator.py" was a monolithic script,
which has now been broken down into functions for each identified
configuration action.

Signed-off-by: J-Alves <joao.alves@arm.com>
Change-Id: I8ee7487f2e07d53e508d17d0fe4510e22957f5ca
This commit is contained in:
J-Alves 2022-03-21 14:11:43 +00:00
parent b1e6a41572
commit a96a07bfb6

235
tools/sptool/sp_mk_generator.py Executable file → Normal file
View file

@ -47,111 +47,174 @@ A typical SP_LAYOUT_FILE file will look like
""" """
import getopt
import json import json
import os import os
import re import re
import sys import sys
import uuid import uuid
from spactions import SpSetupActions
with open(sys.argv[2],'r') as in_file:
data = json.load(in_file)
json_file = os.path.abspath(sys.argv[2])
json_dir = os.path.dirname(json_file)
gen_file = os.path.abspath(sys.argv[1])
out_dir = os.path.abspath(sys.argv[3])
dtb_dir = out_dir + "/fdts/"
MAX_SP = 8 MAX_SP = 8
dualroot = sys.argv[4].lower() == "dualroot" UUID_LEN = 4
split = int(MAX_SP / 2)
print(dtb_dir)
platform_count = 1
sip_count = 1
with open(gen_file, 'w') as out_file: # Some helper functions to access args propagated to the action functions in
for idx, key in enumerate(data.keys()): # SpSetupActions framework.
pkg_num = idx + 1 def check_sp_mk_gen(args :dict):
if "sp_gen_mk" not in args.keys():
raise Exception(f"Path to file sp_gen.mk needs to be in 'args'.")
if (pkg_num > MAX_SP): def check_out_dir(args :dict):
print("WARNING: Too many secure partitions\n") if "out_dir" not in args.keys() or not os.path.isdir(args["out_dir"]):
exit(-1) raise Exception("Define output folder with \'out_dir\' key.")
if dualroot: def check_sp_layout_dir(args :dict):
owner = data[key].get('owner') if "sp_layout_dir" not in args.keys() or not os.path.isdir(args["sp_layout_dir"]):
if owner == "Plat": raise Exception("Define output folder with \'sp_layout_dir\' key.")
if (platform_count > split):
print("WARNING: Maximum Secure partitions by Plat " + def write_to_sp_mk_gen(content, args :dict):
"have been exceeded (" + str(split) + ")\n") check_sp_mk_gen(args)
exit(-1) with open(args["sp_gen_mk"], "a") as f:
pkg_num = split + platform_count f.write(f"{content}\n")
platform_count += 1
elif (sip_count > split): def get_sp_manifest_full_path(sp_node, args :dict):
print("WARNING: Maximum Secure partitions by SiP " + check_sp_layout_dir(args)
"have been exceeded (" + str(split) + ")\n") return os.path.join(args["sp_layout_dir"], get_file_from_layout(sp_node["pm"]))
exit(-1)
def get_sp_img_full_path(sp_node, args :dict):
check_sp_layout_dir(args)
return os.path.join(args["sp_layout_dir"], get_file_from_layout(sp_node["image"]))
def get_sp_pkg(sp, args :dict):
check_out_dir(args)
return os.path.join(args["out_dir"], f"{sp}.pkg")
def is_line_in_sp_gen(line, args :dict):
with open(args["sp_gen_mk"], "r") as f:
sppkg_rule = [l for l in f if line in l]
return len(sppkg_rule) is not 0
def get_file_from_layout(node):
''' Helper to fetch a file path from sp_layout.json. '''
if type(node) is dict and "file" in node.keys():
return node["file"]
return node
def get_offset_from_layout(node):
''' Helper to fetch an offset from sp_layout.json. '''
if type(node) is dict and "offset" in node.keys():
return int(node["offset"], 0)
return None
def get_image_offset(node):
''' Helper to fetch image offset from sp_layout.json '''
return get_offset_from_layout(node["image"])
def get_pm_offset(node):
''' Helper to fetch pm offset from sp_layout.json '''
return get_offset_from_layout(node["pm"])
@SpSetupActions.sp_action(global_action=True)
def check_max_sps(sp_layout, _, args :dict):
''' Check validate the maximum number of SPs is respected. '''
if len(sp_layout.keys()) > MAX_SP:
raise Exception(f"Too many SPs in SP layout file. Max: {MAX_SP}")
return args
@SpSetupActions.sp_action
def gen_fdt_sources(sp_layout, sp, args :dict):
''' Generate FDT_SOURCES values for a given SP. '''
manifest_path = get_sp_manifest_full_path(sp_layout[sp], args)
write_to_sp_mk_gen(f"FDT_SOURCES += {manifest_path}", args)
return args
@SpSetupActions.sp_action
def gen_sptool_args(sp_layout, sp, args):
''' Generate sptool arguments to generate SP Pkg for a given SP. '''
check_out_dir(args)
check_sp_layout_dir(args)
sp_pkg = get_sp_pkg(sp, args)
sp_dtb_name = os.path.basename(sp_layout[sp]["pm"])[:-1] + "b"
sp_dtb = os.path.join(args["out_dir"], f"fdts/{sp_dtb_name}")
sp_bin = os.path.join(args["sp_layout_dir"], sp_layout[sp]["image"])
write_to_sp_mk_gen(f"SPTOOL_ARGS += -i {sp_bin}:{sp_dtb} -o {sp_pkg}\n", args)
return args
@SpSetupActions.sp_action(global_action=True, exec_order=1)
def check_dualroot(sp_layout, _, args :dict):
''' Validate the amount of SPs from SiP and Platform owners. '''
if not args.get("dualroot"):
return args
args["split"] = int(MAX_SP / 2)
owners = [sp_layout[sp].get("owner") for sp in sp_layout]
args["plat_max_count"] = owners.count("Plat")
# If it is owned by the platform owner, it is assigned to the SiP.
args["sip_max_count"] = len(sp_layout.keys()) - args["plat_max_count"]
if args["sip_max_count"] > args["split"] or args["sip_max_count"] > args["split"]:
print(f"WARN: SiP Secure Partitions should not be more than {args['split']}")
# Counters for gen_crt_args.
args["sip_count"] = 1
args["plat_count"] = 1
return args
@SpSetupActions.sp_action
def gen_crt_args(sp_layout, sp, args :dict):
''' Append CRT_ARGS. '''
# If "dualroot" is configured, 'sp_pkg_idx' depends on whether the SP is owned
# by the "SiP" or the "Plat".
if args.get("dualroot"):
# If the owner is not specified as "Plat", default to "SiP".
if sp_layout[sp].get("owner") == "Plat":
if args["plat_count"] > args["plat_max_count"]:
raise ValueError("plat_count can't surpass plat_max_count in args.")
sp_pkg_idx = args["plat_count"] + args["split"]
args["plat_count"] += 1
else: else:
pkg_num = sip_count if args["sip_count"] > args["sip_max_count"]:
sip_count += 1 raise ValueError("sip_count can't surpass sip_max_count in args.")
sp_pkg_idx = args["sip_count"]
args["sip_count"] += 1
else:
sp_pkg_idx = [k for k in sp_layout.keys()].index(sp) + 1
write_to_sp_mk_gen(f"CRT_ARGS += --sp-pkg{sp_pkg_idx} {get_sp_pkg(sp, args)}\n", args)
return args
""" @SpSetupActions.sp_action
Append FDT_SOURCES def gen_fiptool_args(sp_layout, sp, args :dict):
""" ''' Generate arguments for the FIP Tool. '''
dts = os.path.join(json_dir, data[key]['pm']) if "uuid" in sp_layout[sp]:
dtb = dtb_dir + os.path.basename(data[key]['pm'][:-1] + "b") # Extract the UUID from the JSON file if the SP entry has a 'uuid' field
out_file.write("FDT_SOURCES += " + dts + "\n")
"""
Update SPTOOL_ARGS
"""
dst = out_dir + "/" + key + ".pkg"
src = [ json_dir + "/" + data[key]['image'] , dtb ]
out_file.write("SPTOOL_ARGS += -i " + ":".join(src) + " -o " + dst + "\n")
if "uuid" in data[key]:
"""
Extract the UUID from the JSON file if the SP entry has a 'uuid' field
"""
uuid_std = uuid.UUID(data[key]['uuid']) uuid_std = uuid.UUID(data[key]['uuid'])
else: else:
""" with open(get_sp_manifest_full_path(sp_layout[sp], args), "r") as pm_f:
Extract uuid from partition manifest uuid_lines = [l for l in pm_f if 'uuid' in l]
""" assert(len(uuid_lines) is 1)
pm_file = open(dts)
for line in pm_file:
if "uuid" in line:
# re.findall returns a list of string tuples.
# uuid_hex is the first item in this list representing the four
# uuid hex integers from the manifest uuid field. The heading
# '0x' of the hexadecimal representation is stripped out.
# e.g. uuid = <0x1e67b5b4 0xe14f904a 0x13fb1fb8 0xcbdae1da>;
# uuid_hex = ('1e67b5b4', 'e14f904a', '13fb1fb8', 'cbdae1da')
uuid_hex = re.findall(r'0x([0-9a-f]+) 0x([0-9a-f]+) 0x([0-9a-f]+) 0x([0-9a-f]+)', line)[0];
# uuid_hex is a list of four hex string values
if len(uuid_hex) != 4:
print("ERROR: malformed UUID")
exit(-1)
# The uuid field in SP manifest is the little endian representation # The uuid field in SP manifest is the little endian representation
# mapped to arguments as described in SMCCC section 5.3. # mapped to arguments as described in SMCCC section 5.3.
# Convert each unsigned integer value to a big endian representation # Convert each unsigned integer value to a big endian representation
# required by fiptool. # required by fiptool.
y=list(map(bytearray.fromhex, uuid_hex)) uuid_parsed = re.findall("0x([0-9a-f]+)", uuid_lines[0])
z=(int.from_bytes(y[0], byteorder='little', signed=False), y = list(map(bytearray.fromhex, uuid_parsed))
int.from_bytes(y[1], byteorder='little', signed=False), z = [int.from_bytes(i, byteorder='little', signed=False) for i in y]
int.from_bytes(y[2], byteorder='little', signed=False),
int.from_bytes(y[3], byteorder='little', signed=False))
uuid_std = uuid.UUID(f'{z[0]:08x}{z[1]:08x}{z[2]:08x}{z[3]:08x}') uuid_std = uuid.UUID(f'{z[0]:08x}{z[1]:08x}{z[2]:08x}{z[3]:08x}')
write_to_sp_mk_gen(f"FIP_ARGS += --blob uuid={str(uuid_std)},file={get_sp_pkg(sp, args)}\n", args)
return args
""" def init_sp_actions(sys):
Append FIP_ARGS sp_layout_file = os.path.abspath(sys.argv[2])
""" with open(sp_layout_file) as json_file:
out_file.write("FIP_ARGS += --blob uuid=" + str(uuid_std) + ",file=" + dst + "\n") sp_layout = json.load(json_file)
# Initialize arguments for the SP actions framework
args = {}
args["sp_gen_mk"] = os.path.abspath(sys.argv[1])
args["sp_layout_dir"] = os.path.dirname(sp_layout_file)
args["out_dir"] = os.path.abspath(sys.argv[3])
args["dualroot"] = sys.argv[4] == "dualroot"
#Clear content of file "sp_gen.mk".
with open(args["sp_gen_mk"], "w"):
None
return args, sp_layout
""" if __name__ == "__main__":
Append CRT_ARGS args, sp_layout = init_sp_actions(sys)
""" SpSetupActions.run_actions(sp_layout, args)
out_file.write("CRT_ARGS += --sp-pkg" + str(pkg_num) + " " + dst + "\n")
out_file.write("\n")