From b1e6a41572240839e62099aa00298174b18c696a Mon Sep 17 00:00:00 2001 From: J-Alves Date: Mon, 21 Mar 2022 14:08:27 +0000 Subject: [PATCH 1/6] feat(sptool): add python SpSetupActions framework Developed python framework to help with SPs configuration. The framework allows for functions (dubbed "actions" in the framework) to be defined that should process the "sp_layout.json" file. Signed-off-by: J-Alves Change-Id: I278cd5a7aa0574168473e28f3b0fe231d7b548ee --- tools/sptool/spactions.py | 155 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 tools/sptool/spactions.py diff --git a/tools/sptool/spactions.py b/tools/sptool/spactions.py new file mode 100644 index 000000000..ff28ebbcd --- /dev/null +++ b/tools/sptool/spactions.py @@ -0,0 +1,155 @@ +#!/usr/bin/python3 +# Copyright (c) 2022, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +''' +This is a python module for defining and executing SP setup actions, targeting +a system deploying an SPM implementation. +Each action consists of a function, that processes the SP layout json file and +other provided arguments. +At the core of this is the SpSetupActions which provides a means to register +the functions into a table of actions, and execute them all when invoking +SpSetupActions.run_actions. +Registering the function is done by using the decorator '@SpSetupActions.sp_action' +at function definition. + +Functions can be called: +- once only, or per SP defined in the SP layout file; +- following an order, from lowest to highest of their execution order. +More information in the doc comments below. +''' +import bisect + +DEFAULT_ACTION_ORDER = 100 + +class _ConfiguredAction: + """ + Wraps action function with its configuration. + """ + def __init__(self, action, exec_order=DEFAULT_ACTION_ORDER, global_action=True, log_calls = False): + self.exec_order = exec_order + self.__name__ = action.__name__ + def logged_action(action): + def inner_logged_action(sp_layout, sp, args :dict): + print(f"Calling {action.__name__} -> {sp}") + return action(sp_layout, sp, args) + return inner_logged_action + self.action = logged_action(action) if log_calls is True else action + self.global_action = global_action + + def __lt__(self, other): + """ + To allow for ordered inserts in a list of actions. + """ + return self.exec_order < other.exec_order + + def __call__(self, sp_layout, sp, args :dict): + """ + Calls action function. + """ + return self.action(sp_layout, sp, args) + + def __repr__(self) -> str: + """ + Pretty format to show debug information about the action. + """ + return f"func: {self.__name__}; global:{self.global_action}; exec_order: {self.exec_order}" + +class SpSetupActions: + actions = [] + + def sp_action(in_action = None, global_action = False, log_calls=False, exec_order=DEFAULT_ACTION_ORDER): + """ + Function decorator that registers and configures action. + + :param in_action - function to register + :param global_action - make the function global, i.e. make it be + only called once. + :param log_calls - at every call to action, a useful log will be printed. + :param exec_order - action's calling order. + """ + def append_action(action): + action = _ConfiguredAction(action, exec_order, global_action, log_calls) + bisect.insort(SpSetupActions.actions, action) + return action + if in_action is not None: + return append_action(in_action) + return append_action + + def run_actions(sp_layout: dict, args: dict, verbose=False): + """ + Executes all actions in accordance to their registering configuration: + - If set as "global" it will be called once. + - Actions are called respecting the order established by their "exec_order" field. + + :param sp_layout - dictionary containing the SP layout information. + :param args - arguments to be propagated through the call of actions. + :param verbose - prints actions information in order of execution. + """ + args["called"] = [] # for debug purposes + def append_called(action, sp, args :dict): + args["called"].append(f"{action.__name__} -> {sp}") + return args + + for action in SpSetupActions.actions: + if verbose: + print(f"Calling {action}") + if action.global_action: + scope = "global" + args = action(sp_layout, scope, args) + args = append_called(action, scope, args) + else: + # Functions that are not global called for each SP defined in + # the SP layout. + for sp in sp_layout.keys(): + args = action(sp_layout, sp, args) + args = append_called(action, sp, args) + +if __name__ == "__main__": + # Executing this module will have the following test code/playground executed + sp_layout = { + "partition1" : { + "boot-info": True, + "image": { + "file": "partition.bin", + "offset":"0x2000" + }, + "pm": { + "file": "cactus.dts", + "offset":"0x1000" + }, + "owner": "SiP" + }, + "partition2" : { + "image": "partition.bin", + "pm": "cactus-secondary.dts", + "owner": "Plat" + }, + "partition3" : { + "image": "partition.bin", + "pm": "cactus-tertiary.dts", + "owner": "Plat" + }, + "partition4" : { + "image": "ivy.bin", + "pm": "ivy.dts", + "owner": "Plat" + } + } + + #Example of how to use this module + @SpSetupActions.sp_action(global_action=True) + def my_action1(sp_layout, _, args :dict): + print(f"inside function my_action1{sp_layout}\n\n args:{args})") + return args # Always return args in action function. + @SpSetupActions.sp_action(exec_order=1) + def my_action2(sp_layout, sp_name, args :dict): + print(f"inside function my_action2; SP: {sp_name} {sp_layout} args:{args}") + return args + + # Example arguments to be propagated through the functions. + # 'args' can be extended in the action functions. + args = dict() + args["arg1"] = 0xEEE + args["arg2"] = 0xFF + SpSetupActions.run_actions(sp_layout, args) From a96a07bfb66b7d38fe3da824e8ba183967659008 Mon Sep 17 00:00:00 2001 From: J-Alves Date: Mon, 21 Mar 2022 14:11:43 +0000 Subject: [PATCH 2/6] 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 Change-Id: I8ee7487f2e07d53e508d17d0fe4510e22957f5ca --- tools/sptool/sp_mk_generator.py | 243 ++++++++++++++++++++------------ 1 file changed, 153 insertions(+), 90 deletions(-) mode change 100755 => 100644 tools/sptool/sp_mk_generator.py diff --git a/tools/sptool/sp_mk_generator.py b/tools/sptool/sp_mk_generator.py old mode 100755 new mode 100644 index 82d5c1bbb..2cc96cb21 --- a/tools/sptool/sp_mk_generator.py +++ b/tools/sptool/sp_mk_generator.py @@ -47,111 +47,174 @@ A typical SP_LAYOUT_FILE file will look like """ -import getopt import json import os import re import sys 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 -dualroot = sys.argv[4].lower() == "dualroot" -split = int(MAX_SP / 2) -print(dtb_dir) -platform_count = 1 -sip_count = 1 +UUID_LEN = 4 -with open(gen_file, 'w') as out_file: - for idx, key in enumerate(data.keys()): +# Some helper functions to access args propagated to the action functions in +# 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): - print("WARNING: Too many secure partitions\n") - exit(-1) +def check_out_dir(args :dict): + if "out_dir" not in args.keys() or not os.path.isdir(args["out_dir"]): + raise Exception("Define output folder with \'out_dir\' key.") - if dualroot: - owner = data[key].get('owner') - if owner == "Plat": - if (platform_count > split): - print("WARNING: Maximum Secure partitions by Plat " + - "have been exceeded (" + str(split) + ")\n") - exit(-1) - pkg_num = split + platform_count - platform_count += 1 - elif (sip_count > split): - print("WARNING: Maximum Secure partitions by SiP " + - "have been exceeded (" + str(split) + ")\n") - exit(-1) - else: - pkg_num = sip_count - sip_count += 1 +def check_sp_layout_dir(args :dict): + if "sp_layout_dir" not in args.keys() or not os.path.isdir(args["sp_layout_dir"]): + raise Exception("Define output folder with \'sp_layout_dir\' key.") - """ - Append FDT_SOURCES - """ - dts = os.path.join(json_dir, data[key]['pm']) - dtb = dtb_dir + os.path.basename(data[key]['pm'][:-1] + "b") - out_file.write("FDT_SOURCES += " + dts + "\n") +def write_to_sp_mk_gen(content, args :dict): + check_sp_mk_gen(args) + with open(args["sp_gen_mk"], "a") as f: + f.write(f"{content}\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") +def get_sp_manifest_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["pm"])) - 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']) +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: - """ - Extract uuid from partition manifest - """ - 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]; + if args["sip_count"] > args["sip_max_count"]: + 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 - # uuid_hex is a list of four hex string values - if len(uuid_hex) != 4: - print("ERROR: malformed UUID") - exit(-1) +@SpSetupActions.sp_action +def gen_fiptool_args(sp_layout, sp, args :dict): + ''' Generate arguments for the FIP Tool. ''' + if "uuid" in sp_layout[sp]: + # Extract the UUID from the JSON file if the SP entry has a 'uuid' field + uuid_std = uuid.UUID(data[key]['uuid']) + else: + with open(get_sp_manifest_full_path(sp_layout[sp], args), "r") as pm_f: + uuid_lines = [l for l in pm_f if 'uuid' in l] + assert(len(uuid_lines) is 1) + # The uuid field in SP manifest is the little endian representation + # mapped to arguments as described in SMCCC section 5.3. + # Convert each unsigned integer value to a big endian representation + # required by fiptool. + uuid_parsed = re.findall("0x([0-9a-f]+)", uuid_lines[0]) + y = list(map(bytearray.fromhex, uuid_parsed)) + z = [int.from_bytes(i, byteorder='little', signed=False) for i in y] + 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 - # The uuid field in SP manifest is the little endian representation - # mapped to arguments as described in SMCCC section 5.3. - # Convert each unsigned integer value to a big endian representation - # required by fiptool. - y=list(map(bytearray.fromhex, uuid_hex)) - z=(int.from_bytes(y[0], byteorder='little', signed=False), - int.from_bytes(y[1], byteorder='little', signed=False), - 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}') +def init_sp_actions(sys): + sp_layout_file = os.path.abspath(sys.argv[2]) + with open(sp_layout_file) as json_file: + 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 - """ - Append FIP_ARGS - """ - out_file.write("FIP_ARGS += --blob uuid=" + str(uuid_std) + ",file=" + dst + "\n") - - """ - Append CRT_ARGS - """ - - out_file.write("CRT_ARGS += --sp-pkg" + str(pkg_num) + " " + dst + "\n") - out_file.write("\n") +if __name__ == "__main__": + args, sp_layout = init_sp_actions(sys) + SpSetupActions.run_actions(sp_layout, args) From 2e82874cc9b7922e000dd4d7718e3153e347b1d7 Mon Sep 17 00:00:00 2001 From: J-Alves Date: Mon, 21 Mar 2022 16:27:56 +0000 Subject: [PATCH 3/6] feat(sptool): python version of the sptool To cope with the changes/design decisions in the implementation of boot protocol, from FF-A v1.1 specification in the S-EL2 SPM, we have changed the format of the sp pkg header. These changes need to be reflected in the sptool, used for packaging the SP binary, and the SP's FF-A manifest. Now the SP pkg can contain the boot information blob as defined by the FF-A specification. To cater for these changes, bring to the TF-A project an equivalent to the tool used in the Hafnium project. Signed-off-by: J-Alves Change-Id: I046f5d6e3c2ef0ba6c87f65302e127dedef34c28 --- tools/sptool/sptool.py | 145 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100755 tools/sptool/sptool.py diff --git a/tools/sptool/sptool.py b/tools/sptool/sptool.py new file mode 100755 index 000000000..ae7df92c1 --- /dev/null +++ b/tools/sptool/sptool.py @@ -0,0 +1,145 @@ +#!/usr/bin/python3 +# Copyright (c) 2022, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +# +# Copyright 2022 The Hafnium Authors. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://opensource.org/licenses/BSD-3-Clause. + +""" +Script which generates a Secure Partition package. +https://trustedfirmware-a.readthedocs.io/en/latest/components/secure-partition-manager.html#secure-partition-packages +""" + +import argparse +from collections import namedtuple +import sys +from shutil import copyfileobj +import os + +HF_PAGE_SIZE = 0x1000 # bytes +HEADER_ELEMENT_BYTES = 4 # bytes +MANIFEST_IMAGE_SPLITTER=':' +PM_OFFSET_DEFAULT = "0x1000" +IMG_OFFSET_DEFAULT = "0x4000" + +def split_dtb_bin(i : str): + return i.split(MANIFEST_IMAGE_SPLITTER) + +def align_to_page(n): + return HF_PAGE_SIZE * \ + (round(n / HF_PAGE_SIZE) + \ + (1 if n % HF_PAGE_SIZE else 0)) + +def to_bytes(value): + return int(value).to_bytes(HEADER_ELEMENT_BYTES, 'little') + +class SpPkg: + def __init__(self, pm_path : str, img_path : str, pm_offset: int, + img_offset: int): + if not os.path.isfile(pm_path) or not os.path.isfile(img_path): + raise Exception(f"Parameters should be path. \ + manifest: {pm_path}; img: {img_path}") + self.pm_path = pm_path + self.img_path = img_path + self._SpPkgHeader = namedtuple("SpPkgHeader", + ("magic", "version", + "pm_offset", "pm_size", + "img_offset", "img_size")) + + if pm_offset >= img_offset: + raise ValueError("pm_offset must be smaller than img_offset") + + is_hfpage_aligned = lambda val : val % HF_PAGE_SIZE == 0 + if not is_hfpage_aligned(pm_offset) or not is_hfpage_aligned(img_offset): + raise ValueError(f"Offsets provided need to be page aligned: pm-{pm_offset}, img-{img_offset}") + + if img_offset - pm_offset < self.pm_size: + raise ValueError(f"pm_offset and img_offset do not fit the specified file:{pm_path})") + + self.pm_offset = pm_offset + self.img_offset = img_offset + + def __str__(self): + return \ + f'''--SP package Info-- + header:{self.header} + pm: {self.pm_path} + img: {self.img_path} + ''' + + @property + def magic(self): + return "SPKG".encode() + + @property + def version(self): + return 0x2 + + @property + def pm_size(self): + return os.path.getsize(self.pm_path) + + @property + def img_size(self): + return os.path.getsize(self.img_path) + + @property + def header(self): + return self._SpPkgHeader( + self.magic, + self.version, + self.pm_offset, + self.pm_size, + self.img_offset, + self.img_size) + + @property + def header_size(self): + return len(self._SpPkgHeader._fields) + + def generate(self, f_out : str): + with open(f_out, "wb+") as output: + for h in self.header: + to_write = h if type(h) is bytes else to_bytes(h) + output.write(to_write) + output.seek(self.pm_offset) + with open(self.pm_path, "rb") as pm: + copyfileobj(pm, output) + output.seek(self.img_offset) + with open(self.img_path, "rb") as img: + copyfileobj(img, output) + +def Main(): + parser = argparse.ArgumentParser() + parser.add_argument("-i", required=True, + help="path to partition's image and manifest separated by a colon.") + parser.add_argument("--pm-offset", required=False, default=PM_OFFSET_DEFAULT, + help="set partitition manifest offset.") + parser.add_argument("--img-offset", required=False, default=IMG_OFFSET_DEFAULT, + help="set partition image offset.") + parser.add_argument("-o", required=True, help="set output file path.") + parser.add_argument("-v", required=False, action="store_true", + help="print package information.") + args = parser.parse_args() + + if not os.path.exists(os.path.dirname(args.o)): + raise Exception("Provide a valid output file path!\n") + + image_path, manifest_path = split_dtb_bin(args.i) + pm_offset = int(args.pm_offset, 0) + img_offset = int(args.img_offset, 0) + pkg = SpPkg(manifest_path, image_path, pm_offset, img_offset) + pkg.generate(args.o) + + if args.v is True: + print(pkg) + + return 0 + +if __name__ == "__main__": + sys.exit(Main()) From 822c72791f791d26e233df0c15a655c3dbd8b117 Mon Sep 17 00:00:00 2001 From: J-Alves Date: Tue, 22 Mar 2022 16:28:51 +0000 Subject: [PATCH 4/6] feat(sptool): use python version of sptool Change-Id: I567ef0b977c69c38323740a592dd9451e261a407 Signed-off-by: J-Alves --- .gitignore | 1 + Makefile | 5 ++--- tools/sptool/sp_mk_generator.py | 30 +++++++++++++++++++++--------- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index f52465811..3b0a5ce63 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ tools/stm32image/stm32image tools/stm32image/stm32image.exe tools/sptool/sptool tools/sptool/sptool.exe +tools/sptool/__pycache__/ # GNU GLOBAL files GPATH diff --git a/Makefile b/Makefile index 0f045e541..ac4624fb1 100644 --- a/Makefile +++ b/Makefile @@ -918,7 +918,7 @@ FIPTOOL ?= ${FIPTOOLPATH}/fiptool${BIN_EXT} # Variables for use with sptool SPTOOLPATH ?= tools/sptool -SPTOOL ?= ${SPTOOLPATH}/sptool${BIN_EXT} +SPTOOL ?= ${SPTOOLPATH}/sptool.py SP_MK_GEN ?= ${SPTOOLPATH}/sp_mk_generator.py # Variables for use with ROMLIB @@ -1336,8 +1336,7 @@ endif ifeq (${NEED_SP_PKG},yes) $(BUILD_PLAT)/sp_gen.mk: ${SP_MK_GEN} ${SP_LAYOUT_FILE} | ${BUILD_PLAT} ${Q}${PYTHON} "$<" "$@" $(filter-out $<,$^) $(BUILD_PLAT) ${COT} -sp: $(SPTOOL) $(DTBS) $(BUILD_PLAT)/sp_gen.mk - ${Q}$(SPTOOL) $(SPTOOL_ARGS) +sp: $(DTBS) $(BUILD_PLAT)/sp_gen.mk $(SP_PKGS) @${ECHO_BLANK_LINE} @echo "Built SP Images successfully" @${ECHO_BLANK_LINE} diff --git a/tools/sptool/sp_mk_generator.py b/tools/sptool/sp_mk_generator.py index 2cc96cb21..f4045d340 100644 --- a/tools/sptool/sp_mk_generator.py +++ b/tools/sptool/sp_mk_generator.py @@ -46,7 +46,6 @@ A typical SP_LAYOUT_FILE file will look like } """ - import json import os import re @@ -59,7 +58,6 @@ UUID_LEN = 4 # Some helper functions to access args propagated to the action functions in # SpSetupActions framework. - 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'.") @@ -129,15 +127,29 @@ def gen_fdt_sources(sp_layout, sp, args :dict): 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) +def gen_sptool_args(sp_layout, sp, args :dict): + ''' Generate Sp Pkgs rules. ''' sp_pkg = get_sp_pkg(sp, args) - sp_dtb_name = os.path.basename(sp_layout[sp]["pm"])[:-1] + "b" + sp_dtb_name = os.path.basename(get_file_from_layout(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) + + # Do not generate rule if already there. + if is_line_in_sp_gen(f'{sp_pkg}:', args): + return args + write_to_sp_mk_gen(f"SP_PKGS += {sp_pkg}\n", args) + + sptool_args = f" -i {get_sp_img_full_path(sp_layout[sp], args)}:{sp_dtb}" + pm_offset = get_pm_offset(sp_layout[sp]) + sptool_args += f" --pm-offset {pm_offset}" if pm_offset is not None else "" + image_offset = get_image_offset(sp_layout[sp]) + sptool_args += f" --img-offset {image_offset}" if image_offset is not None else "" + sptool_args += f" -o {sp_pkg}" + sppkg_rule = f''' +{sp_pkg}: +\t$(Q)echo Generating {sp_pkg} +\t$(Q)$(PYTHON) $(SPTOOL) {sptool_args} +''' + write_to_sp_mk_gen(sppkg_rule, args) return args @SpSetupActions.sp_action(global_action=True, exec_order=1) From f4ec47613fef8db8037195147dc2ac6fb6f154ff Mon Sep 17 00:00:00 2001 From: J-Alves Date: Tue, 22 Mar 2022 19:17:44 +0000 Subject: [PATCH 5/6] feat(sptool): delete c version of the sptool Change-Id: I224762ef66624c78dd87729dac80b2c956ee50ba Signed-off-by: J-Alves --- .gitignore | 2 - Makefile | 5 - include/tools_share/sptool.h | 25 --- tools/sptool/sptool.c | 360 ----------------------------------- 4 files changed, 392 deletions(-) delete mode 100644 include/tools_share/sptool.h delete mode 100644 tools/sptool/sptool.c diff --git a/.gitignore b/.gitignore index 3b0a5ce63..b005fab38 100644 --- a/.gitignore +++ b/.gitignore @@ -30,8 +30,6 @@ tools/amlogic/doimage tools/stm32image/*.o tools/stm32image/stm32image tools/stm32image/stm32image.exe -tools/sptool/sptool -tools/sptool/sptool.exe tools/sptool/__pycache__/ # GNU GLOBAL files diff --git a/Makefile b/Makefile index ac4624fb1..434dce671 100644 --- a/Makefile +++ b/Makefile @@ -1376,7 +1376,6 @@ else # to pass the gnumake flags to nmake. ${Q}set MAKEFLAGS= && ${MSVC_NMAKE} /nologo /f ${FIPTOOLPATH}/Makefile.msvc FIPTOOLPATH=$(subst /,\,$(FIPTOOLPATH)) FIPTOOL=$(subst /,\,$(FIPTOOL)) realclean endif - ${Q}${MAKE} --no-print-directory -C ${SPTOOLPATH} clean ${Q}${MAKE} PLAT=${PLAT} --no-print-directory -C ${CRTTOOLPATH} realclean ${Q}${MAKE} PLAT=${PLAT} --no-print-directory -C ${ENCTOOLPATH} realclean ${Q}${MAKE} --no-print-directory -C ${ROMLIBPATH} clean @@ -1470,10 +1469,6 @@ else ${Q}set MAKEFLAGS= && ${MSVC_NMAKE} /nologo /f ${FIPTOOLPATH}/Makefile.msvc FIPTOOLPATH=$(subst /,\,$(FIPTOOLPATH)) FIPTOOL=$(subst /,\,$(FIPTOOL)) endif -sptool: ${SPTOOL} -${SPTOOL}: FORCE - ${Q}${MAKE} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" SPTOOL=${SPTOOL} --no-print-directory -C ${SPTOOLPATH} - romlib.bin: libraries FORCE ${Q}${MAKE} PLAT_DIR=${PLAT_DIR} BUILD_PLAT=${BUILD_PLAT} ENABLE_BTI=${ENABLE_BTI} ARM_ARCH_MINOR=${ARM_ARCH_MINOR} INCLUDES='${INCLUDES}' DEFINES='${DEFINES}' --no-print-directory -C ${ROMLIBPATH} all diff --git a/include/tools_share/sptool.h b/include/tools_share/sptool.h deleted file mode 100644 index 53668e09c..000000000 --- a/include/tools_share/sptool.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2018-2020, Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef SPTOOL_H -#define SPTOOL_H - -#include - -/* 4 Byte magic name "SPKG" */ -#define SECURE_PARTITION_MAGIC 0x474B5053 - -/* Header for a secure partition package. */ -struct sp_pkg_header { - uint32_t magic; - uint32_t version; - uint32_t pm_offset; - uint32_t pm_size; - uint32_t img_offset; - uint32_t img_size; -}; - -#endif /* SPTOOL_H */ diff --git a/tools/sptool/sptool.c b/tools/sptool/sptool.c deleted file mode 100644 index 38baa2cd9..000000000 --- a/tools/sptool/sptool.c +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright (c) 2018-2020, Arm Limited. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "sptool.h" - -#define PAGE_SIZE 4096 - -/* - * Entry describing Secure Partition package. - */ -struct sp_pkg_info { - /* Location of the files in the host's RAM. */ - void *img_data, *pm_data; - - /* Size of the files. */ - uint32_t img_size, pm_size; - - /* Location of the binary files inside the package output file */ - uint32_t img_offset, pm_offset; -}; - -/* - * List of input provided by user - */ -struct arg_list { - char *usr_input; - struct arg_list *next; -}; - -/* Align an address to a power-of-two boundary. */ -static unsigned int align_to(unsigned int address, unsigned int boundary) -{ - unsigned int mask = boundary - 1U; - - if ((address & mask) != 0U) - return (address + boundary) & ~mask; - else - return address; -} - -/* Allocate a memory area of 'size' bytes and zero it. */ -static void *xzalloc(size_t size, const char *msg) -{ - void *d; - - d = malloc(size); - if (d == NULL) { - fprintf(stderr, "error: malloc: %s\n", msg); - exit(1); - } - - memset(d, 0, size); - - return d; -} - -/* - * Write 'size' bytes from 'buf' into the specified file stream. - * Exit the program on error. - */ -static void xfwrite(void *buf, size_t size, FILE *fp) -{ - if (fwrite(buf, 1, size, fp) != size) { - fprintf(stderr, "error: Failed to write to output file.\n"); - exit(1); - } -} - -/* - * Set the file position indicator for the specified file stream. - * Exit the program on error. - */ -static void xfseek(FILE *fp, long offset, int whence) -{ - if (fseek(fp, offset, whence) != 0) { - fprintf(stderr, "error: Failed to set file to offset 0x%lx (%d).\n", - offset, whence); - perror(NULL); - exit(1); - } -} - -/* - * Free SP package structure - */ -static void cleanup(struct sp_pkg_info *sp) -{ - - if (sp != NULL) { - if (sp->img_data != NULL) { - free(sp->img_data); - } - - if (sp->pm_data != NULL) { - free(sp->pm_data); - } - - free(sp); - - } -} - -/* - * Free argument list structure - */ -static void freelist(struct arg_list *head) -{ - struct arg_list *tmp; - - while (head != NULL) { - tmp = head; - head = head->next; - free(tmp); - } -} - -/* - * Append user inputs in argument list structure - */ -static void append_user_input(struct arg_list **head, char *args) -{ - struct arg_list *tmp = *head; - - if (tmp == NULL) { - tmp = xzalloc(sizeof(struct arg_list), - "Failed to allocate arg_list struct"); - tmp->usr_input = args; - *head = tmp; - } else { - while (tmp->next != NULL) { - tmp = tmp->next; - } - tmp->next = xzalloc(sizeof(struct arg_list), - "Failed to allocate arg_list struct"); - tmp = tmp->next; - tmp->usr_input = args; - } -} - -/* - * Allocate a buffer big enough to store the content of the specified file and - * load the file into it. Fill 'size' with the file size. Exit the program on - * error. - */ -static void load_file(const char *path, void **ptr, uint32_t *size) -{ - FILE *f = fopen(path, "rb"); - if (f == NULL) { - fprintf(stderr, "error: %s couldn't be opened.\n", path); - exit(1); - } - - xfseek(f, 0, SEEK_END); - *size = ftell(f); - if (*size == 0) { - fprintf(stderr, "error: Size of %s is 0\n", path); - exit(1); - } - - rewind(f); - - *ptr = malloc(*size); - if (*ptr == NULL) { - fprintf(stderr, "error: Not enough memory to load %s\n", path); - exit(1); - } - - if (fread(*ptr, *size, 1, f) != 1) { - fprintf(stderr, "error: Couldn't read %s\n", path); - exit(1); - } - - fclose(f); -} - -/* - * Parse the string containing input payloads and fill in the - * SP Package data structure. - */ -static void load_sp_pm(char *path, struct sp_pkg_info **sp_out) -{ - struct sp_pkg_info *sp_pkg; - - char *split_mark = strstr(path, ":"); - - *split_mark = '\0'; - - char *sp_path = path; - char *pm_path = split_mark + 1; - - sp_pkg = xzalloc(sizeof(struct sp_pkg_info), - "Failed to allocate sp_pkg_info struct"); - - load_file(pm_path, &sp_pkg->pm_data, &sp_pkg->pm_size); - printf("\nLoaded SP Manifest file %s (%u bytes)\n", pm_path, sp_pkg->pm_size); - - load_file(sp_path, &sp_pkg->img_data, &sp_pkg->img_size); - printf("Loaded SP Image file %s (%u bytes)\n", sp_path, sp_pkg->img_size); - - *sp_out = sp_pkg; -} - -/* - * Write SP package data structure into output file. - */ -static void output_write(const char *path, struct sp_pkg_info *sp, bool header) -{ - struct sp_pkg_header sp_header_info; - unsigned int file_ptr = 0; - - FILE *f = fopen(path, "wb"); - if (f == NULL) { - fprintf(stderr, "error: Failed to open %s\n", path); - exit(1); - } - - /* Reserve Header size */ - if (header) { - file_ptr = sizeof(struct sp_pkg_header); - } - - /* Save partition manifest */ - xfseek(f, file_ptr, SEEK_SET); - printf("Writing SP Manifest at offset 0x%x (%u bytes)\n", - file_ptr, sp->pm_size); - - sp->pm_offset = file_ptr; - xfwrite(sp->pm_data, sp->pm_size, f); - - /* Save partition image aligned to Page size */ - file_ptr = align_to((sp->pm_offset + sp->pm_size), PAGE_SIZE); - xfseek(f, file_ptr, SEEK_SET); - printf("Writing SP Image at offset 0x%x (%u bytes)\n", - file_ptr, sp->img_size); - - sp->img_offset = file_ptr; - xfwrite(sp->img_data, sp->img_size, f); - - /* Finally, write header, if needed */ - if (header) { - sp_header_info.magic = SECURE_PARTITION_MAGIC; - sp_header_info.version = 0x1; - sp_header_info.img_offset = sp->img_offset; - sp_header_info.img_size = sp->img_size; - sp_header_info.pm_offset = sp->pm_offset; - sp_header_info.pm_size = sp->pm_size; - - xfseek(f, 0, SEEK_SET); - - printf("Writing package header\n"); - - xfwrite(&sp_header_info, sizeof(struct sp_pkg_header), f); - } - - /* All information has been written now */ - printf("\nsptool: Built Secure Partition blob %s\n", path); - - fclose(f); -} - -static void usage(void) -{ - printf("usage: sptool "); -#ifdef VERSION - printf(VERSION); -#else - /* If built from sptool directory, VERSION is not set. */ - printf("version unknown"); -#endif - printf(" []\n\n"); - - printf("This tool takes as input set of image binary files and the\n" - "partition manifest blobs as input and generates set of\n" - "output package files\n" - "Usage example: sptool -i sp1.bin:sp1.dtb -o sp1.pkg\n" - " -i sp2.bin:sp2.dtb -o sp2.pkg ...\n\n"); - printf("Commands supported:\n"); - printf(" -o Set output file path.\n"); - printf(" -i Add Secure Partition image and\n" - " Manifest blob (specified in two paths\n" - " separated by a colon).\n"); - printf(" -n Generate package without header\n"); - printf(" -h Show this message.\n"); - exit(1); -} - -int main(int argc, char *argv[]) -{ - struct sp_pkg_info *sp_pkg = NULL; - struct arg_list *in_head = NULL; - struct arg_list *out_head = NULL; - struct arg_list *in_list = NULL; - struct arg_list *out_list = NULL; - unsigned int match_counter = 0; - bool need_header = true; - - int ch; - - if (argc <= 1) { - fprintf(stderr, "error: File paths must be provided.\n\n"); - usage(); - return 1; - } - - while ((ch = getopt(argc, argv, "hni:o:")) != -1) { - switch (ch) { - case 'i': - append_user_input(&in_head, optarg); - match_counter++; - break; - case 'o': - append_user_input(&out_head, optarg); - match_counter--; - break; - case 'n': - need_header = false; - break; - case 'h': - default: - usage(); - } - } - - if (match_counter) { - fprintf(stderr, "error: Input/Output count mismatch.\n\n"); - freelist(in_head); - freelist(out_head); - usage(); - return 1; - } - - in_list = in_head; - out_list = out_head; - while (in_list != NULL) { - load_sp_pm(in_list->usr_input, &sp_pkg); - output_write(out_list->usr_input, sp_pkg, need_header); - in_list = in_list->next; - out_list = out_list->next; - } - - argc -= optind; - argv += optind; - - cleanup(sp_pkg); - freelist(in_head); - freelist(out_head); - - return 0; -} From ca0fdbd8e0d625ece0f87ca16eacabf13db70921 Mon Sep 17 00:00:00 2001 From: J-Alves Date: Fri, 8 Apr 2022 09:52:26 +0100 Subject: [PATCH 6/6] fix(sptool): update Optee FF-A manifest Change the OPTEE FF-A manifest to comply with changes to the sp pkg [1]. The sptool packs the image at the default offset of 0x4000, if it is not provided in the arguments. [1] https://review.trustedfirmware.org/c/TF-A/trusted-firmware-a/+/14507 Signed-off-by: J-Alves Change-Id: I647950410114f7fc24926696212bb7f8101390ac --- plat/arm/board/fvp/fdts/optee_sp_manifest.dts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plat/arm/board/fvp/fdts/optee_sp_manifest.dts b/plat/arm/board/fvp/fdts/optee_sp_manifest.dts index b8033404f..27f4724c3 100644 --- a/plat/arm/board/fvp/fdts/optee_sp_manifest.dts +++ b/plat/arm/board/fvp/fdts/optee_sp_manifest.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021, Arm Limited. All rights reserved. + * Copyright (c) 2020-2022, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -22,7 +22,7 @@ exception-level = <2>; /* S-EL1 */ execution-state = <0>; /* AARCH64 */ load-address = <0x6280000>; - entrypoint-offset = <0x1000>; + entrypoint-offset = <0x4000>; xlat-granule = <0>; /* 4KiB */ boot-order = <0>; messaging-method = <0x3>; /* Direct request/response supported. */