vboot_reference: crossystem: add the "tpm_attack" command

This commands reads/sets a bit in the kernel-reserved area
of the vboot context nvram.  The bit can also be set by the
driver during execution of a TPM command, to check if the
command is interrupted by a panic or power loss.  Under
some circumstances, this correlates with the TPM assuming
it is under attack.

BUG=chromium:431360
TEST=try "crossystem tpm_attack" and variations
BRANCH=none

Change-Id: I87215d5a0becfb5c01e0b69867a339bfe6fd0b68
Reviewed-on: https://chromium-review.googlesource.com/261339
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Commit-Queue: Luigi Semenzato <semenzato@chromium.org>
Tested-by: Luigi Semenzato <semenzato@chromium.org>
diff --git a/host/lib/crossystem.c b/host/lib/crossystem.c
index 1eef737..d97effa 100644
--- a/host/lib/crossystem.c
+++ b/host/lib/crossystem.c
@@ -65,11 +65,12 @@
 /* Masks for kern_nv usage by kernel. */
 #define KERN_NV_FWUPDATE_TRIES_MASK 0x0000000F
 #define KERN_NV_BLOCK_DEVMODE_FLAG  0x00000010
+#define KERN_NV_TPM_ATTACK_FLAG     0x00000020
 /* If you want to use the remaining currently-unused bits in kern_nv
  * for something kernel-y, define a new field (the way we did for
  * fwupdate_tries).  Don't just modify kern_nv directly, because that
  * makes it too easy to accidentally corrupt other sub-fields. */
-#define KERN_NV_CURRENTLY_UNUSED    0xFFFFFFE0
+#define KERN_NV_CURRENTLY_UNUSED    0xFFFFFFC0
 
 /* Return true if the FWID starts with the specified string. */
 int FwidStartsWith(const char *start) {
@@ -482,6 +483,12 @@
       value &= KERN_NV_BLOCK_DEVMODE_FLAG;
       value = !!value;
     }
+  } else if (!strcasecmp(name,"tpm_attack")) {
+    value = VbGetNvStorage(VBNV_KERNEL_FIELD);
+    if (value != -1) {
+      value &= KERN_NV_TPM_ATTACK_FLAG;
+      value = !!value;
+    }
   } else if (!strcasecmp(name,"loc_idx")) {
     value = VbGetNvStorage(VBNV_LOCALIZATION_INDEX);
   } else if (!strcasecmp(name,"backup_nvram_request")) {
@@ -626,7 +633,18 @@
       return -1;
     kern_nv &= ~KERN_NV_BLOCK_DEVMODE_FLAG;
     if (value)
-	kern_nv |= KERN_NV_BLOCK_DEVMODE_FLAG;
+      kern_nv |= KERN_NV_BLOCK_DEVMODE_FLAG;
+    return VbSetNvStorage_WithBackup(VBNV_KERNEL_FIELD, kern_nv);
+  } else if (!strcasecmp(name,"tpm_attack")) {
+    /* This value should only be read and cleared, but we allow setting it to 1
+     * for testing.
+     */
+    int kern_nv = VbGetNvStorage(VBNV_KERNEL_FIELD);
+    if (kern_nv == -1)
+      return -1;
+    kern_nv &= ~KERN_NV_TPM_ATTACK_FLAG;
+    if (value)
+      kern_nv |= KERN_NV_TPM_ATTACK_FLAG;
     return VbSetNvStorage_WithBackup(VBNV_KERNEL_FIELD, kern_nv);
   } else if (!strcasecmp(name,"loc_idx")) {
     return VbSetNvStorage_WithBackup(VBNV_LOCALIZATION_INDEX, value);
diff --git a/utility/crossystem.c b/utility/crossystem.c
index adc828d..3eaeaae 100644
--- a/utility/crossystem.c
+++ b/utility/crossystem.c
@@ -85,6 +85,7 @@
   {"savedmem_size", 0, "RAM debug data area size in bytes"},
   {"sw_wpsw_boot", 0,
    "Firmware write protect software setting enabled at boot"},
+  {"tpm_attack", CAN_WRITE, "TPM was interrupted since this flag was cleared"},
   {"tpm_fwver", 0, "Firmware version stored in TPM", "0x%08x"},
   {"tpm_kernver", 0, "Kernel version stored in TPM", "0x%08x"},
   {"tried_fwb", 0, "Tried firmware B before A this boot"},
@@ -291,9 +292,12 @@
 
     if (i > 1)
       printf(" ");  /* Output params space-delimited */
-    if (has_set)
+    if (has_set) {
       retval = SetParam(p, value);
-    else if (has_expect)
+      if (retval) {
+        fprintf(stderr, "Parameter %s is read-only\n", name);
+      }
+    } else if (has_expect)
       retval = CheckParam(p, value);
     else
       retval = PrintParam(p);