entry.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. # SPDX-License-Identifier: GPL-2.0+
  2. # Copyright (c) 2016 Google, Inc
  3. #
  4. # Base class for all entries
  5. #
  6. # importlib was introduced in Python 2.7 but there was a report of it not
  7. # working in 2.7.12, so we work around this:
  8. # http://lists.denx.de/pipermail/u-boot/2016-October/269729.html
  9. try:
  10. import importlib
  11. have_importlib = True
  12. except:
  13. have_importlib = False
  14. import fdt_util
  15. import os
  16. import sys
  17. import tools
  18. modules = {}
  19. our_path = os.path.dirname(os.path.realpath(__file__))
  20. class Entry(object):
  21. """An Entry in the section
  22. An entry corresponds to a single node in the device-tree description
  23. of the section. Each entry ends up being a part of the final section.
  24. Entries can be placed either right next to each other, or with padding
  25. between them. The type of the entry determines the data that is in it.
  26. This class is not used by itself. All entry objects are subclasses of
  27. Entry.
  28. Attributes:
  29. section: The section containing this entry
  30. node: The node that created this entry
  31. pos: Absolute position of entry within the section, None if not known
  32. size: Entry size in bytes, None if not known
  33. contents_size: Size of contents in bytes, 0 by default
  34. align: Entry start position alignment, or None
  35. align_size: Entry size alignment, or None
  36. align_end: Entry end position alignment, or None
  37. pad_before: Number of pad bytes before the contents, 0 if none
  38. pad_after: Number of pad bytes after the contents, 0 if none
  39. data: Contents of entry (string of bytes)
  40. """
  41. def __init__(self, section, etype, node, read_node=True):
  42. self.section = section
  43. self.etype = etype
  44. self._node = node
  45. self.pos = None
  46. self.size = None
  47. self.contents_size = 0
  48. self.align = None
  49. self.align_size = None
  50. self.align_end = None
  51. self.pad_before = 0
  52. self.pad_after = 0
  53. self.pos_unset = False
  54. if read_node:
  55. self.ReadNode()
  56. @staticmethod
  57. def Create(section, node, etype=None):
  58. """Create a new entry for a node.
  59. Args:
  60. section: Image object containing this node
  61. node: Node object containing information about the entry to create
  62. etype: Entry type to use, or None to work it out (used for tests)
  63. Returns:
  64. A new Entry object of the correct type (a subclass of Entry)
  65. """
  66. if not etype:
  67. etype = fdt_util.GetString(node, 'type', node.name)
  68. # Convert something like 'u-boot@0' to 'u_boot' since we are only
  69. # interested in the type.
  70. module_name = etype.replace('-', '_')
  71. if '@' in module_name:
  72. module_name = module_name.split('@')[0]
  73. module = modules.get(module_name)
  74. # Also allow entry-type modules to be brought in from the etype directory.
  75. # Import the module if we have not already done so.
  76. if not module:
  77. old_path = sys.path
  78. sys.path.insert(0, os.path.join(our_path, 'etype'))
  79. try:
  80. if have_importlib:
  81. module = importlib.import_module(module_name)
  82. else:
  83. module = __import__(module_name)
  84. except ImportError:
  85. raise ValueError("Unknown entry type '%s' in node '%s'" %
  86. (etype, node.path))
  87. finally:
  88. sys.path = old_path
  89. modules[module_name] = module
  90. # Call its constructor to get the object we want.
  91. obj = getattr(module, 'Entry_%s' % module_name)
  92. return obj(section, etype, node)
  93. def ReadNode(self):
  94. """Read entry information from the node
  95. This reads all the fields we recognise from the node, ready for use.
  96. """
  97. self.pos = fdt_util.GetInt(self._node, 'pos')
  98. self.size = fdt_util.GetInt(self._node, 'size')
  99. self.align = fdt_util.GetInt(self._node, 'align')
  100. if tools.NotPowerOfTwo(self.align):
  101. raise ValueError("Node '%s': Alignment %s must be a power of two" %
  102. (self._node.path, self.align))
  103. self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
  104. self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
  105. self.align_size = fdt_util.GetInt(self._node, 'align-size')
  106. if tools.NotPowerOfTwo(self.align_size):
  107. raise ValueError("Node '%s': Alignment size %s must be a power "
  108. "of two" % (self._node.path, self.align_size))
  109. self.align_end = fdt_util.GetInt(self._node, 'align-end')
  110. self.pos_unset = fdt_util.GetBool(self._node, 'pos-unset')
  111. def ObtainContents(self):
  112. """Figure out the contents of an entry.
  113. Returns:
  114. True if the contents were found, False if another call is needed
  115. after the other entries are processed.
  116. """
  117. # No contents by default: subclasses can implement this
  118. return True
  119. def Pack(self, pos):
  120. """Figure out how to pack the entry into the section
  121. Most of the time the entries are not fully specified. There may be
  122. an alignment but no size. In that case we take the size from the
  123. contents of the entry.
  124. If an entry has no hard-coded position, it will be placed at @pos.
  125. Once this function is complete, both the position and size of the
  126. entry will be know.
  127. Args:
  128. Current section position pointer
  129. Returns:
  130. New section position pointer (after this entry)
  131. """
  132. if self.pos is None:
  133. if self.pos_unset:
  134. self.Raise('No position set with pos-unset: should another '
  135. 'entry provide this correct position?')
  136. self.pos = tools.Align(pos, self.align)
  137. needed = self.pad_before + self.contents_size + self.pad_after
  138. needed = tools.Align(needed, self.align_size)
  139. size = self.size
  140. if not size:
  141. size = needed
  142. new_pos = self.pos + size
  143. aligned_pos = tools.Align(new_pos, self.align_end)
  144. if aligned_pos != new_pos:
  145. size = aligned_pos - self.pos
  146. new_pos = aligned_pos
  147. if not self.size:
  148. self.size = size
  149. if self.size < needed:
  150. self.Raise("Entry contents size is %#x (%d) but entry size is "
  151. "%#x (%d)" % (needed, needed, self.size, self.size))
  152. # Check that the alignment is correct. It could be wrong if the
  153. # and pos or size values were provided (i.e. not calculated), but
  154. # conflict with the provided alignment values
  155. if self.size != tools.Align(self.size, self.align_size):
  156. self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
  157. (self.size, self.size, self.align_size, self.align_size))
  158. if self.pos != tools.Align(self.pos, self.align):
  159. self.Raise("Position %#x (%d) does not match align %#x (%d)" %
  160. (self.pos, self.pos, self.align, self.align))
  161. return new_pos
  162. def Raise(self, msg):
  163. """Convenience function to raise an error referencing a node"""
  164. raise ValueError("Node '%s': %s" % (self._node.path, msg))
  165. def GetPath(self):
  166. """Get the path of a node
  167. Returns:
  168. Full path of the node for this entry
  169. """
  170. return self._node.path
  171. def GetData(self):
  172. return self.data
  173. def GetPositions(self):
  174. return {}
  175. def SetPositionSize(self, pos, size):
  176. self.pos = pos
  177. self.size = size
  178. def ProcessContents(self):
  179. pass
  180. def WriteSymbols(self, section):
  181. """Write symbol values into binary files for access at run time
  182. Args:
  183. section: Section containing the entry
  184. """
  185. pass