ec_sync: Run EFS2 in romstage

EFS2 allows EC RO to enable PD for special cases. When doing so, it sets
NO_BOOT flag to avoid booting the OS. AP needs to get NO_BOOT flag from
Cr50 and enforce that.

This patch makes verstage get a boot mode and a mirrored hash stored
in kernel secdata from Cr50.

This patch also makes romstage write an expected EC hash (a.k.a. Hexp) to
Cr50 (if there is an update).

BUG=b:147298634, chromium:1045217, b:148259137
BRANCH=none
TEST=Verify software sync succeeds on Puff.

Signed-off-by: dnojiri <dnojiri@chromium.org>
Change-Id: I1f387b6e920205b9cc4c8536561f2a279c36413d
Reviewed-on: https://review.coreboot.org/c/coreboot/+/40389
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
diff --git a/src/security/vboot/antirollback.h b/src/security/vboot/antirollback.h
index 5af9236..6bc020d 100644
--- a/src/security/vboot/antirollback.h
+++ b/src/security/vboot/antirollback.h
@@ -72,6 +72,12 @@
 uint32_t antirollback_write_space_firmware(struct vb2_context *ctx);
 
 /**
+ * Read and write kernel space in TPM.
+ */
+uint32_t antirollback_read_space_kernel(struct vb2_context *ctx);
+uint32_t antirollback_write_space_kernel(struct vb2_context *ctx);
+
+/**
  * Lock must be called.
  */
 uint32_t antirollback_lock_space_firmware(void);
diff --git a/src/security/vboot/ec_sync.c b/src/security/vboot/ec_sync.c
index 3a177b1..580e6c6 100644
--- a/src/security/vboot/ec_sync.c
+++ b/src/security/vboot/ec_sync.c
@@ -50,7 +50,7 @@
 	ctx->flags |= VB2_CONTEXT_EC_SYNC_SUPPORTED;
 
 	retval = vb2api_ec_sync(ctx);
-	vboot_save_nvdata_only(ctx);
+	vboot_save_data(ctx);
 
 	switch (retval) {
 	case VB2_SUCCESS:
diff --git a/src/security/vboot/secdata_mock.c b/src/security/vboot/secdata_mock.c
index a4957f9..edb6739 100644
--- a/src/security/vboot/secdata_mock.c
+++ b/src/security/vboot/secdata_mock.c
@@ -53,6 +53,17 @@
 	return VB2_SUCCESS;
 }
 
+vb2_error_t antirollback_read_space_kernel(struct vb2_context *ctx)
+{
+	vb2api_secdata_kernel_create(ctx);
+	return VB2_SUCCESS;
+}
+
+vb2_error_t antirollback_write_space_kernel(struct vb2_context *ctx)
+{
+	return VB2_SUCCESS;
+}
+
 vb2_error_t antirollback_lock_space_firmware(void)
 {
 	return VB2_SUCCESS;
diff --git a/src/security/vboot/secdata_tpm.c b/src/security/vboot/secdata_tpm.c
index 0ae9562..b60a1bb 100644
--- a/src/security/vboot/secdata_tpm.c
+++ b/src/security/vboot/secdata_tpm.c
@@ -80,6 +80,22 @@
 	return TPM_E_CORRUPTED_STATE;
 }
 
+uint32_t antirollback_read_space_kernel(struct vb2_context *ctx)
+{
+	uint8_t size = VB2_SECDATA_KERNEL_MIN_SIZE;
+
+	RETURN_ON_FAILURE(tlcl_read(KERNEL_NV_INDEX, ctx->secdata_kernel,
+				    size));
+
+	if (vb2api_secdata_kernel_check(ctx, &size)
+	    == VB2_ERROR_SECDATA_KERNEL_INCOMPLETE)
+		/* Re-read. vboot will run the check and handle errors. */
+		RETURN_ON_FAILURE(tlcl_read(KERNEL_NV_INDEX,
+					    ctx->secdata_kernel, size));
+
+	return TPM_SUCCESS;
+}
+
 static uint32_t read_space_rec_hash(uint8_t *data)
 {
 	RETURN_ON_FAILURE(tlcl_read(REC_HASH_NV_INDEX, data,
@@ -440,6 +456,15 @@
 			     VB2_SECDATA_FIRMWARE_SIZE);
 }
 
+uint32_t antirollback_write_space_kernel(struct vb2_context *ctx)
+{
+	/* Learn the expected size. */
+	uint8_t size = VB2_SECDATA_KERNEL_MIN_SIZE;
+	vb2api_secdata_kernel_check(ctx, &size);
+
+	return write_secdata(KERNEL_NV_INDEX, ctx->secdata_kernel, size);
+}
+
 uint32_t antirollback_read_space_rec_hash(uint8_t *data, uint32_t size)
 {
 	if (size != REC_HASH_NV_SIZE) {
diff --git a/src/security/vboot/vboot_common.h b/src/security/vboot/vboot_common.h
index 50995e6..f25ee46 100644
--- a/src/security/vboot/vboot_common.h
+++ b/src/security/vboot/vboot_common.h
@@ -61,7 +61,6 @@
 static inline int vboot_locate_cbfs(struct region_device *rdev) { return -1; }
 #endif
 
-void vboot_save_nvdata_only(struct vb2_context *ctx);
 void vboot_save_data(struct vb2_context *ctx);
 
 /*
diff --git a/src/security/vboot/vboot_logic.c b/src/security/vboot/vboot_logic.c
index 9e9e82a..8e82e40 100644
--- a/src/security/vboot/vboot_logic.c
+++ b/src/security/vboot/vboot_logic.c
@@ -7,6 +7,7 @@
 #include <cbmem.h>
 #include <fmap.h>
 #include <security/tpm/tspi/crtm.h>
+#include <security/tpm/tss/vendor/cr50/cr50.h>
 #include <security/vboot/misc.h>
 #include <security/vboot/vbnv.h>
 #include <security/vboot/tpm_common.h>
@@ -207,10 +208,21 @@
 	return VB2_SUCCESS;
 }
 
-void vboot_save_nvdata_only(struct vb2_context *ctx)
+void vboot_save_data(struct vb2_context *ctx)
 {
-	assert(!(ctx->flags & (VB2_CONTEXT_SECDATA_FIRMWARE_CHANGED |
-			       VB2_CONTEXT_SECDATA_KERNEL_CHANGED)));
+	if (ctx->flags & VB2_CONTEXT_SECDATA_FIRMWARE_CHANGED &&
+	    (CONFIG(VBOOT_MOCK_SECDATA) || tlcl_lib_init() == VB2_SUCCESS)) {
+		printk(BIOS_INFO, "Saving secdata firmware\n");
+		antirollback_write_space_firmware(ctx);
+		ctx->flags &= ~VB2_CONTEXT_SECDATA_FIRMWARE_CHANGED;
+	}
+
+	if (ctx->flags & VB2_CONTEXT_SECDATA_KERNEL_CHANGED &&
+	    (CONFIG(VBOOT_MOCK_SECDATA) || tlcl_lib_init() == VB2_SUCCESS)) {
+		printk(BIOS_INFO, "Saving secdata kernel\n");
+		antirollback_write_space_kernel(ctx);
+		ctx->flags &= ~VB2_CONTEXT_SECDATA_KERNEL_CHANGED;
+	}
 
 	if (ctx->flags & VB2_CONTEXT_NVDATA_CHANGED) {
 		printk(BIOS_INFO, "Saving nvdata\n");
@@ -219,23 +231,57 @@
 	}
 }
 
-void vboot_save_data(struct vb2_context *ctx)
-{
-	if (ctx->flags & VB2_CONTEXT_SECDATA_FIRMWARE_CHANGED) {
-		printk(BIOS_INFO, "Saving secdata\n");
-		antirollback_write_space_firmware(ctx);
-		ctx->flags &= ~VB2_CONTEXT_SECDATA_FIRMWARE_CHANGED;
-	}
-
-	vboot_save_nvdata_only(ctx);
-}
-
 static uint32_t extend_pcrs(struct vb2_context *ctx)
 {
 	return vboot_extend_pcr(ctx, 0, BOOT_MODE_PCR) ||
 		   vboot_extend_pcr(ctx, 1, HWID_DIGEST_PCR);
 }
 
+#define EC_EFS_BOOT_MODE_NORMAL		0x00
+#define EC_EFS_BOOT_MODE_NO_BOOT	0x01
+
+static const char *get_boot_mode_string(uint8_t boot_mode)
+{
+	if (boot_mode == EC_EFS_BOOT_MODE_NORMAL)
+		return "NORMAL";
+	else if (boot_mode == EC_EFS_BOOT_MODE_NO_BOOT)
+		return "NO_BOOT";
+	else
+		return "UNDEFINED";
+}
+
+static void check_boot_mode(struct vb2_context *ctx)
+{
+	uint8_t boot_mode;
+	int rv;
+
+	rv = tlcl_cr50_get_boot_mode(&boot_mode);
+	switch (rv) {
+	case TPM_E_NO_SUCH_COMMAND:
+		printk(BIOS_WARNING, "Cr50 does not support GET_BOOT_MODE.\n");
+		/* Proceed to legacy boot model. */
+		return;
+	case TPM_SUCCESS:
+		break;
+	default:
+		printk(BIOS_ERR,
+		       "Communication error in getting Cr50 boot mode.\n");
+		if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE)
+			/* Continue to boot in recovery mode */
+			return;
+		vb2api_fail(ctx, VB2_RECOVERY_CR50_BOOT_MODE, rv);
+		vboot_save_data(ctx);
+		vboot_reboot();
+		return;
+	}
+
+	printk(BIOS_INFO, "Cr50 says boot_mode is %s(0x%02x).\n",
+	       get_boot_mode_string(boot_mode), boot_mode);
+
+	if (boot_mode == EC_EFS_BOOT_MODE_NO_BOOT)
+		ctx->flags |= VB2_CONTEXT_NO_BOOT;
+}
+
 /**
  * Verify and select the firmware in the RW image
  *
@@ -268,8 +314,10 @@
 	 * check the return value here because vb2api_fw_phase1 will catch
 	 * invalid secdata and tell us what to do (=reboot). */
 	timestamp_add_now(TS_START_TPMINIT);
-	if (vboot_setup_tpm(ctx) == TPM_SUCCESS)
+	if (vboot_setup_tpm(ctx) == TPM_SUCCESS) {
 		antirollback_read_space_firmware(ctx);
+		antirollback_read_space_kernel(ctx);
+	}
 	timestamp_add_now(TS_END_TPMINIT);
 
 	if (get_recovery_mode_switch()) {
@@ -359,6 +407,9 @@
 		timestamp_add_now(TS_END_TPMPCR);
 	}
 
+	if (CONFIG(TPM_CR50))
+		check_boot_mode(ctx);
+
 	/* Lock TPM */
 
 	timestamp_add_now(TS_START_TPMLOCK);