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>
diff --git a/src/include/cbfs_glue.h b/src/include/cbfs_glue.h
index d4fe367..99dc48d 100644
--- a/src/include/cbfs_glue.h
+++ b/src/include/cbfs_glue.h
@@ -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__)
diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c
index 4f2d9ca..d2829d1 100644
--- a/src/lib/cbfs.c
+++ b/src/lib/cbfs.c
@@ -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>
@@ -57,7 +58,10 @@
 			   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);
 	}
@@ -160,8 +164,14 @@
 			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;
 		}
 	}
diff --git a/src/mainboard/google/asurada/Makefile.inc b/src/mainboard/google/asurada/Makefile.inc
index 601b485..84600b6 100644
--- a/src/mainboard/google/asurada/Makefile.inc
+++ b/src/mainboard/google/asurada/Makefile.inc
@@ -12,6 +12,7 @@
 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
 
diff --git a/src/mainboard/google/cherry/Makefile.inc b/src/mainboard/google/cherry/Makefile.inc
index e1e363c..4edbd38 100644
--- a/src/mainboard/google/cherry/Makefile.inc
+++ b/src/mainboard/google/cherry/Makefile.inc
@@ -10,6 +10,7 @@
 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
 
diff --git a/src/mainboard/google/corsola/Makefile.inc b/src/mainboard/google/corsola/Makefile.inc
index 30ee6c1..d11f6ba 100644
--- a/src/mainboard/google/corsola/Makefile.inc
+++ b/src/mainboard/google/corsola/Makefile.inc
@@ -10,6 +10,7 @@
 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
 
diff --git a/src/mainboard/google/geralt/Makefile.inc b/src/mainboard/google/geralt/Makefile.inc
index 85748dd..006b4f2 100644
--- a/src/mainboard/google/geralt/Makefile.inc
+++ b/src/mainboard/google/geralt/Makefile.inc
@@ -9,6 +9,7 @@
 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
 
diff --git a/src/security/vboot/Kconfig b/src/security/vboot/Kconfig
index a36510d..93e1845 100644
--- a/src/security/vboot/Kconfig
+++ b/src/security/vboot/Kconfig
@@ -35,6 +35,20 @@
 	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
diff --git a/src/security/vboot/Makefile.inc b/src/security/vboot/Makefile.inc
index bd73871..e4771dd 100644
--- a/src/security/vboot/Makefile.inc
+++ b/src/security/vboot/Makefile.inc
@@ -61,6 +61,7 @@
 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
 
@@ -68,16 +69,19 @@
 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
diff --git a/src/security/vboot/common.c b/src/security/vboot/common.c
index 7f1aee1..1703933 100644
--- a/src/security/vboot/common.c
+++ b/src/security/vboot/common.c
@@ -63,8 +63,16 @@
 	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)
diff --git a/src/security/vboot/vboot_common.c b/src/security/vboot/vboot_common.c
index 8ecb5d8..dd611d1 100644
--- a/src/security/vboot/vboot_common.c
+++ b/src/security/vboot/vboot_common.c
@@ -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");
diff --git a/src/security/vboot/vboot_loader.c b/src/security/vboot/vboot_loader.c
index 0acb349..7cda690 100644
--- a/src/security/vboot/vboot_loader.c
+++ b/src/security/vboot/vboot_loader.c
@@ -28,15 +28,32 @@
 
 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)
diff --git a/src/security/vboot/vboot_logic.c b/src/security/vboot/vboot_logic.c
index 660b7da..98a044c 100644
--- a/src/security/vboot/vboot_logic.c
+++ b/src/security/vboot/vboot_logic.c
@@ -54,7 +54,7 @@
 	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;
 
@@ -63,14 +63,14 @@
 	 * 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
@@ -78,7 +78,7 @@
 	 * lives in RO CBFS.
 	 */
 	if (!CONFIG(VBOOT_STARTS_IN_BOOTBLOCK))
-		return 0;
+		return VB2_SUCCESS;
 
 	is_resume = platform_is_resuming();
 
@@ -92,12 +92,12 @@
 
 		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");
@@ -111,10 +111,10 @@
 		 * 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,
@@ -179,10 +179,7 @@
 
 	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)
@@ -236,16 +233,10 @@
 		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);
@@ -326,7 +317,6 @@
 			extend_pcrs(ctx); /* ignore failures */
 			goto verstage_main_exit;
 		}
-
 		vboot_save_and_reboot(ctx, rv);
 	}
 
@@ -345,12 +335,22 @@
 		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);
diff --git a/src/soc/mediatek/mt8173/Makefile.inc b/src/soc/mediatek/mt8173/Makefile.inc
index e824e54..7688ecf 100644
--- a/src/soc/mediatek/mt8173/Makefile.inc
+++ b/src/soc/mediatek/mt8173/Makefile.inc
@@ -46,6 +46,7 @@
 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