security/tpm: Add option to init TPM in bootblock

When using a hardware assisted root of trust measurement, like Intel
TXT/CBnT, the TPM init needs to happen inside the bootblock to form a
proper chain of trust.

Change-Id: Ifacba5d9ab19b47968b4f2ed5731ded4aac55022
Signed-off-by: Arthur Heymans <arthur@aheymans.xyz>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/51923
Reviewed-by: Christian Walter <christian.walter@9elements.com>
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/drivers/pc80/tpm/Makefile.inc b/src/drivers/pc80/tpm/Makefile.inc
index a16f6af..8b2a864 100644
--- a/src/drivers/pc80/tpm/Makefile.inc
+++ b/src/drivers/pc80/tpm/Makefile.inc
@@ -1,3 +1,4 @@
+bootblock-$(CONFIG_MAINBOARD_HAS_LPC_TPM) += tis.c
 verstage-$(CONFIG_MAINBOARD_HAS_LPC_TPM) += tis.c
 romstage-$(CONFIG_MAINBOARD_HAS_LPC_TPM) += tis.c
 ramstage-$(CONFIG_MAINBOARD_HAS_LPC_TPM) += tis.c
diff --git a/src/drivers/tpm/Kconfig b/src/drivers/tpm/Kconfig
index 128f9bf..79b860f 100644
--- a/src/drivers/tpm/Kconfig
+++ b/src/drivers/tpm/Kconfig
@@ -1,7 +1,8 @@
 config TPM_INIT_RAMSTAGE
 	bool
 	default y if TPM1 || TPM2
-	depends on !VBOOT && !VENDORCODE_ELTAN_VBOOT && !VENDORCODE_ELTAN_MBOOT
+	depends on !VBOOT && !VENDORCODE_ELTAN_VBOOT && !VENDORCODE_ELTAN_MBOOT \
+		&& !TPM_MEASURED_BOOT_INIT_BOOTBLOCK
 	help
 	  This driver automatically initializes the TPM if vboot is not used.
 	  The TPM driver init is done during the ramstage chip init phase.
diff --git a/src/lib/bootblock.c b/src/lib/bootblock.c
index 23fb392..5989964 100644
--- a/src/lib/bootblock.c
+++ b/src/lib/bootblock.c
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 
+#include <acpi/acpi.h>
 #include <arch/exception.h>
 #include <bootblock_common.h>
 #include <console/console.h>
@@ -8,6 +9,7 @@
 #include <option.h>
 #include <post.h>
 #include <program_loading.h>
+#include <security/tpm/tspi.h>
 #include <symbols.h>
 #include <timestamp.h>
 
@@ -56,6 +58,11 @@
 	bootblock_soc_init();
 	bootblock_mainboard_init();
 
+	if (CONFIG(TPM_MEASURED_BOOT_INIT_BOOTBLOCK)) {
+		int s3resume = acpi_is_wakeup_s3();
+		tpm_setup(s3resume);
+	}
+
 	timestamp_add_now(TS_END_BOOTBLOCK);
 
 	run_romstage();
diff --git a/src/security/intel/cbnt/Kconfig b/src/security/intel/cbnt/Kconfig
index 415092b..9208ab4 100644
--- a/src/security/intel/cbnt/Kconfig
+++ b/src/security/intel/cbnt/Kconfig
@@ -8,6 +8,7 @@
 	select INTEL_TXT
 	# With CBnT the bootblock is set up as a CBnT IBB and needs a fixed size
 	select FIXED_BOOTBLOCK_SIZE
+	select TPM_MEASURED_BOOT_INIT_BOOTBLOCK if TPM_MEASURED_BOOT
 	help
 	  Enables Intel Converged Bootguard and Trusted Execution Technology
 	  Support. This will enable one to add a Key Manifest (KM) and a Boot
diff --git a/src/security/tpm/Kconfig b/src/security/tpm/Kconfig
index e1255d1..13bef06 100644
--- a/src/security/tpm/Kconfig
+++ b/src/security/tpm/Kconfig
@@ -106,6 +106,14 @@
 	help
 	  Enables measured boot (experimental)
 
+config TPM_MEASURED_BOOT_INIT_BOOTBLOCK
+	bool
+	depends on TPM_MEASURED_BOOT && !VBOOT
+	help
+	  Initialize TPM inside the bootblock instead of ramstage. This is
+	  useful with some form of hardware assisted root of trust
+	  measurement like Intel TXT/CBnT.
+
 config TPM_MEASURED_BOOT_RUNTIME_DATA
 	string "Runtime data whitelist"
 	default ""
diff --git a/src/security/tpm/tspi/tspi.c b/src/security/tpm/tspi/tspi.c
index 6ef0138..7a8e2be 100644
--- a/src/security/tpm/tspi/tspi.c
+++ b/src/security/tpm/tspi/tspi.c
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 
+#include <rules.h>
 #include <console/cbmem_console.h>
 #include <console/console.h>
 #include <security/tpm/tspi/crtm.h>
@@ -104,6 +105,9 @@
 		return vboot_logic_executed();
 	}
 
+	if (CONFIG(TPM_MEASURED_BOOT_INIT_BOOTBLOCK))
+		return ENV_BOOTBLOCK ? tpm_is_setup : 1;
+
 	if (ENV_RAMSTAGE)
 		return tpm_is_setup;
 
@@ -180,7 +184,7 @@
 #if CONFIG(TPM1)
 	result = tpm1_invoke_state_machine();
 #endif
-	if (CONFIG(TPM_MEASURED_BOOT))
+	if (CONFIG(TPM_MEASURED_BOOT) && !CONFIG(TPM_MEASURED_BOOT_INIT_BOOTBLOCK))
 		result = tspi_measure_cache_to_pcr();
 
 	tpm_is_setup = 1;