entry.py 7.3 KB

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