]> git.sur5r.net Git - u-boot/blob - tools/binman/entry.py
binman: Tidy up setting of entry contents
[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 SetPrefix(self, prefix):
134         """Set the name prefix for a node
135
136         Args:
137             prefix: Prefix to set, or '' to not use a prefix
138         """
139         if prefix:
140             self.name = prefix + self.name
141
142     def SetContents(self, data):
143         """Set the contents of an entry
144
145         This sets both the data and content_size properties
146
147         Args:
148             data: Data to set to the contents (string)
149         """
150         self.data = data
151         self.contents_size = len(self.data)
152
153     def ProcessContentsUpdate(self, data):
154         """Update the contens of an entry, after the size is fixed
155
156         This checks that the new data is the same size as the old.
157
158         Args:
159             data: Data to set to the contents (string)
160
161         Raises:
162             ValueError if the new data size is not the same as the old
163         """
164         if len(data) != self.contents_size:
165             self.Raise('Cannot update entry size from %d to %d' %
166                        (len(data), self.contents_size))
167         self.SetContents(data)
168
169     def ObtainContents(self):
170         """Figure out the contents of an entry.
171
172         Returns:
173             True if the contents were found, False if another call is needed
174             after the other entries are processed.
175         """
176         # No contents by default: subclasses can implement this
177         return True
178
179     def Pack(self, pos):
180         """Figure out how to pack the entry into the section
181
182         Most of the time the entries are not fully specified. There may be
183         an alignment but no size. In that case we take the size from the
184         contents of the entry.
185
186         If an entry has no hard-coded position, it will be placed at @pos.
187
188         Once this function is complete, both the position and size of the
189         entry will be know.
190
191         Args:
192             Current section position pointer
193
194         Returns:
195             New section position pointer (after this entry)
196         """
197         if self.pos is None:
198             if self.pos_unset:
199                 self.Raise('No position set with pos-unset: should another '
200                            'entry provide this correct position?')
201             self.pos = tools.Align(pos, self.align)
202         needed = self.pad_before + self.contents_size + self.pad_after
203         needed = tools.Align(needed, self.align_size)
204         size = self.size
205         if not size:
206             size = needed
207         new_pos = self.pos + size
208         aligned_pos = tools.Align(new_pos, self.align_end)
209         if aligned_pos != new_pos:
210             size = aligned_pos - self.pos
211             new_pos = aligned_pos
212
213         if not self.size:
214             self.size = size
215
216         if self.size < needed:
217             self.Raise("Entry contents size is %#x (%d) but entry size is "
218                        "%#x (%d)" % (needed, needed, self.size, self.size))
219         # Check that the alignment is correct. It could be wrong if the
220         # and pos or size values were provided (i.e. not calculated), but
221         # conflict with the provided alignment values
222         if self.size != tools.Align(self.size, self.align_size):
223             self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
224                   (self.size, self.size, self.align_size, self.align_size))
225         if self.pos != tools.Align(self.pos, self.align):
226             self.Raise("Position %#x (%d) does not match align %#x (%d)" %
227                   (self.pos, self.pos, self.align, self.align))
228
229         return new_pos
230
231     def Raise(self, msg):
232         """Convenience function to raise an error referencing a node"""
233         raise ValueError("Node '%s': %s" % (self._node.path, msg))
234
235     def GetPath(self):
236         """Get the path of a node
237
238         Returns:
239             Full path of the node for this entry
240         """
241         return self._node.path
242
243     def GetData(self):
244         return self.data
245
246     def GetPositions(self):
247         return {}
248
249     def SetPositionSize(self, pos, size):
250         self.pos = pos
251         self.size = size
252
253     def ProcessContents(self):
254         pass
255
256     def WriteSymbols(self, section):
257         """Write symbol values into binary files for access at run time
258
259         Args:
260           section: Section containing the entry
261         """
262         pass
263
264     def CheckPosition(self):
265         """Check that the entry positions are correct
266
267         This is used for entries which have extra position requirements (other
268         than having to be fully inside their section). Sub-classes can implement
269         this function and raise if there is a problem.
270         """
271         pass
272
273     def WriteMap(self, fd, indent):
274         """Write a map of the entry to a .map file
275
276         Args:
277             fd: File to write the map to
278             indent: Curent indent level of map (0=none, 1=one level, etc.)
279         """
280         print('%s%08x  %08x  %s' % (' ' * indent, self.pos, self.size,
281                                     self.name), file=fd)