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