cbfs | tspi: Join hash calculation for verification and measurement

This patch moves the CBFS file measurement when CONFIG_TPM_MEASURED_BOOT
is enabled from the lookup step into the code where a file is actually
loaded or mapped from flash. This has the advantage that CBFS routines
which just look up a file to inspect its metadata (e.g. cbfs_get_size())
do not cause the file to be measured twice. It also removes the existing
inefficiency that files are loaded twice when measurement is enabled
(once to measure and then again when they are used). When CBFS
verification is enabled and uses the same hash algorithm as the TPM, we
are even able to only hash the file a single time and use the result for
both purposes.

Signed-off-by: Julius Werner <jwerner@chromium.org>
Change-Id: I70d7066c6768195077f083c7ffdfa30d9182b2b7
Reviewed-on: https://review.coreboot.org/c/coreboot/+/59681
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Raul Rangel <rrangel@chromium.org>
diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c
index b4b6eb1..0b58913 100644
--- a/src/lib/cbfs.c
+++ b/src/lib/cbfs.c
@@ -81,10 +81,6 @@
 	if (rdev_chain(rdev, &cbd->rdev, data_offset, be32toh(mdata->h.len)))
 		return CB_ERR;
 
-	if (tspi_measure_cbfs_hook(rdev, name, be32toh(mdata->h.type))) {
-		printk(BIOS_ERR, "CBFS ERROR: error when measuring '%s'\n", name);
-	}
-
 	return CB_SUCCESS;
 }
 
@@ -134,9 +130,8 @@
 		type = &dummy_type;
 
 	ret = cbfs_locate(fh, &rdev, name, type);
-	if (!ret)
-		if (tspi_measure_cbfs_hook(&rdev, name, *type))
-			LOG("error measuring %s in region %s\n", name, region_name);
+	/* No more measuring here, this function will be removed next patch. */
+
 	return ret;
 }
 
@@ -193,19 +188,36 @@
 static bool cbfs_file_hash_mismatch(const void *buffer, size_t size,
 				    const union cbfs_mdata *mdata, bool skip_verification)
 {
-	/* Avoid linking hash functions when verification is disabled. */
-	if (!CONFIG(CBFS_VERIFICATION) || skip_verification)
+	/* Avoid linking hash functions when verification and measurement are disabled. */
+	if (!CONFIG(CBFS_VERIFICATION) && !CONFIG(TPM_MEASURED_BOOT))
 		return false;
 
-	const struct vb2_hash *hash = cbfs_file_hash(mdata);
-	if (!hash) {
-		ERROR("'%s' does not have a file hash!\n", mdata->h.filename);
-		return true;
+	const struct vb2_hash *hash = NULL;
+
+	if (CONFIG(CBFS_VERIFICATION) && !skip_verification) {
+		hash = cbfs_file_hash(mdata);
+		if (!hash) {
+			ERROR("'%s' does not have a file hash!\n", mdata->h.filename);
+			return true;
+		}
+		if (vb2_hash_verify(buffer, size, hash) != VB2_SUCCESS) {
+			ERROR("'%s' file hash mismatch!\n", mdata->h.filename);
+			return true;
+		}
 	}
 
-	if (vb2_hash_verify(buffer, size, hash) != VB2_SUCCESS) {
-		ERROR("'%s' file hash mismatch!\n", mdata->h.filename);
-		return true;
+	if (CONFIG(TPM_MEASURED_BOOT) && !ENV_SMM) {
+		struct vb2_hash calculated_hash;
+
+		/* No need to re-hash file if we already have it from verification. */
+		if (!hash || hash->algo != TPM_MEASURE_ALGO) {
+			vb2_hash_calculate(buffer, size, TPM_MEASURE_ALGO, &calculated_hash);
+			hash = &calculated_hash;
+		}
+
+		if (tspi_cbfs_measurement(mdata->h.filename, be32toh(mdata->h.type), hash))
+			ERROR("failed to measure '%s' into TCPA log\n", mdata->h.filename);
+			/* We intentionally continue to boot on measurement errors. */
 	}
 
 	return false;
@@ -528,9 +540,6 @@
 	if (rdev_chain(&file_rdev, &area_rdev, data_offset, be32toh(mdata.h.len)))
 		return NULL;
 
-	if (tspi_measure_cbfs_hook(&file_rdev, name, be32toh(mdata.h.type)))
-		ERROR("error measuring '%s' in '%s'\n", name, area);
-
 	return do_alloc(&mdata, &file_rdev, allocator, arg, size_out, true);
 }