chromeos/vboot: provide support for x86 memory init verification

For x86 systems which resume through the reset vector one needs to
ensure the the RW slot taken at resume time matches the one at
boot time. The reason is that any assets pulled out of the boot
media need to match how the platform previously booted. To do
that one needs obtain the hash digest of the chosen slot, and it
needs to be saved in a secure place on the normal boot path. On
resume one needs to retrieve the hash digest back to compare it
with the chosen slot. If they don't match resuming won't be
possible.

BUG=chrome-os-partner:46049
BRANCH=glados
TEST=Suspended and resumed on chell. Also, tested with an EC build
     which returns a bad hash to ensure that is properly caught.
CQ-DEPEND=CL:323460

Change-Id: I90ce26813b67f46913aa4026b42d9490a564bb6c
Signed-off-by: Patrick Georgi <pgeorgi@chromium.org>
Original-Commit-Id: 01a42c0ecfc6d60d1d2e5e36a86781d91d5c47a9
Original-Change-Id: I6c6bdce7e06712bc06cc620a3d7a6a6250c59c95
Original-Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/323500
Original-Reviewed-by: Patrick Georgi <pgeorgi@chromium.org>
Original-Reviewed-by: Duncan Laurie <dlaurie@chromium.org>
Reviewed-on: https://review.coreboot.org/13574
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
Aaron Durbin 2016-01-22 15:26:04 -06:00 committed by Patrick Georgi
parent 43e6d6a124
commit 87c9faeb4c
3 changed files with 96 additions and 1 deletions

View File

@ -503,6 +503,15 @@ config HAVE_ACPI_RESUME
bool
default n
config RESUME_PATH_SAME_AS_BOOT
bool
default y if ARCH_X86
depends on HAVE_ACPI_RESUME
help
This option indicates that when a system resumes it takes the
same path as a regular boot. e.g. an x86 system runs from the
reset vector at 0xfffffff0 on both resume and warm/cold boot.
config HAVE_HARD_RESET
bool
default n

View File

@ -26,6 +26,9 @@
#include "../chromeos.h"
#include "misc.h"
/* The max hash size to expect is for SHA512. */
#define VBOOT_MAX_HASH_SIZE VB2_SHA512_DIGEST_SIZE
#define TODO_BLOCK_SIZE 1024
static int is_slot_a(struct vb2_context *ctx)
@ -111,15 +114,77 @@ int vb2ex_hwcrypto_digest_finalize(uint8_t *digest, uint32_t digest_size)
return VB2_ERROR_UNKNOWN;
}
static int handle_digest_result(void *slot_hash, size_t slot_hash_sz)
{
int is_resume;
/*
* Nothing to do since resuming on the platform doesn't require
* vboot verification again.
*/
if (!IS_ENABLED(CONFIG_RESUME_PATH_SAME_AS_BOOT))
return 0;
/*
* Assume that if vboot doesn't start in bootblock verified
* RW memory init code is not employed. i.e. memory init code
* lives in RO CBFS.
*/
if (!IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK))
return 0;
is_resume = vboot_platform_is_resuming();
if (is_resume > 0) {
uint8_t saved_hash[VBOOT_MAX_HASH_SIZE];
const size_t saved_hash_sz = sizeof(saved_hash);
assert(slot_hash_sz == saved_hash_sz);
printk(BIOS_DEBUG, "Platform is resuming.\n");
if (vboot_retrieve_hash(saved_hash, saved_hash_sz)) {
printk(BIOS_ERR, "Couldn't retrieve saved hash.\n");
return -1;
}
if (memcmp(saved_hash, slot_hash, slot_hash_sz)) {
printk(BIOS_ERR, "Hash mismatch on resume.\n");
return -1;
}
} else if (is_resume < 0)
printk(BIOS_ERR, "Unable to determine if platform resuming.\n");
printk(BIOS_DEBUG, "Saving vboot hash.\n");
/* Always save the hash for the current boot. */
if (vboot_save_hash(slot_hash, slot_hash_sz)) {
printk(BIOS_ERR, "Error saving vboot hash.\n");
/* Though this is an error don't report it up since it could
* lead to a reboot loop. The consequence of this is that
* we will most likely fail resuming because of EC issues or
* the hash digest not matching. */
return 0;
}
return 0;
}
static int hash_body(struct vb2_context *ctx, struct region_device *fw_main)
{
uint64_t load_ts;
uint32_t expected_size;
uint8_t block[TODO_BLOCK_SIZE];
uint8_t hash_digest[VBOOT_MAX_HASH_SIZE];
const size_t hash_digest_sz = sizeof(hash_digest);
size_t block_size = sizeof(block);
size_t offset;
int rv;
/* Clear the full digest so that any hash digests less than the
* max have trailing zeros. */
memset(hash_digest, 0, hash_digest_sz);
/*
* Since loading the firmware and calculating its hash is intertwined,
* we use this little trick to measure them separately and pretend it
@ -160,12 +225,15 @@ static int hash_body(struct vb2_context *ctx, struct region_device *fw_main)
timestamp_add_now(TS_DONE_HASHING);
/* Check the result (with RSA signature verification) */
rv = vb2api_check_hash(ctx);
rv = vb2api_check_hash_get_digest(ctx, hash_digest, hash_digest_sz);
if (rv)
return rv;
timestamp_add_now(TS_END_HASH_BODY);
if (handle_digest_result(hash_digest, hash_digest_sz))
return VB2_ERROR_UNKNOWN;
return VB2_SUCCESS;
}

View File

@ -46,6 +46,24 @@ int vboot_recovery_reason(void);
void vboot_reboot(void);
/*
* Save the provided hash digest to a secure location to check against in
* the resume path. Returns 0 on success, < 0 on error.
*/
int vboot_save_hash(void *digest, size_t digest_size);
/*
* Retrieve the previously saved hash digest. Returns 0 on success,
* < 0 on error.
*/
int vboot_retrieve_hash(void *digest, size_t digest_size);
/*
* Determine if the platform is resuming from suspend. Returns 0 when
* not resuming, > 0 if resuming, and < 0 on error.
*/
int vboot_platform_is_resuming(void);
/* Main logic for verified boot. verstage() is the stage entry point
* while the verstage_main() is just the core logic. */
void verstage_main(void);