vboot: Add VBOOT_CBFS_INTEGRATION support

This patch introduces support signing and verification of firmware
slots using CBFS metadata hash verification method for faster initial
verification. To have complete verification, CBFS_VERIFICATION should
also be enabled, as metadata hash covers only files metadata, not their
contents.

This patch also adapts mainboards and SoCs to new vboot reset
requirements.

TEST=Google Volteer/Voxel boots with VBOOT_CBFS_INTEGRATION enabled

Signed-off-by: Jakub Czapiga <jacz@semihalf.com>
Change-Id: I40ae01c477c4e4f7a1c90e4026a8a868ae64b5ca
Reviewed-on: https://review.coreboot.org/c/coreboot/+/66909
Reviewed-by: Yu-Ping Wu <yupingso@google.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
Jakub Czapiga
2022-08-19 12:25:27 +02:00
committed by Julius Werner
parent fe17a7d4d4
commit 967a76bd81
13 changed files with 102 additions and 33 deletions

View File

@ -13,11 +13,17 @@
* safety, we only need to verify the metadata hash in the initial stage and can assume it stays * safety, we only need to verify the metadata hash in the initial stage and can assume it stays
* valid in later stages. If TOCTOU safety is required, we may need them in every stage to * valid in later stages. If TOCTOU safety is required, we may need them in every stage to
* reverify metadata that had to be reloaded from flash (e.g. because it didn't fit the mcache). * reverify metadata that had to be reloaded from flash (e.g. because it didn't fit the mcache).
* Moreover, if VBOOT_CBFS_INTEGRATION and verification are both enabled, then hashing functions
* are required during verification stage.
* Note that this only concerns metadata hashing -- file access functions may still link hashing * Note that this only concerns metadata hashing -- file access functions may still link hashing
* routines independently for file data hashing. * routines independently for file data hashing.
*/ */
#define CBFS_ENABLE_HASHING (CONFIG(CBFS_VERIFICATION) && \ #define CBFS_ENABLE_HASHING (CONFIG(CBFS_VERIFICATION) && \
(CONFIG(TOCTOU_SAFETY) || ENV_INITIAL_STAGE)) (CONFIG(TOCTOU_SAFETY) || ENV_INITIAL_STAGE || \
(CONFIG(VBOOT_CBFS_INTEGRATION) && \
(verification_should_run() || \
(verstage_should_load() && \
CONFIG(VBOOT_RETURN_FROM_VERSTAGE))))))
#define CBFS_HASH_HWCRYPTO vboot_hwcrypto_allowed() #define CBFS_HASH_HWCRYPTO vboot_hwcrypto_allowed()
#define ERROR(...) printk(BIOS_ERR, "CBFS ERROR: " __VA_ARGS__) #define ERROR(...) printk(BIOS_ERR, "CBFS ERROR: " __VA_ARGS__)

View File

@ -12,6 +12,7 @@
#include <list.h> #include <list.h>
#include <metadata_hash.h> #include <metadata_hash.h>
#include <security/tpm/tspi/crtm.h> #include <security/tpm/tspi/crtm.h>
#include <security/vboot/vboot_common.h>
#include <security/vboot/misc.h> #include <security/vboot/misc.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -57,7 +58,10 @@ enum cb_err _cbfs_boot_lookup(const char *name, bool force_ro,
RO CBFS would have been caught when building the mcache in cbfs_get RO CBFS would have been caught when building the mcache in cbfs_get
boot_device(). (Note that TOCTOU_SAFETY implies !NO_CBFS_MCACHE.) */ boot_device(). (Note that TOCTOU_SAFETY implies !NO_CBFS_MCACHE.) */
assert(cbd == vboot_get_cbfs_boot_device()); assert(cbd == vboot_get_cbfs_boot_device());
die("TODO: set metadata_hash to RW metadata hash here.\n"); if (!CONFIG(VBOOT)
|| vb2api_get_metadata_hash(vboot_get_context(), &metadata_hash)
!= VB2_SUCCESS)
die("Failed to get RW metadata hash");
} }
err = cbfs_lookup(&cbd->rdev, name, mdata, &data_offset, metadata_hash); err = cbfs_lookup(&cbd->rdev, name, mdata, &data_offset, metadata_hash);
} }
@ -160,8 +164,14 @@ static bool cbfs_file_hash_mismatch(const void *buffer, size_t size,
ERROR("'%s' does not have a file hash!\n", mdata->h.filename); ERROR("'%s' does not have a file hash!\n", mdata->h.filename);
return true; return true;
} }
if (vb2_hash_verify(vboot_hwcrypto_allowed(), buffer, size, hash)) {
vb2_error_t rv = vb2_hash_verify(vboot_hwcrypto_allowed(), buffer, size, hash);
if (rv != VB2_SUCCESS) {
ERROR("'%s' file hash mismatch!\n", mdata->h.filename); ERROR("'%s' file hash mismatch!\n", mdata->h.filename);
if (CONFIG(VBOOT_CBFS_INTEGRATION) && !vboot_recovery_mode_enabled()
&& vboot_logic_executed())
vboot_fail_and_reboot(vboot_get_context(), VB2_RECOVERY_FW_BODY,
rv);
return true; return true;
} }
} }

View File

@ -12,6 +12,7 @@ romstage-y += memlayout.ld
romstage-y += boardid.c romstage-y += boardid.c
romstage-y += chromeos.c romstage-y += chromeos.c
romstage-y += regulator.c romstage-y += regulator.c
romstage-y += reset.c
romstage-y += romstage.c romstage-y += romstage.c
romstage-y += sdram_configs.c romstage-y += sdram_configs.c

View File

@ -10,6 +10,7 @@ romstage-y += memlayout.ld
romstage-y += boardid.c romstage-y += boardid.c
romstage-y += chromeos.c romstage-y += chromeos.c
romstage-y += regulator.c romstage-y += regulator.c
romstage-y += reset.c
romstage-y += romstage.c romstage-y += romstage.c
romstage-y += sdram_configs.c romstage-y += sdram_configs.c

View File

@ -10,6 +10,7 @@ romstage-y += memlayout.ld
romstage-y += boardid.c romstage-y += boardid.c
romstage-y += chromeos.c romstage-y += chromeos.c
romstage-y += regulator.c romstage-y += regulator.c
romstage-y += reset.c
romstage-y += romstage.c romstage-y += romstage.c
romstage-y += sdram_configs.c romstage-y += sdram_configs.c

View File

@ -9,6 +9,7 @@ verstage-y += reset.c
romstage-y += memlayout.ld romstage-y += memlayout.ld
romstage-y += chromeos.c romstage-y += chromeos.c
romstage-y += regulator.c romstage-y += regulator.c
romstage-y += reset.c
romstage-y += romstage.c romstage-y += romstage.c
romstage-y += sdram_configs.c romstage-y += sdram_configs.c

View File

@ -35,6 +35,20 @@ config VBOOT_SLOTS_RW_AB
help help
Have two update partitions beside the RO partition. Have two update partitions beside the RO partition.
config VBOOT_CBFS_INTEGRATION
bool "Enable vboot and CBFS integration"
default n
depends on VBOOT_SLOTS_RW_A
depends on CBFS_VERIFICATION
help
Say yes here to enable cryptographic verification of RW slots CBFS
metadata. This will replace body hash verification.
This option enables integration of vboot and CBFS. Verification of RW
slots is performed by calculation of their CBFS metadata hash.
It also requires CBFS_VERIFICATION to be enabled, so that CBFS files
contents are correctly verified.
config VBOOT_VBNV_CMOS config VBOOT_VBNV_CMOS
bool bool
default n default n

View File

@ -61,6 +61,7 @@ bootblock-y += vbnv.c
verstage-y += vbnv.c verstage-y += vbnv.c
romstage-y += vbnv.c romstage-y += vbnv.c
ramstage-y += vbnv.c ramstage-y += vbnv.c
postcar-y += vbnv.c
romstage-$(CONFIG_VBOOT_EARLY_EC_SYNC) += ec_sync.c romstage-$(CONFIG_VBOOT_EARLY_EC_SYNC) += ec_sync.c
@ -68,16 +69,19 @@ bootblock-$(CONFIG_VBOOT_VBNV_CMOS) += vbnv_cmos.c
verstage-$(CONFIG_VBOOT_VBNV_CMOS) += vbnv_cmos.c verstage-$(CONFIG_VBOOT_VBNV_CMOS) += vbnv_cmos.c
romstage-$(CONFIG_VBOOT_VBNV_CMOS) += vbnv_cmos.c romstage-$(CONFIG_VBOOT_VBNV_CMOS) += vbnv_cmos.c
ramstage-$(CONFIG_VBOOT_VBNV_CMOS) += vbnv_cmos.c ramstage-$(CONFIG_VBOOT_VBNV_CMOS) += vbnv_cmos.c
postcar-$(CONFIG_VBOOT_VBNV_CMOS) += vbnv_cmos.c
bootblock-$(CONFIG_VBOOT_VBNV_CMOS_BACKUP_TO_FLASH) += vbnv_flash.c bootblock-$(CONFIG_VBOOT_VBNV_CMOS_BACKUP_TO_FLASH) += vbnv_flash.c
verstage-$(CONFIG_VBOOT_VBNV_CMOS_BACKUP_TO_FLASH) += vbnv_flash.c verstage-$(CONFIG_VBOOT_VBNV_CMOS_BACKUP_TO_FLASH) += vbnv_flash.c
romstage-$(CONFIG_VBOOT_VBNV_CMOS_BACKUP_TO_FLASH) += vbnv_flash.c romstage-$(CONFIG_VBOOT_VBNV_CMOS_BACKUP_TO_FLASH) += vbnv_flash.c
ramstage-$(CONFIG_VBOOT_VBNV_CMOS_BACKUP_TO_FLASH) += vbnv_flash.c ramstage-$(CONFIG_VBOOT_VBNV_CMOS_BACKUP_TO_FLASH) += vbnv_flash.c
postcar-$(CONFIG_VBOOT_VBNV_CMOS_BACKUP_TO_FLASH) += vbnv_flash.c
bootblock-$(CONFIG_VBOOT_VBNV_FLASH) += vbnv_flash.c bootblock-$(CONFIG_VBOOT_VBNV_FLASH) += vbnv_flash.c
verstage-$(CONFIG_VBOOT_VBNV_FLASH) += vbnv_flash.c verstage-$(CONFIG_VBOOT_VBNV_FLASH) += vbnv_flash.c
romstage-$(CONFIG_VBOOT_VBNV_FLASH) += vbnv_flash.c romstage-$(CONFIG_VBOOT_VBNV_FLASH) += vbnv_flash.c
ramstage-$(CONFIG_VBOOT_VBNV_FLASH) += vbnv_flash.c ramstage-$(CONFIG_VBOOT_VBNV_FLASH) += vbnv_flash.c
postcar-$(CONFIG_VBOOT_VBNV_FLASH) += vbnv_flash.c
bootblock-y += vboot_loader.c bootblock-y += vboot_loader.c
romstage-y += vboot_loader.c romstage-y += vboot_loader.c

View File

@ -63,8 +63,16 @@ int vboot_locate_firmware(struct vb2_context *ctx, struct region_device *fw)
if (ret) if (ret)
return ret; return ret;
/* Truncate area to the size that was actually signed by vboot. */ /*
return rdev_chain(fw, fw, 0, vb2api_get_firmware_size(ctx)); * Truncate area to the size that was actually signed by vboot.
* It is only required for old verification mechanism calculating full body hash.
* New verification mechanism uses signature with zero data size, so truncation
* is not possible.
*/
if (!CONFIG(VBOOT_CBFS_INTEGRATION))
return rdev_chain(fw, fw, 0, vb2api_get_firmware_size(ctx));
return 0;
} }
static void vboot_setup_cbmem(int unused) static void vboot_setup_cbmem(int unused)

View File

@ -11,6 +11,11 @@
void vboot_save_data(struct vb2_context *ctx) void vboot_save_data(struct vb2_context *ctx)
{ {
if (!verification_should_run() && !(ENV_ROMSTAGE && CONFIG(VBOOT_EARLY_EC_SYNC))
&& (ctx->flags
& (VB2_CONTEXT_SECDATA_FIRMWARE_CHANGED | VB2_CONTEXT_SECDATA_KERNEL_CHANGED)))
die("TPM writeback in " ENV_STRING "?");
if (ctx->flags & VB2_CONTEXT_SECDATA_FIRMWARE_CHANGED && if (ctx->flags & VB2_CONTEXT_SECDATA_FIRMWARE_CHANGED &&
(CONFIG(VBOOT_MOCK_SECDATA) || tlcl_lib_init() == VB2_SUCCESS)) { (CONFIG(VBOOT_MOCK_SECDATA) || tlcl_lib_init() == VB2_SUCCESS)) {
printk(BIOS_INFO, "Saving secdata firmware\n"); printk(BIOS_INFO, "Saving secdata firmware\n");

View File

@ -28,15 +28,32 @@ int vboot_executed;
static void after_verstage(void) static void after_verstage(void)
{ {
struct vb2_hash *metadata_hash = NULL;
struct vb2_context *ctx = NULL;
if (CONFIG(VBOOT_CBFS_INTEGRATION)) {
ctx = vboot_get_context();
vb2_error_t rv = vb2api_get_metadata_hash(ctx, &metadata_hash);
if (rv)
vboot_fail_and_reboot(ctx, VB2_RECOVERY_FW_PREAMBLE, rv);
}
vboot_executed = 1; /* Mark verstage execution complete. */ vboot_executed = 1; /* Mark verstage execution complete. */
const struct cbfs_boot_device *cbd = vboot_get_cbfs_boot_device(); const struct cbfs_boot_device *cbd = vboot_get_cbfs_boot_device();
if (!cbd) /* Can't initialize RW CBFS in recovery mode. */ if (!cbd) /* Can't initialize RW CBFS in recovery mode. */
return; return;
enum cb_err err = cbfs_init_boot_device(cbd, NULL); /* TODO: RW hash */ enum cb_err err = cbfs_init_boot_device(cbd, metadata_hash);
if (err && err != CB_CBFS_CACHE_FULL) /* TODO: -> recovery? */ if (err && err != CB_CBFS_CACHE_FULL) {
die("RW CBFS initialization failure: %d", err); if (CONFIG(VBOOT_CBFS_INTEGRATION)) {
printk(BIOS_ERR, "RW CBFS initialization failed: %d\n", err);
/* CBFS error code does not fit in subcode. Use only lowest byte. */
vboot_fail_and_reboot(ctx, VB2_RECOVERY_FW_BODY, err & 0xFF);
} else {
die("RW CBFS initialization failure: %d", err);
}
}
} }
void vboot_run_logic(void) void vboot_run_logic(void)

View File

@ -54,7 +54,7 @@ vb2_error_t vb2ex_read_resource(struct vb2_context *ctx,
return VB2_SUCCESS; return VB2_SUCCESS;
} }
static int handle_digest_result(void *slot_hash, size_t slot_hash_sz) static vb2_error_t handle_digest_result(void *slot_hash, size_t slot_hash_sz)
{ {
int is_resume; int is_resume;
@ -63,14 +63,14 @@ static int handle_digest_result(void *slot_hash, size_t slot_hash_sz)
* vboot_retrieve_hash(), if Chrome EC is not enabled then return. * vboot_retrieve_hash(), if Chrome EC is not enabled then return.
*/ */
if (!CONFIG(EC_GOOGLE_CHROMEEC)) if (!CONFIG(EC_GOOGLE_CHROMEEC))
return 0; return VB2_SUCCESS;
/* /*
* Nothing to do since resuming on the platform doesn't require * Nothing to do since resuming on the platform doesn't require
* vboot verification again. * vboot verification again.
*/ */
if (!CONFIG(RESUME_PATH_SAME_AS_BOOT)) if (!CONFIG(RESUME_PATH_SAME_AS_BOOT))
return 0; return VB2_SUCCESS;
/* /*
* Assume that if vboot doesn't start in bootblock verified * Assume that if vboot doesn't start in bootblock verified
@ -78,7 +78,7 @@ static int handle_digest_result(void *slot_hash, size_t slot_hash_sz)
* lives in RO CBFS. * lives in RO CBFS.
*/ */
if (!CONFIG(VBOOT_STARTS_IN_BOOTBLOCK)) if (!CONFIG(VBOOT_STARTS_IN_BOOTBLOCK))
return 0; return VB2_SUCCESS;
is_resume = platform_is_resuming(); is_resume = platform_is_resuming();
@ -92,12 +92,12 @@ static int handle_digest_result(void *slot_hash, size_t slot_hash_sz)
if (vboot_retrieve_hash(saved_hash, saved_hash_sz)) { if (vboot_retrieve_hash(saved_hash, saved_hash_sz)) {
printk(BIOS_ERR, "Couldn't retrieve saved hash.\n"); printk(BIOS_ERR, "Couldn't retrieve saved hash.\n");
return -1; return VB2_ERROR_UNKNOWN;
} }
if (memcmp(saved_hash, slot_hash, slot_hash_sz)) { if (memcmp(saved_hash, slot_hash, slot_hash_sz)) {
printk(BIOS_ERR, "Hash mismatch on resume.\n"); printk(BIOS_ERR, "Hash mismatch on resume.\n");
return -1; return VB2_ERROR_UNKNOWN;
} }
} else if (is_resume < 0) } else if (is_resume < 0)
printk(BIOS_ERR, "Unable to determine if platform resuming.\n"); printk(BIOS_ERR, "Unable to determine if platform resuming.\n");
@ -111,10 +111,10 @@ static int handle_digest_result(void *slot_hash, size_t slot_hash_sz)
* lead to a reboot loop. The consequence of this is that * lead to a reboot loop. The consequence of this is that
* we will most likely fail resuming because of EC issues or * we will most likely fail resuming because of EC issues or
* the hash digest not matching. */ * the hash digest not matching. */
return 0; return VB2_SUCCESS;
} }
return 0; return VB2_SUCCESS;
} }
static vb2_error_t hash_body(struct vb2_context *ctx, static vb2_error_t hash_body(struct vb2_context *ctx,
@ -179,10 +179,7 @@ static vb2_error_t hash_body(struct vb2_context *ctx,
timestamp_add_now(TS_HASH_BODY_END); timestamp_add_now(TS_HASH_BODY_END);
if (handle_digest_result(hash_digest, hash_digest_sz)) return handle_digest_result(hash_digest, hash_digest_sz);
return VB2_ERROR_UNKNOWN;
return VB2_SUCCESS;
} }
static uint32_t extend_pcrs(struct vb2_context *ctx) static uint32_t extend_pcrs(struct vb2_context *ctx)
@ -236,16 +233,10 @@ static void check_boot_mode(struct vb2_context *ctx)
ctx->flags |= VB2_CONTEXT_EC_TRUSTED; ctx->flags |= VB2_CONTEXT_EC_TRUSTED;
} }
/** /* Verify and select the firmware in the RW image */
* Verify and select the firmware in the RW image
*
* TODO: Avoid loading a stage twice (once in hash_body & again in load_stage).
* when per-stage verification is ready.
*/
void verstage_main(void) void verstage_main(void)
{ {
struct vb2_context *ctx; struct vb2_context *ctx;
struct region_device fw_body;
vb2_error_t rv; vb2_error_t rv;
timestamp_add_now(TS_VBOOT_START); timestamp_add_now(TS_VBOOT_START);
@ -326,7 +317,6 @@ void verstage_main(void)
extend_pcrs(ctx); /* ignore failures */ extend_pcrs(ctx); /* ignore failures */
goto verstage_main_exit; goto verstage_main_exit;
} }
vboot_save_and_reboot(ctx, rv); vboot_save_and_reboot(ctx, rv);
} }
@ -345,12 +335,22 @@ void verstage_main(void)
vboot_save_and_reboot(ctx, rv); vboot_save_and_reboot(ctx, rv);
printk(BIOS_INFO, "Phase 4\n"); printk(BIOS_INFO, "Phase 4\n");
rv = vboot_locate_firmware(ctx, &fw_body); if (CONFIG(VBOOT_CBFS_INTEGRATION)) {
if (rv) struct vb2_hash *metadata_hash;
die_with_post_code(POST_INVALID_ROM, rv = vb2api_get_metadata_hash(ctx, &metadata_hash);
"Failed to read FMAP to locate firmware"); if (rv == VB2_SUCCESS)
rv = handle_digest_result(metadata_hash->raw,
vb2_digest_size(metadata_hash->algo));
} else {
struct region_device fw_body;
rv = vboot_locate_firmware(ctx, &fw_body);
if (rv)
die_with_post_code(POST_INVALID_ROM,
"Failed to read FMAP to locate firmware");
rv = hash_body(ctx, &fw_body);
}
rv = hash_body(ctx, &fw_body);
if (rv) if (rv)
vboot_save_and_reboot(ctx, rv); vboot_save_and_reboot(ctx, rv);
vboot_save_data(ctx); vboot_save_data(ctx);

View File

@ -46,6 +46,7 @@ romstage-y += ../common/pmic_wrap.c pmic_wrap.c mt6391.c
romstage-y += memory.c romstage-y += memory.c
romstage-y += emi.c dramc_pi_basic_api.c dramc_pi_calibration_api.c romstage-y += emi.c dramc_pi_basic_api.c dramc_pi_calibration_api.c
romstage-$(CONFIG_MEMORY_TEST) += ../common/memory_test.c romstage-$(CONFIG_MEMORY_TEST) += ../common/memory_test.c
romstage-y += ../common/wdt.c ../common/reset.c
romstage-y += ../common/mmu_operations.c mmu_operations.c romstage-y += ../common/mmu_operations.c mmu_operations.c
romstage-y += ../common/rtc.c rtc.c romstage-y += ../common/rtc.c rtc.c