entry.py 10 KB

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