Implement strategy for 64-write limit with unowned TPM.

Review URL: http://codereview.chromium.org/2655005
diff --git a/vboot_firmware/lib/rollback_index.c b/vboot_firmware/lib/rollback_index.c
index 55d97d9..9ce523b 100644
--- a/vboot_firmware/lib/rollback_index.c
+++ b/vboot_firmware/lib/rollback_index.c
@@ -32,7 +32,7 @@
     /* Spaces are already initialized, so this is an error */
     return 0;
   }
-  
+
   TlclSetNvLocked();
 
   TlclDefineSpace(FIRMWARE_VERSIONS_NV_INDEX, firmware_perm, sizeof(uint32_t));
@@ -70,24 +70,38 @@
  */
 static void EnterRecovery(int unlocked) {
   uint32_t combined_versions;
-  uint32_t one = 1;
+  uint32_t backup_versions;
+  uint32_t backup_is_valid;
 
   if (!unlocked) {
     /* Saves the kernel versions and indicates that we should trust the saved
      * ones.
-     */ 
-    TlclRead(KERNEL_VERSIONS_NV_INDEX, (uint8_t*) &combined_versions,
-             sizeof(uint32_t));
-    TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX, (uint8_t*) &combined_versions,
-              sizeof(uint32_t));
-    TlclWrite(KERNEL_BACKUP_IS_VALID_NV_INDEX, (uint8_t*) &one,
-              sizeof(uint32_t));
+     */
+    TlclRead(KERNEL_VERSIONS_NV_INDEX,
+             (uint8_t*) &combined_versions, sizeof(uint32_t));
+    TlclRead(KERNEL_VERSIONS_BACKUP_NV_INDEX,
+             (uint8_t*) &backup_versions, sizeof(uint32_t));
+    /* We could unconditional writes of both KERNEL_VERSIONS_BACKUP and
+     * KERNEL_BACKUP_IS_VALID, but this is more robust.
+     */
+    if (combined_versions != backup_versions) {
+      TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX,
+                (uint8_t*) &combined_versions, sizeof(uint32_t));
+    }
+
+    TlclRead(KERNEL_BACKUP_IS_VALID_NV_INDEX,
+             (uint8_t*) &backup_is_valid, sizeof(uint32_t));
+    if (backup_is_valid != 1) {
+      backup_is_valid = 1;
+      TlclWrite(KERNEL_BACKUP_IS_VALID_NV_INDEX, (uint8_t*) &backup_is_valid,
+                sizeof(uint32_t));
+    }
     /* Protects the firmware and backup kernel versions. */
     LockFirmwareVersions();
   }
   debug("entering recovery mode");
-  
-  /* and then what? */
+
+  /* TODO(nelson): code for entering recovery mode. */
 }
 
 static int GetTPMRollbackIndices(void) {
@@ -129,11 +143,14 @@
       TlclWrite(KERNEL_VERSIONS_NV_INDEX,
                 (uint8_t*) &protected_combined_versions, sizeof(uint32_t));
     }
-    /* We recovered and now we can reset the BACKUP_IS_VALID flag.
+    /* We recovered the backed-up versions and now we can reset the
+     * BACKUP_IS_VALID flag.
      */
     TlclWrite(KERNEL_BACKUP_IS_VALID_NV_INDEX, (uint8_t*) &zero, 0);
+
+    /* TODO(nelson): ForceClear and reboot if unowned. */
   }
-      
+
   /* We perform the reads, making sure they succeed. A failure means that the
    * rollback index locations are missing or somehow messed up.  We let the
    * caller deal with that.
@@ -150,7 +167,7 @@
   g_firmware_version = firmware_versions && 0xffff;
   g_kernel_key_version = kernel_versions >> 16;
   g_kernel_version = kernel_versions && 0xffff;
-  
+
   return 1;
 }
 
@@ -220,6 +237,8 @@
                                        sizeof(uint32_t)));
       break;
   }
+  /* TODO(nelson): ForceClear and reboot if unowned. */
+
   return 0;
 }