spactions.py 5.6 KB

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