entry.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  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.contents_size = 0
  50. self.align = None
  51. self.align_size = None
  52. self.align_end = None
  53. self.pad_before = 0
  54. self.pad_after = 0
  55. self.pos_unset = False
  56. if read_node:
  57. self.ReadNode()
  58. @staticmethod
  59. def Create(section, node, etype=None):
  60. """Create a new entry for a node.
  61. Args:
  62. section: Image object containing this node
  63. node: Node object containing information about the entry to create
  64. etype: Entry type to use, or None to work it out (used for tests)
  65. Returns:
  66. A new Entry object of the correct type (a subclass of Entry)
  67. """
  68. if not etype:
  69. etype = fdt_util.GetString(node, 'type', node.name)
  70. # Convert something like 'u-boot@0' to 'u_boot' since we are only
  71. # interested in the type.
  72. module_name = etype.replace('-', '_')
  73. if '@' in module_name:
  74. module_name = module_name.split('@')[0]
  75. module = modules.get(module_name)
  76. # Also allow entry-type modules to be brought in from the etype directory.
  77. # Import the module if we have not already done so.
  78. if not module:
  79. old_path = sys.path
  80. sys.path.insert(0, os.path.join(our_path, 'etype'))
  81. try:
  82. if have_importlib:
  83. module = importlib.import_module(module_name)
  84. else:
  85. module = __import__(module_name)
  86. except ImportError:
  87. raise ValueError("Unknown entry type '%s' in node '%s'" %
  88. (etype, node.path))
  89. finally:
  90. sys.path = old_path
  91. modules[module_name] = module
  92. # Call its constructor to get the object we want.
  93. obj = getattr(module, 'Entry_%s' % module_name)
  94. return obj(section, etype, node)
  95. def ReadNode(self):
  96. """Read entry information from the node
  97. This reads all the fields we recognise from the node, ready for use.
  98. """
  99. self.pos = fdt_util.GetInt(self._node, 'pos')
  100. self.size = fdt_util.GetInt(self._node, 'size')
  101. self.align = fdt_util.GetInt(self._node, 'align')
  102. if tools.NotPowerOfTwo(self.align):
  103. raise ValueError("Node '%s': Alignment %s must be a power of two" %
  104. (self._node.path, self.align))
  105. self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
  106. self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
  107. self.align_size = fdt_util.GetInt(self._node, 'align-size')
  108. if tools.NotPowerOfTwo(self.align_size):
  109. raise ValueError("Node '%s': Alignment size %s must be a power "
  110. "of two" % (self._node.path, self.align_size))
  111. self.align_end = fdt_util.GetInt(self._node, 'align-end')
  112. self.pos_unset = fdt_util.GetBool(self._node, 'pos-unset')
  113. def SetPrefix(self, prefix):
  114. """Set the name prefix for a node
  115. Args:
  116. prefix: Prefix to set, or '' to not use a prefix
  117. """
  118. if prefix:
  119. self.name = prefix + self.name
  120. def ObtainContents(self):
  121. """Figure out the contents of an entry.
  122. Returns:
  123. True if the contents were found, False if another call is needed
  124. after the other entries are processed.
  125. """
  126. # No contents by default: subclasses can implement this
  127. return True
  128. def Pack(self, pos):
  129. """Figure out how to pack the entry into the section
  130. Most of the time the entries are not fully specified. There may be
  131. an alignment but no size. In that case we take the size from the
  132. contents of the entry.
  133. If an entry has no hard-coded position, it will be placed at @pos.
  134. Once this function is complete, both the position and size of the
  135. entry will be know.
  136. Args:
  137. Current section position pointer
  138. Returns:
  139. New section position pointer (after this entry)
  140. """
  141. if self.pos is None:
  142. if self.pos_unset:
  143. self.Raise('No position set with pos-unset: should another '
  144. 'entry provide this correct position?')
  145. self.pos = tools.Align(pos, self.align)
  146. needed = self.pad_before + self.contents_size + self.pad_after
  147. needed = tools.Align(needed, self.align_size)
  148. size = self.size
  149. if not size:
  150. size = needed
  151. new_pos = self.pos + size
  152. aligned_pos = tools.Align(new_pos, self.align_end)
  153. if aligned_pos != new_pos:
  154. size = aligned_pos - self.pos
  155. new_pos = aligned_pos
  156. if not self.size:
  157. self.size = size
  158. if self.size < needed:
  159. self.Raise("Entry contents size is %#x (%d) but entry size is "
  160. "%#x (%d)" % (needed, needed, self.size, self.size))
  161. # Check that the alignment is correct. It could be wrong if the
  162. # and pos or size values were provided (i.e. not calculated), but
  163. # conflict with the provided alignment values
  164. if self.size != tools.Align(self.size, self.align_size):
  165. self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
  166. (self.size, self.size, self.align_size, self.align_size))
  167. if self.pos != tools.Align(self.pos, self.align):
  168. self.Raise("Position %#x (%d) does not match align %#x (%d)" %
  169. (self.pos, self.pos, self.align, self.align))
  170. return new_pos
  171. def Raise(self, msg):
  172. """Convenience function to raise an error referencing a node"""
  173. raise ValueError("Node '%s': %s" % (self._node.path, msg))
  174. def GetPath(self):
  175. """Get the path of a node
  176. Returns:
  177. Full path of the node for this entry
  178. """
  179. return self._node.path
  180. def GetData(self):
  181. return self.data
  182. def GetPositions(self):
  183. return {}
  184. def SetPositionSize(self, pos, size):
  185. self.pos = pos
  186. self.size = size
  187. def ProcessContents(self):
  188. pass
  189. def WriteSymbols(self, section):
  190. """Write symbol values into binary files for access at run time
  191. Args:
  192. section: Section containing the entry
  193. """
  194. pass
  195. def CheckPosition(self):
  196. """Check that the entry positions are correct
  197. This is used for entries which have extra position requirements (other
  198. than having to be fully inside their section). Sub-classes can implement
  199. this function and raise if there is a problem.
  200. """
  201. pass
  202. def WriteMap(self, fd, indent):
  203. """Write a map of the entry to a .map file
  204. Args:
  205. fd: File to write the map to
  206. indent: Curent indent level of map (0=none, 1=one level, etc.)
  207. """
  208. print('%s%08x %08x %s' % (' ' * indent, self.pos, self.size,
  209. self.name), file=fd)