binman: Add support for Intel IFWI entries

An Integrated Firmware Image is used to hold various binaries used for
booting with Apollolake and some later devices. Add support for this.

Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/tools/binman/etype/intel_descriptor.py b/tools/binman/etype/intel_descriptor.py
index 65ba239..adea578 100644
--- a/tools/binman/etype/intel_descriptor.py
+++ b/tools/binman/etype/intel_descriptor.py
@@ -60,10 +60,12 @@
         for i in range(MAX_REGIONS):
             self._regions.append(Region(self.data, frba, i))
 
-        # Set the offset for ME (Management Engine) only, for now, since the
-        # others are not used
+        # Set the offset for ME (Management Engine) and IFWI (Integrated
+        # Firmware Image), for now, since the others are not used.
         info = {}
         if self.HasSibling('intel-me'):
             info['intel-me'] = [self._regions[REGION_ME].base,
                                 self._regions[REGION_ME].size]
+        if self.HasSibling('intel-ifwi'):
+            info['intel-ifwi'] = [self._regions[REGION_BIOS].base, None]
         return info
diff --git a/tools/binman/etype/intel_ifwi.py b/tools/binman/etype/intel_ifwi.py
new file mode 100644
index 0000000..8c79b2d
--- /dev/null
+++ b/tools/binman/etype/intel_ifwi.py
@@ -0,0 +1,100 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for Intel Management Engine binary blob
+#
+
+from collections import OrderedDict
+
+from entry import Entry
+from blob import Entry_blob
+import fdt_util
+import tools
+
+class Entry_intel_ifwi(Entry_blob):
+    """Entry containing an Intel Integrated Firmware Image (IFWI) file
+
+    Properties / Entry arguments:
+        - filename: Filename of file to read into entry. This is either the
+            IFWI file itself, or a file that can be converted into one using a
+            tool
+        - convert-fit: If present this indicates that the ifwitool should be
+            used to convert the provided file into a IFWI.
+
+    This file contains code and data used by the SoC that is required to make
+    it work. It includes U-Boot TPL, microcode, things related to the CSE
+    (Converged Security Engine, the microcontroller that loads all the firmware)
+    and other items beyond the wit of man.
+
+    A typical filename is 'ifwi.bin' for an IFWI file, or 'fitimage.bin' for a
+    file that will be converted to an IFWI.
+
+    The position of this entry is generally set by the intel-descriptor entry.
+
+    The contents of the IFWI are specified by the subnodes of the IFWI node.
+    Each subnode describes an entry which is placed into the IFWFI with a given
+    sub-partition (and optional entry name).
+
+    See README.x86 for information about x86 binary blobs.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
+        self._convert_fit = fdt_util.GetBool(self._node, 'convert-fit')
+        self._ifwi_entries = OrderedDict()
+        self._ReadSubnodes()
+
+    def ObtainContents(self):
+        """Get the contects for the IFWI
+
+        Unfortunately we cannot create anything from scratch here, as Intel has
+        tools which create precursor binaries with lots of data and settings,
+        and these are not incorporated into binman.
+
+        The first step is to get a file in the IFWI format. This is either
+        supplied directly or is extracted from a fitimage using the 'create'
+        subcommand.
+
+        After that we delete the OBBP sub-partition and add each of the files
+        that we want in the IFWI file, one for each sub-entry of the IWFI node.
+        """
+        self._pathname = tools.GetInputFilename(self._filename)
+
+        # Create the IFWI file if needed
+        if self._convert_fit:
+            inname = self._pathname
+            outname = tools.GetOutputFilename('ifwi.bin')
+            tools.RunIfwiTool(inname, tools.CMD_CREATE, outname)
+            self._filename = 'ifwi.bin'
+            self._pathname = outname
+        else:
+            # Provide a different code path here to ensure we have test coverage
+            inname = self._pathname
+
+        # Delete OBBP if it is there, then add the required new items.
+        tools.RunIfwiTool(inname, tools.CMD_DELETE, subpart='OBBP')
+
+        for entry in self._ifwi_entries.values():
+            # First get the input data and put it in a file
+            if not entry.ObtainContents():
+                return False
+            data = entry.GetData()
+            uniq = self.GetUniqueName()
+            input_fname = tools.GetOutputFilename('input.%s' % uniq)
+            tools.WriteFile(input_fname, data)
+
+            tools.RunIfwiTool(inname,
+                tools.CMD_REPLACE if entry._ifwi_replace else tools.CMD_ADD,
+                input_fname, entry._ifwi_subpart, entry._ifwi_entry_name)
+
+        self.ReadBlobContents()
+        return True
+
+    def _ReadSubnodes(self):
+        """Read the subnodes to find out what should go in this IFWI"""
+        for node in self._node.subnodes:
+            entry = Entry.Create(self.section, node)
+            entry._ifwi_replace = fdt_util.GetBool(node, 'replace')
+            entry._ifwi_subpart = fdt_util.GetString(node, 'ifwi-subpart')
+            entry._ifwi_entry_name = fdt_util.GetString(node, 'ifwi-entry')
+            self._ifwi_entries[entry._ifwi_subpart] = entry
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index 14abfbf..1355c4f 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -27,6 +27,7 @@
 import fdt_util
 import fmap_util
 import test_util
+import gzip
 import state
 import tools
 import tout
@@ -876,6 +877,9 @@
     def testPackX86RomMe(self):
         """Test that an x86 ROM with an ME region can be created"""
         data = self._DoReadFile('031_x86-rom-me.dts')
+        expected_desc = tools.ReadFile(self.TestFile('descriptor.bin'))
+        if data[:0x1000] != expected_desc:
+            self.fail('Expected descriptor binary at start of image')
         self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
 
     def testPackVga(self):
@@ -1956,6 +1960,57 @@
         cfile2 = cbfs.files['hello']
         self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
 
+    def _SetupIfwi(self, fname):
+        """Set up to run an IFWI test
+
+        Args:
+            fname: Filename of input file to provide (fitimage.bin or ifwi.bin)
+        """
+        self._SetupSplElf()
+
+        # Intel Integrated Firmware Image (IFWI) file
+        with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd:
+            data = fd.read()
+        TestFunctional._MakeInputFile(fname,data)
+
+    def _CheckIfwi(self, data):
+        """Check that an image with an IFWI contains the correct output
+
+        Args:
+            data: Conents of output file
+        """
+        expected_desc = tools.ReadFile(self.TestFile('descriptor.bin'))
+        if data[:0x1000] != expected_desc:
+            self.fail('Expected descriptor binary at start of image')
+
+        # We expect to find the TPL wil in subpart IBBP entry IBBL
+        image_fname = tools.GetOutputFilename('image.bin')
+        tpl_fname = tools.GetOutputFilename('tpl.out')
+        tools.RunIfwiTool(image_fname, tools.CMD_EXTRACT, fname=tpl_fname,
+                          subpart='IBBP', entry_name='IBBL')
+
+        tpl_data = tools.ReadFile(tpl_fname)
+        self.assertEqual(tpl_data[:len(U_BOOT_TPL_DATA)], U_BOOT_TPL_DATA)
+
+    def testPackX86RomIfwi(self):
+        """Test that an x86 ROM with Integrated Firmware Image can be created"""
+        self._SetupIfwi('fitimage.bin')
+        data = self._DoReadFile('111_x86-rom-ifwi.dts')
+        self._CheckIfwi(data)
+
+    def testPackX86RomIfwiNoDesc(self):
+        """Test that an x86 ROM with IFWI can be created from an ifwi.bin file"""
+        self._SetupIfwi('ifwi.bin')
+        data = self._DoReadFile('112_x86-rom-ifwi-nodesc.dts')
+        self._CheckIfwi(data)
+
+    def testPackX86RomIfwiNoData(self):
+        """Test that an x86 ROM with IFWI handles missing data"""
+        self._SetupIfwi('ifwi.bin')
+        with self.assertRaises(ValueError) as e:
+            data = self._DoReadFile('113_x86-rom-ifwi-nodata.dts')
+        self.assertIn('Could not complete processing of contents',
+                      str(e.exception))
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/tools/binman/test/111_x86-rom-ifwi.dts b/tools/binman/test/111_x86-rom-ifwi.dts
new file mode 100644
index 0000000..63b5972
--- /dev/null
+++ b/tools/binman/test/111_x86-rom-ifwi.dts
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		end-at-4gb;
+		size = <0x800000>;
+		intel-descriptor {
+			filename = "descriptor.bin";
+		};
+
+		intel-ifwi {
+			offset-unset;
+			filename = "fitimage.bin";
+			convert-fit;
+
+			u-boot-tpl {
+				replace;
+				ifwi-subpart = "IBBP";
+				ifwi-entry = "IBBL";
+			};
+		};
+	};
+};
diff --git a/tools/binman/test/112_x86-rom-ifwi-nodesc.dts b/tools/binman/test/112_x86-rom-ifwi-nodesc.dts
new file mode 100644
index 0000000..21ec465
--- /dev/null
+++ b/tools/binman/test/112_x86-rom-ifwi-nodesc.dts
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		end-at-4gb;
+		size = <0x800000>;
+		intel-descriptor {
+			filename = "descriptor.bin";
+		};
+
+		intel-ifwi {
+			offset-unset;
+			filename = "ifwi.bin";
+
+			u-boot-tpl {
+				replace;
+				ifwi-subpart = "IBBP";
+				ifwi-entry = "IBBL";
+			};
+		};
+	};
+};
diff --git a/tools/binman/test/113_x86-rom-ifwi-nodata.dts b/tools/binman/test/113_x86-rom-ifwi-nodata.dts
new file mode 100644
index 0000000..62486fd
--- /dev/null
+++ b/tools/binman/test/113_x86-rom-ifwi-nodata.dts
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		end-at-4gb;
+		size = <0x800000>;
+		intel-descriptor {
+			filename = "descriptor.bin";
+		};
+
+		intel-ifwi {
+			offset-unset;
+			filename = "ifwi.bin";
+
+			_testing {
+				return-unknown-contents;
+				replace;
+				ifwi-subpart = "IBBP";
+				ifwi-entry = "IBBL";
+			};
+		};
+	};
+};
diff --git a/tools/binman/test/fitimage.bin.gz b/tools/binman/test/fitimage.bin.gz
new file mode 100644
index 0000000..0a9dcfc
--- /dev/null
+++ b/tools/binman/test/fitimage.bin.gz
Binary files differ
diff --git a/tools/binman/test/ifwi.bin.gz b/tools/binman/test/ifwi.bin.gz
new file mode 100644
index 0000000..25d7289
--- /dev/null
+++ b/tools/binman/test/ifwi.bin.gz
Binary files differ