Crossystem should return at-boot switch positions from VbSharedData

This is more reliable than reading them through FDT/ACPI, since it reflects
the positions as shown to verified boot code.

Notes:
1. This affects ALL platforms with virtual dev switches (x86 AND arm)
2. The fix should have no effect on older platforms, but I haven't tested those.

BUG=chrome-os-partner:11805
TEST=manual

1. boot in normal mode.

devsw_boot             = 0                              # Developer switch position at boot
recovery_reason        = 0                              # Recovery mode reason for current boot
recoverysw_boot        = 0                              # Recovery switch position at boot
wpsw_boot              = 1                              # Firmware write protect hardware switch position at boot

2. boot in developer mode.

localhost ~ # crossystem
devsw_boot             = 1                              # Developer switch position at boot
recovery_reason        = 0                              # Recovery mode reason for current boot
recoverysw_boot        = 0                              # Recovery switch position at boot
wpsw_boot              = 1                              # Firmware write protect hardware switch position at boot

3. boot in developer-recovery mode using keyboard combo.

devsw_boot             = 1                              # Developer switch position at boot
recovery_reason        = 2                              # Recovery mode reason for current boot
recoverysw_boot        = 1                              # Recovery switch position at boot
wpsw_boot              = 1                              # Firmware write protect hardware switch position at boot

4. disable WP and reboot.  wpsw_boot should be 0.

Original-Change-Id: If4156b5e14c6923c5b331c7e5feaabbffe1dad37
(cherry picked from commit da8d32dc8d0fb5ebcfffa305f4a3ecb2dd7c79ac)

Change-Id: I6b80c4f507ebbb9accb75ad6b21b0b5bd963921a
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/29320
diff --git a/firmware/include/vboot_struct.h b/firmware/include/vboot_struct.h
index 7000f6e..4b951b2 100644
--- a/firmware/include/vboot_struct.h
+++ b/firmware/include/vboot_struct.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2012 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.
  *
@@ -236,6 +236,12 @@
 /* VbInit() was told the system supports EC software sync */
 #define VBSD_EC_SOFTWARE_SYNC           0x00000800
 
+/* Supported flags by header version.  It's ok to add new flags while keeping
+ * struct version 2 as long as flag-NOT-present is the correct value for
+ * existing hardware (Stumpy/Lumpy). */
+#define VBSD_FLAGS_VERSION_1            0x00000007  /* Alex, ZGB */
+#define VBSD_FLAGS_VERSION_2            0x00000F7F
+
 /* Result codes for VbSharedDataHeader.check_fw_a_result (and b_result) */
 #define VBSD_LF_CHECK_NOT_DONE          0
 #define VBSD_LF_CHECK_DEV_MISMATCH      1
diff --git a/host/arch/arm/lib/crossystem_arch.c b/host/arch/arm/lib/crossystem_arch.c
index 76be158..3ccb0ba 100644
--- a/host/arch/arm/lib/crossystem_arch.c
+++ b/host/arch/arm/lib/crossystem_arch.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2012 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.
  */
@@ -89,20 +89,6 @@
   return 0;
 }
 
-static int ReadFdtBool(const char *property) {
-  char filename[FNAME_SIZE];
-  struct stat tmp;
-  int err;
-
-  snprintf(filename, sizeof(filename), FDT_BASE_PATH "/%s", property);
-  err = stat(filename, &tmp);
-
-  if (err == 0)
-    return 1;
-
-  return 0;
-}
-
 static int ReadFdtInt(const char *property) {
   int value;
   if (ReadFdtValue(property, &value))
@@ -368,19 +354,14 @@
 int VbGetArchPropertyInt(const char* name) {
   if (!strcasecmp(name, "fmap_base"))
     return ReadFdtInt("fmap-offset");
-  else if (!strcasecmp(name, "devsw_boot"))
-    return ReadFdtBool("boot-developer-switch");
-  else if (!strcasecmp(name, "recoverysw_boot"))
-    return ReadFdtBool("boot-recovery-switch");
-  else if (!strcasecmp(name, "wpsw_boot"))
-    return ReadFdtBool("boot-write-protect-switch");
   else if (!strcasecmp(name, "devsw_cur"))
     return VbGetVarGpio("developer-switch");
   else if (!strcasecmp(name, "recoverysw_cur"))
     return VbGetVarGpio("recovery-switch");
   else if (!strcasecmp(name, "wpsw_cur"))
-  return VbGetVarGpio("write-protect-switch");
+    return VbGetVarGpio("write-protect-switch");
   else if (!strcasecmp(name, "recoverysw_ec_boot"))
+    /* TODO: read correct value using ectool */
     return 0;
   else
     return -1;
diff --git a/host/arch/x86/lib/crossystem_arch.c b/host/arch/x86/lib/crossystem_arch.c
index b0ab10f..448a8ff 100644
--- a/host/arch/x86/lib/crossystem_arch.c
+++ b/host/arch/x86/lib/crossystem_arch.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2012 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.
  */
@@ -426,19 +426,8 @@
 
 /* Read the recovery reason.  Returns the reason code or -1 if error. */
 static int VbGetRecoveryReason(void) {
-  VbSharedDataHeader* sh;
   int value = -1;
 
-  /* Try reading from VbSharedData first */
-  sh = VbSharedDataRead();
-  if (sh) {
-    if (sh->struct_version >= 2)
-      value = sh->recovery_reason;
-    free(sh);
-    if (-1 != value)
-      return value;
-  }
-
   /* Try reading type from BINF.4 */
   value = ReadFileInt(ACPI_BINF_PATH ".4");
   if (-1 != value)
@@ -601,13 +590,11 @@
   int value = -1;
 
   /* Values from ACPI */
-  if (!strcasecmp(name,"recovery_reason")) {
-    value = VbGetRecoveryReason();
-  } else if (!strcasecmp(name,"fmap_base")) {
+  if (!strcasecmp(name,"fmap_base"))
     value = ReadFileInt(ACPI_FMAP_PATH);
-  }
+
   /* Switch positions */
-  else if (!strcasecmp(name,"devsw_cur")) {
+  if (!strcasecmp(name,"devsw_cur")) {
     value = ReadGpio(GPIO_SIGNAL_TYPE_DEV);
   } else if (!strcasecmp(name,"recoverysw_cur")) {
     value = ReadGpio(GPIO_SIGNAL_TYPE_RECOVERY);
@@ -615,28 +602,36 @@
     value = ReadGpio(GPIO_SIGNAL_TYPE_WP);
     if (-1 != value && FwidStartsWith("Mario."))
       value = 1 - value;  /* Mario reports this backwards */
-  } else if (!strcasecmp(name,"devsw_boot")) {
-    value = ReadFileBit(ACPI_CHSW_PATH, CHSW_DEV_BOOT);
-  } else if (!strcasecmp(name,"recoverysw_boot")) {
-    value = ReadFileBit(ACPI_CHSW_PATH, CHSW_RECOVERY_BOOT);
   } else if (!strcasecmp(name,"recoverysw_ec_boot")) {
     value = ReadFileBit(ACPI_CHSW_PATH, CHSW_RECOVERY_EC_BOOT);
-  } else if (!strcasecmp(name,"wpsw_boot")) {
-    value = ReadFileBit(ACPI_CHSW_PATH, CHSW_WP_BOOT);
-    if (-1 != value && FwidStartsWith("Mario."))
-      value = 1 - value;  /* Mario reports this backwards */
+  }
+
+  /* Fields for old systems which don't have VbSharedData */
+  if (VbSharedDataVersion() < 2) {
+    if (!strcasecmp(name,"recovery_reason")) {
+      value = VbGetRecoveryReason();
+    } else if (!strcasecmp(name,"devsw_boot")) {
+      value = ReadFileBit(ACPI_CHSW_PATH, CHSW_DEV_BOOT);
+    } else if (!strcasecmp(name,"recoverysw_boot")) {
+      value = ReadFileBit(ACPI_CHSW_PATH, CHSW_RECOVERY_BOOT);
+    } else if (!strcasecmp(name,"wpsw_boot")) {
+      value = ReadFileBit(ACPI_CHSW_PATH, CHSW_WP_BOOT);
+      if (-1 != value && FwidStartsWith("Mario."))
+        value = 1 - value;  /* Mario reports this backwards */
+    }
   }
 
   /* Saved memory is at a fixed location for all H2C BIOS.  If the CHSW
    * path exists in sysfs, it's a H2C BIOS. */
-  else if (!strcasecmp(name,"savedmem_base")) {
+  if (!strcasecmp(name,"savedmem_base")) {
     return (-1 == ReadFileInt(ACPI_CHSW_PATH) ? -1 : 0x00F00000);
   } else if (!strcasecmp(name,"savedmem_size")) {
     return (-1 == ReadFileInt(ACPI_CHSW_PATH) ? -1 : 0x00100000);
   }
+
   /* NV storage values.  If unable to get from NV storage, fall back to the
-   * CMOS reboot field used by older BIOS. */
-  else if (!strcasecmp(name,"recovery_request")) {
+   * CMOS reboot field used by older BIOS (e.g. Mario). */
+  if (!strcasecmp(name,"recovery_request")) {
     value = VbGetNvStorage(VBNV_RECOVERY_REQUEST);
     if (-1 == value)
       value = VbGetCmosRebootField(CMOSRF_RECOVERY);
@@ -649,10 +644,11 @@
     if (-1 == value)
       value = VbGetCmosRebootField(CMOSRF_TRY_B);
   }
+
   /* Firmware update tries is now stored in the kernel field.  On
    * older systems where it's not, it was stored in a file in the
    * stateful partition. */
-  else if (!strcasecmp(name,"fwupdate_tries")) {
+  if (!strcasecmp(name,"fwupdate_tries")) {
     if (-1 != VbGetNvStorage(VBNV_KERNEL_FIELD))
       return -1;  /* NvStorage supported; fail through arch-specific
                    * implementation to normal implementation. */
diff --git a/host/include/crossystem_arch.h b/host/include/crossystem_arch.h
index be6b543..4044c74 100644
--- a/host/include/crossystem_arch.h
+++ b/host/include/crossystem_arch.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2012 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.
  *
@@ -36,8 +36,10 @@
 /* Return true if the FWID starts with the specified string. */
 int FwidStartsWith(const char *start);
 
+/* Return version of VbSharedData struct or -1 if not found. */
+int VbSharedDataVersion(void);
 
-/* APIS WITH ARCH-SPECIFIC IMPLEMENTATIONS */
+/* Apis WITH ARCH-SPECIFIC IMPLEMENTATIONS */
 
 /* Read the non-volatile context from NVRAM.
  *
diff --git a/host/lib/crossystem.c b/host/lib/crossystem.c
index 1515ecf..e120abc 100644
--- a/host/lib/crossystem.c
+++ b/host/lib/crossystem.c
@@ -34,6 +34,12 @@
 /* Fields that GetVdatInt() can get */
 typedef enum VdatIntField {
   VDAT_INT_FLAGS = 0,                /* Flags */
+  VDAT_INT_HEADER_VERSION,           /* Header version for VbSharedData */
+  VDAT_INT_DEVSW_BOOT,               /* Dev switch position at boot */
+  VDAT_INT_DEVSW_VIRTUAL,            /* Dev switch is virtual */
+  VDAT_INT_RECSW_BOOT,               /* Recovery switch position at boot */
+  VDAT_INT_WPSW_BOOT,                /* WP switch position at boot */
+
   VDAT_INT_FW_VERSION_TPM,           /* Current firmware version in TPM */
   VDAT_INT_KERNEL_VERSION_TPM,       /* Current kernel version in TPM */
   VDAT_INT_TRIED_FIRMWARE_B,         /* Tried firmware B due to fwb_tries */
@@ -322,15 +328,13 @@
   if (!sh)
     return -1;
 
+  /* Fields supported in version 1 */
   switch (field) {
     case VDAT_INT_FLAGS:
       value = (int)sh->flags;
       break;
-    case VDAT_INT_FW_VERSION_TPM:
-      value = (int)sh->fw_version_tpm;
-      break;
-    case VDAT_INT_KERNEL_VERSION_TPM:
-      value = (int)sh->kernel_version_tpm;
+    case VDAT_INT_HEADER_VERSION:
+      value = sh->struct_version;
       break;
     case VDAT_INT_TRIED_FIRMWARE_B:
       value = (sh->flags & VBSD_FWB_TRIED ? 1 : 0);
@@ -338,17 +342,47 @@
     case VDAT_INT_KERNEL_KEY_VERIFIED:
       value = (sh->flags & VBSD_KERNEL_KEY_VERIFIED ? 1 : 0);
       break;
-    case VDAT_INT_RECOVERY_REASON:
-      /* Field added in struct version 2 */
-      if (sh->struct_version >= 2)
-        value = sh->recovery_reason;
+    default:
       break;
   }
 
+  /* Fields added in struct version 2 */
+  if (sh->struct_version >= 2) {
+    switch(field) {
+      case VDAT_INT_DEVSW_BOOT:
+        value = (sh->flags & VBSD_BOOT_DEV_SWITCH_ON ? 1 : 0);
+        break;
+      case VDAT_INT_DEVSW_VIRTUAL:
+        value = (sh->flags & VBSD_HONOR_VIRT_DEV_SWITCH ? 1 : 0);
+        break;
+      case VDAT_INT_RECSW_BOOT:
+        value = (sh->flags & VBSD_BOOT_REC_SWITCH_ON ? 1 : 0);
+        break;
+      case VDAT_INT_WPSW_BOOT:
+        value = (sh->flags & VBSD_BOOT_FIRMWARE_WP_ENABLED ? 1 : 0);
+        break;
+      case VDAT_INT_FW_VERSION_TPM:
+        value = (int)sh->fw_version_tpm;
+        break;
+      case VDAT_INT_KERNEL_VERSION_TPM:
+        value = (int)sh->kernel_version_tpm;
+        break;
+      case VDAT_INT_RECOVERY_REASON:
+        value = sh->recovery_reason;
+        break;
+      default:
+        break;
+    }
+  }
+
   free(sh);
   return value;
 }
 
+/* Return version of VbSharedData struct or -1 if not found. */
+int VbSharedDataVersion(void) {
+  return GetVdatInt(VDAT_INT_HEADER_VERSION);
+}
 
 int VbGetSystemPropertyInt(const char* name) {
   int value = -1;
@@ -387,6 +421,14 @@
   /* Other parameters */
   else if (!strcasecmp(name,"cros_debug")) {
     value = VbGetCrosDebug();
+  } else if (!strcasecmp(name,"devsw_boot")) {
+    value = GetVdatInt(VDAT_INT_DEVSW_BOOT);
+  } else if (!strcasecmp(name,"devsw_virtual")) {
+    value = GetVdatInt(VDAT_INT_DEVSW_VIRTUAL);
+  } else if (!strcasecmp(name, "recoverysw_boot")) {
+    value = GetVdatInt(VDAT_INT_RECSW_BOOT);
+  } else if (!strcasecmp(name, "wpsw_boot")) {
+    value = GetVdatInt(VDAT_INT_WPSW_BOOT);
   } else if (!strcasecmp(name,"vdat_flags")) {
     value = GetVdatInt(VDAT_INT_FLAGS);
   } else if (!strcasecmp(name,"tpm_fwver")) {