BUTTERFLY-FIRMWARE: Add more recovery_reason codes

There are several places where the same recovery_reason was used to report
slightly different points of failure. Let's create some new codes instead.

Remember that recovery mode is handled by RO firmware, so if an updated RW
firmware uses one of the new error codes, pressing TAB at the recovery
screen will say "We have no idea what this means". That's not a bug. This CL
deprecates the original codes, so the fact that the RO firmware doesn't
recognize it just means it's a new code reported by a new RW BIOS.

BUG=chromium:245528
TEST=manual
BRANCH=butterfly

Run

  make && make runtests

It should pass. You can test some of the error cases on actual hardware by
using

  crossystem recovery_reason=86
  reboot

and pressing TAB at the recovery screen. For that example you should see the
message

  recovery_reason: 0x56 TPM lock error in rewritable firmare

Change-Id: I386f77f920f39a55c2a2075d0854fea0d0b0f7e5
Original-Change-Id: I123c781e6c6f6fe0284c4fd49f5f5a855eece7df
Reviewed-on: https://gerrit.chromium.org/gerrit/57347
Commit-Queue: Shawn Nematbakhsh <shawnn@chromium.org>
Reviewed-by: Shawn Nematbakhsh <shawnn@chromium.org>
Tested-by: Shawn Nematbakhsh <shawnn@chromium.org>
diff --git a/firmware/include/vboot_nvstorage.h b/firmware/include/vboot_nvstorage.h
index 8748a5c..4aef955 100644
--- a/firmware/include/vboot_nvstorage.h
+++ b/firmware/include/vboot_nvstorage.h
@@ -79,8 +79,8 @@
 #define VBNV_RECOVERY_RO_INVALID_RW   0x03
 /* S3 resume failed */
 #define VBNV_RECOVERY_RO_S3_RESUME    0x04
-/* TPM error in read-only firmware */
-#define VBNV_RECOVERY_RO_TPM_ERROR    0x05
+/* TPM error in read-only firmware (deprecated) */
+#define VBNV_RECOVERY_DEP_RO_TPM_ERROR    0x05
 /* Shared data error in read-only firmware */
 #define VBNV_RECOVERY_RO_SHARED_DATA  0x06
 /* Test error from S3Resume() */
@@ -105,8 +105,8 @@
 #define VBNV_RECOVERY_EC_SOFTWARE_SYNC 0x22
 /* EC software sync - unable to determine active EC image */
 #define VBNV_RECOVERY_EC_UNKNOWN_IMAGE 0x23
-/* EC software sync - error obtaining EC image hash */
-#define VBNV_RECOVERY_EC_HASH         0x24
+/* EC software sync - error obtaining EC image hash (deprecated) */
+#define VBNV_RECOVERY_DEP_EC_HASH         0x24
 /* EC software sync - error obtaining expected EC image */
 #define VBNV_RECOVERY_EC_EXPECTED_IMAGE 0x25
 /* EC software sync - error updating EC */
@@ -124,16 +124,42 @@
 #define VBNV_RECOVERY_RW_NO_OS        0x42
 /* OS kernel failed signature check */
 #define VBNV_RECOVERY_RW_INVALID_OS   0x43
-/* TPM error in rewritable firmware */
-#define VBNV_RECOVERY_RW_TPM_ERROR    0x44
+/* TPM error in rewritable firmware (deprecated) */
+#define VBNV_RECOVERY_DEP_RW_TPM_ERROR    0x44
 /* RW firmware in dev mode, but dev switch is off */
 #define VBNV_RECOVERY_RW_DEV_MISMATCH 0x45
 /* Shared data error in rewritable firmware */
 #define VBNV_RECOVERY_RW_SHARED_DATA  0x46
 /* Test error from LoadKernel() */
 #define VBNV_RECOVERY_RW_TEST_LK      0x47
-/* No bootable disk found */
-#define VBNV_RECOVERY_RW_NO_DISK      0x48
+/* No bootable disk found (deprecated)*/
+#define VBNV_RECOVERY_DEP_RW_NO_DISK      0x48
+/* Rebooting did not correct TPM_E_FAIL or TPM_E_FAILEDSELFTEST  */
+#define VBNV_RECOVERY_TPM_E_FAIL      0x49
+/* TPM setup error in read-only firmware */
+#define VBNV_RECOVERY_RO_TPM_S_ERROR  0x50
+/* TPM write error in read-only firmware */
+#define VBNV_RECOVERY_RO_TPM_W_ERROR  0x51
+/* TPM lock error in read-only firmware */
+#define VBNV_RECOVERY_RO_TPM_L_ERROR  0x52
+/* TPM update error in read-only firmware */
+#define VBNV_RECOVERY_RO_TPM_U_ERROR  0x53
+/* TPM read error in rewritable firmware */
+#define VBNV_RECOVERY_RW_TPM_R_ERROR  0x54
+/* TPM write error in rewritable firmware */
+#define VBNV_RECOVERY_RW_TPM_W_ERROR  0x55
+/* TPM lock error in rewritable firmware */
+#define VBNV_RECOVERY_RW_TPM_L_ERROR  0x56
+/* EC software sync unable to get EC image hash */
+#define VBNV_RECOVERY_EC_HASH_FAILED  0x57
+/* EC software sync invalid image hash size */
+#define VBNV_RECOVERY_EC_HASH_SIZE    0x58
+/* Unspecified error while trying to load kernel */
+#define VBNV_RECOVERY_LK_UNSPECIFIED  0x59
+/* No bootable storage device in system */
+#define VBNV_RECOVERY_RW_NO_DISK      0x5A
+/* No bootable kernel found on disk */
+#define VBNV_RECOVERY_RW_NO_KERNEL    0x5B
 /* Unspecified/unknown error in rewritable firmware */
 #define VBNV_RECOVERY_RW_UNSPECIFIED  0x7F
 /* DM-verity error */
diff --git a/firmware/lib/cgptlib/cgptlib.c b/firmware/lib/cgptlib/cgptlib.c
index e708b7b..c3f3744 100644
--- a/firmware/lib/cgptlib/cgptlib.c
+++ b/firmware/lib/cgptlib/cgptlib.c
@@ -89,7 +89,7 @@
     return GPT_ERROR_NO_VALID_KERNEL;
   }
 
-  VBDEBUG(("GptNextKernelEntry likes that one\n"));
+  VBDEBUG(("GptNextKernelEntry likes partition %d\n", new_kernel+1));
   e = entries + new_kernel;
   *start_sector = e->starting_lba;
   *size = e->ending_lba - e->starting_lba + 1;
diff --git a/firmware/lib/vboot_api_firmware.c b/firmware/lib/vboot_api_firmware.c
index cffc462..1426c9a 100644
--- a/firmware/lib/vboot_api_firmware.c
+++ b/firmware/lib/vboot_api_firmware.c
@@ -62,7 +62,7 @@
       VBPERFEND("VB_TPMU");
       if (0 != tpm_status) {
         VBDEBUG(("Unable to write firmware version to TPM.\n"));
-        VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_ERROR);
+        VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_W_ERROR);
         retval = VBERROR_TPM_WRITE_FIRMWARE;
         goto VbSelectFirmware_exit;
       }
@@ -74,7 +74,7 @@
     VBPERFEND("VB_TPML");
     if (0 != tpm_status) {
       VBDEBUG(("Unable to lock firmware version in TPM.\n"));
-      VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_ERROR);
+      VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_L_ERROR);
       retval = VBERROR_TPM_LOCK_FIRMWARE;
       goto VbSelectFirmware_exit;
     }
@@ -86,7 +86,7 @@
   if (0 != tpm_status) {
     VBDEBUG(("Unable to update the TPM with boot mode information.\n"));
     if (!is_rec) {
-      VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_ERROR);
+      VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_U_ERROR);
       retval = VBERROR_TPM_SET_BOOT_MODE_STATE;
       goto VbSelectFirmware_exit;
     }
diff --git a/firmware/lib/vboot_api_init.c b/firmware/lib/vboot_api_init.c
index 8d1540b..63345cc 100644
--- a/firmware/lib/vboot_api_init.c
+++ b/firmware/lib/vboot_api_init.c
@@ -170,7 +170,7 @@
       }
 
       if (!recovery) {
-        VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_ERROR);
+        VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_S_ERROR);
         retval = VBERROR_TPM_FIRMWARE_SETUP;
         goto VbInit_exit;
       }
diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c
index 87018f1..0fb5d14 100644
--- a/firmware/lib/vboot_api_kernel.c
+++ b/firmware/lib/vboot_api_kernel.c
@@ -92,9 +92,9 @@
       break;
   }
 
-  /* If we didn't succeed, don't return a disk handle */
+  /* If we didn't find any good kernels, don't return a disk handle. */
   if (VBERROR_SUCCESS != retval) {
-    VbSetRecoveryRequest(VBNV_RECOVERY_RW_NO_DISK);
+    VbSetRecoveryRequest(VBNV_RECOVERY_RW_NO_KERNEL);
     p->disk_handle = NULL;
   }
 
@@ -482,13 +482,13 @@
   rv = VbExEcHashRW(&ec_hash, &ec_hash_size);
   if (rv) {
       VBDEBUG(("VbEcSoftwareSync() - VbExEcHashRW() returned %d\n", rv));
-      VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH);
+      VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH_FAILED);
       return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
   }
   if (ec_hash_size != SHA256_DIGEST_SIZE) {
-      VBDEBUG(("VbEcSoftwareSync() - VbExEcHashRW() returned wrong size %d\n",
-               ec_hash_size));
-      VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH);
+      VBDEBUG(("VbEcSoftwareSync() - VbExEcHashRW() says size %d, not %d\n",
+               ec_hash_size, SHA256_DIGEST_SIZE));
+      VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH_SIZE);
       return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
   }
 
@@ -622,7 +622,7 @@
   if (0 != tpm_status) {
     VBDEBUG(("Unable to get kernel versions from TPM\n"));
     if (!shared->recovery_reason) {
-      VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_ERROR);
+      VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_R_ERROR);
       retval = VBERROR_TPM_READ_KERNEL;
       goto VbSelectAndLoadKernel_exit;
     }
@@ -712,7 +712,7 @@
         tpm_status = RollbackKernelWrite(shared->kernel_version_tpm);
         if (0 != tpm_status) {
           VBDEBUG(("Error writing kernel versions to TPM.\n"));
-          VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_ERROR);
+          VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_W_ERROR);
           retval = VBERROR_TPM_WRITE_KERNEL;
           goto VbSelectAndLoadKernel_exit;
         }
@@ -736,7 +736,7 @@
   if (0 != tpm_status) {
     VBDEBUG(("Error locking kernel versions.\n"));
     if (!shared->recovery_reason) {
-      VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_ERROR);
+      VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_L_ERROR);
       retval = VBERROR_TPM_LOCK_KERNEL;
       goto VbSelectAndLoadKernel_exit;
     }
diff --git a/firmware/lib/vboot_display.c b/firmware/lib/vboot_display.c
index b15b518..1e7c0a0 100644
--- a/firmware/lib/vboot_display.c
+++ b/firmware/lib/vboot_display.c
@@ -411,7 +411,7 @@
     return "RW firmware failed signature check";
   case VBNV_RECOVERY_RO_S3_RESUME:
     return "S3 resume failed";
-  case VBNV_RECOVERY_RO_TPM_ERROR:
+  case VBNV_RECOVERY_DEP_RO_TPM_ERROR:
     return "TPM error in read-only firmware";
   case VBNV_RECOVERY_RO_SHARED_DATA:
     return "Shared data error in read-only firmware";
@@ -453,7 +453,7 @@
     return "EC software sync error";
   case VBNV_RECOVERY_EC_UNKNOWN_IMAGE:
     return "EC software sync unable to determine active EC image";
-  case VBNV_RECOVERY_EC_HASH:
+  case VBNV_RECOVERY_DEP_EC_HASH:
     return "EC software sync error obtaining EC image hash";
   case VBNV_RECOVERY_EC_EXPECTED_IMAGE:
     return "EC software sync error obtaining expected EC image from BIOS";
@@ -471,7 +471,7 @@
     return "No OS kernel detected (or kernel rollback attempt?)";
   case VBNV_RECOVERY_RW_INVALID_OS:
     return "OS kernel failed signature check";
-  case VBNV_RECOVERY_RW_TPM_ERROR:
+  case VBNV_RECOVERY_DEP_RW_TPM_ERROR:
     return "TPM error in rewritable firmware";
   case VBNV_RECOVERY_RW_DEV_MISMATCH:
     return "RW firmware in dev mode, but dev switch is off";
@@ -479,8 +479,34 @@
     return "Shared data error in rewritable firmware";
   case VBNV_RECOVERY_RW_TEST_LK:
     return "Test error from LoadKernel()";
-  case VBNV_RECOVERY_RW_NO_DISK:
+  case VBNV_RECOVERY_DEP_RW_NO_DISK:
     return "No bootable disk found";
+  case VBNV_RECOVERY_TPM_E_FAIL:
+    return "TPM error that was not fixed by reboot";
+  case VBNV_RECOVERY_RO_TPM_S_ERROR:
+    return "TPM setup error in read-only firmware";
+  case VBNV_RECOVERY_RO_TPM_W_ERROR:
+    return "TPM write error in read-only firmware";
+  case VBNV_RECOVERY_RO_TPM_L_ERROR:
+    return "TPM lock error in read-only firmware";
+  case VBNV_RECOVERY_RO_TPM_U_ERROR:
+    return "TPM update error in read-only firmware";
+  case VBNV_RECOVERY_RW_TPM_R_ERROR:
+    return "TPM read error in rewritable firmware";
+  case VBNV_RECOVERY_RW_TPM_W_ERROR:
+    return "TPM write error in rewritable firmware";
+  case VBNV_RECOVERY_RW_TPM_L_ERROR:
+    return "TPM lock error in rewritable firmware";
+  case VBNV_RECOVERY_EC_HASH_FAILED:
+    return "EC software sync unable to get EC image hash";
+  case VBNV_RECOVERY_EC_HASH_SIZE:
+    return "EC software sync invalid image hash size";
+  case VBNV_RECOVERY_LK_UNSPECIFIED:
+    return "Unspecified error while trying to load kernel";
+  case VBNV_RECOVERY_RW_NO_DISK:
+    return "No bootable storage device in system";
+  case VBNV_RECOVERY_RW_NO_KERNEL:
+    return "No bootable kernel found on disk";
   case VBNV_RECOVERY_RW_UNSPECIFIED:
     return "Unspecified/unknown error in RW firmware";
   case VBNV_RECOVERY_KE_DM_VERITY:
diff --git a/firmware/lib/vboot_kernel.c b/firmware/lib/vboot_kernel.c
index d1f261d..eff3ee2 100644
--- a/firmware/lib/vboot_kernel.c
+++ b/firmware/lib/vboot_kernel.c
@@ -142,7 +142,7 @@
   uint32_t require_official_os = 0;
 
   VbError_t retval = VBERROR_UNKNOWN;
-  int recovery = VBNV_RECOVERY_RO_UNSPECIFIED;
+  int recovery = VBNV_RECOVERY_LK_UNSPECIFIED;
 
   /* Sanity Checks */
   if (!params ||
diff --git a/tests/vboot_api_firmware_tests.c b/tests/vboot_api_firmware_tests.c
index 03f67b5..e6a84ee 100644
--- a/tests/vboot_api_firmware_tests.c
+++ b/tests/vboot_api_firmware_tests.c
@@ -207,7 +207,7 @@
   ResetMocks();
   mock_lf_tpm_version = 0x30005;
   mock_rfw_retval = TPM_E_IOERROR;
-  TestVbSf(VBERROR_TPM_WRITE_FIRMWARE, VBNV_RECOVERY_RO_TPM_ERROR,
+  TestVbSf(VBERROR_TPM_WRITE_FIRMWARE, VBNV_RECOVERY_RO_TPM_W_ERROR,
            "TPM version update failure");
 
   /* If no change to TPM version, RollbackFirmwareWrite() not called */
@@ -221,7 +221,7 @@
   /* Check errors from SetTPMBootModeState() */
   ResetMocks();
   mock_stbms_retval = TPM_E_IOERROR;
-  TestVbSf(VBERROR_TPM_SET_BOOT_MODE_STATE, VBNV_RECOVERY_RO_TPM_ERROR,
+  TestVbSf(VBERROR_TPM_SET_BOOT_MODE_STATE, VBNV_RECOVERY_RO_TPM_U_ERROR,
            "TPM set boot mode state failure");
   ResetMocks();
   mock_stbms_retval = TPM_E_IOERROR;
@@ -231,7 +231,7 @@
   /* Handle RollbackFirmwareLock() errors */
   ResetMocks();
   mock_rfl_retval = TPM_E_IOERROR;
-  TestVbSf(VBERROR_TPM_LOCK_FIRMWARE, VBNV_RECOVERY_RO_TPM_ERROR,
+  TestVbSf(VBERROR_TPM_LOCK_FIRMWARE, VBNV_RECOVERY_RO_TPM_L_ERROR,
            "TPM lock firmware failure");
 }
 
diff --git a/tests/vboot_api_init_tests.c b/tests/vboot_api_init_tests.c
index 3816e07..5069b5c 100644
--- a/tests/vboot_api_init_tests.c
+++ b/tests/vboot_api_init_tests.c
@@ -321,7 +321,7 @@
   ResetMocks();
   mock_rfs_retval = TPM_E_IOERROR;
   mock_tpm_version = 0x20002;
-  TestVbInit(VBERROR_TPM_FIRMWARE_SETUP, VBNV_RECOVERY_RO_TPM_ERROR,
+  TestVbInit(VBERROR_TPM_FIRMWARE_SETUP, VBNV_RECOVERY_RO_TPM_S_ERROR,
            "Rollback TPM setup error - not in recovery");
   TEST_EQ(shared->fw_version_tpm, 0, "  shared fw_version_tpm not set");
   ResetMocks();