Add TPM version checking

Change-Id: Ic32b7bcf0bc5501e21dc84e79419a256d9b0d095

R=semenzato@chromium.org,reinauer@chromium.org
BUG=chrome-os-partner:2832
TEST=manual

crossystem tpm_fwver tpm_kernver
On a debug system, this will return 0x00010001 0x00010001

Review URL: http://codereview.chromium.org/6685075
diff --git a/firmware/include/vboot_struct.h b/firmware/include/vboot_struct.h
index f436427..7cf8803 100644
--- a/firmware/include/vboot_struct.h
+++ b/firmware/include/vboot_struct.h
@@ -204,9 +204,13 @@
   uint8_t check_fw_b_result;          /* Result of checking RW firmware B */
   uint8_t firmware_index;             /* Firmware index returned by
                                        * LoadFirmware() or 0xFF if failure */
-  uint32_t fw_version_tpm_start;      /* Firmware TPM version at start */
+  uint32_t fw_version_tpm_start;      /* Firmware TPM version at start of
+                                       * LoadFirmware() */
   uint32_t fw_version_lowest;         /* Firmware lowest version found */
 
+  uint32_t fw_version_tpm;            /* Current firmware version in TPM */
+  uint32_t kernel_version_tpm;        /* Current kernel version in TPM */
+
   /* After read-only firmware which uses version 1 is released, any additional
    * fields must be added below, and the struct version must be increased.
    * Before reading/writing those fields, make sure that the struct being
diff --git a/firmware/lib/include/rollback_index.h b/firmware/lib/include/rollback_index.h
index 0e630db..7db8515 100644
--- a/firmware/lib/include/rollback_index.h
+++ b/firmware/lib/include/rollback_index.h
@@ -94,6 +94,11 @@
  * mode. */
 uint32_t RollbackFirmwareSetup(int developer_mode, uint32_t* version);
 
+/* Read may be called to get the version.  This is not necessary in
+ * the normal boot path, because RollbackFirmwareSetup() provides the
+ * same information.  It may be used in the recovery path. */
+uint32_t RollbackFirmwareRead(uint32_t* version);
+
 /* Write may be called if the versions change */
 uint32_t RollbackFirmwareWrite(uint32_t version);
 
@@ -109,8 +114,7 @@
  * mode. */
 uint32_t RollbackKernelRecovery(int developer_mode);
 
-/* Read and write may be called if not in developer mode.  If called in
- * recovery mode, the effect is undefined. */
+/* Read and write may be called to read and write the kernel version. */
 uint32_t RollbackKernelRead(uint32_t* version);
 uint32_t RollbackKernelWrite(uint32_t version);
 
diff --git a/firmware/lib/rollback_index.c b/firmware/lib/rollback_index.c
index 9442b77..697bd0e 100644
--- a/firmware/lib/rollback_index.c
+++ b/firmware/lib/rollback_index.c
@@ -301,6 +301,11 @@
   return TPM_SUCCESS;
 }
 
+uint32_t RollbackFirmwareRead(uint32_t* version) {
+  *version = 0;
+  return TPM_SUCCESS;
+}
+
 uint32_t RollbackFirmwareWrite(uint32_t version) {
   return TPM_SUCCESS;
 }
@@ -357,6 +362,16 @@
   return TPM_SUCCESS;
 }
 
+uint32_t RollbackFirmwareRead(uint32_t* version) {
+  RollbackSpaceFirmware rsf;
+
+  RETURN_ON_FAILURE(ReadSpaceFirmware(&rsf));
+  VBDEBUG(("TPM: RollbackFirmwareRead %x --> %x\n", (int)rsf.fw_versions,
+           (int)version));
+  *version = rsf.fw_versions;
+  VBDEBUG(("TPM: RollbackFirmwareRead %x\n", (int)rsf.fw_versions));
+  return TPM_SUCCESS;
+}
 
 uint32_t RollbackFirmwareWrite(uint32_t version) {
   RollbackSpaceFirmware rsf;
@@ -390,40 +405,32 @@
 }
 
 uint32_t RollbackKernelRead(uint32_t* version) {
-  if (g_rollback_recovery_mode) {
-    *version = 0;
-  } else {
-    RollbackSpaceKernel rsk;
-    uint32_t perms;
+  RollbackSpaceKernel rsk;
+  uint32_t perms;
 
-    /* Read the kernel space and verify its permissions.  If the kernel
-     * space has the wrong permission, or it doesn't contain the right
-     * identifier, we give up.  This will need to be fixed by the
-     * recovery kernel.  We have to worry about this because at any time
-     * (even with PP turned off) the TPM owner can remove and redefine a
-     * PP-protected space (but not write to it). */
-    RETURN_ON_FAILURE(ReadSpaceKernel(&rsk));
-    RETURN_ON_FAILURE(TlclGetPermissions(KERNEL_NV_INDEX, &perms));
-    if (TPM_NV_PER_PPWRITE != perms || ROLLBACK_SPACE_KERNEL_UID != rsk.uid)
-      return TPM_E_CORRUPTED_STATE;
+  /* Read the kernel space and verify its permissions.  If the kernel
+   * space has the wrong permission, or it doesn't contain the right
+   * identifier, we give up.  This will need to be fixed by the
+   * recovery kernel.  We have to worry about this because at any time
+   * (even with PP turned off) the TPM owner can remove and redefine a
+   * PP-protected space (but not write to it). */
+  RETURN_ON_FAILURE(ReadSpaceKernel(&rsk));
+  RETURN_ON_FAILURE(TlclGetPermissions(KERNEL_NV_INDEX, &perms));
+  if (TPM_NV_PER_PPWRITE != perms || ROLLBACK_SPACE_KERNEL_UID != rsk.uid)
+    return TPM_E_CORRUPTED_STATE;
 
-    *version = rsk.kernel_versions;
-    VBDEBUG(("TPM: RollbackKernelRead %x\n", (int)rsk.kernel_versions));
-  }
+  *version = rsk.kernel_versions;
+  VBDEBUG(("TPM: RollbackKernelRead %x\n", (int)rsk.kernel_versions));
   return TPM_SUCCESS;
 }
 
 uint32_t RollbackKernelWrite(uint32_t version) {
-  if (g_rollback_recovery_mode) {
-    return TPM_SUCCESS;
-  } else {
-    RollbackSpaceKernel rsk;
-    RETURN_ON_FAILURE(ReadSpaceKernel(&rsk));
-    VBDEBUG(("TPM: RollbackKernelWrite %x --> %x\n", (int)rsk.kernel_versions,
-             (int)version));
-    rsk.kernel_versions = version;
-    return WriteSpaceKernel(&rsk);
-  }
+  RollbackSpaceKernel rsk;
+  RETURN_ON_FAILURE(ReadSpaceKernel(&rsk));
+  VBDEBUG(("TPM: RollbackKernelWrite %x --> %x\n", (int)rsk.kernel_versions,
+           (int)version));
+  rsk.kernel_versions = version;
+  return WriteSpaceKernel(&rsk);
 }
 
 uint32_t RollbackKernelLock(void) {
diff --git a/firmware/lib/vboot_firmware.c b/firmware/lib/vboot_firmware.c
index dd006f3..6c895e9 100644
--- a/firmware/lib/vboot_firmware.c
+++ b/firmware/lib/vboot_firmware.c
@@ -125,6 +125,7 @@
     goto LoadFirmwareExit;
   }
   shared->fw_version_tpm_start = tpm_version;
+  shared->fw_version_tpm = tpm_version;
   VBPERFEND("VB_TPMI");
 
   /* Read try-b count and decrement if necessary */
@@ -347,6 +348,7 @@
           recovery = VBNV_RECOVERY_RO_TPM_ERROR;
         goto LoadFirmwareExit;
       }
+      shared->fw_version_tpm = (uint32_t)lowest_version;
     }
 
     /* Lock firmware versions in TPM */
diff --git a/firmware/lib/vboot_kernel.c b/firmware/lib/vboot_kernel.c
index e50fd0f..204874f 100644
--- a/firmware/lib/vboot_kernel.c
+++ b/firmware/lib/vboot_kernel.c
@@ -232,6 +232,12 @@
       /* Ignore return code, since we need to boot recovery mode to
        * fix the TPM. */
     }
+
+    /* Read the key indices from the TPM; ignore any errors */
+    if (shared) {
+      RollbackFirmwareRead(&shared->fw_version_tpm);
+      RollbackKernelRead(&shared->kernel_version_tpm);
+    }
   } else {
     /* Use the kernel subkey passed from LoadFirmware(). */
     kernel_subkey = &shared->kernel_subkey;
@@ -247,6 +253,8 @@
         recovery = VBNV_RECOVERY_RW_TPM_ERROR;
       goto LoadKernelExit;
     }
+    if (shared)
+      shared->kernel_version_tpm = tpm_version;
   }
 
   do {
@@ -521,6 +529,8 @@
             recovery = VBNV_RECOVERY_RW_TPM_ERROR;
           goto LoadKernelExit;
         }
+        if (shared)
+          shared->kernel_version_tpm = (uint32_t)lowest_version;
       }
     }
 
diff --git a/firmware/linktest/main.c b/firmware/linktest/main.c
index 16c4bf5..a3ed21a 100644
--- a/firmware/linktest/main.c
+++ b/firmware/linktest/main.c
@@ -31,6 +31,7 @@
   /* rollback_index.h */
   RollbackS3Resume();
   RollbackFirmwareSetup(0, 0);
+  RollbackFirmwareRead(0);
   RollbackFirmwareWrite(0);
   RollbackFirmwareLock();
   RollbackKernelRecovery(0);
diff --git a/host/lib/crossystem.c b/host/lib/crossystem.c
index 6ba67e6..ca61f74 100644
--- a/host/lib/crossystem.c
+++ b/host/lib/crossystem.c
@@ -101,7 +101,9 @@
 
 /* Fields that GetVdatInt() can get */
 typedef enum VdatIntField {
-  VDAT_INT_FLAGS = 0  /* Flags */
+  VDAT_INT_FLAGS = 0,              /* Flags */
+  VDAT_INT_FW_VERSION_TPM,         /* Current firmware version in TPM */
+  VDAT_INT_KERNEL_VERSION_TPM      /* Current kernel version in TPM */
 } VdatIntField;
 
 
@@ -670,7 +672,12 @@
     case VDAT_INT_FLAGS:
       value = (int)sh->flags;
       break;
-
+    case VDAT_INT_FW_VERSION_TPM:
+      value = (int)sh->fw_version_tpm;
+      break;
+    case VDAT_INT_KERNEL_VERSION_TPM:
+      value = (int)sh->kernel_version_tpm;
+      break;
   }
 
   Free(ab);
@@ -747,6 +754,10 @@
     value = VbGetCrosDebug();
   } else if (!strcasecmp(name,"vdat_flags")) {
     value = GetVdatInt(VDAT_INT_FLAGS);
+  } else if (!strcasecmp(name,"tpm_fwver")) {
+    value = GetVdatInt(VDAT_INT_FW_VERSION_TPM);
+  } else if (!strcasecmp(name,"tpm_kernver")) {
+    value = GetVdatInt(VDAT_INT_KERNEL_VERSION_TPM);
   }
 
   return value;
diff --git a/utility/crossystem_main.c b/utility/crossystem_main.c
index fb84283..6f352ed 100644
--- a/utility/crossystem_main.c
+++ b/utility/crossystem_main.c
@@ -41,6 +41,8 @@
   {"tried_fwb", 0, "Tried firmware B before A this boot"},
   {"cros_debug", 0, "OS should allow debug features"},
   {"vdat_flags", 0, "Flags from VbSharedData", "0x%08x"},
+  {"tpm_fwver", 0, "Firmware version stored in TPM", "0x%08x"},
+  {"tpm_kernver", 0, "Kernel version stored in TPM", "0x%08x"},
   /* Read-only strings */
   {"hwid", IS_STRING, "Hardware ID"},
   {"fwid", IS_STRING, "Active firmware ID"},