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