binman: Add support for building x86 ROMs
authorSimon Glass <sjg@chromium.org>
Sat, 26 Nov 2016 03:15:53 +0000 (20:15 -0700)
committerSimon Glass <sjg@chromium.org>
Mon, 19 Dec 2016 19:09:55 +0000 (08:09 +1300)
The structure of x86 ROMs is pretty complex. There are various binary blobs
to place in the image. Microcode requires special handling so that it is
available to very early code and can be used without any memory whatsoever.

Add support for the various entry types that are currently needed, along
with some tests.

Signed-off-by: Simon Glass <sjg@chromium.org>
Tested-by: Bin Meng <bmeng.cn@gmail.com>
23 files changed:
tools/binman/README
tools/binman/etype/intel_descriptor.py [new file with mode: 0644]
tools/binman/etype/intel_me.py [new file with mode: 0644]
tools/binman/etype/intel_mrc.py [new file with mode: 0644]
tools/binman/etype/intel_vga.py [new file with mode: 0644]
tools/binman/etype/u_boot_dtb_with_ucode.py [new file with mode: 0644]
tools/binman/etype/u_boot_ucode.py [new file with mode: 0644]
tools/binman/etype/u_boot_with_ucode_ptr.py [new file with mode: 0644]
tools/binman/etype/x86_start16.py [new file with mode: 0644]
tools/binman/func_test.py
tools/binman/test/27_pack_4gb_no_size.dts [new file with mode: 0644]
tools/binman/test/28_pack_4gb_outside.dts [new file with mode: 0644]
tools/binman/test/29_x86-rom.dts [new file with mode: 0644]
tools/binman/test/30_x86-rom-me-no-desc.dts [new file with mode: 0644]
tools/binman/test/31_x86-rom-me.dts [new file with mode: 0644]
tools/binman/test/32_intel-vga.dts [new file with mode: 0644]
tools/binman/test/33_x86-start16.dts [new file with mode: 0644]
tools/binman/test/34_x86_ucode.dts [new file with mode: 0644]
tools/binman/test/35_x86_single_ucode.dts [new file with mode: 0644]
tools/binman/test/descriptor.bin [new file with mode: 0644]
tools/binman/test/u_boot_ucode_ptr [new file with mode: 0755]
tools/binman/test/u_boot_ucode_ptr.c [new file with mode: 0644]
tools/binman/test/u_boot_ucode_ptr.lds [new file with mode: 0644]

index c73fb3c299a18f7a43da3042d7b5b327240ea6e8..45e741e3b39c131413c077049cf147677fac0d48 100644 (file)
@@ -369,6 +369,17 @@ Examples of the above options can be found in the tests. See the
 tools/binman/test directory.
 
 
+Special properties
+------------------
+
+Some entries support special properties, documented here:
+
+u-boot-with-ucode-ptr:
+       optional-ucode: boolean property to make microcode optional. If the
+               u-boot.bin image does not include microcode, no error will
+               be generated.
+
+
 Order of image creation
 -----------------------
 
diff --git a/tools/binman/etype/intel_descriptor.py b/tools/binman/etype/intel_descriptor.py
new file mode 100644 (file)
index 0000000..7f4ea0b
--- /dev/null
@@ -0,0 +1,55 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for 'u-boot'
+#
+
+import struct
+
+from entry import Entry
+from blob import Entry_blob
+
+FD_SIGNATURE   = struct.pack('<L', 0x0ff0a55a)
+MAX_REGIONS    = 5
+
+(REGION_DESCRIPTOR, REGION_BIOS, REGION_ME, REGION_GBE,
+        REGION_PDATA) = range(5)
+
+class Region:
+    def __init__(self, data, frba, region_num):
+        pos = frba + region_num * 4
+        val = struct.unpack('<L', data[pos:pos + 4])[0]
+        self.base = (val & 0xfff) << 12
+        self.limit = ((val & 0x0fff0000) >> 4) | 0xfff
+        self.size = self.limit - self.base + 1
+
+class Entry_intel_descriptor(Entry_blob):
+    """Intel flash descriptor block (4KB)
+
+    This is placed at the start of flash and provides information about
+    the SPI flash regions. In particular it provides the base address and
+    size of the ME region, allowing us to place the ME binary in the right
+    place.
+    """
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+        self._regions = []
+
+    def GetDefaultFilename(self):
+        return 'descriptor.bin'
+
+    def GetPositions(self):
+        pos = self.data.find(FD_SIGNATURE)
+        if pos == -1:
+            self.Raise('Cannot find FD signature')
+        flvalsig, flmap0, flmap1, flmap2 = struct.unpack('<LLLL',
+                                                    self.data[pos:pos + 16])
+        frba = ((flmap0 >> 16) & 0xff) << 4
+        for i in range(MAX_REGIONS):
+            self._regions.append(Region(self.data, frba, i))
+
+        # Set the offset for ME only, for now, since the others are not used
+        return {'intel-me': [self._regions[REGION_ME].base,
+                             self._regions[REGION_ME].size]}
diff --git a/tools/binman/etype/intel_me.py b/tools/binman/etype/intel_me.py
new file mode 100644 (file)
index 0000000..45ab50c
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for Intel Management Engine binary blob
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_intel_me(Entry_blob):
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'me.bin'
diff --git a/tools/binman/etype/intel_mrc.py b/tools/binman/etype/intel_mrc.py
new file mode 100644 (file)
index 0000000..f6cedb7
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for Intel Memory Reference Code binary blob
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_intel_mrc(Entry_blob):
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'mrc.bin'
diff --git a/tools/binman/etype/intel_vga.py b/tools/binman/etype/intel_vga.py
new file mode 100644 (file)
index 0000000..d8f270b
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for x86 VGA ROM binary blob
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_intel_vga(Entry_blob):
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'vga.bin'
diff --git a/tools/binman/etype/u_boot_dtb_with_ucode.py b/tools/binman/etype/u_boot_dtb_with_ucode.py
new file mode 100644 (file)
index 0000000..732d10a
--- /dev/null
@@ -0,0 +1,76 @@
+# Copyright (c) 2016 Google, Inc
+## Written by Simon Glass <sjg@chromium.org>
+
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for U-Boot device tree with the microcode removed
+#
+
+import fdt_select
+from entry import Entry
+from blob import Entry_blob
+import tools
+
+class Entry_u_boot_dtb_with_ucode(Entry_blob):
+    """A U-Boot device tree file, with the microcode removed
+
+    See Entry_u_boot_ucode for full details of the 3 entries involved in this
+    process.
+    """
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+        self.ucode_data = ''
+        self.collate = False
+        self.ucode_offset = None
+        self.ucode_size = None
+
+    def GetDefaultFilename(self):
+        return 'u-boot.dtb'
+
+    def ObtainContents(self):
+        Entry_blob.ObtainContents(self)
+
+        # If the image does not need microcode, there is nothing to do
+        ucode_dest_entry = self.image.FindEntryType('u-boot-with-ucode-ptr')
+        if not ucode_dest_entry or not ucode_dest_entry.target_pos:
+            return True
+
+        # Create a new file to hold the copied device tree
+        dtb_name = 'u-boot-dtb-with-ucode.dtb'
+        fname = tools.GetOutputFilename(dtb_name)
+        with open(fname, 'wb') as fd:
+            fd.write(self.data)
+
+        # Remove the microcode
+        fdt = fdt_select.FdtScan(fname)
+        fdt.Scan()
+        ucode = fdt.GetNode('/microcode')
+        if not ucode:
+            raise self.Raise("No /microcode node found in '%s'" % fname)
+
+        # There's no need to collate it (move all microcode into one place)
+        # if we only have one chunk of microcode.
+        self.collate = len(ucode.subnodes) > 1
+        for node in ucode.subnodes:
+            data_prop = node.props.get('data')
+            if data_prop:
+                self.ucode_data += ''.join(data_prop.bytes)
+                if not self.collate:
+                    poffset = data_prop.GetOffset()
+                    if poffset is None:
+                        # We cannot obtain a property offset. Collate instead.
+                        self.collate = True
+                    else:
+                        # Find the offset in the device tree of the ucode data
+                        self.ucode_offset = poffset + 12
+                        self.ucode_size = len(data_prop.bytes)
+                if self.collate:
+                    prop = node.DeleteProp('data')
+        if self.collate:
+            fdt.Pack()
+            fdt.Flush()
+
+            # Make this file the contents of this entry
+            self._pathname = fname
+            self.ReadContents()
+        return True
diff --git a/tools/binman/etype/u_boot_ucode.py b/tools/binman/etype/u_boot_ucode.py
new file mode 100644 (file)
index 0000000..8fe27ac
--- /dev/null
@@ -0,0 +1,84 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for a U-Boot binary with an embedded microcode pointer
+#
+
+from entry import Entry
+from blob import Entry_blob
+import tools
+
+class Entry_u_boot_ucode(Entry_blob):
+    """U-Boot microcode block
+
+    U-Boot on x86 needs a single block of microcode. This is collected from
+    the various microcode update nodes in the device tree. It is also unable
+    to read the microcode from the device tree on platforms that use FSP
+    (Firmware Support Package) binaries, because the API requires that the
+    microcode is supplied before there is any SRAM available to use (i.e.
+    the FSP sets up the SRAM / cache-as-RAM but does so in the call that
+    requires the microcode!). To keep things simple, all x86 platforms handle
+    microcode the same way in U-Boot (even non-FSP platforms). This is that
+    a table is placed at _dt_ucode_base_size containing the base address and
+    size of the microcode. This is either passed to the FSP (for FSP
+    platforms), or used to set up the microcode (for non-FSP platforms).
+    This all happens in the build system since it is the only way to get
+    the microcode into a single blob and accessible without SRAM.
+
+    There are two cases to handle. If there is only one microcode blob in
+    the device tree, then the ucode pointer it set to point to that. This
+    entry (u-boot-ucode) is empty. If there is more than one update, then
+    this entry holds the concatenation of all updates, and the device tree
+    entry (u-boot-dtb-with-ucode) is updated to remove the microcode. This
+    last step ensures that that the microcode appears in one contiguous
+    block in the image and is not unnecessarily duplicated in the device
+    tree. It is referred to as 'collation' here.
+
+    Entry types that have a part to play in handling microcode:
+
+        Entry_u_boot_with_ucode_ptr:
+            Contains u-boot-nodtb.bin (i.e. U-Boot without the device tree).
+            It updates it with the address and size of the microcode so that
+            U-Boot can find it early on start-up.
+        Entry_u_boot_dtb_with_ucode:
+            Contains u-boot.dtb. It stores the microcode in a
+            'self.ucode_data' property, which is then read by this class to
+            obtain the microcode if needed. If collation is performed, it
+            removes the microcode from the device tree.
+        Entry_u_boot_ucode:
+            This class. If collation is enabled it reads the microcode from
+            the Entry_u_boot_dtb_with_ucode entry, and uses it as the
+            contents of this entry.
+    """
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def ObtainContents(self):
+        # If the image does not need microcode, there is nothing to do
+        ucode_dest_entry = self.image.FindEntryType('u-boot-with-ucode-ptr')
+        if ucode_dest_entry and not ucode_dest_entry.target_pos:
+            self.data = ''
+            return True
+
+        # Get the microcode from the device tree entry
+        fdt_entry = self.image.FindEntryType('u-boot-dtb-with-ucode')
+        if not fdt_entry or not fdt_entry.ucode_data:
+            return False
+
+        if not fdt_entry.collate:
+            # This section can be empty
+            self.data = ''
+            return True
+
+        # Write it out to a file
+        dtb_name = 'u-boot-ucode.bin'
+        fname = tools.GetOutputFilename(dtb_name)
+        with open(fname, 'wb') as fd:
+            fd.write(fdt_entry.ucode_data)
+
+        self._pathname = fname
+        self.ReadContents()
+
+        return True
diff --git a/tools/binman/etype/u_boot_with_ucode_ptr.py b/tools/binman/etype/u_boot_with_ucode_ptr.py
new file mode 100644 (file)
index 0000000..6f01adb
--- /dev/null
@@ -0,0 +1,87 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for a U-Boot binary with an embedded microcode pointer
+#
+
+import struct
+
+import command
+from entry import Entry
+from blob import Entry_blob
+import fdt_util
+import tools
+
+class Entry_u_boot_with_ucode_ptr(Entry_blob):
+    """U-Boot with embedded microcode pointer
+
+    See Entry_u_boot_ucode for full details of the 3 entries involved in this
+    process.
+    """
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+        self.elf_fname = 'u-boot'
+        self.target_pos = None
+
+    def GetDefaultFilename(self):
+        return 'u-boot-nodtb.bin'
+
+    def ObtainContents(self):
+        # Figure out where to put the microcode pointer
+        fname = tools.GetInputFilename(self.elf_fname)
+        args = [['nm', fname], ['grep', '-w', '_dt_ucode_base_size']]
+        out = (command.RunPipe(args, capture=True, raise_on_error=False).
+               stdout.splitlines())
+        if len(out) == 1:
+            self.target_pos = int(out[0].split()[0], 16)
+        elif not fdt_util.GetBool(self._node, 'optional-ucode'):
+            self.Raise('Cannot locate _dt_ucode_base_size symbol in u-boot')
+
+        return Entry_blob.ObtainContents(self)
+
+    def ProcessContents(self):
+        # If the image does not need microcode, there is nothing to do
+        if not self.target_pos:
+            return
+
+        # Get the position of the microcode
+        ucode_entry = self.image.FindEntryType('u-boot-ucode')
+        if not ucode_entry:
+            self.Raise('Cannot find microcode region u-boot-ucode')
+
+        # Check the target pos is in the image. If it is not, then U-Boot is
+        # being linked incorrectly, or is being placed at the wrong position
+        # in the image.
+        #
+        # The image must be set up so that U-Boot is placed at the
+        # flash address to which it is linked. For example, if
+        # CONFIG_SYS_TEXT_BASE is 0xfff00000, and the ROM is 8MB, then
+        # the U-Boot region must start at position 7MB in the image. In this
+        # case the ROM starts at 0xff800000, so the position of the first
+        # entry in the image corresponds to that.
+        if (self.target_pos < self.pos or
+                self.target_pos >= self.pos + self.size):
+            self.Raise('Microcode pointer _dt_ucode_base_size at %08x is '
+                'outside the image ranging from %08x to %08x' %
+                (self.target_pos, self.pos, self.pos + self.size))
+
+        # Get the microcode, either from u-boot-ucode or u-boot-dtb-with-ucode.
+        # If we have left the microcode in the device tree, then it will be
+        # in the former. If we extracted the microcode from the device tree
+        # and collated it in one place, it will be in the latter.
+        if ucode_entry.size:
+            pos, size = ucode_entry.pos, ucode_entry.size
+        else:
+            dtb_entry = self.image.FindEntryType('u-boot-dtb-with-ucode')
+            if not dtb_entry:
+                self.Raise('Cannot find microcode region u-boot-dtb-with-ucode')
+            pos = dtb_entry.pos + dtb_entry.ucode_offset
+            size = dtb_entry.ucode_size
+
+        # Write the microcode position and size into the entry
+        pos_and_size = struct.pack('<2L', pos, size)
+        self.target_pos -= self.pos
+        self.data = (self.data[:self.target_pos] + pos_and_size +
+                     self.data[self.target_pos + 8:])
diff --git a/tools/binman/etype/x86_start16.py b/tools/binman/etype/x86_start16.py
new file mode 100644 (file)
index 0000000..a44ea68
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for the 16-bit x86 start-up code for U-Boot
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_x86_start16(Entry_blob):
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'u-boot-x86-16bit.bin'
index bf6e9ed6c355f3f4dd64e5078654e949ce74c2aa..341957b59f0f13e096013bb35c7d221de1622645 100644 (file)
@@ -65,10 +65,21 @@ class TestFunctional(unittest.TestCase):
         TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
         TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
         TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
+        TestFunctional._MakeInputFile('me.bin', ME_DATA)
+        TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
         TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
+        TestFunctional._MakeInputFile('u-boot-x86-16bit.bin', X86_START16_DATA)
         TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
         self._output_setup = False
 
+        # ELF file with a '_dt_ucode_base_size' symbol
+        with open(self.TestFile('u_boot_ucode_ptr')) as fd:
+            TestFunctional._MakeInputFile('u-boot', fd.read())
+
+        # Intel flash descriptor file
+        with open(self.TestFile('descriptor.bin')) as fd:
+            TestFunctional._MakeInputFile('descriptor.bin', fd.read())
+
     @classmethod
     def tearDownClass(self):
         """Remove the temporary input directory and its contents"""
@@ -125,6 +136,18 @@ class TestFunctional(unittest.TestCase):
                               '-d', self.TestFile(fname))
 
     def _SetupDtb(self, fname, outfile='u-boot.dtb'):
+        """Set up a new test device-tree file
+
+        The given file is compiled and set up as the device tree to be used
+        for ths test.
+
+        Args:
+            fname: Filename of .dts file to read
+            outfile: Output filename for compiled device tree binary
+
+        Returns:
+            Contents of device tree binary
+        """
         if not self._output_setup:
             tools.PrepareOutputDir(self._indir, True)
             self._output_setup = True
@@ -132,8 +155,9 @@ class TestFunctional(unittest.TestCase):
         with open(dtb) as fd:
             data = fd.read()
             TestFunctional._MakeInputFile(outfile, data)
+            return data
 
-    def _DoReadFile(self, fname, use_real_dtb=False):
+    def _DoReadFileDtb(self, fname, use_real_dtb=False):
         """Run binman and return the resulting image
 
         This runs binman with a given test file and then reads the resulting
@@ -148,10 +172,16 @@ class TestFunctional(unittest.TestCase):
                 the u-boot-dtb entry. Normally this is not needed and the
                 test contents (the U_BOOT_DTB_DATA string) can be used.
                 But in some test we need the real contents.
+
+        Returns:
+            Tuple:
+                Resulting image contents
+                Device tree contents
         """
+        dtb_data = None
         # Use the compiled test file as the u-boot-dtb input
         if use_real_dtb:
-            self._SetupDtb(fname)
+            dtb_data = self._SetupDtb(fname)
 
         try:
             retcode = self._DoTestFile(fname)
@@ -162,12 +192,16 @@ class TestFunctional(unittest.TestCase):
             fname = tools.GetOutputFilename('image.bin')
             self.assertTrue(os.path.exists(fname))
             with open(fname) as fd:
-                return fd.read()
+                return fd.read(), dtb_data
         finally:
             # Put the test file back
             if use_real_dtb:
                 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
 
+    def _DoReadFile(self, fname, use_real_dtb=False):
+        """Helper function which discards the device-tree binary"""
+        return self._DoReadFileDtb(fname, use_real_dtb)[0]
+
     @classmethod
     def _MakeInputFile(self, fname, contents):
         """Create a new test input file, creating directories as needed
@@ -213,6 +247,17 @@ class TestFunctional(unittest.TestCase):
             self.assertEqual(pos, entry.pos)
             pos += entry.size
 
+    def GetFdtLen(self, dtb):
+        """Get the totalsize field from a device tree binary
+
+        Args:
+            dtb: Device tree binary contents
+
+        Returns:
+            Total size of device tree binary, from the header
+        """
+        return struct.unpack('>L', dtb[4:8])[0]
+
     def testRun(self):
         """Test a basic run with valid args"""
         result = self._RunBinman('-h')
@@ -540,3 +585,131 @@ class TestFunctional(unittest.TestCase):
         """Test that a device tree can be added to U-Boot"""
         data = self._DoReadFile('26_pack_u_boot_dtb.dts')
         self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
+
+    def testPackX86RomNoSize(self):
+        """Test that the end-at-4gb property requires a size property"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('27_pack_4gb_no_size.dts')
+        self.assertIn("Image '/binman': Image size must be provided when "
+                      "using end-at-4gb", str(e.exception))
+
+    def testPackX86RomOutside(self):
+        """Test that the end-at-4gb property checks for position boundaries"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('28_pack_4gb_outside.dts')
+        self.assertIn("Node '/binman/u-boot': Position 0x0 (0) is outside "
+                      "the image starting at 0xfffffff0 (4294967280)",
+                      str(e.exception))
+
+    def testPackX86Rom(self):
+        """Test that a basic x86 ROM can be created"""
+        data = self._DoReadFile('29_x86-rom.dts')
+        self.assertEqual(U_BOOT_DATA + chr(0) * 3 + U_BOOT_SPL_DATA +
+                         chr(0) * 6, data)
+
+    def testPackX86RomMeNoDesc(self):
+        """Test that an invalid Intel descriptor entry is detected"""
+        TestFunctional._MakeInputFile('descriptor.bin', '')
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('31_x86-rom-me.dts')
+        self.assertIn("Node '/binman/intel-descriptor': Cannot find FD "
+                      "signature", str(e.exception))
+
+    def testPackX86RomBadDesc(self):
+        """Test that the Intel requires a descriptor entry"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('30_x86-rom-me-no-desc.dts')
+        self.assertIn("Node '/binman/intel-me': No position set with "
+                      "pos-unset: should another entry provide this correct "
+                      "position?", str(e.exception))
+
+    def testPackX86RomMe(self):
+        """Test that an x86 ROM with an ME region can be created"""
+        data = self._DoReadFile('31_x86-rom-me.dts')
+        self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
+
+    def testPackVga(self):
+        """Test that an image with a VGA binary can be created"""
+        data = self._DoReadFile('32_intel-vga.dts')
+        self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
+
+    def testPackStart16(self):
+        """Test that an image with an x86 start16 region can be created"""
+        data = self._DoReadFile('33_x86-start16.dts')
+        self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
+
+    def testPackUbootMicrocode(self):
+        """Test that x86 microcode can be handled correctly
+
+        We expect to see the following in the image, in order:
+            u-boot-nodtb.bin with a microcode pointer inserted at the correct
+                place
+            u-boot.dtb with the microcode removed
+            the microcode
+        """
+        data = self._DoReadFile('34_x86_ucode.dts', True)
+
+        # Now check the device tree has no microcode
+        second = data[len(U_BOOT_NODTB_DATA):]
+        fname = tools.GetOutputFilename('test.dtb')
+        with open(fname, 'wb') as fd:
+            fd.write(second)
+        fdt = fdt_select.FdtScan(fname)
+        ucode = fdt.GetNode('/microcode')
+        self.assertTrue(ucode)
+        for node in ucode.subnodes:
+            self.assertFalse(node.props.get('data'))
+
+        fdt_len = self.GetFdtLen(second)
+        third = second[fdt_len:]
+
+        # Check that the microcode appears immediately after the Fdt
+        # This matches the concatenation of the data properties in
+        # the /microcode/update@xxx nodes in x86_ucode.dts.
+        ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
+                                 0x78235609)
+        self.assertEqual(ucode_data, third[:len(ucode_data)])
+        ucode_pos = len(U_BOOT_NODTB_DATA) + fdt_len
+
+        # Check that the microcode pointer was inserted. It should match the
+        # expected position and size
+        pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
+                                   len(ucode_data))
+        first = data[:len(U_BOOT_NODTB_DATA)]
+        self.assertEqual('nodtb with microcode' + pos_and_size +
+                         ' somewhere in here', first)
+
+    def _RunPackUbootSingleMicrocode(self, collate):
+        """Test that x86 microcode can be handled correctly
+
+        We expect to see the following in the image, in order:
+            u-boot-nodtb.bin with a microcode pointer inserted at the correct
+                place
+            u-boot.dtb with the microcode
+            an empty microcode region
+        """
+        # We need the libfdt library to run this test since only that allows
+        # finding the offset of a property. This is required by
+        # Entry_u_boot_dtb_with_ucode.ObtainContents().
+        if not fdt_select.have_libfdt:
+            return
+        data = self._DoReadFile('35_x86_single_ucode.dts', True)
+
+        second = data[len(U_BOOT_NODTB_DATA):]
+
+        fdt_len = self.GetFdtLen(second)
+        third = second[fdt_len:]
+        second = second[:fdt_len]
+
+        if not collate:
+            ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
+            self.assertIn(ucode_data, second)
+            ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
+
+            # Check that the microcode pointer was inserted. It should match the
+            # expected position and size
+            pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
+                                    len(ucode_data))
+            first = data[:len(U_BOOT_NODTB_DATA)]
+            self.assertEqual('nodtb with microcode' + pos_and_size +
+                            ' somewhere in here', first)
diff --git a/tools/binman/test/27_pack_4gb_no_size.dts b/tools/binman/test/27_pack_4gb_no_size.dts
new file mode 100644 (file)
index 0000000..e0b6519
--- /dev/null
@@ -0,0 +1,18 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               end-at-4gb;
+               u-boot {
+                       pos = <0xfffffff0>;
+               };
+
+               u-boot-spl {
+                       pos = <0xfffffff7>;
+               };
+       };
+};
diff --git a/tools/binman/test/28_pack_4gb_outside.dts b/tools/binman/test/28_pack_4gb_outside.dts
new file mode 100644 (file)
index 0000000..ff468c7
--- /dev/null
@@ -0,0 +1,19 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               end-at-4gb;
+               size = <16>;
+               u-boot {
+                       pos = <0>;
+               };
+
+               u-boot-spl {
+                       pos = <0xfffffff7>;
+               };
+       };
+};
diff --git a/tools/binman/test/29_x86-rom.dts b/tools/binman/test/29_x86-rom.dts
new file mode 100644 (file)
index 0000000..075ede3
--- /dev/null
@@ -0,0 +1,19 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               end-at-4gb;
+               size = <16>;
+               u-boot {
+                       pos = <0xfffffff0>;
+               };
+
+               u-boot-spl {
+                       pos = <0xfffffff7>;
+               };
+       };
+};
diff --git a/tools/binman/test/30_x86-rom-me-no-desc.dts b/tools/binman/test/30_x86-rom-me-no-desc.dts
new file mode 100644 (file)
index 0000000..4578f66
--- /dev/null
@@ -0,0 +1,15 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               end-at-4gb;
+               size = <16>;
+               intel-me {
+                       pos-unset;
+               };
+       };
+};
diff --git a/tools/binman/test/31_x86-rom-me.dts b/tools/binman/test/31_x86-rom-me.dts
new file mode 100644 (file)
index 0000000..b484ab3
--- /dev/null
@@ -0,0 +1,18 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               end-at-4gb;
+               size = <0x800000>;
+               intel-descriptor {
+               };
+
+               intel-me {
+                       pos-unset;
+               };
+       };
+};
diff --git a/tools/binman/test/32_intel-vga.dts b/tools/binman/test/32_intel-vga.dts
new file mode 100644 (file)
index 0000000..1790833
--- /dev/null
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               size = <16>;
+
+               intel-vga {
+               };
+       };
+};
diff --git a/tools/binman/test/33_x86-start16.dts b/tools/binman/test/33_x86-start16.dts
new file mode 100644 (file)
index 0000000..2e279de
--- /dev/null
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               size = <16>;
+
+               x86-start16 {
+               };
+       };
+};
diff --git a/tools/binman/test/34_x86_ucode.dts b/tools/binman/test/34_x86_ucode.dts
new file mode 100644 (file)
index 0000000..64a6c2c
--- /dev/null
@@ -0,0 +1,29 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               end-at-4gb;
+               size = <0x200>;
+               u-boot-with-ucode-ptr {
+               };
+
+               u-boot-dtb-with-ucode {
+               };
+
+               u-boot-ucode {
+               };
+       };
+
+       microcode {
+               update@0 {
+                       data = <0x12345678 0x12345679>;
+               };
+               update@1 {
+                       data = <0xabcd0000 0x78235609>;
+               };
+       };
+};
diff --git a/tools/binman/test/35_x86_single_ucode.dts b/tools/binman/test/35_x86_single_ucode.dts
new file mode 100644 (file)
index 0000000..973e97f
--- /dev/null
@@ -0,0 +1,26 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               end-at-4gb;
+               size = <0x200>;
+               u-boot-with-ucode-ptr {
+               };
+
+               u-boot-dtb-with-ucode {
+               };
+
+               u-boot-ucode {
+               };
+       };
+
+       microcode {
+               update@0 {
+                       data = <0x12345678 0x12345679>;
+               };
+       };
+};
diff --git a/tools/binman/test/descriptor.bin b/tools/binman/test/descriptor.bin
new file mode 100644 (file)
index 0000000..3d54943
Binary files /dev/null and b/tools/binman/test/descriptor.bin differ
diff --git a/tools/binman/test/u_boot_ucode_ptr b/tools/binman/test/u_boot_ucode_ptr
new file mode 100755 (executable)
index 0000000..dbfb184
Binary files /dev/null and b/tools/binman/test/u_boot_ucode_ptr differ
diff --git a/tools/binman/test/u_boot_ucode_ptr.c b/tools/binman/test/u_boot_ucode_ptr.c
new file mode 100644 (file)
index 0000000..434c9f4
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2016 Google, Inc
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ *
+ * Simple program to create a _dt_ucode_base_size symbol which can be read
+ * by 'nm'. This is used by binman tests.
+ *
+ * Build with:
+ * cc -march=i386 -m32 -o u_boot_ucode_ptr -T u_boot_ucode_ptr.lds -nostdlib \
+       u_boot_ucode_ptr.c
+ */
+
+static unsigned long _dt_ucode_base_size[2]
+               __attribute__((section(".ucode"))) = {1, 2};
diff --git a/tools/binman/test/u_boot_ucode_ptr.lds b/tools/binman/test/u_boot_ucode_ptr.lds
new file mode 100644 (file)
index 0000000..167debf
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2016 Google, Inc
+ *
+ * SPDX-License-Identifier:      GPL-2.0+
+ */
+
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+
+SECTIONS
+{
+       . = 0xfffffdf0;
+       _start = .;
+       .ucode : {
+               *(.ucode)
+       }
+}