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