SPDX: Convert all of our single license tags to Linux Kernel style
[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         module_name = etype.replace('-', '_')
76         module = modules.get(module_name)
77
78         # Import the module if we have not already done so.
79         if not module:
80             try:
81                 if have_importlib:
82                     module = importlib.import_module(module_name)
83                 else:
84                     module = __import__(module_name)
85             except ImportError:
86                 raise ValueError("Unknown entry type '%s' in node '%s'" %
87                         (etype, node.path))
88             modules[module_name] = module
89
90         # Call its constructor to get the object we want.
91         obj = getattr(module, 'Entry_%s' % module_name)
92         return obj(image, etype, node)
93
94     def ReadNode(self):
95         """Read entry information from the node
96
97         This reads all the fields we recognise from the node, ready for use.
98         """
99         self.pos = fdt_util.GetInt(self._node, 'pos')
100         self.size = fdt_util.GetInt(self._node, 'size')
101         self.align = fdt_util.GetInt(self._node, 'align')
102         if tools.NotPowerOfTwo(self.align):
103             raise ValueError("Node '%s': Alignment %s must be a power of two" %
104                              (self._node.path, self.align))
105         self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
106         self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
107         self.align_size = fdt_util.GetInt(self._node, 'align-size')
108         if tools.NotPowerOfTwo(self.align_size):
109             raise ValueError("Node '%s': Alignment size %s must be a power "
110                              "of two" % (self._node.path, self.align_size))
111         self.align_end = fdt_util.GetInt(self._node, 'align-end')
112         self.pos_unset = fdt_util.GetBool(self._node, 'pos-unset')
113
114     def ObtainContents(self):
115         """Figure out the contents of an entry.
116
117         Returns:
118             True if the contents were found, False if another call is needed
119             after the other entries are processed.
120         """
121         # No contents by default: subclasses can implement this
122         return True
123
124     def Pack(self, pos):
125         """Figure out how to pack the entry into the image
126
127         Most of the time the entries are not fully specified. There may be
128         an alignment but no size. In that case we take the size from the
129         contents of the entry.
130
131         If an entry has no hard-coded position, it will be placed at @pos.
132
133         Once this function is complete, both the position and size of the
134         entry will be know.
135
136         Args:
137             Current image position pointer
138
139         Returns:
140             New image position pointer (after this entry)
141         """
142         if self.pos is None:
143             if self.pos_unset:
144                 self.Raise('No position set with pos-unset: should another '
145                            'entry provide this correct position?')
146             self.pos = tools.Align(pos, self.align)
147         needed = self.pad_before + self.contents_size + self.pad_after
148         needed = tools.Align(needed, self.align_size)
149         size = self.size
150         if not size:
151             size = needed
152         new_pos = self.pos + size
153         aligned_pos = tools.Align(new_pos, self.align_end)
154         if aligned_pos != new_pos:
155             size = aligned_pos - self.pos
156             new_pos = aligned_pos
157
158         if not self.size:
159             self.size = size
160
161         if self.size < needed:
162             self.Raise("Entry contents size is %#x (%d) but entry size is "
163                        "%#x (%d)" % (needed, needed, self.size, self.size))
164         # Check that the alignment is correct. It could be wrong if the
165         # and pos or size values were provided (i.e. not calculated), but
166         # conflict with the provided alignment values
167         if self.size != tools.Align(self.size, self.align_size):
168             self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
169                   (self.size, self.size, self.align_size, self.align_size))
170         if self.pos != tools.Align(self.pos, self.align):
171             self.Raise("Position %#x (%d) does not match align %#x (%d)" %
172                   (self.pos, self.pos, self.align, self.align))
173
174         return new_pos
175
176     def Raise(self, msg):
177         """Convenience function to raise an error referencing a node"""
178         raise ValueError("Node '%s': %s" % (self._node.path, msg))
179
180     def GetPath(self):
181         """Get the path of a node
182
183         Returns:
184             Full path of the node for this entry
185         """
186         return self._node.path
187
188     def GetData(self):
189         return self.data
190
191     def GetPositions(self):
192         return {}
193
194     def SetPositionSize(self, pos, size):
195         self.pos = pos
196         self.size = size
197
198     def ProcessContents(self):
199         pass
200
201     def WriteSymbols(self, image):
202         """Write symbol values into binary files for access at run time
203
204         Args:
205           image: Image containing the entry
206         """
207         pass