Add support for IFX FieldUpgradeInfoRequest2 command

Add tpm_lite library support for the IFX specific TPM_FieldUpgrade
subcommand "FieldUpgradeInfoRequest2". Expose this via tpmc so it can
be used from shell scripts.

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

Reviewed-on: https://chromium-review.googlesource.com/562772
Commit-Ready: Mattias Nissler <mnissler@chromium.org>
Tested-by: Mattias Nissler <mnissler@chromium.org>
Reviewed-by: Mattias Nissler <mnissler@chromium.org>

BUG=chromium:761803

Change-Id: I80cf4d82de34e5010393918df2a317ea62e17095
Reviewed-on: https://chromium-review.googlesource.com/657427
Reviewed-by: Mattias Nissler <mnissler@chromium.org>
Tested-by: Mattias Nissler <mnissler@chromium.org>
diff --git a/firmware/include/tlcl.h b/firmware/include/tlcl.h
index 293beb6..fba764c 100644
--- a/firmware/include/tlcl.h
+++ b/firmware/include/tlcl.h
@@ -211,4 +211,10 @@
  */
 uint32_t TlclGetVersion(uint32_t *vendor, uint64_t *firmware_version);
 
+/**
+ * Issues the IFX specific FieldUpgradeInfoRequest2 TPM_FieldUpgrade subcommand
+ * and fills in [info] with results.
+ */
+uint32_t TlclIFXFieldUpgradeInfo(TPM_IFX_FIELDUPGRADEINFO *info);
+
 #endif  /* TPM_LITE_TLCL_H_ */
diff --git a/firmware/include/tpm1_tss_constants.h b/firmware/include/tpm1_tss_constants.h
index 12aca82..972f7a3 100644
--- a/firmware/include/tpm1_tss_constants.h
+++ b/firmware/include/tpm1_tss_constants.h
@@ -20,6 +20,7 @@
 
 #define TPM_E_AREA_LOCKED           ((uint32_t) 0x0000003c)
 #define TPM_E_BADINDEX              ((uint32_t) 0x00000002)
+#define TPM_E_BAD_ORDINAL           ((uint32_t) 0x0000000a)
 #define TPM_E_BAD_PRESENCE          ((uint32_t) 0x0000002d)
 #define TPM_E_IOERROR               ((uint32_t) 0x0000001f)
 #define TPM_E_INVALID_POSTINIT      ((uint32_t) 0x00000026)
@@ -168,6 +169,24 @@
     uint8_t nonce[TPM_SHA1BASED_NONCE_LEN];
 } TPM_NONCE;
 
+typedef struct tdTPM_IFX_FIRMWAREPACKAGE {
+    uint32_t FwPackageIdentifier;
+    uint32_t Version;
+    uint32_t StaleVersion;
+} TPM_IFX_FIRMWAREPACKAGE;
+
+typedef struct tdTPM_IFX_FIELDUPGRADEINFO
+{
+    uint16_t wMaxDataSize;
+    TPM_IFX_FIRMWAREPACKAGE sBootloaderFirmwarePackage;
+    TPM_IFX_FIRMWAREPACKAGE sFirmwarePackages[2];
+    uint16_t wSecurityModuleStatus;
+    TPM_IFX_FIRMWAREPACKAGE sProcessFirmwarePackage;
+    uint16_t wFieldUpgradeCounter;
+} TPM_IFX_FIELDUPGRADEINFO;
+
+#define TPM_IFX_FieldUpgradeInfoRequest2  ((uint8_t) 0x11)
+
 /* Ordinals */
 
 #define TPM_ORD_ContinueSelfTest  ((uint32_t) 0x00000053)
@@ -187,5 +206,6 @@
 #define TPM_ORD_SaveState         ((uint32_t) 0x00000098)
 #define TPM_ORD_SelfTestFull      ((uint32_t) 0x00000050)
 #define TPM_ORD_Startup           ((uint32_t) 0x00000099)
+#define TPM_ORD_FieldUpgrade      ((uint32_t) 0x000000AA)
 
 #endif  /* ! __VBOOT_REFERENCE_FIRMWARE_INCLUDE_TPM1_TSS_CONSTANTS_H */
diff --git a/firmware/include/tpm2_tss_constants.h b/firmware/include/tpm2_tss_constants.h
index 0354004..53be3fb 100644
--- a/firmware/include/tpm2_tss_constants.h
+++ b/firmware/include/tpm2_tss_constants.h
@@ -273,6 +273,10 @@
 	uint32_t orderly : 1;
 } TPM_STCLEAR_FLAGS;
 
+typedef struct tdTPM_IFX_FIELDUPGRADEINFO
+{
+} TPM_IFX_FIELDUPGRADEINFO;
+
 /* TODO(apronin): For TPM2 certain properties must be received using
  * TPM2_GetCapability instead of being hardcoded as they are now:
  * TPM_MAX_COMMAND_SIZE -> use TPM_PT_MAX_COMMAND_SIZE for TPM2.
diff --git a/firmware/lib/tpm2_lite/tlcl.c b/firmware/lib/tpm2_lite/tlcl.c
index 56829f1..bb212d2 100644
--- a/firmware/lib/tpm2_lite/tlcl.c
+++ b/firmware/lib/tpm2_lite/tlcl.c
@@ -541,3 +541,9 @@
 	*firmware_version = ((uint64_t) version_1 << 32) | version_2;
 	return TPM_SUCCESS;
 }
+
+uint32_t TlclIFXFieldUpgradeInfo(TPM_IFX_FIELDUPGRADEINFO* info)
+{
+	VB2_DEBUG("NOT YET IMPLEMENTED\n");
+	return TPM_E_IOERROR;
+}
diff --git a/firmware/lib/tpm_lite/include/tlcl_structures.h b/firmware/lib/tpm_lite/include/tlcl_structures.h
index cec3311..6ddb885 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_ifx_fieldupgradeinforequest2_cmd{
+  uint8_t buffer[13];
+} tpm_ifx_fieldupgradeinforequest2_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0xaa, 0x11, 0x0, 0x0, },
+};
+
 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, },
diff --git a/firmware/lib/tpm_lite/mocked_tlcl.c b/firmware/lib/tpm_lite/mocked_tlcl.c
index 2de6ec7..def4810 100644
--- a/firmware/lib/tpm_lite/mocked_tlcl.c
+++ b/firmware/lib/tpm_lite/mocked_tlcl.c
@@ -205,3 +205,9 @@
 {
 	return TPM_SUCCESS;
 }
+
+uint32_t TlclIFXFieldUpgradeInfo(TPM_IFX_FIELDUPGRADEINFO* info)
+{
+	memset(info, 0, sizeof(*info));
+	return TPM_SUCCESS;
+}
diff --git a/firmware/lib/tpm_lite/tlcl.c b/firmware/lib/tpm_lite/tlcl.c
index 14acf96..59dd120 100644
--- a/firmware/lib/tpm_lite/tlcl.c
+++ b/firmware/lib/tpm_lite/tlcl.c
@@ -548,3 +548,74 @@
 
 	return TPM_SUCCESS;
 }
+
+static void ParseIFXFirmwarePackage(uint8_t** cursor,
+				    TPM_IFX_FIRMWAREPACKAGE* firmware_package) {
+
+	FromTpmUint32(*cursor, &firmware_package->FwPackageIdentifier);
+	*cursor += sizeof(firmware_package->FwPackageIdentifier);
+	FromTpmUint32(*cursor, &firmware_package->Version);
+	*cursor += sizeof(firmware_package->Version);
+	FromTpmUint32(*cursor, &firmware_package->StaleVersion);
+	*cursor += sizeof(firmware_package->StaleVersion);
+}
+
+uint32_t TlclIFXFieldUpgradeInfo(TPM_IFX_FIELDUPGRADEINFO* info) {
+	uint32_t vendor;
+	uint64_t firmware_version;
+	uint32_t result = TlclGetVersion(&vendor, &firmware_version);
+	if (result != TPM_SUCCESS) {
+		return result;
+	}
+	if (vendor != 0x49465800) {
+		return TPM_E_BAD_ORDINAL;
+	}
+
+	uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
+	result = TlclSendReceive(tpm_ifx_fieldupgradeinforequest2_cmd.buffer,
+				 response, sizeof(response));
+	if (result != TPM_SUCCESS) {
+		return result;
+	}
+
+	uint8_t* cursor = response + kTpmResponseHeaderLength;
+
+	uint16_t size;
+	FromTpmUint16(cursor, &size);
+	cursor += sizeof(size);
+
+	/* Comments below indicate skipped fields of unknown purpose that are
+	 * marked "internal" in the firmware updater source. */
+	cursor += sizeof(uint16_t);  /* internal1 */
+	FromTpmUint16(cursor, &info->wMaxDataSize);
+	cursor += sizeof(info->wMaxDataSize);
+	cursor += sizeof(uint16_t);  /* sSecurityModuleLogic.internal1 */
+	cursor += sizeof(uint32_t);  /* sSecurityModuleLogic.internal2 */
+	cursor += sizeof(uint8_t[34]);  /* sSecurityModuleLogic.internal3 */
+	ParseIFXFirmwarePackage(&cursor, &info->sBootloaderFirmwarePackage);
+	uint16_t fw_entry_count;
+	FromTpmUint16(cursor, &fw_entry_count);
+	cursor += sizeof(fw_entry_count);
+	if (fw_entry_count > ARRAY_SIZE(info->sFirmwarePackages)) {
+		return TPM_E_IOERROR;
+	}
+	uint16_t i;
+	for (i = 0; i < fw_entry_count; ++i) {
+		ParseIFXFirmwarePackage(&cursor, &info->sFirmwarePackages[i]);
+	}
+	FromTpmUint16(cursor, &info->wSecurityModuleStatus);
+	cursor += sizeof(info->wSecurityModuleStatus);
+	ParseIFXFirmwarePackage(&cursor, &info->sProcessFirmwarePackage);
+	cursor += sizeof(uint16_t);  /* internal6 */
+	cursor += sizeof(uint8_t[6]);  /* internal7 */
+	FromTpmUint16(cursor, &info->wFieldUpgradeCounter);
+	cursor += sizeof(info->wFieldUpgradeCounter);
+
+	uint32_t parsed_bytes = cursor - response;
+	VbAssert(parsed_bytes <= TPM_LARGE_ENOUGH_COMMAND_SIZE);
+	if (parsed_bytes > kTpmResponseHeaderLength + sizeof(size) + size) {
+		return TPM_E_IOERROR;
+	}
+
+	return result;
+}
diff --git a/tests/tlcl_tests.c b/tests/tlcl_tests.c
index f06947b..b8d64a8 100644
--- a/tests/tlcl_tests.c
+++ b/tests/tlcl_tests.c
@@ -376,6 +376,105 @@
 	TEST_EQ(calls[0].req_cmd, TPM_ORD_GetCapability, "  cmd");
 }
 
+/**
+ * Test IFX FieldUpgradeInfoRequest2
+ */
+static void IFXFieldUpgradeInfoTest(void)
+{
+	uint8_t version_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
+	};
+	uint8_t upgrade_info_response[] = {
+		0x00, 0xc4, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x6a, 0x03, 0x02, 0x04, 0x9c,
+		0x04, 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x08,
+		0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x01,
+		0x01, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0x00,
+		0x00, 0x00, 0x04, 0x01, 0x02, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0xff, 0xff, 0xee, 0xee, 0x5a, 0x3c,
+		0x04, 0x01, 0x02, 0x00, 0x00, 0x00, 0x08, 0x32,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x3f
+	};
+
+	ResetMocks();
+	calls[0].rsp = version_response;
+	calls[0].rsp_size = sizeof(version_response);
+	calls[1].rsp = upgrade_info_response;
+	calls[1].rsp_size = sizeof(upgrade_info_response);
+	TPM_IFX_FIELDUPGRADEINFO info;
+	TEST_EQ(TlclIFXFieldUpgradeInfo(&info), 0, "IFXFieldUpgradeInfo");
+	TEST_EQ(calls[1].req_cmd, TPM_ORD_FieldUpgrade, "  cmd");
+	TEST_EQ(info.wMaxDataSize, 1180, "  wMaxDatasize");
+	TEST_EQ(info.sBootloaderFirmwarePackage.FwPackageIdentifier, 0x50100,
+		"  bootloader FWPackageIdeintifier");
+	TEST_EQ(info.sBootloaderFirmwarePackage.Version, 0xffff,
+		"  bootloader Version");
+	TEST_EQ(info.sBootloaderFirmwarePackage.StaleVersion, 0x0,
+		"  bootloader StaleVersion");
+	TEST_EQ(info.sFirmwarePackages[0].FwPackageIdentifier, 0x4010100,
+		"  fw[0] FWPackageIdeintifier");
+	TEST_EQ(info.sFirmwarePackages[0].Version, 0xbe,
+		"  fw[0] Version");
+	TEST_EQ(info.sFirmwarePackages[0].StaleVersion, 0x0,
+		"  fw[0] StaleVersion");
+	TEST_EQ(info.sFirmwarePackages[1].FwPackageIdentifier, 0x4010200,
+		"  fw[1] FWPackageIdeintifier");
+	TEST_EQ(info.sFirmwarePackages[1].Version, 0x0,
+		"  fw[1] Version");
+	TEST_EQ(info.sFirmwarePackages[1].StaleVersion, 0xffffeeee,
+		"  fw[1] StaleVersion");
+	TEST_EQ(info.wSecurityModuleStatus, 0x5a3c, "  wSecurityModuleStatus");
+	TEST_EQ(info.sProcessFirmwarePackage.FwPackageIdentifier, 0x4010200,
+		"  process FWPackageIdeintifier");
+	TEST_EQ(info.sProcessFirmwarePackage.Version, 0x832,
+		"  process Version");
+	TEST_EQ(info.sProcessFirmwarePackage.StaleVersion, 0x0,
+		"  process StaleVersion");
+	TEST_EQ(info.wFieldUpgradeCounter, 0x3f, "  wFieldUpgradeCounter");
+
+	ResetMocks();
+	calls[0].rsp = version_response;
+	calls[0].rsp_size = sizeof(version_response);
+	SetResponse(1, TPM_E_IOERROR, sizeof(upgrade_info_response) - 1);
+	TEST_EQ(TlclIFXFieldUpgradeInfo(&info), TPM_E_IOERROR,
+		"IFXFieldUpgradeInfo - error");
+	TEST_EQ(calls[1].req_cmd, TPM_ORD_FieldUpgrade, "  cmd");
+
+	/* Adjust response to indicate a 1 byte too short payload size. */
+	ToTpmUint16(upgrade_info_response + kTpmRequestHeaderLength,
+		    sizeof(upgrade_info_response) - kTpmRequestHeaderLength -
+		    sizeof(uint16_t) - 1);
+	ResetMocks();
+	calls[0].rsp = version_response;
+	calls[0].rsp_size = sizeof(version_response);
+	calls[1].rsp = upgrade_info_response;
+	calls[1].rsp_size = sizeof(upgrade_info_response);
+	TEST_EQ(TlclIFXFieldUpgradeInfo(&info), TPM_E_IOERROR,
+		"IFXFieldUpgradeInfo - short");
+	TEST_EQ(calls[1].req_cmd, TPM_ORD_FieldUpgrade, "  cmd");
+
+	/* Adjust version response to claim a non-IFX vendor. */
+	ToTpmUint32(version_response + kTpmResponseHeaderLength +
+		    sizeof(uint32_t), 0);
+	ResetMocks();
+	calls[0].rsp = version_response;
+	calls[0].rsp_size = sizeof(version_response);
+	TEST_EQ(TlclIFXFieldUpgradeInfo(&info), TPM_E_IOERROR,
+		"IFXFieldUpgradeInfo - bad vendor");
+	TEST_EQ(calls[1].req_cmd, TPM_ORD_FieldUpgrade, "  cmd");
+}
+
 int main(void)
 {
 	TlclTest();
@@ -385,6 +484,7 @@
 	FlagsTest();
 	RandomTest();
 	GetVersionTest();
+	IFXFieldUpgradeInfoTest();
 
 	return gTestSuccess ? 0 : 255;
 }
diff --git a/utility/tlcl_generator.c b/utility/tlcl_generator.c
index 70ce5fd..bb7ed81 100644
--- a/utility/tlcl_generator.c
+++ b/utility/tlcl_generator.c
@@ -404,6 +404,21 @@
   return cmd;
 }
 
+Command* BuildIFXFieldUpgradeInfoRequest2Command(void) {
+  int size = (kTpmRequestHeaderLength +
+              sizeof(TPM_IFX_FieldUpgradeInfoRequest2) +
+              sizeof(uint16_t));
+  Command* cmd = newCommand(TPM_ORD_FieldUpgrade, size);
+  cmd->name = "tpm_ifx_fieldupgradeinforequest2_cmd";
+  AddInitializedField(cmd, kTpmRequestHeaderLength,
+                      sizeof(TPM_IFX_FieldUpgradeInfoRequest2),
+                      TPM_IFX_FieldUpgradeInfoRequest2);
+  AddInitializedField(cmd, kTpmRequestHeaderLength +
+                      sizeof(TPM_IFX_FieldUpgradeInfoRequest2),
+                      sizeof(uint16_t), 0);
+  return cmd;
+}
+
 /* Output the fields of a structure.
  */
 void OutputFields(Field* fld) {
@@ -526,6 +541,7 @@
   BuildGetRandomCommand,
   BuildExtendCommand,
   BuildGetVersionValCommand,
+  BuildIFXFieldUpgradeInfoRequest2Command,
 };
 
 static void FreeFields(Field* fld) {
diff --git a/utility/tpmc.c b/utility/tpmc.c
index ae45ca1..a36f7e9 100644
--- a/utility/tpmc.c
+++ b/utility/tpmc.c
@@ -468,6 +468,31 @@
   return result;
 }
 
+#ifndef TPM2_MODE
+static void PrintIFXFirmwarePackage(TPM_IFX_FIRMWAREPACKAGE* firmware_package,
+                                    const char* prefix) {
+  printf("%s_package_id %08x\n", prefix,
+         firmware_package->FwPackageIdentifier);
+  printf("%s_version %08x\n", prefix, firmware_package->Version);
+  printf("%s_stale_version %08x\n", prefix, firmware_package->StaleVersion);
+}
+
+static uint32_t HandlerIFXFieldUpgradeInfo(void) {
+  TPM_IFX_FIELDUPGRADEINFO info;
+  uint32_t result = TlclIFXFieldUpgradeInfo(&info);
+  if (result == 0) {
+    printf("max_data_size %u\n", info.wMaxDataSize);
+    PrintIFXFirmwarePackage(&info.sBootloaderFirmwarePackage, "bootloader");
+    PrintIFXFirmwarePackage(&info.sFirmwarePackages[0], "fw0");
+    PrintIFXFirmwarePackage(&info.sFirmwarePackages[1], "fw1");
+    printf("status %04x\n", info.wSecurityModuleStatus);
+    PrintIFXFirmwarePackage(&info.sProcessFirmwarePackage, "process_fw");
+    printf("field_upgrade_counter %u\n", info.wFieldUpgradeCounter);
+  }
+  return result;
+}
+#endif
+
 #ifdef TPM2_MODE
 static uint32_t HandlerDoNothingForTPM2(void) {
   return 0;
@@ -548,6 +573,9 @@
     HandlerSendRaw },
   { "getversion", "getver", "get TPM vendor and firmware version",
     HandlerGetVersion },
+  { "ifxfieldupgradeinfo", "ifxfui",
+    TPM20_NOT_IMPLEMENTED("read and print IFX field upgrade info",
+      HandlerIFXFieldUpgradeInfo) },
 };
 
 static int n_commands = sizeof(command_table) / sizeof(command_table[0]);