VBoot Reference: Add kernel rollback prevention and choosing logic.

Also adds tests for kernel rollback prevention.

Review URL: http://codereview.chromium.org/1320010
diff --git a/include/kernel_image.h b/include/kernel_image.h
index c098c07..6adf3f8 100644
--- a/include/kernel_image.h
+++ b/include/kernel_image.h
@@ -17,6 +17,9 @@
 #define KERNEL_MAGIC_SIZE 8
 #define KERNEL_CMD_LINE_SIZE 4096
 
+#define DEV_MODE_ENABLED 1
+#define DEV_MODE_DISABLED 0
+
 /* Kernel config file options according to the Chrome OS drive map design. */
 typedef struct kconfig_options {
   uint32_t version[2];  /* Configuration file version. */
@@ -221,4 +224,44 @@
 int AddKernelSignature(KernelImage* image,
                        const char* kernel_sigining_key_file);
 
+/* Returns the logical version of a kernel blob which is calculated as
+ * (kernel_key_version << 16 | kernel_version). */
+uint32_t GetLogicalKernelVersion(uint8_t* kernel_blob);
+
+#define  BOOT_KERNEL_A_CONTINUE 1
+#define  BOOT_KERNEL_B_CONTINUE 2
+#define  BOOT_KERNEL_RECOVERY_CONTINUE 3
+
+/* Contains information about the kernel paritition
+ * gleaned from the GPT partition table.
+ *
+ * Based on the Chromium OS Drive Map design document by
+ * rspangler@chromium.org.
+ *
+*/
+typedef struct kernel_entry {
+  uint8_t* kernel_blob;  /* Pointer to actual kernel. */
+  uint8_t boot_priority;  /* 15 = highest, 1 = lowest, 0 = not bootable. */
+  uint8_t boot_tries_remaining;  /* Used when boot_priority = 0. */
+  uint8_t boot_success_flag;  /* Set to 1 on successful boot by AU. */
+} kernel_entry;
+
+void PrintKernelEntry(kernel_entry* entry);
+
+/* This function is the driver used by the RW firmware to
+ * determine which copy of the kernel to boot from. It performs
+ * the requisite priority and remaining tries checking for a specific
+ * kernel partition, does rollback index checking, including updating
+ * if required.
+ *
+ * Returns the code path to follow. It is one of:
+ * BOOT_KERNEL_A_CONTINUE          Boot from Kenrel A
+ * BOOT_KERNEL_B_CONTINUE          Boot from Kernel B
+ * BOOT_KERNEL_RECOVERY_CONTINUE   Jump to recovery mode
+ */
+int VerifyKernelDriver_f(uint8_t* firmware_key_blob,
+                         kernel_entry* kernelA,
+                         kernel_entry* kernelB,
+                         int dev_mode);
+
 #endif  /* VBOOT_REFERENCE_KERNEL_IMAGE_H_ */
diff --git a/tests/Makefile b/tests/Makefile
index d2e70c3..5475bfb 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -16,6 +16,7 @@
 	firmware_rollback_tests \
 	firmware_verify_benchmark \
 	kernel_image_tests \
+	kernel_rollback_tests \
 	kernel_verify_benchmark \
 	rsa_padding_test \
 	rsa_verify_benchmark \
@@ -38,6 +39,10 @@
 kernel_image_tests: kernel_image_tests.c rollback_index_mock.c test_common.c
 	$(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(LIBS)
 
+kernel_rollback_tests: kernel_rollback_tests.c rollback_index_mock.c \
+	test_common.c
+	$(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(LIBS)
+
 kernel_verify_benchmark: kernel_verify_benchmark.c timer_utils.c \
 	rollback_index_mock.c test_common.c
 	$(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ -lrt $(LIBS)
@@ -68,6 +73,7 @@
 		firmware_rollback_tests \
 		firmware_verify_benchmark \
 		kernel_image_tests \
+		kernel_rollback_tests \
 		kernel_verify_benchmark \
 		rsa_padding_test \
 		rsa_verify_benchmark \
diff --git a/tests/firmware_rollback_tests.c b/tests/firmware_rollback_tests.c
index 0df0c3c..e36c003 100644
--- a/tests/firmware_rollback_tests.c
+++ b/tests/firmware_rollback_tests.c
@@ -30,10 +30,10 @@
   g_firmware_key_version = 1;
   g_firmware_version = 1;
 
-  valid_firmwareA = GenerateRollbackTestImage(1, 1, 0);
-  valid_firmwareB = GenerateRollbackTestImage(1, 1, 0);
-  corrupt_firmwareA = GenerateRollbackTestImage(1, 1, 1);
-  corrupt_firmwareB = GenerateRollbackTestImage(1, 1, 1);
+  valid_firmwareA = GenerateRollbackTestFirmwareBlob(1, 1, 0);
+  valid_firmwareB = GenerateRollbackTestFirmwareBlob(1, 1, 0);
+  corrupt_firmwareA = GenerateRollbackTestFirmwareBlob(1, 1, 1);
+  corrupt_firmwareB = GenerateRollbackTestFirmwareBlob(1, 1, 1);
 
   TEST_EQ(VerifyFirmwareDriver_f(root_key_pub,
                                  valid_firmwareA, valid_firmwareB),
diff --git a/tests/kernel_image_tests.c b/tests/kernel_image_tests.c
index a565011..3d37bb7 100644
--- a/tests/kernel_image_tests.c
+++ b/tests/kernel_image_tests.c
@@ -14,9 +14,6 @@
 #include "test_common.h"
 #include "utility.h"
 
-#define DEV_MODE_ENABLED 1
-#define DEV_MODE_DISABLED 0
-
 /* Normal Kernel Blob Verification Tests. */
 void VerifyKernelTest(uint8_t* kernel_blob, uint8_t* firmware_key_blob) {
   TEST_EQ(VerifyKernel(firmware_key_blob, kernel_blob, DEV_MODE_ENABLED),
diff --git a/tests/kernel_rollback_tests.c b/tests/kernel_rollback_tests.c
new file mode 100644
index 0000000..c956300
--- /dev/null
+++ b/tests/kernel_rollback_tests.c
@@ -0,0 +1,149 @@
+/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Tests for checking kernel rollback-prevention logic.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "file_keys.h"
+#include "kernel_image.h"
+#include "rsa_utility.h"
+#include "rollback_index.h"
+#include "test_common.h"
+#include "utility.h"
+
+/* Tests that check for correctness of the VerifyFirmwareDriver_f() logic
+ * and rollback prevention. */
+void VerifyKernelDriverTest(void) {
+  uint64_t len;
+  uint8_t* firmware_key_pub = BufferFromFile("testkeys/key_rsa1024.keyb",
+                                             &len);
+  /* Initialize kernel blobs, including their associated parition
+   * table attributed. */
+  kernel_entry valid_kernelA =  {
+    GenerateRollbackTestKernelBlob(1, 1, 0),
+    15,  /* Highest Priority. */
+    5,  /* Enough for tests. */
+    0  /* Assume we haven't boot off it yet. */
+  };
+  kernel_entry corrupt_kernelA = {
+    GenerateRollbackTestKernelBlob(1, 1, 1),
+    15,  /* Highest Priority. */
+    5,  /* Enough for tests. */
+    0  /* Assume we haven't boot off it yet. */
+  };
+  kernel_entry valid_kernelB = {
+    GenerateRollbackTestKernelBlob(1, 1, 0),
+    1,  /* Lower Priority. */
+    5,  /* Enough for tests. */
+    0  /* Assume we haven't boot off it yet. */
+  };
+  kernel_entry corrupt_kernelB = {
+    GenerateRollbackTestKernelBlob(1, 1, 1),
+    1,  /* Lower Priority. */
+    5,  /* Enough for tests. */
+    0  /* Assume we haven't boot off it yet. */
+  };
+
+  /* Initialize rollback index state. */
+  g_kernel_key_version = 1;
+  g_kernel_version = 1;
+
+  /* Note: This test just checks the rollback prevention mechanism and not
+   * the full blown kernel boot logic. Updates to the kernel attributes
+   * in the paritition table are not tested.
+   */
+  fprintf(stderr, "Kernel A boot priority(15) > Kernel B boot priority(1)\n");
+  TEST_EQ(VerifyKernelDriver_f(firmware_key_pub,
+                               &valid_kernelA, &valid_kernelB,
+                               DEV_MODE_DISABLED),
+          BOOT_KERNEL_A_CONTINUE,
+          "(Valid Kernel A (current version)\n"
+          " Valid Kernel B (current version) runs A):");
+  TEST_EQ(VerifyKernelDriver_f(firmware_key_pub,
+                               &corrupt_kernelA, &valid_kernelB,
+                               DEV_MODE_DISABLED),
+          BOOT_KERNEL_B_CONTINUE,
+          "(Corrupt Kernel A (current version)\n"
+          " Valid Kernel B (current version) runs B):");
+  TEST_EQ(VerifyKernelDriver_f(firmware_key_pub,
+                               &valid_kernelA, &corrupt_kernelB,
+                               DEV_MODE_DISABLED),
+          BOOT_KERNEL_A_CONTINUE,
+          "(Valid Kernel A (current version)\n"
+          " Corrupt Kernel B (current version) runs A):");
+  TEST_EQ(VerifyKernelDriver_f(firmware_key_pub,
+                               &corrupt_kernelA, &corrupt_kernelB,
+                               DEV_MODE_DISABLED),
+          BOOT_KERNEL_RECOVERY_CONTINUE,
+          "(Corrupt Kernel A (current version)\n"
+          " Corrupt Kernel B (current version) runs Recovery):");
+
+  fprintf(stderr, "\nSwapping boot priorities...\n"
+         "Kernel B boot priority(15) > Kernel A boot priority(1)\n");
+  valid_kernelA.boot_priority = corrupt_kernelA.boot_priority = 1;
+  valid_kernelB.boot_priority = corrupt_kernelB.boot_priority = 15;
+  TEST_EQ(VerifyKernelDriver_f(firmware_key_pub,
+                               &valid_kernelA, &valid_kernelB,
+                               DEV_MODE_DISABLED),
+          BOOT_KERNEL_B_CONTINUE,
+          "(Valid Kernel A (current version)\n"
+          " Valid Kernel B (current version) runs B):");
+  TEST_EQ(VerifyKernelDriver_f(firmware_key_pub,
+                               &corrupt_kernelA, &valid_kernelB,
+                               DEV_MODE_DISABLED),
+          BOOT_KERNEL_B_CONTINUE,
+          "(Corrupt Kernel A (current version)\n"
+          " Valid Kernel B (current version) runs B):");
+  TEST_EQ(VerifyKernelDriver_f(firmware_key_pub,
+                               &valid_kernelA, &corrupt_kernelB,
+                               DEV_MODE_DISABLED),
+          BOOT_KERNEL_A_CONTINUE,
+          "(Valid Kernel A (current version)\n"
+          " Corrupt Kernel B (current version) runs A):");
+  TEST_EQ(VerifyKernelDriver_f(firmware_key_pub,
+                               &corrupt_kernelA, &corrupt_kernelB,
+                               DEV_MODE_DISABLED),
+          BOOT_KERNEL_RECOVERY_CONTINUE,
+          "(Corrupt Kernel A (current version)\n"
+          " Corrupt Kernel B (current version) runs Recovery):");
+
+  fprintf(stderr, "\nUpdating stored version information. Obsoleting "
+          "exiting kernel images.\n");
+  g_kernel_key_version = 2;
+  g_kernel_version = 2;
+  TEST_EQ(VerifyKernelDriver_f(firmware_key_pub,
+                               &valid_kernelA, &valid_kernelB,
+                               DEV_MODE_DISABLED),
+          BOOT_KERNEL_RECOVERY_CONTINUE,
+          "(Valid Kernel A (old version)\n"
+          " Valid Kernel B (old version) runs Recovery):");
+
+  fprintf(stderr, "\nGenerating updated Kernel A blob with "
+          "new version.\n");
+  Free(valid_kernelA.kernel_blob);
+  valid_kernelA.kernel_blob = GenerateRollbackTestKernelBlob(3, 3, 0);
+  TEST_EQ(VerifyKernelDriver_f(firmware_key_pub,
+                               &valid_kernelA, &valid_kernelB,
+                               DEV_MODE_DISABLED),
+          BOOT_KERNEL_A_CONTINUE,
+          "(Valid Kernel A (new version)\n"
+          " Valid Kernel B (old version) runs A):");
+
+  Free(firmware_key_pub);
+  Free(valid_kernelA.kernel_blob);
+  Free(valid_kernelB.kernel_blob);
+  Free(corrupt_kernelA.kernel_blob);
+  Free(corrupt_kernelB.kernel_blob);
+}
+
+int main(int argc, char* argv[]) {
+  int error_code = 0;
+  VerifyKernelDriverTest();
+  if (!gTestSuccess)
+    error_code = 255;
+  return error_code;
+}
diff --git a/tests/rollback_index_mock.c b/tests/rollback_index_mock.c
index 047fb8a..6317214 100644
--- a/tests/rollback_index_mock.c
+++ b/tests/rollback_index_mock.c
@@ -16,7 +16,9 @@
 uint16_t g_kernel_version = 0;
 
 void SetupTPM(void) {
+#ifndef NDEBUG
   fprintf(stderr, "Rollback Index Library Mock: TPM initialized.\n");
+#endif
 }
 
 uint16_t GetStoredVersion(int type) {
@@ -52,10 +54,14 @@
       g_kernel_version = version;
       break;
   }
+#ifndef NDEBUG
   fprintf(stderr, "Rollback Index Library Mock: Stored Version written.\n");
+#endif
   return 1;
 }
 
 void LockStoredVersion(int type) {
+#ifndef NDEBUG
   fprintf(stderr, "Rollback Index Library Mock: Version Locked.\n");
+#endif
 }
diff --git a/tests/test_common.c b/tests/test_common.c
index 5cc0805..c1d1108 100644
--- a/tests/test_common.c
+++ b/tests/test_common.c
@@ -23,11 +23,11 @@
 
 int TEST_EQ(int result, int expected_result, char* testname) {
   if (result == expected_result) {
-    fprintf(stderr, "%s Test " COL_GREEN " PASSED\n" COL_STOP, testname);
+    fprintf(stderr, "%s Test " COL_GREEN "PASSED\n" COL_STOP, testname);
     return 1;
   }
   else {
-    fprintf(stderr, "%s Test " COL_RED " FAILED\n" COL_STOP, testname);
+    fprintf(stderr, "%s Test " COL_RED "FAILED\n" COL_STOP, testname);
     gTestSuccess = 0;
     return 0;
   }
@@ -102,9 +102,9 @@
   return firmware_blob;
 }
 
-uint8_t* GenerateRollbackTestImage(int firmware_key_version,
-                                   int firmware_version,
-                                   int is_corrupt) {
+uint8_t* GenerateRollbackTestFirmwareBlob(int firmware_key_version,
+                                          int firmware_version,
+                                          int is_corrupt) {
   FirmwareImage* image = NULL;
   uint64_t len;
   uint8_t* firmware_blob = NULL;
@@ -213,3 +213,33 @@
   KernelImageFree(image);
   return kernel_blob;
 }
+
+uint8_t* GenerateRollbackTestKernelBlob(int kernel_key_version,
+                                        int kernel_version,
+                                        int is_corrupt) {
+ KernelImage* image = NULL;
+ uint64_t len;
+ uint8_t* kernel_blob = NULL;
+ uint8_t* kernel_sign_key = NULL;
+ kernel_sign_key = BufferFromFile("testkeys/key_rsa1024.keyb",
+                                  &len);
+ if (!kernel_sign_key)
+   return NULL;
+ image = GenerateTestKernelImage(0,  /* Firmware algo: RSA1024/SHA1 */
+                                 0,  /* Kernel algo: RSA1024/SHA1 */
+                                 kernel_sign_key,
+                                 kernel_key_version,
+                                 kernel_version,
+                                 1,  /* kernel length. */
+                                 "testkeys/key_rsa1024.pem",
+                                 "testkeys/key_rsa1024.pem");
+ if (!image)
+   return NULL;
+ if (is_corrupt) {
+   /* Invalidate image. */
+   Memset(image->kernel_data, 'X', image->options.kernel_len);
+ }
+ kernel_blob = GetKernelBlob(image, &len);
+ KernelImageFree(image);
+ return kernel_blob;
+}
diff --git a/tests/test_common.h b/tests/test_common.h
index a449bdd..d203d76 100644
--- a/tests/test_common.h
+++ b/tests/test_common.h
@@ -50,11 +50,18 @@
                                 int kernel_len,
                                 const char* firmware_key_file,
                                 const char* kernel_key_file);
+
 /* Generates a test firmware image for rollback tests with a given
  * [firmware_key_version] and [firmware_version]. If [is_corrupt] is 1,
  * then the image has invalid signatures and will fail verification. */
-uint8_t* GenerateRollbackTestImage(int firmware_key_version,
-                                   int firmware_version,
-                                   int is_corrupt);
+uint8_t* GenerateRollbackTestFirmwareBlob(int firmware_key_version,
+                                          int firmware_version,
+                                          int is_corrupt);
 
+/* Generates a test kernel iamge for rollback tests with a given
+ * [kernel_key_version} and [kernel_version]. If [is_corrupt] is 1,
+ * then the image has invalid signatures and will fail verification. */
+uint8_t* GenerateRollbackTestKernelBlob(int kernel_key_version,
+                                        int kernel_version,
+                                        int is_corrupt);
 #endif  /* VBOOT_REFERENCE_TEST_COMMON_H_ */
diff --git a/utils/firmware_image.c b/utils/firmware_image.c
index 13eba6c..a197708 100644
--- a/utils/firmware_image.c
+++ b/utils/firmware_image.c
@@ -680,15 +680,15 @@
     /* Stored version may need to be updated but only if FirmwareB
      * is successfully verified and has a logical version greater than
      * the stored logical version. */
-    if (VERIFY_FIRMWARE_SUCCESS == VerifyFirmware(root_key_blob, firmwareB)) {
-        if (stored_lversion < firmwareB_lversion) {
-          WriteStoredVersion(FIRMWARE_KEY_VERSION,
-                             (uint16_t) (min_lversion >> 16));
-          WriteStoredVersion(FIRMWARE_VERSION,
-                             (uint16_t) (min_lversion & 0x00FFFF));
-          stored_lversion = min_lversion;  /* Update stored version as it's used
-                                            * later. */
-        }
+    if (stored_lversion < firmwareB_lversion) {
+      if (VERIFY_FIRMWARE_SUCCESS == VerifyFirmware(root_key_blob, firmwareB)) {
+        WriteStoredVersion(FIRMWARE_KEY_VERSION,
+                           (uint16_t) (min_lversion >> 16));
+        WriteStoredVersion(FIRMWARE_VERSION,
+                           (uint16_t) (min_lversion & 0x00FFFF));
+        stored_lversion = min_lversion;  /* Update stored version as it's used
+                                          * later. */
+      }
     }
   }
   /* Lock Firmware TPM rollback indices from further writes. */
@@ -724,7 +724,7 @@
      */
     if (stored_lversion <= firmwareB_lversion &&
         (VERIFY_FIRMWARE_SUCCESS == VerifyFirmware(root_key_blob, firmwareB)))
-      return BOOT_FIRMWARE_B_CONTINUE;
+        return BOOT_FIRMWARE_B_CONTINUE;
   }
   /* D'oh: No bootable firmware. */
   return BOOT_FIRMWARE_RECOVERY_CONTINUE;
diff --git a/utils/kernel_image.c b/utils/kernel_image.c
index 991374f..e635291 100644
--- a/utils/kernel_image.c
+++ b/utils/kernel_image.c
@@ -15,6 +15,7 @@
 
 #include "file_keys.h"
 #include "padding.h"
+#include "rollback_index.h"
 #include "rsa_utility.h"
 #include "sha_utility.h"
 #include "signature_digest.h"
@@ -710,3 +711,147 @@
   Free(kernel_signature);
   return 1;
 }
+
+uint32_t GetLogicalKernelVersion(uint8_t* kernel_blob) {
+  uint8_t* kernel_ptr;
+  uint16_t kernel_key_version;
+  uint16_t kernel_version;
+  uint16_t firmware_sign_algorithm;
+  uint16_t kernel_sign_algorithm;
+  int kernel_key_signature_len;
+  int kernel_sign_key_len;
+  kernel_ptr = kernel_blob + (FIELD_LEN(magic) +
+                              FIELD_LEN(header_version) +
+                              FIELD_LEN(header_len));
+  Memcpy(&firmware_sign_algorithm, kernel_ptr, sizeof(firmware_sign_algorithm));
+  kernel_ptr += FIELD_LEN(firmware_sign_algorithm);
+  Memcpy(&kernel_sign_algorithm, kernel_ptr, sizeof(kernel_sign_algorithm));
+  kernel_ptr += FIELD_LEN(kernel_sign_algorithm);
+  Memcpy(&kernel_key_version, kernel_ptr, sizeof(kernel_key_version));
+
+  if (firmware_sign_algorithm >= kNumAlgorithms)
+    return 0;
+  if (kernel_sign_algorithm >= kNumAlgorithms)
+    return 0;
+  kernel_key_signature_len = siglen_map[firmware_sign_algorithm];
+  kernel_sign_key_len = RSAProcessedKeySize(kernel_sign_algorithm);
+  kernel_ptr += (FIELD_LEN(kernel_key_version) +
+                 kernel_sign_key_len +
+                 FIELD_LEN(header_checksum) +
+                 kernel_key_signature_len);
+  Memcpy(&kernel_version, kernel_ptr, sizeof(kernel_version));
+  return CombineUint16Pair(kernel_key_version, kernel_version);
+}
+
+void PrintKernelEntry(kernel_entry* entry) {
+  fprintf(stderr, "Boot Priority = %d\n", entry->boot_priority);
+  fprintf(stderr, "Boot Tries Remaining = %d\n", entry->boot_tries_remaining);
+  fprintf(stderr, "Boot Success Flag = %d\n", entry->boot_success_flag);
+}
+
+int VerifyKernelDriver_f(uint8_t* firmware_key_blob,
+                         kernel_entry* kernelA,
+                         kernel_entry* kernelB,
+                         int dev_mode) {
+  int i;
+  /* Contains the logical kernel version (32-bit) which is calculated as
+   * (kernel_key_version << 16 | kernel_version) where
+   * [kernel_key_version], [firmware_version] are both 16-bit.
+   */
+  uint32_t kernelA_lversion, kernelB_lversion;
+  uint32_t min_lversion;  /* Minimum of kernel A and kernel B lversion. */
+  uint32_t stored_lversion;  /* Stored logical version in the TPM. */
+  kernel_entry* try_kernel[2];  /* Kernel in try order. */
+  int try_kernel_which[2];  /* Which corresponding kernel in the try order */
+  uint32_t try_kernel_lversion[2];  /* Their logical versions. */
+
+  /* [kernel_to_boot] will eventually contain the boot path to follow
+   * and is returned to the caller. Initially, we set it to recovery. If
+   * a valid bootable kernel is found, it will be set to that. */
+  int kernel_to_boot = BOOT_KERNEL_RECOVERY_CONTINUE;
+
+
+  /* The TPM must already have be initialized, so no need to call SetupTPM(). */
+
+  /* We get the key versions by reading directly from the image blobs without
+   * any additional (expensive) sanity checking on the blob since it's faster to
+   * outright reject a kernel with an older kernel key version. A malformed
+   * or corrupted kernel blob will still fail when VerifyKernel() is called
+   * on it.
+   */
+  kernelA_lversion = GetLogicalKernelVersion(kernelA->kernel_blob);
+  kernelB_lversion = GetLogicalKernelVersion(kernelB->kernel_blob);
+  min_lversion  = Min(kernelA_lversion, kernelB_lversion);
+  stored_lversion = CombineUint16Pair(GetStoredVersion(KERNEL_KEY_VERSION),
+                                      GetStoredVersion(KERNEL_VERSION));
+
+  /* TODO(gauravsh): The kernel entries kernelA and kernelB come from the
+   * partition table - verify its signature/checksum before proceeding
+   * further. */
+
+  /* The logic for deciding which kernel to boot from is taken from the
+   * the Chromium OS Drive Map design document.
+   *
+   * We went to consider the kernels in their according to their boot
+   * priority attribute value.
+   */
+
+  if (kernelA->boot_priority >= kernelB->boot_priority) {
+    try_kernel[0] = kernelA;
+    try_kernel_which[0] = BOOT_KERNEL_A_CONTINUE;
+    try_kernel_lversion[0] = kernelA_lversion;
+    try_kernel[1] = kernelB;
+    try_kernel_which[1] = BOOT_KERNEL_B_CONTINUE;
+    try_kernel_lversion[1] = kernelB_lversion;
+  } else {
+    try_kernel[0] = kernelB;
+    try_kernel_which[0] = BOOT_KERNEL_B_CONTINUE;
+    try_kernel_lversion[0] = kernelB_lversion;
+    try_kernel[1] = kernelA;
+    try_kernel_which[1] = BOOT_KERNEL_A_CONTINUE;
+    try_kernel_lversion[1] = kernelA_lversion;
+  }
+
+  /* TODO(gauravsh): Changes to boot_tries_remaining and boot_priority
+   * below should be propagated to partition table. This will be added
+   * once the firmware parition table parsing code is in. */
+  for (i = 0; i < 2; i++) {
+    if ((try_kernel[i]->boot_success_flag ||
+         try_kernel[i]->boot_tries_remaining) &&
+        (VERIFY_KERNEL_SUCCESS == VerifyKernel(firmware_key_blob,
+                                               try_kernel[i]->kernel_blob,
+                                               dev_mode))) {
+      if (try_kernel[i]->boot_tries_remaining > 0)
+        try_kernel[i]->boot_tries_remaining--;
+      if (stored_lversion > try_kernel_lversion[i])
+        continue;  /* Rollback: I am afraid I can't let you do that Dave. */
+      if (i == 0 && (stored_lversion < try_kernel_lversion[1])) {
+        /* The higher priority kernel is valid and bootable, See if we
+         * need to update the stored version for rollback prevention. */
+        if (VERIFY_KERNEL_SUCCESS == VerifyKernel(firmware_key_blob,
+                                                  try_kernel[1]->kernel_blob,
+                                                  dev_mode)) {
+          WriteStoredVersion(KERNEL_KEY_VERSION,
+                             (uint16_t) (min_lversion >> 16));
+          WriteStoredVersion(KERNEL_VERSION,
+                             (uint16_t) (min_lversion & 0xFFFF));
+          stored_lversion = min_lversion;  /* Update stored version as it's
+                                            * used later. */
+        }
+      }
+      kernel_to_boot = try_kernel_which[i];
+      break;  /* We found a valid kernel. */
+    }
+    try_kernel[i]->boot_priority = 0;
+    }  /* for loop. */
+
+  /* Lock Kernel TPM rollback indices from further writes.
+   * TODO(gauravsh): Figure out if these can be combined into one
+   * 32-bit location since we seem to always use them together. This can help
+   * us minimize the number of NVRAM writes/locks (which are limited over flash
+   * memory lifetimes.
+   */
+  LockStoredVersion(KERNEL_KEY_VERSION);
+  LockStoredVersion(KERNEL_VERSION);
+  return kernel_to_boot;
+}