binman: Support multithreading for building images
[platform/kernel/u-boot.git] / tools / binman / image.py
index bbb5e23..cdc58b3 100644 (file)
@@ -5,22 +5,21 @@
 # Class for an image, the output of binman
 #
 
-from __future__ import print_function
-
 from collections import OrderedDict
 import fnmatch
 from operator import attrgetter
+import os
 import re
 import sys
 
-from entry import Entry
-from etype import fdtmap
-from etype import image_header
-from etype import section
-import fdt
-import fdt_util
-import tools
-import tout
+from binman.entry import Entry
+from binman.etype import fdtmap
+from binman.etype import image_header
+from binman.etype import section
+from dtoc import fdt
+from dtoc import fdt_util
+from patman import tools
+from patman import tout
 
 class Image(section.Entry_section):
     """A Image, representing an output from binman
@@ -32,22 +31,61 @@ class Image(section.Entry_section):
 
     Attributes:
         filename: Output filename for image
+        image_node: Name of node containing the description for this image
+        fdtmap_dtb: Fdt object for the fdtmap when loading from a file
+        fdtmap_data: Contents of the fdtmap when loading from a file
+        allow_repack: True to add properties to allow the image to be safely
+            repacked later
+        test_section_timeout: Use a zero timeout for section multi-threading
+            (for testing)
 
     Args:
+        copy_to_orig: Copy offset/size to orig_offset/orig_size after reading
+            from the device tree
         test: True if this is being called from a test of Images. This this case
             there is no device tree defining the structure of the section, so
             we create a section manually.
+        ignore_missing: Ignore any missing entry arguments (i.e. don't raise an
+            exception). This should be used if the Image is being loaded from
+            a file rather than generated. In that case we obviously don't need
+            the entry arguments since the contents already exists.
+        use_expanded: True if we are updating the FDT wth entry offsets, etc.
+            and should use the expanded versions of the U-Boot entries.
+            Any entry type that includes a devicetree must put it in a
+            separate entry so that it will be updated. For example. 'u-boot'
+            normally just picks up 'u-boot.bin' which includes the
+            devicetree, but this is not updateable, since it comes into
+            binman as one piece and binman doesn't know that it is actually
+            an executable followed by a devicetree. Of course it could be
+            taught this, but then when reading an image (e.g. 'binman ls')
+            it may need to be able to split the devicetree out of the image
+            in order to determine the location of things. Instead we choose
+            to ignore 'u-boot-bin' in this case, and build it ourselves in
+            binman with 'u-boot-dtb.bin' and 'u-boot.dtb'. See
+            Entry_u_boot_expanded and Entry_blob_phase for details.
     """
-    def __init__(self, name, node, test=False):
-        self.image = self
-        section.Entry_section.__init__(self, None, 'section', node, test)
+    def __init__(self, name, node, copy_to_orig=True, test=False,
+                 ignore_missing=False, use_expanded=False):
+        super().__init__(None, 'section', node, test=test)
+        self.copy_to_orig = copy_to_orig
         self.name = 'main-section'
         self.image_name = name
         self._filename = '%s.bin' % self.image_name
+        self.fdtmap_dtb = None
+        self.fdtmap_data = None
+        self.allow_repack = False
+        self._ignore_missing = ignore_missing
+        self.use_expanded = use_expanded
+        self.test_section_timeout = False
         if not test:
-            filename = fdt_util.GetString(self._node, 'filename')
-            if filename:
-                self._filename = filename
+            self.ReadNode()
+
+    def ReadNode(self):
+        super().ReadNode()
+        filename = fdt_util.GetString(self._node, 'filename')
+        if filename:
+            self._filename = filename
+        self.allow_repack = fdt_util.GetBool(self._node, 'allow-repack')
 
     @classmethod
     def FromFile(cls, fname):
@@ -78,11 +116,23 @@ class Image(section.Entry_section):
             data[pos + fdtmap.FDTMAP_HDR_LEN:pos + 256])
         dtb_size = probe_dtb.GetFdtObj().totalsize()
         fdtmap_data = data[pos:pos + dtb_size + fdtmap.FDTMAP_HDR_LEN]
-        dtb = fdt.Fdt.FromData(fdtmap_data[fdtmap.FDTMAP_HDR_LEN:])
+        fdt_data = fdtmap_data[fdtmap.FDTMAP_HDR_LEN:]
+        out_fname = tools.GetOutputFilename('fdtmap.in.dtb')
+        tools.WriteFile(out_fname, fdt_data)
+        dtb = fdt.Fdt(out_fname)
         dtb.Scan()
 
         # Return an Image with the associated nodes
-        return Image('image', dtb.GetRoot())
+        root = dtb.GetRoot()
+        image = Image('image', root, copy_to_orig=False, ignore_missing=True)
+
+        image.image_node = fdt_util.GetString(root, 'image-node', 'image')
+        image.fdtmap_dtb = dtb
+        image.fdtmap_data = fdtmap_data
+        image._data = data
+        image._filename = fname
+        image.image_name, _ = os.path.splitext(fname)
+        return image
 
     def Raise(self, msg):
         """Convenience function to raise an error referencing an image"""
@@ -90,11 +140,11 @@ class Image(section.Entry_section):
 
     def PackEntries(self):
         """Pack all entries into the image"""
-        section.Entry_section.Pack(self, 0)
+        super().Pack(0)
 
     def SetImagePos(self):
         # This first section in the image so it starts at 0
-        section.Entry_section.SetImagePos(self, 0)
+        super().SetImagePos(0)
 
     def ProcessEntryContents(self):
         """Call the ProcessContents() method for each entry
@@ -104,27 +154,20 @@ class Image(section.Entry_section):
         Returns:
             True if the new data size is OK, False if expansion is needed
         """
-        sizes_ok = True
-        for entry in self._entries.values():
-            if not entry.ProcessContents():
-                sizes_ok = False
-                tout.Debug("Entry '%s' size change" % self._node.path)
-        return sizes_ok
+        return super().ProcessContents()
 
     def WriteSymbols(self):
         """Write symbol values into binary files for access at run time"""
-        section.Entry_section.WriteSymbols(self, self)
-
-    def BuildSection(self, fd, base_offset):
-        """Write the section to a file"""
-        fd.seek(base_offset)
-        fd.write(self.GetData())
+        super().WriteSymbols(self)
 
     def BuildImage(self):
         """Write the image to a file"""
         fname = tools.GetOutputFilename(self._filename)
+        tout.Info("Writing image to '%s'" % fname)
         with open(fname, 'wb') as fd:
-            self.BuildSection(fd, 0)
+            data = self.GetPaddedData()
+            fd.write(data)
+        tout.Info("Wrote %#x bytes" % len(data))
 
     def WriteMap(self):
         """Write a map of the image to a .map file
@@ -137,7 +180,7 @@ class Image(section.Entry_section):
         with open(fname, 'w') as fd:
             print('%8s  %8s  %8s  %s' % ('ImagePos', 'Offset', 'Size', 'Name'),
                   file=fd)
-            section.Entry_section.WriteMap(self, fd, 0)
+            super().WriteMap(fd, 0)
         return fname
 
     def BuildEntryList(self):
@@ -175,6 +218,8 @@ class Image(section.Entry_section):
         return entry
 
     def ReadData(self, decomp=True):
+        tout.Debug("Image '%s' ReadData(), size=%#x" %
+                   (self.GetPath(), len(self._data)))
         return self._data
 
     def GetListEntries(self, entry_paths):
@@ -298,3 +343,48 @@ class Image(section.Entry_section):
             _DoLine(lines, _EntryToStrings(entry))
             selected_entries.append(entry)
         return selected_entries, lines, widths
+
+    def LookupImageSymbol(self, sym_name, optional, msg, base_addr):
+        """Look up a symbol in an ELF file
+
+        Looks up a symbol in an ELF file. Only entry types which come from an
+        ELF image can be used by this function.
+
+        This searches through this image including all of its subsections.
+
+        At present the only entry properties supported are:
+            offset
+            image_pos - 'base_addr' is added if this is not an end-at-4gb image
+            size
+
+        Args:
+            sym_name: Symbol name in the ELF file to look up in the format
+                _binman_<entry>_prop_<property> where <entry> is the name of
+                the entry and <property> is the property to find (e.g.
+                _binman_u_boot_prop_offset). As a special case, you can append
+                _any to <entry> to have it search for any matching entry. E.g.
+                _binman_u_boot_any_prop_offset will match entries called u-boot,
+                u-boot-img and u-boot-nodtb)
+            optional: True if the symbol is optional. If False this function
+                will raise if the symbol is not found
+            msg: Message to display if an error occurs
+            base_addr: Base address of image. This is added to the returned
+                image_pos in most cases so that the returned position indicates
+                where the targeted entry/binary has actually been loaded. But
+                if end-at-4gb is used, this is not done, since the binary is
+                already assumed to be linked to the ROM position and using
+                execute-in-place (XIP).
+
+        Returns:
+            Value that should be assigned to that symbol, or None if it was
+                optional and not found
+
+        Raises:
+            ValueError if the symbol is invalid or not found, or references a
+                property which is not supported
+        """
+        entries = OrderedDict()
+        entries_by_name = {}
+        self._CollectEntries(entries, entries_by_name, self)
+        return self.LookupSymbol(sym_name, optional, msg, base_addr,
+                                 entries_by_name)