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