vboot: on S3 resume from Alt OS legacy, disable TPM

When booting into Alt OS legacy mode, we plan to disable TPM before
handing off control to the OS.  On S3 resume, the TPM is re-enabled,
and thus we need to disable it in coreboot's S3 resume path.

Add a helper function to check for the ALT_OS_LEGACY_BOOT flag in
vboot_handoff's VbSharedDataHeader.

Extend vboot's BS_OS_RESUME hook to disable TPM if the
ALT_OS_LEGACY_BOOT flag is detected.

BUG=b:70681930,b:118202153
TEST=Boot into Alt OS
     Suspend and resume
     Check that TPM is successfully disabled in AP console output
CQ-DEPEND=CL:1339599,CL:1362268

Signed-off-by: Joel Kitching <kitching@google.com>
Change-Id: Ie2c176d924cb6bde3c407dfc0d2b0b89b0d7799d
Reviewed-on: https://chromium-review.googlesource.com/c/1339681
Tested-by: Joel Kitching <kitching@chromium.org>
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Reviewed-by: Namyoon Woo <namyoon@chromium.org>
Reviewed-by: Hung-Te Lin <hungte@chromium.org>
Commit-Queue: Joel Kitching <kitching@chromium.org>
diff --git a/src/vboot/vboot_common.c b/src/vboot/vboot_common.c
index 515b368..6092805 100644
--- a/src/vboot/vboot_common.c
+++ b/src/vboot/vboot_common.c
@@ -87,6 +87,19 @@
 	return vboot_get_handoff_flag(VB_INIT_OUT_ENABLE_RECOVERY);
 }
 
+int vboot_handoff_check_alt_os_legacy_boot_flag(void)
+{
+	struct vboot_handoff *vbho;
+	VbSharedDataHeader *sd;
+
+	if (vboot_get_handoff_info((void **)&vbho, NULL))
+		return 0;
+
+	sd = (VbSharedDataHeader *)vbho->shared_data;
+
+	return !!(sd->flags & VBSD_ALT_OS_LEGACY_BOOT);
+}
+
 int vboot_handoff_get_recovery_reason(void)
 {
 	struct vboot_handoff *vbho;
diff --git a/src/vboot/vboot_common.h b/src/vboot/vboot_common.h
index 8c92f7f..be2dcb5 100644
--- a/src/vboot/vboot_common.h
+++ b/src/vboot/vboot_common.h
@@ -64,6 +64,7 @@
 int vboot_handoff_skip_display_init(void);
 int vboot_handoff_check_recovery_flag(void);
 int vboot_handoff_check_developer_flag(void);
+int vboot_handoff_check_alt_os_legacy_boot_flag(void);
 int vboot_handoff_get_recovery_reason(void);
 
 /* ============================ VBOOT REBOOT ============================== */
diff --git a/src/vendorcode/google/chromeos/tpm2.c b/src/vendorcode/google/chromeos/tpm2.c
index fd1dac9..679e37b 100644
--- a/src/vendorcode/google/chromeos/tpm2.c
+++ b/src/vendorcode/google/chromeos/tpm2.c
@@ -13,11 +13,22 @@
  * GNU General Public License for more details.
  */
 
+/**
+ * Needed to get access to VBNV_RECOVERY_RO_S3_RESUME from vboot_reference.
+ * TODO: Formally expose a way to set this recovery mode from within
+ * vboot2 API.
+ */
+#define NEED_VB20_INTERNALS
+
 #include <bootstate.h>
 #include <console/console.h>
+#include <lib/tpm2_tlcl_structures.h>
 #include <tpm_lite/tlcl.h>
 #include <vb2_api.h>
 
+#include "vboot/misc.h"
+#include "vboot/vbnv.h"
+
 static void disable_platform_hierarchy(void *unused)
 {
 	int ret;
@@ -41,5 +52,47 @@
 			ret);
 }
 
-BOOT_STATE_INIT_ENTRY(BS_OS_RESUME, BS_ON_ENTRY, disable_platform_hierarchy,
-			NULL);
+static int disable_tpm_helper(void *unused)
+{
+	if (tlcl_lib_init() != VB2_SUCCESS)
+		printk(BIOS_ERR,
+		       "TPM driver initialization failed, aborting resume\n");
+	else if (tlcl_save_state() != TPM_SUCCESS)
+		printk(BIOS_ERR,
+		       "Could not save TPM state, aborting resume\n");
+	else if (tlcl_cr50_set_tpm_mode(TpmModeDisabled))
+		printk(BIOS_ERR,
+		       "Could not disable TPM, aborting resume\n");
+	else
+		return 0;
+	return 1;
+}
+
+static void disable_tpm(void *unused)
+{
+	/**
+	 * If Alt OS legacy mode was selected on boot, then we are in the middle
+	 * of an S3 resume, and we must do some extra work to disable the TPM.
+	 */
+	if (vboot_handoff_check_alt_os_legacy_boot_flag()) {
+		printk(BIOS_INFO,
+		       "Alt OS legacy boot detected - "
+		       "disable TPM before resuming\n");
+		if (disable_tpm_helper(unused)) {
+			printk(BIOS_ERR,
+			       "TPM disable failed, "
+			       "setting recovery reason and rebooting\n");
+			set_recovery_mode_into_vbnv(VBNV_RECOVERY_RO_S3_RESUME);
+			vboot_reboot();
+		} else
+			printk(BIOS_INFO, "TPM disabled successfully\n");
+	}
+}
+
+static void tpm_tasks(void *unused)
+{
+	disable_platform_hierarchy(unused);
+	disable_tpm(unused);
+}
+
+BOOT_STATE_INIT_ENTRY(BS_OS_RESUME, BS_ON_ENTRY, tpm_tasks, NULL);