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);