Add TPM initialization

Change-Id: I40ad3a4a9b43a2511696acf2a0801e862af66704
diff --git a/arch/arm/boards/optimus/Makefile b/arch/arm/boards/optimus/Makefile
index 3cb53e2..301a92a 100644
--- a/arch/arm/boards/optimus/Makefile
+++ b/arch/arm/boards/optimus/Makefile
@@ -1,2 +1,3 @@
 obj-y += optimus.o
 obj-y += ddr.o
+obj-$(CONFIG_TPM) += tpm.o
diff --git a/arch/arm/boards/optimus/optimus.c b/arch/arm/boards/optimus/optimus.c
index 83db7b7..59e3344 100644
--- a/arch/arm/boards/optimus/optimus.c
+++ b/arch/arm/boards/optimus/optimus.c
@@ -51,6 +51,7 @@
 #include <mach/otp.h>
 #include <mach/ddr.h>
 #include <board_id.h>
+#include <tpm_lite/tlcl.h>
 
 #define PHY_DEVICE      "phy0"
 
@@ -257,6 +258,10 @@
 }
 EXPORT_SYMBOL(get_board_id)
 
+#ifdef	CONFIG_TPM
+uint32_t tpm_init(void);
+#endif
+
 static int c2000_device_init(void)
 {
 #ifdef	CONFIG_COMCERTO_BOOTLOADER
@@ -419,6 +424,12 @@
 		devfs_add_partition("nor0", 0x120000, 0x20000, PARTITION_FIXED, "env0");
 		protect_file("/dev/env0", 1);
 	}
+
+#ifdef	CONFIG_TPM
+	if (tpm_init() != TPM_SUCCESS) {
+		printf("TPM initialization failed\n");
+	}
+#endif
 #endif
 	return 0;
 }
diff --git a/arch/arm/boards/optimus/tpm.c b/arch/arm/boards/optimus/tpm.c
new file mode 100644
index 0000000..084cc3f
--- /dev/null
+++ b/arch/arm/boards/optimus/tpm.c
@@ -0,0 +1,122 @@
+/*
+ * (C) Copyright 2015 Google Inc.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <tpm_lite/tlcl.h>
+#include <secure_boot.h>
+
+#define RETURN_ON_FAILURE(tpm_cmd) do {			\
+	uint32_t result_;				\
+	if ((result_ = (tpm_cmd)) != TPM_SUCCESS) {			\
+		printk(KERN_DEBUG "TPM: %08x returned by " #tpm_cmd     \
+			"\n", (int)result_);                            \
+		return result_;						\
+	}								\
+	} while (0)
+
+#define PCR_DIGEST_LENGTH	20
+
+#define SHA1_NON_SECURE_BOOT	0
+#define SHA1_SECURE_BOOT	1
+
+static const uint8_t bootmode_digests[2][PCR_DIGEST_LENGTH] = {
+	/* non-secure boot [0, 0, 2] */
+	{0x1e, 0xf6, 0x24, 0x48, 0x2d, 0x62, 0x0e, 0x43, 0xe6, 0xd3,
+	 0x4d, 0xa1, 0xaf, 0xe4, 0x62, 0x67, 0xfc, 0x69, 0x5d, 0x9b},
+
+	/* secure boot [0, 0, 1] */
+	{0x25, 0x47, 0xcc, 0x73, 0x6e, 0x95, 0x1f, 0xa4, 0x91, 0x98,
+	 0x53, 0xc4, 0x3a, 0xe8, 0x90, 0x86, 0x1a, 0x3b, 0x32, 0x64}
+};
+
+static const uint8_t sha256_gfsc100[PCR_DIGEST_LENGTH] = {
+	0x4f, 0x8c, 0x4d, 0xaf, 0x4d, 0xf3, 0x2e, 0xaf, 0xb5, 0x57,
+	0xff, 0x76, 0x58, 0x86, 0x02, 0x94, 0x42, 0xdc, 0xce, 0x06
+};
+
+static const uint8_t PCR_uninitialized[PCR_DIGEST_LENGTH] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+uint32_t tpm_init(void) {
+	uint32_t result;
+	uint8_t disable;
+	uint8_t deactivated;
+	uint8_t pcr_value[PCR_DIGEST_LENGTH];
+	TPM_STCLEAR_FLAGS sflags;
+
+	RETURN_ON_FAILURE(tlcl_lib_init());
+
+	result = tlcl_startup();
+	if ((result != TPM_SUCCESS) &&
+		(result != TPM_E_INVALID_POSTINIT)) {
+		/* Invalid postinit indicates that TPM_Startup has already been
+		   executed. This situation occurs on reboots on the GFSC100
+		   platform because a reboot does not powercycle the TPM */
+		printk(KERN_WARNING "TPM_Startup failed: 0x%08x\n", result);
+		return result;
+	}
+
+	/* Continue or start the self-test of all TPM functions. Untested
+	   functions will return TPM_NEEDS_SELFTEST otherwise */
+	RETURN_ON_FAILURE(tlcl_continue_self_test());
+
+	RETURN_ON_FAILURE(tlcl_get_flags(&disable, &deactivated, NULL));
+	if (disable || deactivated) {
+		printk(KERN_DEBUG "TPM: disabled (%d) or deactivated (%d). "
+			"Fixing...\n", disable, deactivated);
+		RETURN_ON_FAILURE(tlcl_assert_physical_presence());
+		RETURN_ON_FAILURE(tlcl_set_enable());
+		RETURN_ON_FAILURE(tlcl_set_deactivated(0));
+	}
+
+	RETURN_ON_FAILURE(tlcl_get_stclear_flags(&sflags));
+	if (sflags.deactivated) {
+		printk(KERN_DEBUG "TPM: Must reboot to re-enable\n");
+		return TPM_E_MUST_REBOOT;
+	}
+
+	RETURN_ON_FAILURE(tlcl_read_pcr(0, pcr_value));
+	if (!memcmp(pcr_value, PCR_uninitialized, PCR_DIGEST_LENGTH)) {
+		/* PCR0 not initialized, extend it with the boot mode */
+		const uint8_t *digest;
+
+		if (get_secure_boot_mode() == SECURE) {
+			digest = bootmode_digests[SHA1_SECURE_BOOT];
+		} else {
+			digest = bootmode_digests[SHA1_NON_SECURE_BOOT];
+		}
+
+		RETURN_ON_FAILURE(tlcl_extend(0, digest, NULL));
+	}
+
+	RETURN_ON_FAILURE(tlcl_read_pcr(1, pcr_value));
+	if (!memcmp(pcr_value, PCR_uninitialized, PCR_DIGEST_LENGTH)) {
+		/* PCR1 not initialized, extend it with the platform name */
+		RETURN_ON_FAILURE(tlcl_extend(1, sha256_gfsc100, NULL));
+	}
+
+	printk(KERN_DEBUG "TPM: Initialization successful\n");
+
+	return TPM_SUCCESS;
+}
+EXPORT_SYMBOL(tpm_init)