| #!/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) |