vboot_api_kernel: check TPM mode on normal boot

When booting into Alt OS legacy mode, we plan to disable TPM
before handing off control to the OS.  On a warm reboot back
to Chrome OS, we must check the TPM mode.  If it is disabled,
a hard reboot should be triggered to restore TPM functionality.

Add this check to VbBootNormal.  Only accept the TPM mode
VB2_TPM_MODE_ENABLED_TENTATIVE (0).

BUG=b:119203340
TEST=compile, flash, and boot eve
TEST=run `gsctool -a -m disable`
     validate that the following messages show up on boot:
       Calling VbSelectAndLoadKernel().
       VbCheckTPM: Checking if TPM needs resetting (TPM_MODE)
       cr50 TPM 2.0 (i2c 0x50 id 0x28)
       tpm_internal_mode: Invalid header code: 1286
       VbCheckTPM: TPM encountered some error; reset Cr50
       tpm_internal_cr50_reset: Asking Cr50 to reset after 500 ms
       VbCheckTPM: Shut down AP and wait for Cr50 reset
       VbSelectAndLoadKernel: Returning 65549
       Powering off.
       Exiting depthcharge with code 2 at timestamp: 6603861

Change-Id: If6097aa67eb135e24c735bd7948ec25091bed3be
Reviewed-on: https://chromium-review.googlesource.com/c/1354140
Tested-by: Joel Kitching <kitching@chromium.org>
Reviewed-by: Namyoon Woo <namyoon@chromium.org>
Commit-Queue: Joel Kitching <kitching@chromium.org>
diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c
index 2647255..48daa6b 100644
--- a/firmware/lib/vboot_api_kernel.c
+++ b/firmware/lib/vboot_api_kernel.c
@@ -520,6 +520,40 @@
 
 	return VBERROR_SUCCESS;
 }
+
+VbError_t VbCheckTPM(void)
+{
+	const int cr50_reset_delay_msec = 500;
+	enum vb2_tpm_mode tpm_mode;
+	int ret;
+	int need_reset = 0;
+
+	VB2_DEBUG("Checking if TPM needs resetting (TPM_MODE)\n");
+	ret = vb2ex_tpm_get_mode(&tpm_mode);
+	if (ret == VB2_ERROR_EX_TPM_NO_SUCH_COMMAND) {
+		VB2_DEBUG("TPM does not support command, assume good state\n");
+	} else if (ret != VB2_SUCCESS) {
+		VB2_DEBUG("TPM encountered some error; reset Cr50\n");
+		need_reset = 1;
+	} else if (tpm_mode != VB2_TPM_MODE_ENABLED_TENTATIVE) {
+		VB2_DEBUG("Invalid TPM mode (%d, expected: %d); reset Cr50\n",
+			  tpm_mode, VB2_TPM_MODE_ENABLED_TENTATIVE);
+		need_reset = 1;
+	} else {
+		VB2_DEBUG("TPM is in good state\n");
+	}
+
+	if (!need_reset)
+		return VBERROR_SUCCESS;
+
+	if (vb2ex_tpm_cr50_reset(cr50_reset_delay_msec)) {
+		VB2_DEBUG("Reset Cr50 failed\n");
+		return VBERROR_UNKNOWN;
+	} else {
+		VB2_DEBUG("Shut down AP and wait for Cr50 reset\n");
+		return VBERROR_SHUTDOWN_REQUESTED;
+	}
+}
 #endif  /* ALT_OS */
 
 VbError_t VbSelectAndLoadKernel(VbCommonParams *cparams,
@@ -527,8 +561,19 @@
 {
 	VbSharedDataHeader *shared =
 		(VbSharedDataHeader *)cparams->shared_data_blob;
+	VbError_t retval;
 
-	VbError_t retval = vb2_kernel_setup(cparams, kparams);
+#ifdef ALT_OS
+	/*
+	 * TPM may be disabled from a previous untrusted Alt OS boot.
+	 * Check the TPM state and request a Cr50 reset if necessary.
+	 */
+	retval = VbCheckTPM();
+	if (retval)
+		goto VbSelectAndLoadKernel_exit;
+#endif  /* ALT_OS */
+
+	retval = vb2_kernel_setup(cparams, kparams);
 	if (retval)
 		goto VbSelectAndLoadKernel_exit;