Add crossystem support for nvram_cleared and kern_nv

Fix try_b processing

And move key block flags check up in LoadFirmware(), which speeds up
boot when the dev switch is off because it doesn't do a signature
check and then throw it out.

BUG=12282
TEST=build firmware, try by hand

Review URL: http://codereview.chromium.org/6596081

Change-Id: I10474e9e0ae324906dfe02a351347d04ce847f67
diff --git a/firmware/lib/vboot_firmware.c b/firmware/lib/vboot_firmware.c
index e8c8a86..036441f 100644
--- a/firmware/lib/vboot_firmware.c
+++ b/firmware/lib/vboot_firmware.c
@@ -116,10 +116,7 @@
     uint8_t* body_digest;
 
     /* If try B count is non-zero try firmware B first */
-    index = (try_b_count ? i : 1 - i);
-
-    /* Verify the key block */
-    VBPERFSTART("VB_VKB");
+    index = (try_b_count ? 1 - i : i);
     if (0 == index) {
       key_block = (VbKeyBlockHeader*)params->verification_block_0;
       vblock_size = params->verification_size_0;
@@ -127,14 +124,10 @@
       key_block = (VbKeyBlockHeader*)params->verification_block_1;
       vblock_size = params->verification_size_1;
     }
-    if ((0 != KeyBlockVerify(key_block, vblock_size, root_key, 0))) {
-      VBDEBUG(("Key block verification failed.\n"));
-      VBPERFEND("VB_VKB");
-      continue;
-    }
-    VBPERFEND("VB_VKB");
 
-    /* Check the key block flags against the current boot mode. */
+    /* Check the key block flags against the current boot mode.  Do this
+     * before verifying the key block, since flags are faster to check than
+     * the RSA signature. */
     if (!(key_block->key_block_flags &
           (is_dev ? KEY_BLOCK_FLAG_DEVELOPER_1 :
            KEY_BLOCK_FLAG_DEVELOPER_0))) {
@@ -147,6 +140,15 @@
       continue;
     }
 
+    /* Verify the key block */
+    VBPERFSTART("VB_VKB");
+    if ((0 != KeyBlockVerify(key_block, vblock_size, root_key, 0))) {
+      VBDEBUG(("Key block verification failed.\n"));
+      VBPERFEND("VB_VKB");
+      continue;
+    }
+    VBPERFEND("VB_VKB");
+
     /* Check for rollback of key version. */
     key_version = key_block->data_key.key_version;
     if (key_version < (tpm_version >> 16)) {
diff --git a/firmware/lib/vboot_nvstorage.c b/firmware/lib/vboot_nvstorage.c
index d915804..419a9fb 100644
--- a/firmware/lib/vboot_nvstorage.c
+++ b/firmware/lib/vboot_nvstorage.c
@@ -21,7 +21,7 @@
 
 #define BOOT_OFFSET                  1
 #define BOOT_DEBUG_RESET_MODE           0x80
-#define BOOT_TRY_B_COUNT                0x0F
+#define BOOT_TRY_B_COUNT_MASK           0x0F
 
 #define RECOVERY_OFFSET              2
 #define LOCALIZATION_OFFSET          3
@@ -107,7 +107,7 @@
       return 0;
 
     case VBNV_TRY_B_COUNT:
-      *dest = raw[BOOT_OFFSET] & BOOT_TRY_B_COUNT;
+      *dest = raw[BOOT_OFFSET] & BOOT_TRY_B_COUNT_MASK;
       return 0;
 
     case VBNV_RECOVERY_REQUEST:
@@ -172,10 +172,10 @@
 
     case VBNV_TRY_B_COUNT:
       /* Clip to valid range. */
-      if (value > BOOT_TRY_B_COUNT)
-        value = BOOT_TRY_B_COUNT - 1;
+      if (value > BOOT_TRY_B_COUNT_MASK)
+        value = BOOT_TRY_B_COUNT_MASK;
 
-      raw[BOOT_OFFSET] &= ~BOOT_TRY_B_COUNT;
+      raw[BOOT_OFFSET] &= ~BOOT_TRY_B_COUNT_MASK;
       raw[BOOT_OFFSET] |= (uint8_t)value;
       break;
 
diff --git a/host/lib/crossystem.c b/host/lib/crossystem.c
index 8e482d3..5951e10 100644
--- a/host/lib/crossystem.c
+++ b/host/lib/crossystem.c
@@ -489,6 +489,8 @@
     value = VbGetNvStorage(VBNV_TRIED_FIRMWARE_B);
   } else if (!strcasecmp(name,"kern_nv")) {
     value = VbGetNvStorage(VBNV_KERNEL_FIELD);
+  } else if (!strcasecmp(name,"nvram_cleared")) {
+    value = VbGetNvStorage(VBNV_KERNEL_SETTINGS_RESET);
   }
   /* NV storage values.  If unable to get from NV storage, fall back to the
    * CMOS reboot field used by older BIOS. */
@@ -512,10 +514,6 @@
     value = ReadFileInt(ACPI_FMAP_PATH);
   }
 
-  /* TODO: implement the following properties:
-   *   nvram_cleared
-   */
-
   return value;
 }
 
@@ -574,7 +572,10 @@
 int VbSetSystemPropertyInt(const char* name, int value) {
 
   /* NV storage values with no defaults for older BIOS. */
-  if (!strcasecmp(name,"kern_nv")) {
+  if (!strcasecmp(name,"nvram_cleared")) {
+    /* Can only clear this flag; it's set inside the NV storage library. */
+    return VbSetNvStorage(VBNV_KERNEL_SETTINGS_RESET, 0);
+  } else if (!strcasecmp(name,"kern_nv")) {
     return VbSetNvStorage(VBNV_KERNEL_FIELD, value);
   }
   /* NV storage values.  If unable to get from NV storage, fall back to the
@@ -593,10 +594,6 @@
     return VbSetCmosRebootField(CMOSRF_TRY_B, value);
   }
 
-  /* TODO: implement the following:
-   *   nvram_cleared
-   */
-
   return -1;
 }
 
diff --git a/utility/crossystem_main.c b/utility/crossystem_main.c
index ebd6ed5..35d6ea7 100644
--- a/utility/crossystem_main.c
+++ b/utility/crossystem_main.c
@@ -43,10 +43,11 @@
   {"ecfw_act", 1, 0, "Active EC firmware"},
   {"kernkey_vfy", 1, 0, "Type of verification done on kernel key block"},
   /* Writable integers */
+  {"nvram_cleared", 0, 1, "Have NV settings been lost?  Write 0 to clear"},
+  {"kern_nv", 0, 1, "Non-volatile field for kernel use", "0x%08x"},
   {"recovery_request", 0, 1, "Recovery mode request (writable)"},
   {"dbg_reset", 0, 1, "Debug reset mode request (writable)"},
   {"fwb_tries", 0, 1, "Try firmware B count (writable)"},
-  {"kern_nv", 0, 1, "Non-volatile field for kernel use", "0x%08x"},
 
   /* TODO: implement the following:
    *   nvram_cleared