clk: fixed-rate: Enable DM_FLAG_PRE_RELOC flag
[platform/kernel/u-boot.git] / tools / binman / etype / section.py
1 # SPDX-License-Identifier:      GPL-2.0+
2 # Copyright (c) 2018 Google, Inc
3 # Written by Simon Glass <sjg@chromium.org>
4
5 """Entry-type module for sections (groups of entries)
6
7 Sections are entries which can contain other entries. This allows hierarchical
8 images to be created.
9 """
10
11 from collections import OrderedDict
12 import re
13 import sys
14
15 from binman.entry import Entry
16 from dtoc import fdt_util
17 from patman import tools
18 from patman import tout
19
20
21 class Entry_section(Entry):
22     """Entry that contains other entries
23
24     Properties / Entry arguments: (see binman README for more information)
25         pad-byte: Pad byte to use when padding
26         sort-by-offset: True if entries should be sorted by offset, False if
27             they must be in-order in the device tree description
28         end-at-4gb: Used to build an x86 ROM which ends at 4GB (2^32)
29         skip-at-start: Number of bytes before the first entry starts. These
30             effectively adjust the starting offset of entries. For example,
31             if this is 16, then the first entry would start at 16. An entry
32             with offset = 20 would in fact be written at offset 4 in the image
33             file, since the first 16 bytes are skipped when writing.
34         name-prefix: Adds a prefix to the name of every entry in the section
35             when writing out the map
36
37     Properties:
38         _allow_missing: True if this section permits external blobs to be
39             missing their contents. The second will produce an image but of
40             course it will not work.
41
42     Since a section is also an entry, it inherits all the properies of entries
43     too.
44
45     A section is an entry which can contain other entries, thus allowing
46     hierarchical images to be created. See 'Sections and hierarchical images'
47     in the binman README for more information.
48     """
49     def __init__(self, section, etype, node, test=False):
50         if not test:
51             super().__init__(section, etype, node)
52         self._entries = OrderedDict()
53         self._pad_byte = 0
54         self._sort = False
55         self._skip_at_start = None
56         self._end_4gb = False
57         self._allow_missing = False
58         self.missing = False
59
60     def ReadNode(self):
61         """Read properties from the image node"""
62         super().ReadNode()
63         self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0)
64         self._sort = fdt_util.GetBool(self._node, 'sort-by-offset')
65         self._end_4gb = fdt_util.GetBool(self._node, 'end-at-4gb')
66         self._skip_at_start = fdt_util.GetInt(self._node, 'skip-at-start')
67         if self._end_4gb:
68             if not self.size:
69                 self.Raise("Section size must be provided when using end-at-4gb")
70             if self._skip_at_start is not None:
71                 self.Raise("Provide either 'end-at-4gb' or 'skip-at-start'")
72             else:
73                 self._skip_at_start = 0x100000000 - self.size
74         else:
75             if self._skip_at_start is None:
76                 self._skip_at_start = 0
77         self._name_prefix = fdt_util.GetString(self._node, 'name-prefix')
78         filename = fdt_util.GetString(self._node, 'filename')
79         if filename:
80             self._filename = filename
81
82         self._ReadEntries()
83
84     def _ReadEntries(self):
85         for node in self._node.subnodes:
86             if node.name == 'hash':
87                 continue
88             entry = Entry.Create(self, node)
89             entry.ReadNode()
90             entry.SetPrefix(self._name_prefix)
91             self._entries[node.name] = entry
92
93     def _Raise(self, msg):
94         """Raises an error for this section
95
96         Args:
97             msg: Error message to use in the raise string
98         Raises:
99             ValueError()
100         """
101         raise ValueError("Section '%s': %s" % (self._node.path, msg))
102
103     def GetFdts(self):
104         fdts = {}
105         for entry in self._entries.values():
106             fdts.update(entry.GetFdts())
107         return fdts
108
109     def ProcessFdt(self, fdt):
110         """Allow entries to adjust the device tree
111
112         Some entries need to adjust the device tree for their purposes. This
113         may involve adding or deleting properties.
114         """
115         todo = self._entries.values()
116         for passnum in range(3):
117             next_todo = []
118             for entry in todo:
119                 if not entry.ProcessFdt(fdt):
120                     next_todo.append(entry)
121             todo = next_todo
122             if not todo:
123                 break
124         if todo:
125             self.Raise('Internal error: Could not complete processing of Fdt: remaining %s' %
126                        todo)
127         return True
128
129     def ExpandEntries(self):
130         """Expand out any entries which have calculated sub-entries
131
132         Some entries are expanded out at runtime, e.g. 'files', which produces
133         a section containing a list of files. Process these entries so that
134         this information is added to the device tree.
135         """
136         super().ExpandEntries()
137         for entry in self._entries.values():
138             entry.ExpandEntries()
139
140     def AddMissingProperties(self):
141         """Add new properties to the device tree as needed for this entry"""
142         super().AddMissingProperties()
143         for entry in self._entries.values():
144             entry.AddMissingProperties()
145
146     def ObtainContents(self):
147         return self.GetEntryContents()
148
149     def GetData(self):
150         section_data = b''
151
152         for entry in self._entries.values():
153             data = entry.GetData()
154             base = self.pad_before + (entry.offset or 0) - self._skip_at_start
155             pad = base - len(section_data)
156             if pad > 0:
157                 section_data += tools.GetBytes(self._pad_byte, pad)
158             section_data += data
159         if self.size:
160             pad = self.size - len(section_data)
161             if pad > 0:
162                 section_data += tools.GetBytes(self._pad_byte, pad)
163         self.Detail('GetData: %d entries, total size %#x' %
164                     (len(self._entries), len(section_data)))
165         return section_data
166
167     def GetOffsets(self):
168         """Handle entries that want to set the offset/size of other entries
169
170         This calls each entry's GetOffsets() method. If it returns a list
171         of entries to update, it updates them.
172         """
173         self.GetEntryOffsets()
174         return {}
175
176     def ResetForPack(self):
177         """Reset offset/size fields so that packing can be done again"""
178         super().ResetForPack()
179         for entry in self._entries.values():
180             entry.ResetForPack()
181
182     def Pack(self, offset):
183         """Pack all entries into the section"""
184         self._PackEntries()
185         return super().Pack(offset)
186
187     def _PackEntries(self):
188         """Pack all entries into the image"""
189         offset = self._skip_at_start
190         for entry in self._entries.values():
191             offset = entry.Pack(offset)
192         self.size = self.CheckSize()
193
194     def _ExpandEntries(self):
195         """Expand any entries that are permitted to"""
196         exp_entry = None
197         for entry in self._entries.values():
198             if exp_entry:
199                 exp_entry.ExpandToLimit(entry.offset)
200                 exp_entry = None
201             if entry.expand_size:
202                 exp_entry = entry
203         if exp_entry:
204             exp_entry.ExpandToLimit(self.size)
205
206     def _SortEntries(self):
207         """Sort entries by offset"""
208         entries = sorted(self._entries.values(), key=lambda entry: entry.offset)
209         self._entries.clear()
210         for entry in entries:
211             self._entries[entry._node.name] = entry
212
213     def CheckEntries(self):
214         """Check that entries do not overlap or extend outside the image"""
215         if self._sort:
216             self._SortEntries()
217         self._ExpandEntries()
218         offset = 0
219         prev_name = 'None'
220         for entry in self._entries.values():
221             entry.CheckOffset()
222             if (entry.offset < self._skip_at_start or
223                     entry.offset + entry.size > self._skip_at_start +
224                     self.size):
225                 entry.Raise("Offset %#x (%d) is outside the section starting "
226                             "at %#x (%d)" %
227                             (entry.offset, entry.offset, self._skip_at_start,
228                              self._skip_at_start))
229             if entry.offset < offset and entry.size:
230                 entry.Raise("Offset %#x (%d) overlaps with previous entry '%s' "
231                             "ending at %#x (%d)" %
232                             (entry.offset, entry.offset, prev_name, offset, offset))
233             offset = entry.offset + entry.size
234             prev_name = entry.GetPath()
235
236     def WriteSymbols(self, section):
237         """Write symbol values into binary files for access at run time"""
238         for entry in self._entries.values():
239             entry.WriteSymbols(self)
240
241     def SetCalculatedProperties(self):
242         super().SetCalculatedProperties()
243         for entry in self._entries.values():
244             entry.SetCalculatedProperties()
245
246     def SetImagePos(self, image_pos):
247         super().SetImagePos(image_pos)
248         for entry in self._entries.values():
249             entry.SetImagePos(image_pos + self.offset)
250
251     def ProcessContents(self):
252         sizes_ok_base = super(Entry_section, self).ProcessContents()
253         sizes_ok = True
254         for entry in self._entries.values():
255             if not entry.ProcessContents():
256                 sizes_ok = False
257         return sizes_ok and sizes_ok_base
258
259     def CheckOffset(self):
260         self.CheckEntries()
261
262     def WriteMap(self, fd, indent):
263         """Write a map of the section to a .map file
264
265         Args:
266             fd: File to write the map to
267         """
268         Entry.WriteMapLine(fd, indent, self.name, self.offset or 0,
269                            self.size, self.image_pos)
270         for entry in self._entries.values():
271             entry.WriteMap(fd, indent + 1)
272
273     def GetEntries(self):
274         return self._entries
275
276     def GetContentsByPhandle(self, phandle, source_entry):
277         """Get the data contents of an entry specified by a phandle
278
279         This uses a phandle to look up a node and and find the entry
280         associated with it. Then it returnst he contents of that entry.
281
282         Args:
283             phandle: Phandle to look up (integer)
284             source_entry: Entry containing that phandle (used for error
285                 reporting)
286
287         Returns:
288             data from associated entry (as a string), or None if not found
289         """
290         node = self._node.GetFdt().LookupPhandle(phandle)
291         if not node:
292             source_entry.Raise("Cannot find node for phandle %d" % phandle)
293         for entry in self._entries.values():
294             if entry._node == node:
295                 return entry.GetData()
296         source_entry.Raise("Cannot find entry for node '%s'" % node.name)
297
298     def LookupSymbol(self, sym_name, optional, msg, base_addr):
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
304         At present the only entry properties supported are:
305             offset
306             image_pos - 'base_addr' is added if this is not an end-at-4gb image
307             size
308
309         Args:
310             sym_name: Symbol name in the ELF file to look up in the format
311                 _binman_<entry>_prop_<property> where <entry> is the name of
312                 the entry and <property> is the property to find (e.g.
313                 _binman_u_boot_prop_offset). As a special case, you can append
314                 _any to <entry> to have it search for any matching entry. E.g.
315                 _binman_u_boot_any_prop_offset will match entries called u-boot,
316                 u-boot-img and u-boot-nodtb)
317             optional: True if the symbol is optional. If False this function
318                 will raise if the symbol is not found
319             msg: Message to display if an error occurs
320             base_addr: Base address of image. This is added to the returned
321                 image_pos in most cases so that the returned position indicates
322                 where the targetted entry/binary has actually been loaded. But
323                 if end-at-4gb is used, this is not done, since the binary is
324                 already assumed to be linked to the ROM position and using
325                 execute-in-place (XIP).
326
327         Returns:
328             Value that should be assigned to that symbol, or None if it was
329                 optional and not found
330
331         Raises:
332             ValueError if the symbol is invalid or not found, or references a
333                 property which is not supported
334         """
335         m = re.match(r'^_binman_(\w+)_prop_(\w+)$', sym_name)
336         if not m:
337             raise ValueError("%s: Symbol '%s' has invalid format" %
338                              (msg, sym_name))
339         entry_name, prop_name = m.groups()
340         entry_name = entry_name.replace('_', '-')
341         entry = self._entries.get(entry_name)
342         if not entry:
343             if entry_name.endswith('-any'):
344                 root = entry_name[:-4]
345                 for name in self._entries:
346                     if name.startswith(root):
347                         rest = name[len(root):]
348                         if rest in ['', '-img', '-nodtb']:
349                             entry = self._entries[name]
350         if not entry:
351             err = ("%s: Entry '%s' not found in list (%s)" %
352                    (msg, entry_name, ','.join(self._entries.keys())))
353             if optional:
354                 print('Warning: %s' % err, file=sys.stderr)
355                 return None
356             raise ValueError(err)
357         if prop_name == 'offset':
358             return entry.offset
359         elif prop_name == 'image_pos':
360             value = entry.image_pos
361             if not self.GetImage()._end_4gb:
362                 value += base_addr
363             return value
364         if prop_name == 'size':
365             return entry.size
366         else:
367             raise ValueError("%s: No such property '%s'" % (msg, prop_name))
368
369     def GetRootSkipAtStart(self):
370         """Get the skip-at-start value for the top-level section
371
372         This is used to find out the starting offset for root section that
373         contains this section. If this is a top-level section then it returns
374         the skip-at-start offset for this section.
375
376         This is used to get the absolute position of section within the image.
377
378         Returns:
379             Integer skip-at-start value for the root section containing this
380                 section
381         """
382         if self.section:
383             return self.section.GetRootSkipAtStart()
384         return self._skip_at_start
385
386     def GetStartOffset(self):
387         """Get the start offset for this section
388
389         Returns:
390             The first available offset in this section (typically 0)
391         """
392         return self._skip_at_start
393
394     def GetImageSize(self):
395         """Get the size of the image containing this section
396
397         Returns:
398             Image size as an integer number of bytes, which may be None if the
399                 image size is dynamic and its sections have not yet been packed
400         """
401         return self.GetImage().size
402
403     def FindEntryType(self, etype):
404         """Find an entry type in the section
405
406         Args:
407             etype: Entry type to find
408         Returns:
409             entry matching that type, or None if not found
410         """
411         for entry in self._entries.values():
412             if entry.etype == etype:
413                 return entry
414         return None
415
416     def GetEntryContents(self):
417         """Call ObtainContents() for the section
418         """
419         todo = self._entries.values()
420         for passnum in range(3):
421             next_todo = []
422             for entry in todo:
423                 if not entry.ObtainContents():
424                     next_todo.append(entry)
425             todo = next_todo
426             if not todo:
427                 break
428         if todo:
429             self.Raise('Internal error: Could not complete processing of contents: remaining %s' %
430                        todo)
431         return True
432
433     def _SetEntryOffsetSize(self, name, offset, size):
434         """Set the offset and size of an entry
435
436         Args:
437             name: Entry name to update
438             offset: New offset, or None to leave alone
439             size: New size, or None to leave alone
440         """
441         entry = self._entries.get(name)
442         if not entry:
443             self._Raise("Unable to set offset/size for unknown entry '%s'" %
444                         name)
445         entry.SetOffsetSize(self._skip_at_start + offset if offset is not None
446                             else None, size)
447
448     def GetEntryOffsets(self):
449         """Handle entries that want to set the offset/size of other entries
450
451         This calls each entry's GetOffsets() method. If it returns a list
452         of entries to update, it updates them.
453         """
454         for entry in self._entries.values():
455             offset_dict = entry.GetOffsets()
456             for name, info in offset_dict.items():
457                 self._SetEntryOffsetSize(name, *info)
458
459
460     def CheckSize(self):
461         """Check that the image contents does not exceed its size, etc."""
462         contents_size = 0
463         for entry in self._entries.values():
464             contents_size = max(contents_size, entry.offset + entry.size)
465
466         contents_size -= self._skip_at_start
467
468         size = self.size
469         if not size:
470             size = self.pad_before + contents_size + self.pad_after
471             size = tools.Align(size, self.align_size)
472
473         if self.size and contents_size > self.size:
474             self._Raise("contents size %#x (%d) exceeds section size %#x (%d)" %
475                         (contents_size, contents_size, self.size, self.size))
476         if not self.size:
477             self.size = size
478         if self.size != tools.Align(self.size, self.align_size):
479             self._Raise("Size %#x (%d) does not match align-size %#x (%d)" %
480                         (self.size, self.size, self.align_size,
481                          self.align_size))
482         return size
483
484     def ListEntries(self, entries, indent):
485         """List the files in the section"""
486         Entry.AddEntryInfo(entries, indent, self.name, 'section', self.size,
487                            self.image_pos, None, self.offset, self)
488         for entry in self._entries.values():
489             entry.ListEntries(entries, indent + 1)
490
491     def LoadData(self, decomp=True):
492         for entry in self._entries.values():
493             entry.LoadData(decomp)
494         self.Detail('Loaded data')
495
496     def GetImage(self):
497         """Get the image containing this section
498
499         Note that a top-level section is actually an Image, so this function may
500         return self.
501
502         Returns:
503             Image object containing this section
504         """
505         if not self.section:
506             return self
507         return self.section.GetImage()
508
509     def GetSort(self):
510         """Check if the entries in this section will be sorted
511
512         Returns:
513             True if to be sorted, False if entries will be left in the order
514                 they appear in the device tree
515         """
516         return self._sort
517
518     def ReadData(self, decomp=True):
519         tout.Info("ReadData path='%s'" % self.GetPath())
520         parent_data = self.section.ReadData(True)
521         tout.Info('%s: Reading data from offset %#x-%#x, size %#x' %
522                   (self.GetPath(), self.offset, self.offset + self.size,
523                    self.size))
524         data = parent_data[self.offset:self.offset + self.size]
525         return data
526
527     def ReadChildData(self, child, decomp=True):
528         tout.Debug("ReadChildData for child '%s'" % child.GetPath())
529         parent_data = self.ReadData(True)
530         offset = child.offset - self._skip_at_start
531         tout.Debug("Extract for child '%s': offset %#x, skip_at_start %#x, result %#x" %
532                    (child.GetPath(), child.offset, self._skip_at_start, offset))
533         data = parent_data[offset:offset + child.size]
534         if decomp:
535             indata = data
536             data = tools.Decompress(indata, child.compress)
537             if child.uncomp_size:
538                 tout.Info("%s: Decompressing data size %#x with algo '%s' to data size %#x" %
539                             (child.GetPath(), len(indata), child.compress,
540                             len(data)))
541         return data
542
543     def WriteChildData(self, child):
544         return True
545
546     def SetAllowMissing(self, allow_missing):
547         """Set whether a section allows missing external blobs
548
549         Args:
550             allow_missing: True if allowed, False if not allowed
551         """
552         self._allow_missing = allow_missing
553         for entry in self._entries.values():
554             entry.SetAllowMissing(allow_missing)
555
556     def GetAllowMissing(self):
557         """Get whether a section allows missing external blobs
558
559         Returns:
560             True if allowed, False if not allowed
561         """
562         return self._allow_missing
563
564     def CheckMissing(self, missing_list):
565         """Check if any entries in this section have missing external blobs
566
567         If there are missing blobs, the entries are added to the list
568
569         Args:
570             missing_list: List of Entry objects to be added to
571         """
572         for entry in self._entries.values():
573             entry.CheckMissing(missing_list)