blob: 70a6ec17760931549ffbd79ebe6d4365b3679621 [file] [log] [blame]
Simon Glasseca32212018-06-01 09:38:12 -06001# SPDX-License-Identifier: GPL-2.0+
2# Copyright (c) 2018 Google, Inc
3# Written by Simon Glass <sjg@chromium.org>
4#
5# Base class for sections (collections of entries)
6#
7
8from __future__ import print_function
9
10from collections import OrderedDict
11import sys
12
13import fdt_util
14import re
15import tools
16
17class Section(object):
18 """A section which contains multiple entries
19
20 A section represents a collection of entries. There must be one or more
21 sections in an image. Sections are used to group entries together.
22
23 Attributes:
24 _node: Node object that contains the section definition in device tree
25 _size: Section size in bytes, or None if not known yet
26 _align_size: Section size alignment, or None
27 _pad_before: Number of bytes before the first entry starts. This
Simon Glasse8561af2018-08-01 15:22:37 -060028 effectively changes the place where entry offset 0 starts
Simon Glasseca32212018-06-01 09:38:12 -060029 _pad_after: Number of bytes after the last entry ends. The last
30 entry will finish on or before this boundary
31 _pad_byte: Byte to use to pad the section where there is no entry
Simon Glasse8561af2018-08-01 15:22:37 -060032 _sort: True if entries should be sorted by offset, False if they
Simon Glasseca32212018-06-01 09:38:12 -060033 must be in-order in the device tree description
34 _skip_at_start: Number of bytes before the first entry starts. These
Simon Glasse8561af2018-08-01 15:22:37 -060035 effectively adjust the starting offset of entries. For example,
Simon Glasseca32212018-06-01 09:38:12 -060036 if _pad_before is 16, then the first entry would start at 16.
Simon Glasse8561af2018-08-01 15:22:37 -060037 An entry with offset = 20 would in fact be written at offset 4
Simon Glasseca32212018-06-01 09:38:12 -060038 in the image file.
39 _end_4gb: Indicates that the section ends at the 4GB boundary. This is
Simon Glasse8561af2018-08-01 15:22:37 -060040 used for x86 images, which want to use offsets such that a memory
41 address (like 0xff800000) is the first entry offset. This causes
42 _skip_at_start to be set to the starting memory address.
Simon Glass3b78d532018-06-01 09:38:21 -060043 _name_prefix: Prefix to add to the name of all entries within this
44 section
Simon Glasseca32212018-06-01 09:38:12 -060045 _entries: OrderedDict() of entries
46 """
47 def __init__(self, name, node, test=False):
48 global entry
49 global Entry
50 import entry
51 from entry import Entry
52
Simon Glass3a9a2b82018-07-17 13:25:28 -060053 self._name = name
Simon Glasseca32212018-06-01 09:38:12 -060054 self._node = node
Simon Glasse8561af2018-08-01 15:22:37 -060055 self._offset = 0
Simon Glasseca32212018-06-01 09:38:12 -060056 self._size = None
57 self._align_size = None
58 self._pad_before = 0
59 self._pad_after = 0
60 self._pad_byte = 0
61 self._sort = False
62 self._skip_at_start = 0
63 self._end_4gb = False
Simon Glass3b78d532018-06-01 09:38:21 -060064 self._name_prefix = ''
Simon Glasseca32212018-06-01 09:38:12 -060065 self._entries = OrderedDict()
66 if not test:
67 self._ReadNode()
68 self._ReadEntries()
69
70 def _ReadNode(self):
71 """Read properties from the section node"""
72 self._size = fdt_util.GetInt(self._node, 'size')
73 self._align_size = fdt_util.GetInt(self._node, 'align-size')
74 if tools.NotPowerOfTwo(self._align_size):
75 self._Raise("Alignment size %s must be a power of two" %
76 self._align_size)
77 self._pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
78 self._pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
79 self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0)
Simon Glasse8561af2018-08-01 15:22:37 -060080 self._sort = fdt_util.GetBool(self._node, 'sort-by-offset')
Simon Glasseca32212018-06-01 09:38:12 -060081 self._end_4gb = fdt_util.GetBool(self._node, 'end-at-4gb')
82 if self._end_4gb and not self._size:
83 self._Raise("Section size must be provided when using end-at-4gb")
84 if self._end_4gb:
85 self._skip_at_start = 0x100000000 - self._size
Simon Glass3b78d532018-06-01 09:38:21 -060086 self._name_prefix = fdt_util.GetString(self._node, 'name-prefix')
Simon Glasseca32212018-06-01 09:38:12 -060087
88 def _ReadEntries(self):
89 for node in self._node.subnodes:
Simon Glass3b78d532018-06-01 09:38:21 -060090 entry = Entry.Create(self, node)
91 entry.SetPrefix(self._name_prefix)
92 self._entries[node.name] = entry
Simon Glasseca32212018-06-01 09:38:12 -060093
Simon Glass3a9a2b82018-07-17 13:25:28 -060094 def SetOffset(self, offset):
95 self._offset = offset
96
Simon Glasse22f8fa2018-07-06 10:27:41 -060097 def AddMissingProperties(self):
Simon Glass3a9a2b82018-07-17 13:25:28 -060098 """Add new properties to the device tree as needed for this entry"""
Simon Glass9dcc8612018-08-01 15:22:42 -060099 for prop in ['offset', 'size', 'image-pos']:
Simon Glass3a9a2b82018-07-17 13:25:28 -0600100 if not prop in self._node.props:
101 self._node.AddZeroProp(prop)
Simon Glasse22f8fa2018-07-06 10:27:41 -0600102 for entry in self._entries.values():
103 entry.AddMissingProperties()
104
105 def SetCalculatedProperties(self):
Simon Glass3a9a2b82018-07-17 13:25:28 -0600106 self._node.SetInt('offset', self._offset)
107 self._node.SetInt('size', self._size)
Simon Glass9dcc8612018-08-01 15:22:42 -0600108 self._node.SetInt('image-pos', self._image_pos)
Simon Glasse22f8fa2018-07-06 10:27:41 -0600109 for entry in self._entries.values():
110 entry.SetCalculatedProperties()
111
Simon Glass92307732018-07-06 10:27:40 -0600112 def ProcessFdt(self, fdt):
113 todo = self._entries.values()
114 for passnum in range(3):
115 next_todo = []
116 for entry in todo:
117 if not entry.ProcessFdt(fdt):
118 next_todo.append(entry)
119 todo = next_todo
120 if not todo:
121 break
122 if todo:
123 self._Raise('Internal error: Could not complete processing of Fdt: '
124 'remaining %s' % todo)
125 return True
126
Simon Glasseca32212018-06-01 09:38:12 -0600127 def CheckSize(self):
128 """Check that the section contents does not exceed its size, etc."""
129 contents_size = 0
130 for entry in self._entries.values():
Simon Glasse8561af2018-08-01 15:22:37 -0600131 contents_size = max(contents_size, entry.offset + entry.size)
Simon Glasseca32212018-06-01 09:38:12 -0600132
133 contents_size -= self._skip_at_start
134
135 size = self._size
136 if not size:
137 size = self._pad_before + contents_size + self._pad_after
138 size = tools.Align(size, self._align_size)
139
140 if self._size and contents_size > self._size:
141 self._Raise("contents size %#x (%d) exceeds section size %#x (%d)" %
142 (contents_size, contents_size, self._size, self._size))
143 if not self._size:
144 self._size = size
145 if self._size != tools.Align(self._size, self._align_size):
146 self._Raise("Size %#x (%d) does not match align-size %#x (%d)" %
147 (self._size, self._size, self._align_size, self._align_size))
148 return size
149
150 def _Raise(self, msg):
151 """Raises an error for this section
152
153 Args:
154 msg: Error message to use in the raise string
155 Raises:
156 ValueError()
157 """
158 raise ValueError("Section '%s': %s" % (self._node.path, msg))
159
160 def GetPath(self):
161 """Get the path of an image (in the FDT)
162
163 Returns:
164 Full path of the node for this image
165 """
166 return self._node.path
167
168 def FindEntryType(self, etype):
169 """Find an entry type in the section
170
171 Args:
172 etype: Entry type to find
173 Returns:
174 entry matching that type, or None if not found
175 """
176 for entry in self._entries.values():
177 if entry.etype == etype:
178 return entry
179 return None
180
181 def GetEntryContents(self):
182 """Call ObtainContents() for each entry
183
184 This calls each entry's ObtainContents() a few times until they all
185 return True. We stop calling an entry's function once it returns
186 True. This allows the contents of one entry to depend on another.
187
188 After 3 rounds we give up since it's likely an error.
189 """
190 todo = self._entries.values()
191 for passnum in range(3):
192 next_todo = []
193 for entry in todo:
194 if not entry.ObtainContents():
195 next_todo.append(entry)
196 todo = next_todo
197 if not todo:
198 break
Simon Glass6ba679c2018-07-06 10:27:17 -0600199 if todo:
200 self._Raise('Internal error: Could not complete processing of '
201 'contents: remaining %s' % todo)
202 return True
Simon Glasseca32212018-06-01 09:38:12 -0600203
Simon Glasse8561af2018-08-01 15:22:37 -0600204 def _SetEntryOffsetSize(self, name, offset, size):
205 """Set the offset and size of an entry
Simon Glasseca32212018-06-01 09:38:12 -0600206
207 Args:
208 name: Entry name to update
Simon Glasse8561af2018-08-01 15:22:37 -0600209 offset: New offset
Simon Glasseca32212018-06-01 09:38:12 -0600210 size: New size
211 """
212 entry = self._entries.get(name)
213 if not entry:
Simon Glasse8561af2018-08-01 15:22:37 -0600214 self._Raise("Unable to set offset/size for unknown entry '%s'" %
215 name)
216 entry.SetOffsetSize(self._skip_at_start + offset, size)
Simon Glasseca32212018-06-01 09:38:12 -0600217
Simon Glasse8561af2018-08-01 15:22:37 -0600218 def GetEntryOffsets(self):
219 """Handle entries that want to set the offset/size of other entries
Simon Glasseca32212018-06-01 09:38:12 -0600220
Simon Glasse8561af2018-08-01 15:22:37 -0600221 This calls each entry's GetOffsets() method. If it returns a list
Simon Glasseca32212018-06-01 09:38:12 -0600222 of entries to update, it updates them.
223 """
224 for entry in self._entries.values():
Simon Glasse8561af2018-08-01 15:22:37 -0600225 offset_dict = entry.GetOffsets()
226 for name, info in offset_dict.iteritems():
227 self._SetEntryOffsetSize(name, *info)
Simon Glasseca32212018-06-01 09:38:12 -0600228
229 def PackEntries(self):
230 """Pack all entries into the section"""
Simon Glasse8561af2018-08-01 15:22:37 -0600231 offset = self._skip_at_start
Simon Glasseca32212018-06-01 09:38:12 -0600232 for entry in self._entries.values():
Simon Glasse8561af2018-08-01 15:22:37 -0600233 offset = entry.Pack(offset)
234 self._size = self.CheckSize()
Simon Glasseca32212018-06-01 09:38:12 -0600235
236 def _SortEntries(self):
Simon Glasse8561af2018-08-01 15:22:37 -0600237 """Sort entries by offset"""
238 entries = sorted(self._entries.values(), key=lambda entry: entry.offset)
Simon Glasseca32212018-06-01 09:38:12 -0600239 self._entries.clear()
240 for entry in entries:
241 self._entries[entry._node.name] = entry
242
243 def CheckEntries(self):
244 """Check that entries do not overlap or extend outside the section"""
245 if self._sort:
246 self._SortEntries()
Simon Glasse8561af2018-08-01 15:22:37 -0600247 offset = 0
Simon Glasseca32212018-06-01 09:38:12 -0600248 prev_name = 'None'
249 for entry in self._entries.values():
Simon Glasse8561af2018-08-01 15:22:37 -0600250 entry.CheckOffset()
251 if (entry.offset < self._skip_at_start or
252 entry.offset >= self._skip_at_start + self._size):
253 entry.Raise("Offset %#x (%d) is outside the section starting "
Simon Glasseca32212018-06-01 09:38:12 -0600254 "at %#x (%d)" %
Simon Glasse8561af2018-08-01 15:22:37 -0600255 (entry.offset, entry.offset, self._skip_at_start,
Simon Glasseca32212018-06-01 09:38:12 -0600256 self._skip_at_start))
Simon Glasse8561af2018-08-01 15:22:37 -0600257 if entry.offset < offset:
258 entry.Raise("Offset %#x (%d) overlaps with previous entry '%s' "
Simon Glasseca32212018-06-01 09:38:12 -0600259 "ending at %#x (%d)" %
Simon Glasse8561af2018-08-01 15:22:37 -0600260 (entry.offset, entry.offset, prev_name, offset, offset))
261 offset = entry.offset + entry.size
Simon Glasseca32212018-06-01 09:38:12 -0600262 prev_name = entry.GetPath()
263
Simon Glass9dcc8612018-08-01 15:22:42 -0600264 def SetImagePos(self, image_pos):
265 self._image_pos = image_pos
266 for entry in self._entries.values():
267 entry.SetImagePos(image_pos)
268
Simon Glasseca32212018-06-01 09:38:12 -0600269 def ProcessEntryContents(self):
270 """Call the ProcessContents() method for each entry
271
272 This is intended to adjust the contents as needed by the entry type.
273 """
274 for entry in self._entries.values():
275 entry.ProcessContents()
276
277 def WriteSymbols(self):
278 """Write symbol values into binary files for access at run time"""
279 for entry in self._entries.values():
280 entry.WriteSymbols(self)
281
Simon Glasse8561af2018-08-01 15:22:37 -0600282 def BuildSection(self, fd, base_offset):
Simon Glasseca32212018-06-01 09:38:12 -0600283 """Write the section to a file"""
Simon Glasse8561af2018-08-01 15:22:37 -0600284 fd.seek(base_offset)
Simon Glasseca32212018-06-01 09:38:12 -0600285 fd.write(self.GetData())
286
287 def GetData(self):
Simon Glass3a9a2b82018-07-17 13:25:28 -0600288 """Get the contents of the section"""
Simon Glasseca32212018-06-01 09:38:12 -0600289 section_data = chr(self._pad_byte) * self._size
290
291 for entry in self._entries.values():
292 data = entry.GetData()
Simon Glasse8561af2018-08-01 15:22:37 -0600293 base = self._pad_before + entry.offset - self._skip_at_start
Simon Glasseca32212018-06-01 09:38:12 -0600294 section_data = (section_data[:base] + data +
295 section_data[base + len(data):])
296 return section_data
297
298 def LookupSymbol(self, sym_name, optional, msg):
299 """Look up a symbol in an ELF file
300
301 Looks up a symbol in an ELF file. Only entry types which come from an
302 ELF image can be used by this function.
303
Simon Glasse8561af2018-08-01 15:22:37 -0600304 At present the only entry property supported is offset.
Simon Glasseca32212018-06-01 09:38:12 -0600305
306 Args:
307 sym_name: Symbol name in the ELF file to look up in the format
308 _binman_<entry>_prop_<property> where <entry> is the name of
309 the entry and <property> is the property to find (e.g.
Simon Glasse8561af2018-08-01 15:22:37 -0600310 _binman_u_boot_prop_offset). As a special case, you can append
Simon Glasseca32212018-06-01 09:38:12 -0600311 _any to <entry> to have it search for any matching entry. E.g.
Simon Glasse8561af2018-08-01 15:22:37 -0600312 _binman_u_boot_any_prop_offset will match entries called u-boot,
Simon Glasseca32212018-06-01 09:38:12 -0600313 u-boot-img and u-boot-nodtb)
314 optional: True if the symbol is optional. If False this function
315 will raise if the symbol is not found
316 msg: Message to display if an error occurs
317
318 Returns:
319 Value that should be assigned to that symbol, or None if it was
320 optional and not found
321
322 Raises:
323 ValueError if the symbol is invalid or not found, or references a
324 property which is not supported
325 """
326 m = re.match(r'^_binman_(\w+)_prop_(\w+)$', sym_name)
327 if not m:
328 raise ValueError("%s: Symbol '%s' has invalid format" %
329 (msg, sym_name))
330 entry_name, prop_name = m.groups()
331 entry_name = entry_name.replace('_', '-')
332 entry = self._entries.get(entry_name)
333 if not entry:
334 if entry_name.endswith('-any'):
335 root = entry_name[:-4]
336 for name in self._entries:
337 if name.startswith(root):
338 rest = name[len(root):]
339 if rest in ['', '-img', '-nodtb']:
340 entry = self._entries[name]
341 if not entry:
342 err = ("%s: Entry '%s' not found in list (%s)" %
343 (msg, entry_name, ','.join(self._entries.keys())))
344 if optional:
345 print('Warning: %s' % err, file=sys.stderr)
346 return None
347 raise ValueError(err)
Simon Glasse8561af2018-08-01 15:22:37 -0600348 if prop_name == 'offset':
349 return entry.offset
Simon Glass9dcc8612018-08-01 15:22:42 -0600350 elif prop_name == 'image_pos':
351 return entry.image_pos
Simon Glasseca32212018-06-01 09:38:12 -0600352 else:
353 raise ValueError("%s: No such property '%s'" % (msg, prop_name))
354
355 def GetEntries(self):
Simon Glass3a9a2b82018-07-17 13:25:28 -0600356 """Get the number of entries in a section
357
358 Returns:
359 Number of entries in a section
360 """
Simon Glasseca32212018-06-01 09:38:12 -0600361 return self._entries
Simon Glass30732662018-06-01 09:38:20 -0600362
Simon Glass3a9a2b82018-07-17 13:25:28 -0600363 def GetSize(self):
364 """Get the size of a section in bytes
365
366 This is only meaningful if the section has a pre-defined size, or the
367 entries within it have been packed, so that the size has been
368 calculated.
369
370 Returns:
371 Entry size in bytes
372 """
373 return self._size
374
Simon Glass30732662018-06-01 09:38:20 -0600375 def WriteMap(self, fd, indent):
376 """Write a map of the section to a .map file
377
378 Args:
379 fd: File to write the map to
380 """
Simon Glass3a9a2b82018-07-17 13:25:28 -0600381 Entry.WriteMapLine(fd, indent, self._name, self._offset, self._size)
Simon Glass30732662018-06-01 09:38:20 -0600382 for entry in self._entries.values():
Simon Glass3a9a2b82018-07-17 13:25:28 -0600383 entry.WriteMap(fd, indent + 1)
Simon Glass5c350162018-07-17 13:25:47 -0600384
385 def GetContentsByPhandle(self, phandle, source_entry):
386 """Get the data contents of an entry specified by a phandle
387
388 This uses a phandle to look up a node and and find the entry
389 associated with it. Then it returnst he contents of that entry.
390
391 Args:
392 phandle: Phandle to look up (integer)
393 source_entry: Entry containing that phandle (used for error
394 reporting)
395
396 Returns:
397 data from associated entry (as a string), or None if not found
398 """
399 node = self._node.GetFdt().LookupPhandle(phandle)
400 if not node:
401 source_entry.Raise("Cannot find node for phandle %d" % phandle)
402 for entry in self._entries.values():
403 if entry._node == node:
404 if entry.data is None:
405 return None
406 return entry.data
407 source_entry.Raise("Cannot find entry for node '%s'" % node.name)