1#!/usr/bin/python3 2# Copyright (c) 2022, Arm Limited. All rights reserved. 3# 4# SPDX-License-Identifier: BSD-3-Clause 5''' 6This is a python module for defining and executing SP setup actions, targeting 7a system deploying an SPM implementation. 8Each action consists of a function, that processes the SP layout json file and 9other provided arguments. 10At the core of this is the SpSetupActions which provides a means to register 11the functions into a table of actions, and execute them all when invoking 12SpSetupActions.run_actions. 13Registering the function is done by using the decorator '@SpSetupActions.sp_action' 14at function definition. 15 16Functions can be called: 17- once only, or per SP defined in the SP layout file; 18- following an order, from lowest to highest of their execution order. 19More information in the doc comments below. 20''' 21import bisect 22 23DEFAULT_ACTION_ORDER = 100 24 25class _ConfiguredAction: 26 """ 27 Wraps action function with its configuration. 28 """ 29 def __init__(self, action, exec_order=DEFAULT_ACTION_ORDER, global_action=True, log_calls = False): 30 self.exec_order = exec_order 31 self.__name__ = action.__name__ 32 def logged_action(action): 33 def inner_logged_action(sp_layout, sp, args :dict): 34 print(f"Calling {action.__name__} -> {sp}") 35 return action(sp_layout, sp, args) 36 return inner_logged_action 37 self.action = logged_action(action) if log_calls is True else action 38 self.global_action = global_action 39 40 def __lt__(self, other): 41 """ 42 To allow for ordered inserts in a list of actions. 43 """ 44 return self.exec_order < other.exec_order 45 46 def __call__(self, sp_layout, sp, args :dict): 47 """ 48 Calls action function. 49 """ 50 return self.action(sp_layout, sp, args) 51 52 def __repr__(self) -> str: 53 """ 54 Pretty format to show debug information about the action. 55 """ 56 return f"func: {self.__name__}; global:{self.global_action}; exec_order: {self.exec_order}" 57 58class SpSetupActions: 59 actions = [] 60 61 def sp_action(in_action = None, global_action = False, log_calls=False, exec_order=DEFAULT_ACTION_ORDER): 62 """ 63 Function decorator that registers and configures action. 64 65 :param in_action - function to register 66 :param global_action - make the function global, i.e. make it be 67 only called once. 68 :param log_calls - at every call to action, a useful log will be printed. 69 :param exec_order - action's calling order. 70 """ 71 def append_action(action): 72 action = _ConfiguredAction(action, exec_order, global_action, log_calls) 73 bisect.insort(SpSetupActions.actions, action) 74 return action 75 if in_action is not None: 76 return append_action(in_action) 77 return append_action 78 79 def run_actions(sp_layout: dict, args: dict, verbose=False): 80 """ 81 Executes all actions in accordance to their registering configuration: 82 - If set as "global" it will be called once. 83 - Actions are called respecting the order established by their "exec_order" field. 84 85 :param sp_layout - dictionary containing the SP layout information. 86 :param args - arguments to be propagated through the call of actions. 87 :param verbose - prints actions information in order of execution. 88 """ 89 args["called"] = [] # for debug purposes 90 def append_called(action, sp, args :dict): 91 args["called"].append(f"{action.__name__} -> {sp}") 92 return args 93 94 for action in SpSetupActions.actions: 95 if verbose: 96 print(f"Calling {action}") 97 if action.global_action: 98 scope = "global" 99 args = action(sp_layout, scope, args) 100 args = append_called(action, scope, args) 101 else: 102 # Functions that are not global called for each SP defined in 103 # the SP layout. 104 for sp in sp_layout.keys(): 105 args = action(sp_layout, sp, args) 106 args = append_called(action, sp, args) 107 108if __name__ == "__main__": 109 # Executing this module will have the following test code/playground executed 110 sp_layout = { 111 "partition1" : { 112 "boot-info": True, 113 "image": { 114 "file": "partition.bin", 115 "offset":"0x2000" 116 }, 117 "pm": { 118 "file": "cactus.dts", 119 "offset":"0x1000" 120 }, 121 "owner": "SiP" 122 }, 123 "partition2" : { 124 "image": "partition.bin", 125 "pm": "cactus-secondary.dts", 126 "owner": "Plat" 127 }, 128 "partition3" : { 129 "image": "partition.bin", 130 "pm": "cactus-tertiary.dts", 131 "owner": "Plat" 132 }, 133 "partition4" : { 134 "image": "ivy.bin", 135 "pm": "ivy.dts", 136 "owner": "Plat" 137 } 138 } 139 140 #Example of how to use this module 141 @SpSetupActions.sp_action(global_action=True) 142 def my_action1(sp_layout, _, args :dict): 143 print(f"inside function my_action1{sp_layout}\n\n args:{args})") 144 return args # Always return args in action function. 145 @SpSetupActions.sp_action(exec_order=1) 146 def my_action2(sp_layout, sp_name, args :dict): 147 print(f"inside function my_action2; SP: {sp_name} {sp_layout} args:{args}") 148 return args 149 150 # Example arguments to be propagated through the functions. 151 # 'args' can be extended in the action functions. 152 args = dict() 153 args["arg1"] = 0xEEE 154 args["arg2"] = 0xFF 155 SpSetupActions.run_actions(sp_layout, args) 156