This is necessary to pass additional information about whether firmware A or B is being run.  This change also passes the GBB directly into LoadFirmware() and LoadKernel().

Change-Id: I976c11c82c3d665a4feb88226e919f16c2440f60

BUG=chrome-os-partner:1657
TEST=manual - see below

make && make runtests

Then test verifying a test image in both dev mode (-b1, no key specified) and recovery mode (key specified)

build/utility/load_kernel_test -b1 ~/b/USB_images/0.11.224.0-alex/chromiumos_test_image.bin

build/utility/load_kernel_test ~/b/USB_images/0.11.224.0-alex/chromiumos_test_image.bin tests/devkeys/recovery_key.vbpubk

And make sure the firmware with this change actually boots to USB and SSD.

NOTE: u-boot-next needs to change to work with this change.  will attempt a follow-up CL with that change

Review URL: http://codereview.chromium.org/6626045
diff --git a/firmware/include/load_firmware_fw.h b/firmware/include/load_firmware_fw.h
index 271063d..1e42f1c 100644
--- a/firmware/include/load_firmware_fw.h
+++ b/firmware/include/load_firmware_fw.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  *
@@ -12,10 +12,8 @@
 #include "sysincludes.h"
 #include "vboot_nvstorage.h"
 
-/* Recommended size of kernel_sign_key_blob in bytes, for
- * implementations which must preallocate a transfer buffer between
- * boot phases */
-#define LOAD_FIRMWARE_KEY_BLOB_REC_SIZE 2104
+/* Recommended size of shared_data_blob in bytes. */
+#define LOAD_FIRMWARE_SHARED_DATA_REC_SIZE 16384
 
 /* Return codes for LoadFirmware() and S3Resume(). */
 #define LOAD_FIRMWARE_SUCCESS 0   /* Success */
@@ -29,19 +27,21 @@
 
 typedef struct LoadFirmwareParams {
   /* Inputs to LoadFirmware() */
-  void *firmware_root_key_blob;  /* Key used to sign firmware header */
-  void *verification_block_0;    /* Key block + preamble for firmware 0 */
-  void *verification_block_1;    /* Key block + preamble for firmware 1 */
+  void* gbb_data;                /* Pointer to GBB data */
+  uint64_t gbb_size;             /* Size of GBB data in bytes */
+  void* verification_block_0;    /* Key block + preamble for firmware 0 */
+  void* verification_block_1;    /* Key block + preamble for firmware 1 */
   uint64_t verification_size_0;  /* Verification block 0 size in bytes */
   uint64_t verification_size_1;  /* Verification block 1 size in bytes */
-  void *kernel_sign_key_blob;    /* Destination buffer for key to use
-                                  * when loading kernel.  Pass this
+  void* shared_data_blob;        /* Destination buffer for data shared between
+                                  * LoadFirmware() and LoadKernel().  Pass this
                                   * data to LoadKernel() in
-                                  * LoadKernelParams.header_sign_key_blob. */
-  uint64_t kernel_sign_key_size; /* Size of kernel signing key blob
-                                  * buffer, in bytes.  On output, this
-                                  * will contain the actual key blob
-                                  * size placed into the buffer. */
+                                  * LoadKernelParams.shared_data_blob. */
+  uint64_t shared_data_size;     /* Size of shared data blob buffer, in bytes.
+                                  * On output, this will contain the actual
+                                  * data size placed into the buffer.  Caller
+                                  * need only pass this much data to
+                                  * LoadKernel().*/
   uint64_t boot_flags;           /* Boot flags */
   VbNvContext* nv_context;       /* Context for NV storage.  nv_context->raw
                                   * must be filled before calling
diff --git a/firmware/include/load_kernel_fw.h b/firmware/include/load_kernel_fw.h
index 45d18e2..5815abf 100644
--- a/firmware/include/load_kernel_fw.h
+++ b/firmware/include/load_kernel_fw.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  *
@@ -34,7 +34,15 @@
 
 typedef struct LoadKernelParams {
   /* Inputs to LoadKernel() */
-  void* header_sign_key_blob;   /* Key blob used to sign the kernel header */
+  void* shared_data_blob;       /* Buffer for data shared between
+                                 * LoadFirmware() and LoadKernel().  Pass the
+                                 * same buffer which was passed to
+                                 * LoadFirmware(). */
+  uint64_t shared_data_size;    /* Size of shared data blob buffer, in bytes.
+                                 * On output, this will contain the actual
+                                 * data size placed into the buffer. */
+  void* gbb_data;               /* Pointer to GBB data */
+  uint64_t gbb_size;            /* Size of GBB data in bytes */
   uint64_t bytes_per_lba;       /* Bytes per lba sector on current device */
   uint64_t ending_lba;          /* Last addressable lba sector on current
                                  * device */
diff --git a/firmware/include/vboot_nvstorage.h b/firmware/include/vboot_nvstorage.h
index 5ae40b4..824dfac 100644
--- a/firmware/include/vboot_nvstorage.h
+++ b/firmware/include/vboot_nvstorage.h
@@ -74,6 +74,8 @@
 #define VBNV_RECOVERY_RO_S3_RESUME    0x04
 /* TPM error in read-only firmware */
 #define VBNV_RECOVERY_RO_TPM_ERROR    0x05
+/* Shared data error in read-only firmware */
+#define VBNV_RECOVERY_RO_SHARED_DATA  0x06
 /* Unspecified/unknown error in read-only firmware */
 #define VBNV_RECOVERY_RO_UNSPECIFIED  0x3F
 /* User manually requested recovery by pressing a key at developer
@@ -87,6 +89,8 @@
 #define VBNV_RECOVERY_RW_TPM_ERROR    0x44
 /* RW firmware in dev mode, but dev switch is off */
 #define VBNV_RECOVERY_RW_DEV_MISMATCH 0x45
+/* Shared data error in rewritable firmware */
+#define VBNV_RECOVERY_RW_SHARED_DATA  0x46
 /* Unspecified/unknown error in rewritable firmware */
 #define VBNV_RECOVERY_RW_UNSPECIFIED  0x7F
 /* DM-verity error */
diff --git a/firmware/include/vboot_struct.h b/firmware/include/vboot_struct.h
index ed83f3e..1e988ee 100644
--- a/firmware/include/vboot_struct.h
+++ b/firmware/include/vboot_struct.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  *
@@ -131,6 +131,49 @@
 
 #define EXPECTED_VBKERNELPREAMBLEHEADER_SIZE 96
 
+/* Minimum and recommended size of shared_data_blob in bytes. */
+#define VB_SHARED_DATA_MIN_SIZE 3072
+#define VB_SHARED_DATA_REC_SIZE 16384
+
+/* Data shared between LoadFirmware(), LoadKernel(), and OS.
+ *
+ * The boot process is:
+ *   1) Caller allocates buffer, at least VB_SHARED_DATA_MIN bytes, ideally
+ *      VB_SHARED_DATA_REC_SIZE bytes.
+ *   2) If non-recovery boot, this is passed to LoadFirmware(), which
+ *      initializes the buffer, adding this header and some data.
+ *   3) Buffer is passed to LoadKernel().  If this is a recovery boot,
+ *      LoadKernel() initializes the buffer, adding this header.  Regardless
+ *      of boot type, LoadKernel() adds some data to the buffer.
+ *   4) Caller makes data available to the OS in a platform-dependent manner.
+ *      For example, via ACPI or ATAGs. */
+typedef struct VbSharedDataHeader {
+  /* Fields present in version 1 */
+  uint32_t struct_version;            /* Version of this structure */
+  uint64_t struct_size;               /* Size of this structure in bytes */
+  uint64_t data_size;                 /* Size of shared data buffer in bytes */
+  uint64_t data_used;                 /* Amount of shared data used so far */
+
+  VbPublicKey kernel_subkey;          /* Kernel subkey, from firmware */
+  uint64_t kernel_subkey_data_offset; /* Offset of kernel subkey data from
+                                       * start of this struct */
+  uint64_t kernel_subkey_data_size;   /* Offset of kernel subkey data */
+
+  uint64_t flags;                     /* Flags */
+
+  /* After read-only firmware which uses version 1 is released, any additional
+   * fields must be added below, and the struct version must be increased.
+   * Before reading/writing those fields, make sure that the struct being
+   * accessed is at least version 2.
+   *
+   * It's always ok for an older firmware to access a newer struct, since all
+   * the fields it knows about are present.  Newer firmware needs to use
+   * reasonable defaults when accessing older structs. */
+
+} __attribute__((packed)) VbSharedDataHeader;
+
+#define VB_SHARED_DATA_VERSION 1      /* Version for struct_version */
+
 __pragma(pack(pop)) /* Support packing for MSVC. */
 
 #endif  /* VBOOT_REFERENCE_VBOOT_STRUCT_H_ */
diff --git a/firmware/lib/include/vboot_common.h b/firmware/lib/include/vboot_common.h
index 5951286..ea172af 100644
--- a/firmware/lib/include/vboot_common.h
+++ b/firmware/lib/include/vboot_common.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  *
@@ -23,6 +23,7 @@
   VBOOT_PREAMBLE_INVALID,    /* Preamble internal structure is
                               * invalid */
   VBOOT_PREAMBLE_SIGNATURE,  /* Preamble signature check failed */
+  VBOOT_SHARED_DATA_INVALID, /* Shared data is invalid. */
   VBOOT_ERROR_MAX,
 };
 extern char* kVbootErrors[VBOOT_ERROR_MAX];
@@ -107,6 +108,20 @@
                          uint64_t size, const RSAPublicKey* key);
 
 
+/* Initialize a verified boot shared data structure.
+ *
+ * Returns 0 if success, non-zero if error. */
+int VbSharedDataInit(VbSharedDataHeader* header, uint64_t size);
+
+/* Reserve [size] bytes of the shared data area.  Returns the offset of the
+ * reserved data from the start of the shared data buffer, or 0 if error. */
+uint64_t VbSharedDataReserve(VbSharedDataHeader* header, uint64_t size);
+
+/* Copy the kernel subkey into the shared data.
+ *
+ * Returns 0 if success, non-zero if error. */
+int VbSharedDataSetKernelKey(VbSharedDataHeader* header,
+                             const VbPublicKey* src);
 
 
 #endif  /* VBOOT_REFERENCE_VBOOT_COMMON_H_ */
diff --git a/firmware/lib/vboot_common.c b/firmware/lib/vboot_common.c
index be76b05..abba130 100644
--- a/firmware/lib/vboot_common.c
+++ b/firmware/lib/vboot_common.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  *
@@ -19,6 +19,7 @@
   "Public key invalid.",
   "Preamble invalid.",
   "Preamble signature check failed.",
+  "Shared data invalid."
 };
 
 
@@ -377,3 +378,67 @@
   /* Success */
   return VBOOT_SUCCESS;
 }
+
+
+int VbSharedDataInit(VbSharedDataHeader* header, uint64_t size) {
+  if (size < sizeof(VbSharedDataHeader)) {
+    VBDEBUG(("Not enough data for header.\n"));
+    return VBOOT_SHARED_DATA_INVALID;
+  }
+  if (size < VB_SHARED_DATA_MIN_SIZE) {
+    VBDEBUG(("Shared data buffer too small.\n"));
+    return VBOOT_SHARED_DATA_INVALID;
+  }
+
+  if (!header)
+    return VBOOT_SHARED_DATA_INVALID;
+
+  /* Zero the header */
+  Memset(header, 0, sizeof(VbSharedDataHeader));
+
+  /* Initialize fields */
+  header->struct_version = VB_SHARED_DATA_VERSION;
+  header->struct_size = sizeof(VbSharedDataHeader);
+  header->data_size = size;
+  header->data_used = sizeof(VbSharedDataHeader);
+
+  /* Success */
+  return VBOOT_SUCCESS;
+}
+
+
+uint64_t VbSharedDataReserve(VbSharedDataHeader* header, uint64_t size) {
+  uint64_t offs = header->data_used;
+
+  if (!header || size > header->data_size - header->data_used) {
+    VBDEBUG(("VbSharedData buffer out of space.\n"));
+    return 0;  /* Not initialized, or not enough space left. */
+  }
+  header->data_used += size;
+  return offs;
+}
+
+
+int VbSharedDataSetKernelKey(VbSharedDataHeader* header,
+                             const VbPublicKey* src) {
+
+  VbPublicKey *kdest = &header->kernel_subkey;
+
+  if (!header)
+    return VBOOT_SHARED_DATA_INVALID;
+
+  /* Attempt to allocate space for the key, if it hasn't been allocated yet */
+  if (!header->kernel_subkey_data_offset) {
+    header->kernel_subkey_data_offset = VbSharedDataReserve(header,
+                                                          src->key_size);
+    if (!header->kernel_subkey_data_offset)
+      return VBOOT_SHARED_DATA_INVALID;
+    header->kernel_subkey_data_size = src->key_size;
+  }
+
+  /* Copy the kernel sign key blob into the destination buffer */
+  PublicKeyInit(kdest, (uint8_t*)header + header->kernel_subkey_data_offset,
+                header->kernel_subkey_data_size);
+
+  return PublicKeyCopy(kdest, src);
+}
diff --git a/firmware/lib/vboot_firmware.c b/firmware/lib/vboot_firmware.c
index bbae095..2f35852 100644
--- a/firmware/lib/vboot_firmware.c
+++ b/firmware/lib/vboot_firmware.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  *
@@ -6,6 +6,7 @@
  * (Firmware portion)
  */
 
+#include "gbb_header.h"
 #include "load_firmware_fw.h"
 #include "rollback_index.h"
 #include "utility.h"
@@ -39,8 +40,9 @@
 
 
 int LoadFirmware(LoadFirmwareParams* params) {
-
-  VbPublicKey* root_key = (VbPublicKey*)params->firmware_root_key_blob;
+  VbSharedDataHeader* shared = (VbSharedDataHeader*)params->shared_data_blob;
+  GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)params->gbb_data;
+  VbPublicKey* root_key;
   VbLoadFirmwareInternal* lfi;
   VbNvContext* vnc = params->nv_context;
 
@@ -64,16 +66,19 @@
   /* Setup NV storage */
   VbNvSetup(vnc);
 
-  if (params->kernel_sign_key_size < sizeof(VbPublicKey)) {
-    VBDEBUG(("Kernel sign key buffer too small\n"));
+  /* Initialize shared data structure. */
+  if (0 != VbSharedDataInit(shared, params->shared_data_size)) {
+    VBDEBUG(("Shared data init error\n"));
+    recovery = VBNV_RECOVERY_RO_SHARED_DATA;
     goto LoadFirmwareExit;
   }
 
-  /* Must have a root key */
-  if (!root_key) {
-    VBDEBUG(("No root key\n"));
+  /* Must have a root key from the GBB */
+  if (!gbb) {
+    VBDEBUG(("No GBB\n"));
     goto LoadFirmwareExit;
   }
+  root_key = (VbPublicKey*)((uint8_t*)gbb + gbb->rootkey_offset);
 
   /* Parse flags */
   is_dev = (params->boot_flags & BOOT_FLAG_DEVELOPER ? 1 : 0);
@@ -235,21 +240,13 @@
     /* If we're still here, the firmware is valid. */
     VBDEBUG(("Firmware %d is valid.\n", index));
     if (-1 == good_index) {
-      VbPublicKey *kdest = (VbPublicKey*)params->kernel_sign_key_blob;
-
-      /* Copy the kernel sign key blob into the destination buffer */
-      PublicKeyInit(kdest, (uint8_t*)(kdest + 1),
-                    (params->kernel_sign_key_size - sizeof(VbPublicKey)));
-
-      if (0 != PublicKeyCopy(kdest, &preamble->kernel_subkey)) {
-        VBDEBUG(("Kernel subkey too big for buffer.\n"));
+      /* Save the key we actually used */
+      if (0 != VbSharedDataSetKernelKey(shared, &preamble->kernel_subkey)) {
+        VBDEBUG(("Unable to save kernel subkey to shared data.\n"));
         continue;  /* The firmware signature was good, but the public
                     * key was bigger that the caller can handle. */
       }
 
-      /* Save the key size we actually used */
-      params->kernel_sign_key_size = kdest->key_offset + kdest->key_size;
-
       /* Save the good index, now that we're sure we can actually use
        * this firmware.  That's the one we'll boot. */
       good_index = index;
@@ -314,6 +311,10 @@
           recovery : VBNV_RECOVERY_NOT_REQUESTED);
   VbNvTeardown(vnc);
 
+  /* Note that we don't reduce params->shared_data_size to shared->data_used,
+   * since we want to leave space for LoadKernel() to add to the shared data
+   * buffer. */
+
   return retval;
 }
 
diff --git a/firmware/lib/vboot_kernel.c b/firmware/lib/vboot_kernel.c
index ea39f7b..ee2890c 100644
--- a/firmware/lib/vboot_kernel.c
+++ b/firmware/lib/vboot_kernel.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  *
@@ -11,6 +11,7 @@
 #include "boot_device.h"
 #include "cgptlib.h"
 #include "cgptlib_internal.h"
+#include "gbb_header.h"
 #include "load_kernel_fw.h"
 #include "rollback_index.h"
 #include "utility.h"
@@ -118,7 +119,9 @@
 __pragma(warning(disable: 4127))
 
 int LoadKernel(LoadKernelParams* params) {
+  VbSharedDataHeader* shared = (VbSharedDataHeader*)params->shared_data_blob;
   VbNvContext* vnc = params->nv_context;
+  GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)params->gbb_data;
   VbPublicKey* kernel_subkey;
   GptData gpt;
   uint64_t part_start, part_size;
@@ -154,7 +157,6 @@
   }
 
   /* Initialization */
-  kernel_subkey = (VbPublicKey*)params->header_sign_key_blob;
   blba = params->bytes_per_lba;
   kbuf_sectors = KBUF_SIZE / blba;
   if (0 == kbuf_sectors) {
@@ -187,14 +189,30 @@
   params->bootloader_address = 0;
   params->bootloader_size = 0;
 
-  /* Let the TPM know if we're in recovery mode */
   if (kBootRecovery == boot_mode) {
+    /* Initialize the shared data structure, since LoadFirmware() didn't do it
+     * for us. */
+    if (0 != VbSharedDataInit(shared, params->shared_data_size)) {
+      /* Error initializing the shared data, but we can keep going.  We just
+       * can't use the shared data. */
+      VBDEBUG(("Shared data init error\n"));
+      params->shared_data_size = 0;
+      shared = NULL;
+    }
+
+    /* Use the recovery key to verify the kernel */
+    kernel_subkey = (VbPublicKey*)((uint8_t*)gbb + gbb->recovery_key_offset);
+
+    /* Let the TPM know if we're in recovery mode */
     if (0 != RollbackKernelRecovery(dev_switch)) {
       VBDEBUG(("Error setting up TPM for recovery kernel\n"));
       /* Ignore return code, since we need to boot recovery mode to
        * fix the TPM. */
     }
   } else {
+    /* Use the kernel subkey passed from LoadFirmware(). */
+    kernel_subkey = &shared->kernel_subkey;
+
     /* Read current kernel key index from TPM.  Assumes TPM is already
      * initialized. */
     status = RollbackKernelRead(&tpm_version);
@@ -505,5 +523,9 @@
           recovery : VBNV_RECOVERY_NOT_REQUESTED);
   VbNvTeardown(vnc);
 
+  /* Store how much shared data we used, if any */
+  if (shared)
+    params->shared_data_size = shared->data_used;
+
   return retval;
 }
diff --git a/firmware/linktest/main.c b/firmware/linktest/main.c
index b171f19..6f5b83e 100644
--- a/firmware/linktest/main.c
+++ b/firmware/linktest/main.c
@@ -79,6 +79,9 @@
   KeyBlockVerify(0, 0, 0, 0);
   VerifyFirmwarePreamble(0, 0, 0);
   VerifyKernelPreamble(0, 0, 0);
+  VbSharedDataInit(0, 0);
+  VbSharedDataReserve(0, 0);
+  VbSharedDataSetKernelKey(0, 0);
 
   VbNvSetup(0);
   VbNvGet(0, 0, 0);
diff --git a/firmware/stub/load_firmware_stub.c b/firmware/stub/load_firmware_stub.c
index 94d0bfa..0b0671a 100644
--- a/firmware/stub/load_firmware_stub.c
+++ b/firmware/stub/load_firmware_stub.c
@@ -63,7 +63,8 @@
  * volumes, this call will still be slow.  Once we reach feature
  * complete, you should modify your code to call LoadImage()
  * directly. */
-int VerifyFirmwareDriver_stub(uint8_t* root_key_blob,
+int VerifyFirmwareDriver_stub(uint8_t* gbb_data,
+                              uint64_t gbb_size,
                               uint8_t* verification_headerA,
                               uint8_t* firmwareA,
                               uint8_t* verification_headerB,
@@ -91,14 +92,15 @@
 
   /* Set up the params for LoadFirmware() */
   p.caller_internal = &ci;
-  p.firmware_root_key_blob = root_key_blob;
+  p.gbb_data = gbb_data;
+  p.gbb_size = gbb_size;
   p.verification_block_0 = verification_headerA;
   p.verification_block_1 = verification_headerB;
   p.nv_context = &vnc;
 
-  /* Allocate a key blob buffer */
-  p.kernel_sign_key_blob = Malloc(LOAD_FIRMWARE_KEY_BLOB_REC_SIZE);
-  p.kernel_sign_key_size = LOAD_FIRMWARE_KEY_BLOB_REC_SIZE;
+  /* Allocate a shared data buffer */
+  p.shared_data_blob = Malloc(LOAD_FIRMWARE_SHARED_DATA_REC_SIZE);
+  p.shared_data_size = LOAD_FIRMWARE_SHARED_DATA_REC_SIZE;
 
   /* TODO: YOU NEED TO SET THE BOOT FLAGS SOMEHOW */
   p.boot_flags = 0;
@@ -111,8 +113,8 @@
   }
 
   if (LOAD_FIRMWARE_SUCCESS == rv) {
-    /* TODO: YOU NEED TO KEEP TRACK OF p.kernel_sign_key_blob AND
-     * p.kernel_sign_key_size SO YOU CAN PASS THEM TO LoadKernel(). */
+    /* TODO: YOU NEED TO KEEP TRACK OF p.shared_data_blob AND
+     * p.shared_data_size SO YOU CAN PASS THEM TO LoadKernel(). */
 
     return (0 == p.firmware_index ? BOOT_FIRMWARE_A_CONTINUE :
             BOOT_FIRMWARE_B_CONTINUE);