Implement tpmc getversion command.

This command exposes the vendor and TPM firmware version.

BRANCH=none
BUG=chromium:728130
TEST=Builds and tpmc getversion prints plausible results.

Change-Id: Iec556a298e025e10bda00121b40a25d8dc3839d1
Reviewed-on: https://chromium-review.googlesource.com/565287
Commit-Ready: Mattias Nissler <mnissler@chromium.org>
Tested-by: Mattias Nissler <mnissler@chromium.org>
Reviewed-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
Reviewed-by: Andrey Pronin <apronin@chromium.org>
diff --git a/firmware/include/tlcl.h b/firmware/include/tlcl.h
index 5373120..293beb6 100644
--- a/firmware/include/tlcl.h
+++ b/firmware/include/tlcl.h
@@ -206,4 +206,9 @@
  */
 uint32_t TlclGetRandom(uint8_t *data, uint32_t length, uint32_t *size);
 
+/**
+ * Requests version information from the TPM.
+ */
+uint32_t TlclGetVersion(uint32_t *vendor, uint64_t *firmware_version);
+
 #endif  /* TPM_LITE_TLCL_H_ */
diff --git a/firmware/include/tpm1_tss_constants.h b/firmware/include/tpm1_tss_constants.h
index b4449ab..12aca82 100644
--- a/firmware/include/tpm1_tss_constants.h
+++ b/firmware/include/tpm1_tss_constants.h
@@ -63,9 +63,10 @@
 #define TPM_CAP_FLAG_PERMANENT ((uint32_t) 0x00000108)
 #define TPM_CAP_FLAG_VOLATILE  ((uint32_t) 0x00000109)
 
-#define TPM_CAP_PROPERTY       ((uint32_t) 0x00000005)
-#define TPM_CAP_PROP_OWNER     ((uint32_t) 0x00000111)
-#define TPM_CAP_NV_INDEX       ((uint32_t) 0x00000011)
+#define TPM_CAP_PROPERTY        ((uint32_t) 0x00000005)
+#define TPM_CAP_PROP_OWNER      ((uint32_t) 0x00000111)
+#define TPM_CAP_NV_INDEX        ((uint32_t) 0x00000011)
+#define TPM_CAP_GET_VERSION_VAL ((uint32_t) 0x0000001a)
 
 #define TPM_ST_CLEAR       ((uint16_t) 0x0001)
 #define TPM_ST_STATE       ((uint16_t) 0x0002)
diff --git a/firmware/include/tpm2_tss_constants.h b/firmware/include/tpm2_tss_constants.h
index 454bf81..0354004 100644
--- a/firmware/include/tpm2_tss_constants.h
+++ b/firmware/include/tpm2_tss_constants.h
@@ -54,6 +54,9 @@
 #define TPM_PT_NONE                     ((TPM_PT)0x00000000)
 #define PT_GROUP                        ((TPM_PT)0x00000100)
 #define PT_FIXED                        PT_GROUP
+#define TPM_PT_MANUFACTURER             (PT_FIXED + 5)
+#define TPM_PT_FIRMWARE_VERSION_1       (PT_FIXED + 11)
+#define TPM_PT_FIRMWARE_VERSION_2       (PT_FIXED + 12)
 #define PT_VAR                          (PT_GROUP * 2)
 #define TPM_PT_PERMANENT                (PT_VAR + 0)
 #define TPM_PT_STARTUP_CLEAR            (PT_VAR + 1)
diff --git a/firmware/lib/tpm2_lite/tlcl.c b/firmware/lib/tpm2_lite/tlcl.c
index 0af34d0..56829f1 100644
--- a/firmware/lib/tpm2_lite/tlcl.c
+++ b/firmware/lib/tpm2_lite/tlcl.c
@@ -522,3 +522,22 @@
 	VB2_DEBUG("NOT YET IMPLEMENTED\n");
 	return TPM_E_IOERROR;
 }
+
+uint32_t TlclGetVersion(uint32_t* vendor, uint64_t* firmware_version)
+{
+	uint32_t result =  tlcl_get_tpm_property(TPM_PT_MANUFACTURER, vendor);
+	if (result != TPM_SUCCESS)
+		return result;
+
+	uint32_t version_1;
+	uint32_t version_2;
+	result = tlcl_get_tpm_property(TPM_PT_FIRMWARE_VERSION_1, &version_1);
+	if (result != TPM_SUCCESS)
+		return result;
+	result = tlcl_get_tpm_property(TPM_PT_FIRMWARE_VERSION_2, &version_2);
+	if (result != TPM_SUCCESS)
+		return result;
+
+	*firmware_version = ((uint64_t) version_1 << 32) | version_2;
+	return TPM_SUCCESS;
+}
diff --git a/firmware/lib/tpm_lite/include/tlcl_structures.h b/firmware/lib/tpm_lite/include/tlcl_structures.h
index 36c1bb9..cec3311 100644
--- a/firmware/lib/tpm_lite/include/tlcl_structures.h
+++ b/firmware/lib/tpm_lite/include/tlcl_structures.h
@@ -1,5 +1,10 @@
 /* This file is automatically generated */
 
+const struct s_tpm_getversionval_cmd{
+  uint8_t buffer[18];
+} tpm_getversionval_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0x12, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x0, 0x0, 0x0, },
+};
+
 const struct s_tpm_extend_cmd{
   uint8_t buffer[34];
   uint16_t pcrNum;
diff --git a/firmware/lib/tpm_lite/mocked_tlcl.c b/firmware/lib/tpm_lite/mocked_tlcl.c
index 0176296..2de6ec7 100644
--- a/firmware/lib/tpm_lite/mocked_tlcl.c
+++ b/firmware/lib/tpm_lite/mocked_tlcl.c
@@ -186,6 +186,13 @@
 	return TPM_SUCCESS;
 }
 
+uint32_t TlclGetVersion(uint32_t* vendor, uint64_t* firmware_version)
+{
+	*vendor = 0x4e4f4e45;
+	*firmware_version = 0x1;
+	return TPM_SUCCESS;
+}
+
 int TlclPacketSize(const uint8_t* packet)
 {
 	uint32_t size;
diff --git a/firmware/lib/tpm_lite/tlcl.c b/firmware/lib/tpm_lite/tlcl.c
index 5a99fdc..14acf96 100644
--- a/firmware/lib/tpm_lite/tlcl.c
+++ b/firmware/lib/tpm_lite/tlcl.c
@@ -512,3 +512,39 @@
 
 	return result;
 }
+
+uint32_t TlclGetVersion(uint32_t* vendor, uint64_t* firmware_version) {
+	uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
+	uint32_t result = TlclSendReceive(tpm_getversionval_cmd.buffer,
+					  response, sizeof(response));
+	if (result != TPM_SUCCESS)
+		return result;
+
+	uint8_t* cursor = response + kTpmResponseHeaderLength;
+
+	uint32_t size;
+	FromTpmUint32(cursor, &size);
+	cursor += sizeof(size);
+
+	/* Verify size >= sizeof(TPM_CAP_VERSION_INFO). */
+	const uint32_t kSizeofCapVersionInfo = 15;
+	if (size < kSizeofCapVersionInfo) {
+		return TPM_E_IOERROR;
+	}
+
+	cursor += sizeof(uint16_t);  /* tag */
+	cursor += sizeof(uint16_t);  /* spec version */
+
+	uint16_t version;
+	FromTpmUint16(cursor, &version);
+	cursor += sizeof(version);
+	*firmware_version = version;
+
+	cursor += sizeof(uint16_t);  /* specLevel */
+	cursor += sizeof(uint8_t);  /* errataRev */
+
+	FromTpmUint32(cursor, vendor);
+	cursor += sizeof(*vendor);
+
+	return TPM_SUCCESS;
+}
diff --git a/tests/tlcl_tests.c b/tests/tlcl_tests.c
index 5037161..f06947b 100644
--- a/tests/tlcl_tests.c
+++ b/tests/tlcl_tests.c
@@ -335,6 +335,47 @@
 	TEST_EQ(size, 0, "  size 0");
 }
 
+/**
+ * Test GetVersion
+ */
+static void GetVersionTest(void)
+{
+	uint8_t response[] = {
+		0x00, 0xc4, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x30,
+		0x01, 0x02, 0x04, 0x20, 0x00, 0x02, 0x03, 0x49,
+		0x46, 0x58, 0x00, 0x00, 0x0d, 0x04, 0x20, 0x03,
+		0x6f, 0x00, 0x74, 0x70, 0x6d, 0x33, 0x38, 0xff,
+		0xff, 0xff
+	};
+
+	uint32_t vendor;
+	uint64_t firmware_version;
+
+	ResetMocks();
+	calls[0].rsp = response;
+	calls[0].rsp_size = sizeof(response);
+	TEST_EQ(TlclGetVersion(&vendor, &firmware_version), 0, "GetVersion");
+	TEST_EQ(calls[0].req_cmd, TPM_ORD_GetCapability, "  cmd");
+	TEST_EQ(vendor, 0x49465800, "  vendor");
+	TEST_EQ(firmware_version, 0x420, "  firmware_version");
+
+	ResetMocks();
+	SetResponse(0, TPM_E_IOERROR, 0);
+	TEST_EQ(TlclGetVersion(&vendor, &firmware_version), TPM_E_IOERROR,
+		"GetVersion - error");
+	TEST_EQ(calls[0].req_cmd, TPM_ORD_GetCapability, "  cmd");
+
+	/* Adjust response to indicate a 1 byte too short payload size. */
+	ToTpmUint32(response + kTpmResponseHeaderLength, 14);
+	ResetMocks();
+	calls[0].rsp = response;
+	calls[0].rsp_size = sizeof(response);
+	TEST_EQ(TlclGetVersion(&vendor, &firmware_version), TPM_E_IOERROR,
+		"GetVersion -- short");
+	TEST_EQ(calls[0].req_cmd, TPM_ORD_GetCapability, "  cmd");
+}
+
 int main(void)
 {
 	TlclTest();
@@ -343,6 +384,7 @@
 	PcrTest();
 	FlagsTest();
 	RandomTest();
+	GetVersionTest();
 
 	return gTestSuccess ? 0 : 255;
 }
diff --git a/utility/tlcl_generator.c b/utility/tlcl_generator.c
index 7ed1d6c..70ce5fd 100644
--- a/utility/tlcl_generator.c
+++ b/utility/tlcl_generator.c
@@ -389,6 +389,21 @@
   return cmd;
 }
 
+Command* BuildGetVersionValCommand(void) {
+  int size = (kTpmRequestHeaderLength +
+              sizeof(TPM_CAPABILITY_AREA) +   /* capArea */
+              sizeof(uint32_t));              /* subCapSize */
+
+  Command* cmd = newCommand(TPM_ORD_GetCapability, size);
+  cmd->name = "tpm_getversionval_cmd";
+  AddInitializedField(cmd, kTpmRequestHeaderLength,
+                      sizeof(TPM_CAPABILITY_AREA), TPM_CAP_GET_VERSION_VAL);
+  AddInitializedField(cmd, kTpmRequestHeaderLength +
+                      sizeof(TPM_CAPABILITY_AREA),
+                      sizeof(uint32_t), 0);
+  return cmd;
+}
+
 /* Output the fields of a structure.
  */
 void OutputFields(Field* fld) {
@@ -510,6 +525,7 @@
   BuildGetOwnershipCommand,
   BuildGetRandomCommand,
   BuildExtendCommand,
+  BuildGetVersionValCommand,
 };
 
 static void FreeFields(Field* fld) {
diff --git a/utility/tpmc.c b/utility/tpmc.c
index 1e4e302..ae45ca1 100644
--- a/utility/tpmc.c
+++ b/utility/tpmc.c
@@ -9,6 +9,7 @@
  * for other errors.
  */
 
+#include <inttypes.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -456,6 +457,17 @@
   return result;
 }
 
+static uint32_t HandlerGetVersion(void) {
+  uint32_t vendor;
+  uint64_t firmware_version;
+  uint32_t result = TlclGetVersion(&vendor, &firmware_version);
+  if (result == 0) {
+    printf("vendor %08x\nfirmware_version %016" PRIx64 "\n",
+           vendor, firmware_version);
+  }
+  return result;
+}
+
 #ifdef TPM2_MODE
 static uint32_t HandlerDoNothingForTPM2(void) {
   return 0;
@@ -534,6 +546,8 @@
   { "savestate", "save", "execute TPM_SaveState", TlclSaveState },
   { "sendraw", "raw", "send a raw request and print raw response",
     HandlerSendRaw },
+  { "getversion", "getver", "get TPM vendor and firmware version",
+    HandlerGetVersion },
 };
 
 static int n_commands = sizeof(command_table) / sizeof(command_table[0]);