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