binman: Add support for Chromium GBB
authorSimon Glass <sjg@chromium.org>
Tue, 17 Jul 2018 19:25:44 +0000 (13:25 -0600)
committerSimon Glass <sjg@chromium.org>
Wed, 1 Aug 2018 22:30:48 +0000 (16:30 -0600)
This entry contains a Google Binary Block, used to store keys and bitmaps
in a Chromium image.

Signed-off-by: Simon Glass <sjg@chromium.org>
tools/binman/README.entries
tools/binman/etype/gbb.py [new file with mode: 0644]
tools/binman/ftest.py
tools/binman/test/71_gbb.dts [new file with mode: 0644]
tools/binman/test/72_gbb_too_small.dts [new file with mode: 0644]
tools/binman/test/73_gbb_no_size.dts [new file with mode: 0644]

index 6f09626b24b48d31a7b5b7922d4cd0c55df9dbcb..41b70192c087134af0e036c9900a9e777ef3cf97 100644 (file)
@@ -91,6 +91,25 @@ FMAP does not support this.
 
 
 
+Entry: gbb: An entry which contains a Chromium OS Google Binary Block
+---------------------------------------------------------------------
+
+Properties / Entry arguments:
+    - hardware-id: Hardware ID to use for this build (a string)
+    - keydir: Directory containing the public keys to use
+    - bmpblk: Filename containing images used by recovery
+
+Chromium OS uses a GBB to store various pieces of information, in particular
+the root and recovery keys that are used to verify the boot process. Some
+more details are here:
+
+    https://www.chromium.org/chromium-os/firmware-porting-guide/2-concepts
+
+but note that the page dates from 2013 so is quite out of date. See
+README.chromium for how to obtain the required keys and tools.
+
+
+
 Entry: intel-cmc: Entry containing an Intel Chipset Micro Code (CMC) file
 -------------------------------------------------------------------------
 
diff --git a/tools/binman/etype/gbb.py b/tools/binman/etype/gbb.py
new file mode 100644 (file)
index 0000000..8fe10f4
--- /dev/null
@@ -0,0 +1,96 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+
+# Support for a Chromium OS Google Binary Block, used to record read-only
+# information mostly used by firmware.
+
+from collections import OrderedDict
+
+import command
+from entry import Entry, EntryArg
+
+import fdt_util
+import tools
+
+# Build GBB flags.
+# (src/platform/vboot_reference/firmware/include/gbb_header.h)
+gbb_flag_properties = {
+  'dev-screen-short-delay': 0x1,
+  'load-option-roms': 0x2,
+  'enable-alternate-os': 0x4,
+  'force-dev-switch-on': 0x8,
+  'force-dev-boot-usb': 0x10,
+  'disable-fw-rollback-check': 0x20,
+  'enter-triggers-tonorm': 0x40,
+  'force-dev-boot-legacy': 0x80,
+  'faft-key-override': 0x100,
+  'disable-ec-software-sync': 0x200,
+  'default-dev-boot-legacy': 0x400,
+  'disable-pd-software-sync': 0x800,
+  'disable-lid-shutdown': 0x1000,
+  'force-dev-boot-fastboot-full-cap': 0x2000,
+  'enable-serial': 0x4000,
+  'disable-dwmp': 0x8000,
+}
+
+
+class Entry_gbb(Entry):
+    """An entry which contains a Chromium OS Google Binary Block
+
+    Properties / Entry arguments:
+        - hardware-id: Hardware ID to use for this build (a string)
+        - keydir: Directory containing the public keys to use
+        - bmpblk: Filename containing images used by recovery
+
+    Chromium OS uses a GBB to store various pieces of information, in particular
+    the root and recovery keys that are used to verify the boot process. Some
+    more details are here:
+
+        https://www.chromium.org/chromium-os/firmware-porting-guide/2-concepts
+
+    but note that the page dates from 2013 so is quite out of date. See
+    README.chromium for how to obtain the required keys and tools.
+    """
+    def __init__(self, section, etype, node):
+        Entry.__init__(self, section, etype, node)
+        self.hardware_id, self.keydir, self.bmpblk = self.GetEntryArgsOrProps(
+            [EntryArg('hardware-id', str),
+             EntryArg('keydir', str),
+             EntryArg('bmpblk', str)])
+
+        # Read in the GBB flags from the config
+        self.gbb_flags = 0
+        flags_node = node.FindNode('flags')
+        if flags_node:
+            for flag, value in gbb_flag_properties.iteritems():
+                if fdt_util.GetBool(flags_node, flag):
+                    self.gbb_flags |= value
+
+    def ObtainContents(self):
+        gbb = 'gbb.bin'
+        fname = tools.GetOutputFilename(gbb)
+        if not self.size:
+            self.Raise('GBB must have a fixed size')
+        gbb_size = self.size
+        bmpfv_size = gbb_size - 0x2180
+        if bmpfv_size < 0:
+            self.Raise('GBB is too small (minimum 0x2180 bytes)')
+        sizes = [0x100, 0x1000, bmpfv_size, 0x1000]
+        sizes = ['%#x' % size for size in sizes]
+        keydir = tools.GetInputFilename(self.keydir)
+        gbb_set_command = [
+            'gbb_utility', '-s',
+            '--hwid=%s' % self.hardware_id,
+            '--rootkey=%s/root_key.vbpubk' % keydir,
+            '--recoverykey=%s/recovery_key.vbpubk' % keydir,
+            '--flags=%d' % self.gbb_flags,
+            '--bmpfv=%s' % tools.GetInputFilename(self.bmpblk),
+            fname]
+
+        tools.Run('futility', 'gbb_utility', '-c', ','.join(sizes), fname)
+        tools.Run('futility', *gbb_set_command)
+
+        self.SetContents(tools.ReadFile(fname))
+        return True
index 4e467145d711380928e7407d374204db19747b39..f15b215c60891e9f10763a635067a9eb04579e6f 100644 (file)
@@ -47,6 +47,8 @@ TEXT_DATA             = 'text'
 TEXT_DATA2            = 'text2'
 TEXT_DATA3            = 'text3'
 CROS_EC_RW_DATA       = 'ecrw'
+GBB_DATA              = 'gbbd'
+BMPBLK_DATA           = 'bmp'
 
 
 class TestFunctional(unittest.TestCase):
@@ -95,6 +97,8 @@ class TestFunctional(unittest.TestCase):
         TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
         TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
         TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
+        TestFunctional._MakeInputDir('devkeys')
+        TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
         self._output_setup = False
 
         # ELF file with a '_dt_ucode_base_size' symbol
@@ -287,6 +291,21 @@ class TestFunctional(unittest.TestCase):
             fd.write(contents)
         return pathname
 
+    @classmethod
+    def _MakeInputDir(self, dirname):
+        """Create a new test input directory, creating directories as needed
+
+        Args:
+            dirname: Directory name to create
+
+        Returns:
+            Full pathname of directory created
+        """
+        pathname = os.path.join(self._indir, dirname)
+        if not os.path.exists(pathname):
+            os.makedirs(pathname)
+        return pathname
+
     @classmethod
     def TestFile(self, fname):
         return os.path.join(self._binman_dir, 'test', fname)
@@ -1248,6 +1267,43 @@ class TestFunctional(unittest.TestCase):
         self.assertIn("'fill' entry must have a size property",
                       str(e.exception))
 
+    def _HandleGbbCommand(self, pipe_list):
+        """Fake calls to the futility utility"""
+        if pipe_list[0][0] == 'futility':
+            fname = pipe_list[0][-1]
+            # Append our GBB data to the file, which will happen every time the
+            # futility command is called.
+            with open(fname, 'a') as fd:
+                fd.write(GBB_DATA)
+            return command.CommandResult()
+
+    def testGbb(self):
+        """Test for the Chromium OS Google Binary Block"""
+        command.test_result = self._HandleGbbCommand
+        entry_args = {
+            'keydir': 'devkeys',
+            'bmpblk': 'bmpblk.bin',
+        }
+        data, _, _, _ = self._DoReadFileDtb('71_gbb.dts', entry_args=entry_args)
+
+        # Since futility
+        expected = GBB_DATA + GBB_DATA + 8 * chr(0) + (0x2180 - 16) * chr(0)
+        self.assertEqual(expected, data)
+
+    def testGbbTooSmall(self):
+        """Test for the Chromium OS Google Binary Block being large enough"""
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFileDtb('72_gbb_too_small.dts')
+        self.assertIn("Node '/binman/gbb': GBB is too small",
+                      str(e.exception))
+
+    def testGbbNoSize(self):
+        """Test for the Chromium OS Google Binary Block having a size"""
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFileDtb('73_gbb_no_size.dts')
+        self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
+                      str(e.exception))
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/tools/binman/test/71_gbb.dts b/tools/binman/test/71_gbb.dts
new file mode 100644 (file)
index 0000000..5517563
--- /dev/null
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               gbb {
+                       size = <0x2180>;
+                       flags {
+                               dev-screen-short-delay;
+                               load-option-roms;
+                               enable-alternate-os;
+                               force-dev-switch-on;
+                               force-dev-boot-usb;
+                               disable-fw-rollback-check;
+                               enter-triggers-tonorm;
+                               force-dev-boot-legacy;
+                               faft-key-override;
+                               disable-ec-software-sync;
+                               default-dev-boot-legacy;
+                               disable-pd-software-sync;
+                               disable-lid-shutdown;
+                               force-dev-boot-fastboot-full-cap;
+                               enable-serial;
+                               disable-dwmp;
+                       };
+               };
+       };
+};
diff --git a/tools/binman/test/72_gbb_too_small.dts b/tools/binman/test/72_gbb_too_small.dts
new file mode 100644 (file)
index 0000000..c088f36
--- /dev/null
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+       binman {
+               gbb {
+                       size = <0x200>;
+               };
+       };
+};
diff --git a/tools/binman/test/73_gbb_no_size.dts b/tools/binman/test/73_gbb_no_size.dts
new file mode 100644 (file)
index 0000000..83be403
--- /dev/null
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+       binman {
+               gbb {
+               };
+       };
+};