Attention is currently required from: Julius Werner, Nick Vaccaro, Yu-Ping Wu.
Vladimir Serbinenko has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/81508?usp=email )
Change subject: Support for creating hybrid vboot images ......................................................................
Support for creating hybrid vboot images
This allows creating an image where RO is triggered by default with normal RW secrets locked out. On a signal in CMOS, clear the signal and follow normal RW_A/RW_B path. This allows dual-boot between stock ChromeOS and an alternative payload while keeping compatibility with ChromeOS updates.
Change-Id: I9b26c332f5bf6befd62b5930b19d1b20e76261e7 Signed-off-by: Vladimir Serbinenko phcoder@gmail.com --- M src/drivers/mrc_cache/mrc_cache.c M src/mainboard/google/volteer/Kconfig M src/security/vboot/Kconfig M src/security/vboot/misc.h M src/security/vboot/vboot_common.h M src/security/vboot/vboot_loader.c M src/security/vboot/vboot_logic.c M src/soc/intel/common/block/cse/cse_eop.c 8 files changed, 110 insertions(+), 9 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/08/81508/1
diff --git a/src/drivers/mrc_cache/mrc_cache.c b/src/drivers/mrc_cache/mrc_cache.c index 17f5fee..49f5f7f 100644 --- a/src/drivers/mrc_cache/mrc_cache.c +++ b/src/drivers/mrc_cache/mrc_cache.c @@ -134,7 +134,7 @@ int i; int flags;
- if (CONFIG(VBOOT_STARTS_IN_BOOTBLOCK) && vboot_recovery_mode_enabled()) + if (CONFIG(VBOOT_STARTS_IN_BOOTBLOCK) && (CONFIG(VBOOT_HYBRID) || vboot_recovery_mode_enabled())) flags = RECOVERY_FLAG; else flags = NORMAL_FLAG; @@ -296,7 +296,7 @@ * switch is set. */ if (CONFIG(VBOOT_STARTS_IN_BOOTBLOCK) && vboot_recovery_mode_enabled() - && get_recovery_mode_retrain_switch()) + && get_recovery_mode_retrain_switch() && !CONFIG(VBOOT_HYBRID)) return -1;
cr = lookup_region(®ion, type); @@ -614,6 +614,9 @@ if (!CONFIG(HAS_RECOVERY_MRC_CACHE) && CONFIG(VBOOT_STARTS_IN_ROMSTAGE)) return;
+ if (CONFIG(VBOOT_HYBRID)) + return; + /* We only invalidate the normal cache in recovery mode. */ if (!vboot_recovery_mode_enabled()) return; diff --git a/src/mainboard/google/volteer/Kconfig b/src/mainboard/google/volteer/Kconfig index bc5ee6c..0ab6ee0 100644 --- a/src/mainboard/google/volteer/Kconfig +++ b/src/mainboard/google/volteer/Kconfig @@ -148,7 +148,7 @@ select GBB_FLAG_FORCE_DEV_SWITCH_ON select GBB_FLAG_FORCE_MANUAL_RECOVERY select HAS_RECOVERY_MRC_CACHE - select VBOOT_EARLY_EC_SYNC + select VBOOT_EARLY_EC_SYNC if !VBOOT_HYBRID select VBOOT_LID_SWITCH
config VBOOT_GSCVD diff --git a/src/security/vboot/Kconfig b/src/security/vboot/Kconfig index d42dc74..7549a83 100644 --- a/src/security/vboot/Kconfig +++ b/src/security/vboot/Kconfig @@ -21,6 +21,13 @@
if VBOOT
+config VBOOT_HYBRID + bool "Create hybrid vboot image." + default n + help + Enable VBOOT only in bootblock and add code to control it from CMOS. Useful for creating hybrid ROM with RW going vboot path and RO not. + + comment "Anti-Rollback Protection disabled because mocking secdata is enabled." depends on VBOOT_MOCK_SECDATA
diff --git a/src/security/vboot/misc.h b/src/security/vboot/misc.h index a7069f3..1ac9df4 100644 --- a/src/security/vboot/misc.h +++ b/src/security/vboot/misc.h @@ -67,6 +67,9 @@ { extern int vboot_executed; /* should not be globally accessible */
+ if (CONFIG(VBOOT_HYBRID) && !ENV_BOOTBLOCK) + return false; + /* If we are in the stage that runs verification, or in the stage that both loads the verstage and is returned to from it afterwards, we need to check a global to see if verification has run. */ diff --git a/src/security/vboot/vboot_common.h b/src/security/vboot/vboot_common.h index 2399bf3..fc2d65b 100644 --- a/src/security/vboot/vboot_common.h +++ b/src/security/vboot/vboot_common.h @@ -51,7 +51,7 @@ * Main logic for verified boot. verstage_main() is just the core vboot logic. * If the verstage is a separate stage, it should be entered via main(). */ -void verstage_main(void); +int verstage_main(void); void verstage_mainboard_early_init(void); void verstage_mainboard_init(void);
diff --git a/src/security/vboot/vboot_loader.c b/src/security/vboot/vboot_loader.c index 70e6685..4fbde61 100644 --- a/src/security/vboot/vboot_loader.c +++ b/src/security/vboot/vboot_loader.c @@ -60,8 +60,8 @@ { if (verification_should_run()) { /* Note: this path is not used for VBOOT_RETURN_FROM_VERSTAGE */ - verstage_main(); - after_verstage(); + if (verstage_main()) + after_verstage(); } else if (verstage_should_load()) { struct prog verstage = PROG_INIT(PROG_VERSTAGE, diff --git a/src/security/vboot/vboot_logic.c b/src/security/vboot/vboot_logic.c index f98b083..784f1d0 100644 --- a/src/security/vboot/vboot_logic.c +++ b/src/security/vboot/vboot_logic.c @@ -16,6 +16,7 @@ #include <timestamp.h> #include <vb2_api.h> #include <boot_device.h> +#include <pc80/mc146818rtc.h>
#include "antirollback.h"
@@ -241,12 +242,92 @@ ctx->flags |= VB2_CONTEXT_EC_TRUSTED; }
+#define RECOVERY_OVERRIDE_ADDR 0xf7 + +static bool use_vboot(bool is_s3) +{ +#if CONFIG(VBOOT_HYBRID) + uint8_t recovery_override = cmos_read(RECOVERY_OVERRIDE_ADDR); + int counter = recovery_override & 0xf; + + printk(BIOS_INFO, "recovery_override=0x%x, is_s3=%d\n", recovery_override, is_s3); + + if ((recovery_override & 0xf0) != 0xc0) + return 0; + + if (is_s3) + return 1; + + if (counter == 0) { + cmos_write(0xff, RECOVERY_OVERRIDE_ADDR); + return 0; + } + + if (counter != 0xf) + cmos_write(0xc0 | (counter - 1), RECOVERY_OVERRIDE_ADDR); +#endif + + return 1; +} + +static bool ensure_tpm_rw_is_locked(bool is_s3) +{ + static const uint8_t boot_mode_digest[VB2_PCR_DIGEST_RECOMMENDED_SIZE] = { + /* sha256("skipmode") */ + 0x73, 0x17, 0x91, 0x09, 0x91, 0x2a, 0xbd, 0xcc, + 0x23, 0xad, 0x82, 0x2c, 0x2f, 0xd5, 0x81, 0xad, + 0xc5, 0xa6, 0xef, 0xc2, 0xae, 0x73, 0xfd, 0xb3, + 0x7b, 0x56, 0xb7, 0x14, 0xbd, 0xb9, 0x82, 0x99, + }; + static const uint8_t hwid_unknown_digest[VB2_PCR_DIGEST_RECOMMENDED_SIZE] = { + /* sha256("unknown") */ + 0xb2, 0x3a, 0x6a, 0x84, 0x39, 0xc0, 0xdd, 0xe5, + 0x51, 0x58, 0x93, 0xe7, 0xc9, 0x0c, 0x1e, 0x32, + 0x33, 0xb8, 0x61, 0x6e, 0x63, 0x44, 0x70, 0xf2, + 0x0d, 0xc4, 0x92, 0x8b, 0xcf, 0x36, 0x09, 0xbc + }; + uint8_t buffer[VB2_PCR_DIGEST_RECOMMENDED_SIZE]; + + int algo = CONFIG(TPM1) ? VB2_HASH_SHA1 : VB2_HASH_SHA256; + int digest_size = CONFIG(TPM1) ? VB2_SHA1_DIGEST_SIZE : VB2_SHA256_DIGEST_SIZE; + int rv = tpm_setup(is_s3); + + if (rv) { + printk(BIOS_ERR, "TPM setup failed with 0x%x\n", rv); + return 0; + } + + rv = tpm_extend_pcr(CONFIG_PCR_BOOT_MODE, algo, boot_mode_digest, digest_size, "VBOOT: boot mode"); + if (rv) { + printk(BIOS_ERR, "Boot mode extend failed with 0x%x\n", rv); + return 0; + } + + memcpy(buffer, hwid_unknown_digest, sizeof(buffer)); + + struct region_device rdev; + + if (fmap_locate_area_as_rdev("GBB", &rdev)) + rdev_readat(&rdev, buffer, 48, 32); + + rv = tpm_extend_pcr(CONFIG_PCR_HWID, algo, buffer, digest_size, "VBOOT: GBB HWID"); + if (rv) { + printk(BIOS_ERR, "HWID extend failed with 0x%x\n", rv); + return 0; + } + + printk(BIOS_INFO, "Successfully locked-out RW secrets\n"); + + return 1; +} + /* Verify and select the firmware in the RW image */ -void verstage_main(void) +int verstage_main(void) { struct vb2_context *ctx; tpm_result_t tpm_rc; vb2_error_t rv; + bool is_s3 = platform_is_resuming();
timestamp_add_now(TS_VBOOT_START);
@@ -254,6 +335,11 @@ if (CONFIG(BOOTMEDIA_LOCK_IN_VERSTAGE)) boot_device_security_lockdown();
+ if (!use_vboot(is_s3)) { + if (ensure_tpm_rw_is_locked(is_s3)) + return 0; + } + /* Set up context and work buffer */ ctx = vboot_get_context();
@@ -265,7 +351,7 @@ * does verification of memory init and thus must ensure it resumes with * the same slot that it booted from. */ if (CONFIG(RESUME_PATH_SAME_AS_BOOT) && - platform_is_resuming()) + is_s3) ctx->flags |= VB2_CONTEXT_S3_RESUME;
if (!CONFIG(VBOOT_SLOTS_RW_AB)) @@ -420,4 +506,6 @@
verstage_main_exit: timestamp_add_now(TS_VBOOT_END); + + return 1; } diff --git a/src/soc/intel/common/block/cse/cse_eop.c b/src/soc/intel/common/block/cse/cse_eop.c index 265fe04..f883cc9 100644 --- a/src/soc/intel/common/block/cse/cse_eop.c +++ b/src/soc/intel/common/block/cse/cse_eop.c @@ -209,7 +209,7 @@ printk(BIOS_ERR, "Failed to send EOP to CSE, %d\n", result); /* For vboot, trigger recovery mode if applicable, as there is likely something very broken in this case. */ - if (CONFIG(VBOOT) && !vboot_recovery_mode_enabled()) + if (CONFIG(VBOOT) && !CONFIG(VBOOT_HYBRID) && !vboot_recovery_mode_enabled()) cse_trigger_vboot_recovery(CSE_EOP_FAIL);
/* In non-vboot builds or recovery mode, follow the BWG in order