diff options
Diffstat (limited to 'tools/sptool/spactions.py')
-rw-r--r-- | tools/sptool/spactions.py | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/tools/sptool/spactions.py b/tools/sptool/spactions.py new file mode 100644 index 0000000..ff28ebb --- /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) |