Skip to content

Commit

Permalink
vboot: Add VBOOT_CBFS_INTEGRATION support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jakub Czapiga authored and jwerner-chromium committed Nov 8, 2022
1 parent fe17a7d commit 967a76b
Show file tree
Hide file tree
Showing 13 changed files with 102 additions and 33 deletions.
8 changes: 7 additions & 1 deletion src/include/cbfs_glue.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@
* 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
* 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
* routines independently for file data hashing.
*/
#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 ERROR(...) printk(BIOS_ERR, "CBFS ERROR: " __VA_ARGS__)
Expand Down
14 changes: 12 additions & 2 deletions src/lib/cbfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <list.h>
#include <metadata_hash.h>
#include <security/tpm/tspi/crtm.h>
#include <security/vboot/vboot_common.h>
#include <security/vboot/misc.h>
#include <stdlib.h>
#include <string.h>
Expand Down Expand Up @@ -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
boot_device(). (Note that TOCTOU_SAFETY implies !NO_CBFS_MCACHE.) */
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);
}
Expand Down Expand Up @@ -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);
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);
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;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/mainboard/google/asurada/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ romstage-y += memlayout.ld
romstage-y += boardid.c
romstage-y += chromeos.c
romstage-y += regulator.c
romstage-y += reset.c
romstage-y += romstage.c
romstage-y += sdram_configs.c

Expand Down
1 change: 1 addition & 0 deletions src/mainboard/google/cherry/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ romstage-y += memlayout.ld
romstage-y += boardid.c
romstage-y += chromeos.c
romstage-y += regulator.c
romstage-y += reset.c
romstage-y += romstage.c
romstage-y += sdram_configs.c

Expand Down
1 change: 1 addition & 0 deletions src/mainboard/google/corsola/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ romstage-y += memlayout.ld
romstage-y += boardid.c
romstage-y += chromeos.c
romstage-y += regulator.c
romstage-y += reset.c
romstage-y += romstage.c
romstage-y += sdram_configs.c

Expand Down
1 change: 1 addition & 0 deletions src/mainboard/google/geralt/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ verstage-y += reset.c
romstage-y += memlayout.ld
romstage-y += chromeos.c
romstage-y += regulator.c
romstage-y += reset.c
romstage-y += romstage.c
romstage-y += sdram_configs.c

Expand Down
14 changes: 14 additions & 0 deletions src/security/vboot/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ config VBOOT_SLOTS_RW_AB
help
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
bool
default n
Expand Down
4 changes: 4 additions & 0 deletions src/security/vboot/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -61,23 +61,27 @@ bootblock-y += vbnv.c
verstage-y += vbnv.c
romstage-y += vbnv.c
ramstage-y += vbnv.c
postcar-y += vbnv.c

romstage-$(CONFIG_VBOOT_EARLY_EC_SYNC) += ec_sync.c

bootblock-$(CONFIG_VBOOT_VBNV_CMOS) += vbnv_cmos.c
verstage-$(CONFIG_VBOOT_VBNV_CMOS) += vbnv_cmos.c
romstage-$(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
verstage-$(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
postcar-$(CONFIG_VBOOT_VBNV_CMOS_BACKUP_TO_FLASH) += vbnv_flash.c

bootblock-$(CONFIG_VBOOT_VBNV_FLASH) += vbnv_flash.c
verstage-$(CONFIG_VBOOT_VBNV_FLASH) += vbnv_flash.c
romstage-$(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
romstage-y += vboot_loader.c
Expand Down
12 changes: 10 additions & 2 deletions src/security/vboot/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,16 @@ int vboot_locate_firmware(struct vb2_context *ctx, struct region_device *fw)
if (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)
Expand Down
5 changes: 5 additions & 0 deletions src/security/vboot/vboot_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@

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 &&
(CONFIG(VBOOT_MOCK_SECDATA) || tlcl_lib_init() == VB2_SUCCESS)) {
printk(BIOS_INFO, "Saving secdata firmware\n");
Expand Down
23 changes: 20 additions & 3 deletions src/security/vboot/vboot_loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,32 @@ int vboot_executed;

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. */

const struct cbfs_boot_device *cbd = vboot_get_cbfs_boot_device();
if (!cbd) /* Can't initialize RW CBFS in recovery mode. */
return;

enum cb_err err = cbfs_init_boot_device(cbd, NULL); /* TODO: RW hash */
if (err && err != CB_CBFS_CACHE_FULL) /* TODO: -> recovery? */
die("RW CBFS initialization failure: %d", err);
enum cb_err err = cbfs_init_boot_device(cbd, metadata_hash);
if (err && err != CB_CBFS_CACHE_FULL) {
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)
Expand Down
50 changes: 25 additions & 25 deletions src/security/vboot/vboot_logic.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ vb2_error_t vb2ex_read_resource(struct vb2_context *ctx,
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;

Expand All @@ -63,22 +63,22 @@ static int handle_digest_result(void *slot_hash, size_t slot_hash_sz)
* vboot_retrieve_hash(), if Chrome EC is not enabled then return.
*/
if (!CONFIG(EC_GOOGLE_CHROMEEC))
return 0;
return VB2_SUCCESS;

/*
* Nothing to do since resuming on the platform doesn't require
* vboot verification again.
*/
if (!CONFIG(RESUME_PATH_SAME_AS_BOOT))
return 0;
return VB2_SUCCESS;

/*
* 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 (!CONFIG(VBOOT_STARTS_IN_BOOTBLOCK))
return 0;
return VB2_SUCCESS;

is_resume = platform_is_resuming();

Expand All @@ -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)) {
printk(BIOS_ERR, "Couldn't retrieve saved hash.\n");
return -1;
return VB2_ERROR_UNKNOWN;
}

if (memcmp(saved_hash, slot_hash, slot_hash_sz)) {
printk(BIOS_ERR, "Hash mismatch on resume.\n");
return -1;
return VB2_ERROR_UNKNOWN;
}
} else if (is_resume < 0)
printk(BIOS_ERR, "Unable to determine if platform resuming.\n");
Expand All @@ -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
* we will most likely fail resuming because of EC issues or
* 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,
Expand Down Expand Up @@ -179,10 +179,7 @@ static vb2_error_t hash_body(struct vb2_context *ctx,

timestamp_add_now(TS_HASH_BODY_END);

if (handle_digest_result(hash_digest, hash_digest_sz))
return VB2_ERROR_UNKNOWN;

return VB2_SUCCESS;
return handle_digest_result(hash_digest, hash_digest_sz);
}

static uint32_t extend_pcrs(struct vb2_context *ctx)
Expand Down Expand Up @@ -236,16 +233,10 @@ static void check_boot_mode(struct vb2_context *ctx)
ctx->flags |= VB2_CONTEXT_EC_TRUSTED;
}

/**
* 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.
*/
/* Verify and select the firmware in the RW image */
void verstage_main(void)
{
struct vb2_context *ctx;
struct region_device fw_body;
vb2_error_t rv;

timestamp_add_now(TS_VBOOT_START);
Expand Down Expand Up @@ -326,7 +317,6 @@ void verstage_main(void)
extend_pcrs(ctx); /* ignore failures */
goto verstage_main_exit;
}

vboot_save_and_reboot(ctx, rv);
}

Expand All @@ -345,12 +335,22 @@ void verstage_main(void)
vboot_save_and_reboot(ctx, rv);

printk(BIOS_INFO, "Phase 4\n");
rv = vboot_locate_firmware(ctx, &fw_body);
if (rv)
die_with_post_code(POST_INVALID_ROM,
"Failed to read FMAP to locate firmware");
if (CONFIG(VBOOT_CBFS_INTEGRATION)) {
struct vb2_hash *metadata_hash;
rv = vb2api_get_metadata_hash(ctx, &metadata_hash);
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)
vboot_save_and_reboot(ctx, rv);
vboot_save_data(ctx);
Expand Down
1 change: 1 addition & 0 deletions src/soc/mediatek/mt8173/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ romstage-y += ../common/pmic_wrap.c pmic_wrap.c mt6391.c
romstage-y += memory.c
romstage-y += emi.c dramc_pi_basic_api.c dramc_pi_calibration_api.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/rtc.c rtc.c

Expand Down

0 comments on commit 967a76b

Please sign in to comment.