(firmware-parrot)Add recovery_subcode field to nvstorage and crossystem

This just adds a one-byte field in the nvstorage region for use in debugging
hard-to-catch errors. There's no official meaning or expectation for this
field. It's just a handy place to emit some information.

BUG=chrome-os-partner:11534
BRANCH=parrot
TEST=manual

Just change the value and ensure that it persists across a (working) reboot.
It's only updated at specific points under very exacting error conditions,
so all we really want to test is that it works as a place to store some
extra info.

  crossystem recovery_subcode
  crossystem recovery_subcode=14
  reboot
  crossystem recovery_subcode

The recovery_subcode byte is at index [6] of the VbNv.raw bytes that appear
when you press TAB, so you can find it there too:

  VbNv.raw: 60 20 00 00 00 00 0e 00 00 00 00 00 00 00 00 65

Decimal 14 == 0x0e

Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/39803
Reviewed-by: Randall Spangler <rspangler@chromium.org>

(cherry picked from commit 699ebf398f84bb0a6138856c38db1d693e581f85)

Change-Id: I37df34af234802dfec1d899dd716bf4c35e22b75
Reviewed-on: https://gerrit.chromium.org/gerrit/39905
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Tested-by: Jay Kim <yongjaek@chromium.org>
diff --git a/firmware/include/vboot_nvstorage.h b/firmware/include/vboot_nvstorage.h
index bcd14a0..7b78faf 100644
--- a/firmware/include/vboot_nvstorage.h
+++ b/firmware/include/vboot_nvstorage.h
@@ -61,6 +61,8 @@
   VBNV_CLEAR_TPM_OWNER_REQUEST,
   /* Flag that TPM owner was cleared on request. */
   VBNV_CLEAR_TPM_OWNER_DONE,
+  /* More details on recovery reason */
+  VBNV_RECOVERY_SUBCODE,
 } VbNvParam;
 
 
diff --git a/firmware/lib/vboot_api_init.c b/firmware/lib/vboot_api_init.c
index 63345cc..6d4a3b0 100644
--- a/firmware/lib/vboot_api_init.c
+++ b/firmware/lib/vboot_api_init.c
@@ -151,7 +151,8 @@
                                        &is_virt_dev, &tpm_version);
     VBPERFEND("VB_TPMI");
     if (0 != tpm_status) {
-      VBDEBUG(("Unable to setup TPM and read firmware version.\n"));
+      VBDEBUG(("Unable to setup TPM and read firmware version (0x%x)\n",
+                tpm_status));
 
       if (TPM_E_MUST_REBOOT == tpm_status) {
         /* TPM wants to reboot into the same mode we're in now */
@@ -171,6 +172,7 @@
 
       if (!recovery) {
         VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_S_ERROR);
+        VbNvSet(&vnc, VBNV_RECOVERY_SUBCODE, tpm_status);
         retval = VBERROR_TPM_FIRMWARE_SETUP;
         goto VbInit_exit;
       }
diff --git a/firmware/lib/vboot_nvstorage.c b/firmware/lib/vboot_nvstorage.c
index 0f4633b..8c503c3 100644
--- a/firmware/lib/vboot_nvstorage.c
+++ b/firmware/lib/vboot_nvstorage.c
@@ -37,6 +37,8 @@
 #define TPM_CLEAR_OWNER_REQUEST         0x01
 #define TPM_CLEAR_OWNER_DONE            0x02
 
+#define RECOVERY_SUBCODE_OFFSET      6
+
 #define KERNEL_FIELD_OFFSET         11
 #define CRC_OFFSET                  15
 
@@ -101,6 +103,10 @@
       *dest = raw[RECOVERY_OFFSET];
       return 0;
 
+    case VBNV_RECOVERY_SUBCODE:
+      *dest = raw[RECOVERY_SUBCODE_OFFSET];
+      return 0;
+
     case VBNV_LOCALIZATION_INDEX:
       *dest = raw[LOCALIZATION_OFFSET];
       return 0;
@@ -189,6 +195,10 @@
       raw[RECOVERY_OFFSET] = (uint8_t)value;
       break;
 
+    case VBNV_RECOVERY_SUBCODE:
+      raw[RECOVERY_SUBCODE_OFFSET] = (uint8_t)value;
+      break;
+
     case VBNV_LOCALIZATION_INDEX:
       /* Map values outside the valid range to the default index. */
       if (value > 0xFF)
diff --git a/host/lib/crossystem.c b/host/lib/crossystem.c
index d2a3102..6476a8e 100644
--- a/host/lib/crossystem.c
+++ b/host/lib/crossystem.c
@@ -387,6 +387,8 @@
     value = VbGetNvStorage(VBNV_DEV_BOOT_SIGNED_ONLY);
   } else if (!strcasecmp(name,"oprom_needed")) {
     value = VbGetNvStorage(VBNV_OPROM_NEEDED);
+  } else if (!strcasecmp(name,"recovery_subcode")) {
+    value = VbGetNvStorage(VBNV_RECOVERY_SUBCODE);
   }
   /* Other parameters */
   else if (!strcasecmp(name,"cros_debug")) {
@@ -451,6 +453,8 @@
     return VbSetNvStorage(VBNV_KERNEL_SETTINGS_RESET, 0);
   } else if (!strcasecmp(name,"recovery_request")) {
     return VbSetNvStorage(VBNV_RECOVERY_REQUEST, value);
+  } else if (!strcasecmp(name,"recovery_subcode")) {
+    return VbSetNvStorage(VBNV_RECOVERY_SUBCODE, value);
   } else if (!strcasecmp(name,"dbg_reset")) {
     return VbSetNvStorage(VBNV_DEBUG_RESET_MODE, value);
   } else if (!strcasecmp(name,"disable_dev_request")) {
diff --git a/utility/crossystem_main.c b/utility/crossystem_main.c
index 1b92665..4cb02cc 100644
--- a/utility/crossystem_main.c
+++ b/utility/crossystem_main.c
@@ -64,6 +64,7 @@
   {"platform_family", IS_STRING, "Platform family type"},
   {"recovery_reason", 0, "Recovery mode reason for current boot"},
   {"recovery_request", CAN_WRITE, "Recovery mode request (writable)"},
+  {"recovery_subcode", CAN_WRITE, "Recovery reason subcode (writable)"},
   {"recoverysw_boot", 0, "Recovery switch position at boot"},
   {"recoverysw_cur", 0, "Recovery switch current position"},
   {"recoverysw_ec_boot", 0, "Recovery switch position at EC boot"},