39da7f86022b7eb0472a59ac582337df73917258
[oweals/u-boot.git] / tools / binman / etype / 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 # importlib was introduced in Python 2.7 but there was a report of it not
8 # working in 2.7.12, so we work around this:
9 # http://lists.denx.de/pipermail/u-boot/2016-October/269729.html
10 try:
11     import importlib
12     have_importlib = True
13 except:
14     have_importlib = False
15
16 import fdt_util
17 import tools
18
19 modules = {}
20
21 class Entry(object):
22     """An Entry in the image
23
24     An entry corresponds to a single node in the device-tree description
25     of the image. Each entry ends up being a part of the final image.
26     Entries can be placed either right next to each other, or with padding
27     between them. The type of the entry determines the data that is in it.
28
29     This class is not used by itself. All entry objects are subclasses of
30     Entry.
31
32     Attributes:
33         image: The image containing this entry
34         node: The node that created this entry
35         pos: Absolute position of entry within the image, None if not known
36         size: Entry size in bytes, None if not known
37         contents_size: Size of contents in bytes, 0 by default
38         align: Entry start position alignment, or None
39         align_size: Entry size alignment, or None
40         align_end: Entry end position alignment, or None
41         pad_before: Number of pad bytes before the contents, 0 if none
42         pad_after: Number of pad bytes after the contents, 0 if none
43         data: Contents of entry (string of bytes)
44     """
45     def __init__(self, image, etype, node, read_node=True):
46         self.image = image
47         self.etype = etype
48         self._node = node
49         self.pos = None
50         self.size = None
51         self.contents_size = 0
52         self.align = None
53         self.align_size = None
54         self.align_end = None
55         self.pad_before = 0
56         self.pad_after = 0
57         self.pos_unset = False
58         if read_node:
59             self.ReadNode()
60
61     @staticmethod
62     def Create(image, node, etype=None):
63         """Create a new entry for a node.
64
65         Args:
66             image:  Image object containing this node
67             node:   Node object containing information about the entry to create
68             etype:  Entry type to use, or None to work it out (used for tests)
69
70         Returns:
71             A new Entry object of the correct type (a subclass of Entry)
72         """
73         if not etype:
74             etype = fdt_util.GetString(node, 'type', node.name)
75
76         # Convert something like 'u-boot@0' to 'u_boot' since we are only
77         # interested in the type.
78         module_name = etype.replace('-', '_')
79         if '@' in module_name:
80             module_name = module_name.split('@')[0]
81         module = modules.get(module_name)
82
83         # Import the module if we have not already done so.
84         if not module:
85             try:
86                 if have_importlib:
87                     module = importlib.import_module(module_name)
88                 else:
89                     module = __import__(module_name)
90             except ImportError:
91                 raise ValueError("Unknown entry type '%s' in node '%s'" %
92                         (etype, node.path))
93             modules[module_name] = module
94
95         # Call its constructor to get the object we want.
96         obj = getattr(module, 'Entry_%s' % module_name)
97         return obj(image, etype, node)
98
99     def ReadNode(self):
100         """Read entry information from the node
101
102         This reads all the fields we recognise from the node, ready for use.
103         """
104         self.pos = fdt_util.GetInt(self._node, 'pos')
105         self.size = fdt_util.GetInt(self._node, 'size')
106         self.align = fdt_util.GetInt(self._node, 'align')
107         if tools.NotPowerOfTwo(self.align):
108             raise ValueError("Node '%s': Alignment %s must be a power of two" %
109                              (self._node.path, self.align))
110         self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
111         self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
112         self.align_size = fdt_util.GetInt(self._node, 'align-size')
113         if tools.NotPowerOfTwo(self.align_size):
114             raise ValueError("Node '%s': Alignment size %s must be a power "
115                              "of two" % (self._node.path, self.align_size))
116         self.align_end = fdt_util.GetInt(self._node, 'align-end')
117         self.pos_unset = fdt_util.GetBool(self._node, 'pos-unset')
118
119     def ObtainContents(self):
120         """Figure out the contents of an entry.
121
122         Returns:
123             True if the contents were found, False if another call is needed
124             after the other entries are processed.
125         """
126         # No contents by default: subclasses can implement this
127         return True
128
129     def Pack(self, pos):
130         """Figure out how to pack the entry into the image
131
132         Most of the time the entries are not fully specified. There may be
133         an alignment but no size. In that case we take the size from the
134         contents of the entry.
135
136         If an entry has no hard-coded position, it will be placed at @pos.
137
138         Once this function is complete, both the position and size of the
139         entry will be know.
140
141         Args:
142             Current image position pointer
143
144         Returns:
145             New image position pointer (after this entry)
146         """
147         if self.pos is None:
148             if self.pos_unset:
149                 self.Raise('No position set with pos-unset: should another '
150                            'entry provide this correct position?')
151             self.pos = tools.Align(pos, self.align)
152         needed = self.pad_before + self.contents_size + self.pad_after
153         needed = tools.Align(needed, self.align_size)
154         size = self.size
155         if not size:
156             size = needed
157         new_pos = self.pos + size
158         aligned_pos = tools.Align(new_pos, self.align_end)
159         if aligned_pos != new_pos:
160             size = aligned_pos - self.pos
161             new_pos = aligned_pos
162
163         if not self.size:
164             self.size = size
165
166         if self.size < needed:
167             self.Raise("Entry contents size is %#x (%d) but entry size is "
168                        "%#x (%d)" % (needed, needed, self.size, self.size))
169         # Check that the alignment is correct. It could be wrong if the
170         # and pos or size values were provided (i.e. not calculated), but
171         # conflict with the provided alignment values
172         if self.size != tools.Align(self.size, self.align_size):
173             self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
174                   (self.size, self.size, self.align_size, self.align_size))
175         if self.pos != tools.Align(self.pos, self.align):
176             self.Raise("Position %#x (%d) does not match align %#x (%d)" %
177                   (self.pos, self.pos, self.align, self.align))
178
179         return new_pos
180
181     def Raise(self, msg):
182         """Convenience function to raise an error referencing a node"""
183         raise ValueError("Node '%s': %s" % (self._node.path, msg))
184
185     def GetPath(self):
186         """Get the path of a node
187
188         Returns:
189             Full path of the node for this entry
190         """
191         return self._node.path
192
193     def GetData(self):
194         return self.data
195
196     def GetPositions(self):
197         return {}
198
199     def SetPositionSize(self, pos, size):
200         self.pos = pos
201         self.size = size
202
203     def ProcessContents(self):
204         pass
205
206     def WriteSymbols(self, section):
207         """Write symbol values into binary files for access at run time
208
209         Args:
210           section: Section containing the entry
211         """
212         pass