tss: implement tlcl_save_state

When an untrusted OS is running, we would like to use the Cr50
vendor-specific VENDOR_CC_TPM_MODE command to disable TPM.
Before doing this, we should save TPM state.  Implement
tlcl_save_state for this purpose.

This needs to live in coreboot codebase since on S3 resume path,
depthcharge is not reached.

Implement the function in both tcg-1.2 and tcg-2.0 for
completeness.

BUG=b:70681930,b:118202153
TEST=hack a call to tlcl_save_state into coreboot on S3 resume
     verify in AP console that it is called

Signed-off-by: Joel Kitching <kitching@google.com>
Change-Id: I8b51ca68456fc9b655e4dc2d0958b7c040d50510
Reviewed-on: https://review.coreboot.org/c/29646
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Philipp Deppenwiese <zaolin.daisuki@gmail.com>
diff --git a/src/security/tpm/tss/tcg-2.0/tss.c b/src/security/tpm/tss/tcg-2.0/tss.c
index c67fdfa..e579bff 100644
--- a/src/security/tpm/tss/tcg-2.0/tss.c
+++ b/src/security/tpm/tss/tcg-2.0/tss.c
@@ -87,6 +87,35 @@
 	return tlcl_send_startup(TPM_SU_STATE);
 }
 
+static uint32_t tlcl_send_shutdown(TPM_SU type)
+{
+	struct tpm2_shutdown shutdown;
+	struct tpm2_response *response;
+
+	shutdown.shutdown_type = type;
+	response = tpm_process_command(TPM2_Shutdown, &shutdown);
+
+	/* IO error, tpm2_response pointer is empty. */
+	if (response == NULL) {
+		printk(BIOS_ERR, "%s: TPM communication error\n", __func__);
+		return TPM_E_IOERROR;
+	}
+
+	printk(BIOS_INFO, "%s: Shutdown return code is %x\n",
+	       __func__, response->hdr.tpm_code);
+
+	if (response->hdr.tpm_code == TPM2_RC_SUCCESS)
+		return TPM_SUCCESS;
+
+	/* Collapse any other errors into TPM_E_IOERROR. */
+	return TPM_E_IOERROR;
+}
+
+uint32_t tlcl_save_state(void)
+{
+	return tlcl_send_shutdown(TPM_SU_STATE);
+}
+
 uint32_t tlcl_assert_physical_presence(void)
 {
 	/*