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