FACTORY: temp: crossystem workaround for factory branch

Change-Id: I503271a44cfd1afe93b7da3a6061e1db6756d2a7
diff --git a/host/arch/arm/lib/crossystem_arch.c b/host/arch/arm/lib/crossystem_arch.c
index 4304ce7..3eb7f70 100644
--- a/host/arch/arm/lib/crossystem_arch.c
+++ b/host/arch/arm/lib/crossystem_arch.c
@@ -20,75 +20,561 @@
 #include "vboot_struct.h"
 
 
+/* ACPI constants from Chrome OS Main Processor Firmware Spec */
+/* Boot reasons from BINF.0, from early H2C firmware */
+/* Unknown */
+#define BINF0_UNKNOWN                  0
+/* Normal boot to Chrome OS */
+#define BINF0_NORMAL                   1
+/* Developer mode boot (developer mode warning displayed) */
+#define BINF0_DEVELOPER                2
+/* Recovery initiated by user, using recovery button */
+#define BINF0_RECOVERY_BUTTON          3
+/* Recovery initiated by user pressing a key at developer mode warning
+ * screen */
+#define BINF0_RECOVERY_DEV_SCREEN_KEY  4
+/* Recovery caused by BIOS failed signature check (neither rewritable
+ * firmware was valid) */
+#define BINF0_RECOVERY_RW_FW_BAD       5
+/* Recovery caused by no OS kernel detected */
+#define BINF0_RECOVERY_NO_OS           6
+/* Recovery caused by OS kernel failed signature check */
+#define BINF0_RECOVERY_BAD_OS          7
+/* Recovery initiated by OS */
+#define BINF0_RECOVERY_OS_INITIATED    8
+/* OS-initiated S3 diagnostic path (debug mode boot) */
+#define BINF0_S3_DIAGNOSTIC_PATH       9
+/* S3 resume failed */
+#define BINF0_S3_RESUME_FAILED        10
+/* Recovery caused by TPM error */
+#define BINF0_RECOVERY_TPM_ERROR      11
+/* Firmware types from BINF.3 */
+#define BINF3_RECOVERY   0
+#define BINF3_NORMAL     1
+#define BINF3_DEVELOPER  2
+/* CHSW bitflags */
+#define CHSW_RECOVERY_BOOT     0x00000002
+#define CHSW_RECOVERY_EC_BOOT  0x00000004
+#define CHSW_DEV_BOOT          0x00000020
+#define CHSW_WP_BOOT           0x00000200
+/* CMOS reboot field bitflags */
+#define CMOSRF_RECOVERY        0x80
+#define CMOSRF_DEBUG_RESET     0x40
+#define CMOSRF_TRY_B           0x20
+/* GPIO signal types */
+#define GPIO_SIGNAL_TYPE_RECOVERY 1
+#define GPIO_SIGNAL_TYPE_DEV 2
+#define GPIO_SIGNAL_TYPE_WP 3
+
+/* Base name for ACPI files */
+#define ACPI_BASE_PATH "/sys/devices/platform/chromeos_acpi"
+/* Paths for frequently used ACPI files */
+#define ACPI_BINF_PATH ACPI_BASE_PATH "/BINF"
+#define ACPI_CHNV_PATH ACPI_BASE_PATH "/CHNV"
+#define ACPI_CHSW_PATH ACPI_BASE_PATH "/CHSW"
+#define ACPI_FMAP_PATH ACPI_BASE_PATH "/FMAP"
+#define ACPI_GPIO_PATH ACPI_BASE_PATH "/GPIO"
+#define ACPI_VBNV_PATH ACPI_BASE_PATH "/VBNV"
+#define ACPI_VDAT_PATH ACPI_BASE_PATH "/VDAT"
+
+/* Base name for GPIO files */
+#define GPIO_BASE_PATH "/sys/class/gpio"
+#define GPIO_EXPORT_PATH GPIO_BASE_PATH "/export"
+
+/* Filename for NVRAM file */
+#define NVRAM_PATH "/dev/nvram"
+
+/* Filename for legacy firmware update tries */
+#define NEED_FWUPDATE_PATH "/mnt/stateful_partition/.need_firmware_update"
+
+
 int VbReadNvStorage(VbNvContext* vnc) {
-  /* TODO: IMPLEMENT ME! */
-  return -1;
+  FILE* f;
+  int offs;
+
+  /* Get the byte offset from VBNV */
+  offs = ReadFileInt(ACPI_VBNV_PATH ".0");
+  if (offs == -1)
+    return -1;
+  if (VBNV_BLOCK_SIZE > ReadFileInt(ACPI_VBNV_PATH ".1"))
+    return -1;  /* NV storage block is too small */
+
+  f = fopen(NVRAM_PATH, "rb");
+  if (!f)
+    return -1;
+
+  if (0 != fseek(f, offs, SEEK_SET) ||
+      1 != fread(vnc->raw, VBNV_BLOCK_SIZE, 1, f)) {
+    fclose(f);
+    return -1;
+  }
+
+  fclose(f);
+  return 0;
 }
 
 
 int VbWriteNvStorage(VbNvContext* vnc) {
-  /* TODO: IMPLEMENT ME! */
-  return -1;
+  FILE* f;
+  int offs;
+
+  if (!vnc->raw_changed)
+    return 0;  /* Nothing changed, so no need to write */
+
+  /* Get the byte offset from VBNV */
+  offs = ReadFileInt(ACPI_VBNV_PATH ".0");
+  if (offs == -1)
+    return -1;
+  if (VBNV_BLOCK_SIZE > ReadFileInt(ACPI_VBNV_PATH ".1"))
+    return -1;  /* NV storage block is too small */
+
+  f = fopen(NVRAM_PATH, "w+b");
+  if (!f)
+    return -1;
+
+  if (0 != fseek(f, offs, SEEK_SET) ||
+      1 != fwrite(vnc->raw, VBNV_BLOCK_SIZE, 1, f)) {
+    fclose(f);
+    return -1;
+  }
+
+  fclose(f);
+  return 0;
+}
+
+
+/*
+ * Get buffer data from ACPI.
+ *
+ * Buffer data is expected to be represented by a file which is a text dump of
+ * the buffer, representing each byte by two hex numbers, space and newline
+ * separated.
+ *
+ * On success, stores the amount of data read in bytes to *buffer_size; on
+ * erros, sets *buffer_size=0.
+ *
+ * Input - ACPI file name to get data from.
+ *
+ * Output: a pointer to AcpiBuffer structure containing the binary
+ *         representation of the data. The caller is responsible for
+ *         deallocating the pointer, this will take care of both the structure
+ *         and the buffer. Null in case of error.
+ */
+static uint8_t* VbGetBuffer(const char* filename, int* buffer_size)
+{
+  FILE* f = NULL;
+  char* file_buffer = NULL;
+  uint8_t* output_buffer = NULL;
+  uint8_t* return_value = NULL;
+
+  /* Assume error until proven otherwise */
+  if (buffer_size)
+    *buffer_size = 0;
+
+  do {
+    struct stat fs;
+    uint8_t* output_ptr;
+    int rv, i, real_size;
+    int parsed_size = 0;
+
+    rv = stat(filename, &fs);
+    if (rv || !S_ISREG(fs.st_mode))
+      break;
+
+    f = fopen(filename, "r");
+    if (!f)
+      break;
+
+    file_buffer = Malloc(fs.st_size + 1);
+    if (!file_buffer)
+      break;
+
+    real_size = fread(file_buffer, 1, fs.st_size, f);
+    if (!real_size)
+      break;
+    file_buffer[real_size] = '\0';
+
+    /* Each byte in the output will replace two characters and a space
+     * in the input, so the output size does not exceed input side/3
+     * (a little less if account for newline characters). */
+    output_buffer = Malloc(real_size/3);
+    if (!output_buffer)
+      break;
+    output_ptr = output_buffer;
+
+    /* process the file contents */
+    for (i = 0; i < real_size; i++) {
+      char* base, *end;
+
+      base = file_buffer + i;
+
+      if (!isxdigit(*base))
+        continue;
+
+      output_ptr[parsed_size++] = strtol(base, &end, 16) & 0xff;
+
+      if ((end - base) != 2)
+        /* Input file format error */
+        break;
+
+      i += 2; /* skip the second character and the following space */
+    }
+
+    if (i == real_size) {
+      /* all is well */
+      return_value = output_buffer;
+      output_buffer = NULL; /* prevent it from deallocating */
+      if (buffer_size)
+        *buffer_size = parsed_size;
+    }
+  } while(0);
+
+  /* wrap up */
+  if (f)
+    fclose(f);
+
+  if (file_buffer)
+    Free(file_buffer);
+
+  if (output_buffer)
+    Free(output_buffer);
+
+  return return_value;
 }
 
 
 VbSharedDataHeader* VbSharedDataRead(void) {
-  /* TODO: IMPLEMENT ME! */
-  return NULL;
+
+  VbSharedDataHeader* sh;
+  int got_size = 0;
+
+  sh = (VbSharedDataHeader*)VbGetBuffer(ACPI_VDAT_PATH, &got_size);
+  if (!sh)
+    return NULL;
+  if (got_size < sizeof(VbSharedDataHeader)) {
+    Free(sh);
+    return NULL;
+  }
+  if (sh->data_size > got_size)
+    sh->data_size = got_size;  /* Truncated read */
+
+  return sh;
+}
+
+
+/* Read the CMOS reboot field in NVRAM.
+ *
+ * Returns 0 if the mask is clear in the field, 1 if set, or -1 if error. */
+static int VbGetCmosRebootField(uint8_t mask) {
+  FILE* f;
+  int chnv, nvbyte;
+
+  /* Get the byte offset from CHNV */
+  chnv = ReadFileInt(ACPI_CHNV_PATH);
+  if (chnv == -1)
+    return -1;
+
+  f = fopen(NVRAM_PATH, "rb");
+  if (!f)
+    return -1;
+
+  if (0 != fseek(f, chnv, SEEK_SET) || EOF == (nvbyte = fgetc(f))) {
+    fclose(f);
+    return -1;
+  }
+
+  fclose(f);
+  return (nvbyte & mask ? 1 : 0);
+}
+
+
+/* Write the CMOS reboot field in NVRAM.
+ *
+ * Sets (value=0) or clears (value!=0) the mask in the byte.
+ *
+ * Returns 0 if success, or -1 if error. */
+static int VbSetCmosRebootField(uint8_t mask, int value) {
+  FILE* f;
+  int chnv, nvbyte;
+
+  /* Get the byte offset from CHNV */
+  chnv = ReadFileInt(ACPI_CHNV_PATH);
+  if (chnv == -1)
+    return -1;
+
+  f = fopen(NVRAM_PATH, "w+b");
+  if (!f)
+    return -1;
+
+  /* Read the current value */
+  if (0 != fseek(f, chnv, SEEK_SET) || EOF == (nvbyte = fgetc(f))) {
+    fclose(f);
+    return -1;
+  }
+
+  /* Set/clear the mask */
+  if (value)
+    nvbyte |= mask;
+  else
+    nvbyte &= ~mask;
+
+  /* Write the byte back */
+  if (0 != fseek(f, chnv, SEEK_SET) || EOF == (fputc(nvbyte, f))) {
+    fclose(f);
+    return -1;
+  }
+
+  /* Success */
+  fclose(f);
+  return 0;
+}
+
+
+/* Read the active main firmware type into the destination buffer.
+ * Passed the destination and its size.  Returns the destination, or
+ * NULL if error. */
+static const char* VbReadMainFwType(char* dest, int size) {
+
+  /* Try reading type from BINF.3 */
+  switch(ReadFileInt(ACPI_BINF_PATH ".3")) {
+    case BINF3_RECOVERY:
+      return StrCopy(dest, "recovery", size);
+    case BINF3_NORMAL:
+      return StrCopy(dest, "normal", size);
+    case BINF3_DEVELOPER:
+      return StrCopy(dest, "developer", size);
+    default:
+      break;  /* Fall through to legacy handling */
+  }
+
+  /* Fall back to BINF.0 for legacy systems like Mario. */
+  switch(ReadFileInt(ACPI_BINF_PATH ".0")) {
+    case -1:
+      /* Both BINF.0 and BINF.3 are missing, so this isn't Chrome OS
+       * firmware. */
+      return StrCopy(dest, "nonchrome", size);
+    case BINF0_NORMAL:
+      return StrCopy(dest, "normal", size);
+    case BINF0_DEVELOPER:
+      return StrCopy(dest, "developer", size);
+    case BINF0_RECOVERY_BUTTON:
+    case BINF0_RECOVERY_DEV_SCREEN_KEY:
+    case BINF0_RECOVERY_RW_FW_BAD:
+    case BINF0_RECOVERY_NO_OS:
+    case BINF0_RECOVERY_BAD_OS:
+    case BINF0_RECOVERY_OS_INITIATED:
+    case BINF0_RECOVERY_TPM_ERROR:
+      /* Assorted flavors of recovery boot reason. */
+      return StrCopy(dest, "recovery", size);
+    default:
+      /* Other values don't map cleanly to firmware type. */
+      return NULL;
+  }
+}
+
+
+/* Read the recovery reason.  Returns the reason code or -1 if error. */
+static int VbGetRecoveryReason(void) {
+  int value;
+
+  /* Try reading type from BINF.4 */
+  value = ReadFileInt(ACPI_BINF_PATH ".4");
+  if (-1 != value)
+    return value;
+
+  /* Fall back to BINF.0 for legacy systems like Mario. */
+  switch(ReadFileInt(ACPI_BINF_PATH ".0")) {
+    case BINF0_NORMAL:
+    case BINF0_DEVELOPER:
+      return VBNV_RECOVERY_NOT_REQUESTED;
+    case BINF0_RECOVERY_BUTTON:
+      return VBNV_RECOVERY_RO_MANUAL;
+    case BINF0_RECOVERY_DEV_SCREEN_KEY:
+      return VBNV_RECOVERY_RW_DEV_SCREEN;
+    case BINF0_RECOVERY_RW_FW_BAD:
+    case BINF0_RECOVERY_NO_OS:
+      return VBNV_RECOVERY_RW_NO_OS;
+    case BINF0_RECOVERY_BAD_OS:
+      return VBNV_RECOVERY_RW_INVALID_OS;
+    case BINF0_RECOVERY_OS_INITIATED:
+      return VBNV_RECOVERY_LEGACY;
+    default:
+      /* Other values don't map cleanly to firmware type. */
+      return -1;
+  }
+}
+
+
+/* Read a GPIO of the specified signal type (see ACPI GPIO SignalType).
+ *
+ * Returns 1 if the signal is asserted, 0 if not asserted, or -1 if error. */
+static int ReadGpio(int signal_type) {
+  char name[128];
+  int index = 0;
+  int gpio_type;
+  int active_high;
+  int controller_offset;
+  char controller_name[128];
+  int value;
+
+  /* Scan GPIO.* to find a matching signal type */
+  for (index = 0; ; index++) {
+    snprintf(name, sizeof(name), "%s.%d/GPIO.0", ACPI_GPIO_PATH, index);
+    gpio_type = ReadFileInt(name);
+    if (gpio_type == signal_type)
+      break;
+    else if (gpio_type == -1)
+      return -1;  /* Ran out of GPIOs before finding a match */
+  }
+
+  /* Read attributes and controller info for the GPIO */
+  snprintf(name, sizeof(name), "%s.%d/GPIO.1", ACPI_GPIO_PATH, index);
+  active_high = ReadFileBit(name, 0x00000001);
+  snprintf(name, sizeof(name), "%s.%d/GPIO.2", ACPI_GPIO_PATH, index);
+  controller_offset = ReadFileInt(name);
+  if (active_high == -1 || controller_offset == -1)
+    return -1;  /* Missing needed info */
+
+  /* We only support the NM10 for now */
+  snprintf(name, sizeof(name), "%s.%d/GPIO.3", ACPI_GPIO_PATH, index);
+  if (!ReadFileString(controller_name, sizeof(controller_name), name))
+    return -1;
+  if (0 != strcmp(controller_name, "NM10"))
+    return -1;
+
+  /* Assume the NM10 has offset 192 */
+  /* TODO: should really check gpiochipNNN/label to see if it's the
+   * address we expect for the NM10, and then read the offset from
+   * gpiochipNNN/base. */
+  controller_offset += 192;
+
+  /* Try reading the GPIO value */
+  snprintf(name, sizeof(name), "%s/gpio%d/value",
+           GPIO_BASE_PATH, controller_offset);
+  value = ReadFileInt(name);
+
+  if (value == -1) {
+    /* Try exporting the GPIO */
+    FILE* f = fopen(GPIO_EXPORT_PATH, "wt");
+    if (!f)
+      return -1;
+    fprintf(f, "%d", controller_offset);
+    fclose(f);
+
+    /* Try re-reading the GPIO value */
+    value = ReadFileInt(name);
+  }
+
+  if (value == -1)
+    return -1;
+
+  /* Compare the GPIO value with the active value and return 1 if match. */
+  return (value == active_high ? 1 : 0);
 }
 
 
 int VbGetArchPropertyInt(const char* name) {
+  int value = -1;
 
-  /* TODO: IMPLEMENT ME!  For now, return reasonable defaults for
-   * values where reasonable defaults exist. */
+  /* Values from ACPI */
   if (!strcasecmp(name,"recovery_reason")) {
+    value = VbGetRecoveryReason();
   } else if (!strcasecmp(name,"fmap_base")) {
+    value = ReadFileInt(ACPI_FMAP_PATH);
   }
   /* Switch positions */
   else if (!strcasecmp(name,"devsw_cur")) {
-    return 1;
+    value = ReadGpio(GPIO_SIGNAL_TYPE_DEV);
   } else if (!strcasecmp(name,"recoverysw_cur")) {
-    return 0;
+    value = ReadGpio(GPIO_SIGNAL_TYPE_RECOVERY);
   } else if (!strcasecmp(name,"wpsw_cur")) {
-    return 1;
+    value = ReadGpio(GPIO_SIGNAL_TYPE_WP);
+    if (-1 != value && FwidStartsWith("Mario."))
+      value = 1 - value;  /* Mario reports this backwards */
   } else if (!strcasecmp(name,"devsw_boot")) {
-    return 1;
+    value = ReadFileBit(ACPI_CHSW_PATH, CHSW_DEV_BOOT);
   } else if (!strcasecmp(name,"recoverysw_boot")) {
-    return 0;
+    value = ReadFileBit(ACPI_CHSW_PATH, CHSW_RECOVERY_BOOT);
   } else if (!strcasecmp(name,"recoverysw_ec_boot")) {
-    return 0;
+    value = ReadFileBit(ACPI_CHSW_PATH, CHSW_RECOVERY_EC_BOOT);
   } else if (!strcasecmp(name,"wpsw_boot")) {
-    return 1;
+    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")) {
+    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")) {
+    value = VbGetNvStorage(VBNV_RECOVERY_REQUEST);
+    if (-1 == value)
+      value = VbGetCmosRebootField(CMOSRF_RECOVERY);
+  } else if (!strcasecmp(name,"dbg_reset")) {
+    value = VbGetNvStorage(VBNV_DEBUG_RESET_MODE);
+    if (-1 == value)
+      value = VbGetCmosRebootField(CMOSRF_DEBUG_RESET);
+  } else if (!strcasecmp(name,"fwb_tries")) {
+    value = VbGetNvStorage(VBNV_TRY_B_COUNT);
+    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 (-1 != VbGetNvStorage(VBNV_KERNEL_FIELD))
+      return -1;  /* NvStorage supported; fail through arch-specific
+                   * implementation to normal implementation. */
+
+    /* Read value from file; missing file means value=0. */
+    value = ReadFileInt(NEED_FWUPDATE_PATH);
+    if (-1 == value)
+      value = 0;
   }
 
-  return -1;
+  return value;
 }
 
 
 const char* VbGetArchPropertyString(const char* name, char* dest, int size) {
-  /* TODO: IMPLEMENT ME!  For now, return reasonable defaults for
-   * values where reasonable defaults exist. */
+
   if (!strcasecmp(name,"arch")) {
     return StrCopy(dest, "arm", size);
   } else if (!strcasecmp(name,"hwid")) {
-    return StrCopy(dest, "UnknownArmHwid", size);
+    return ReadFileString(dest, size, ACPI_BASE_PATH "/HWID");
   } else if (!strcasecmp(name,"fwid")) {
-    return StrCopy(dest, "UnknownArmFwid", size);
+    return ReadFileString(dest, size, ACPI_BASE_PATH "/FWID");
   } else if (!strcasecmp(name,"ro_fwid")) {
-    return StrCopy(dest, "UnknownArmRoFwid", size);
+    return ReadFileString(dest, size, ACPI_BASE_PATH "/FRID");
   } else if (!strcasecmp(name,"mainfw_act")) {
-    return StrCopy(dest, "A", size);
+    switch(ReadFileInt(ACPI_BINF_PATH ".1")) {
+      case 0:
+        return StrCopy(dest, "recovery", size);
+      case 1:
+        return StrCopy(dest, "A", size);
+      case 2:
+        return StrCopy(dest, "B", size);
+      default:
+        return NULL;
+    }
   } else if (!strcasecmp(name,"mainfw_type")) {
-    return StrCopy(dest, "developer", size);
+    return VbReadMainFwType(dest, size);
   } else if (!strcasecmp(name,"ecfw_act")) {
-    return StrCopy(dest, "RO", size);
+    switch(ReadFileInt(ACPI_BINF_PATH ".2")) {
+      case 0:
+        return StrCopy(dest, "RO", size);
+      case 1:
+        return StrCopy(dest, "RW", size);
+      default:
+        return NULL;
+    }
   }
 
   return NULL;
@@ -96,7 +582,40 @@
 
 
 int VbSetArchPropertyInt(const char* name, int value) {
-  /* TODO: IMPLEMENT ME! */
+  /* NV storage values.  If unable to get from NV storage, fall back to the
+   * CMOS reboot field used by older BIOS. */
+  if (!strcasecmp(name,"recovery_request")) {
+    if (0 == VbSetNvStorage(VBNV_RECOVERY_REQUEST, value))
+      return 0;
+    return VbSetCmosRebootField(CMOSRF_RECOVERY, value);
+  } else if (!strcasecmp(name,"dbg_reset")) {
+    if (0 == VbSetNvStorage(VBNV_DEBUG_RESET_MODE, value))
+      return 0;
+    return  VbSetCmosRebootField(CMOSRF_DEBUG_RESET, value);
+  } else if (!strcasecmp(name,"fwb_tries")) {
+    if (0 == VbSetNvStorage(VBNV_TRY_B_COUNT, value))
+      return 0;
+    return VbSetCmosRebootField(CMOSRF_TRY_B, value);
+  }
+  /* 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 (-1 != VbGetNvStorage(VBNV_KERNEL_FIELD))
+      return -1;  /* NvStorage supported; fail through arch-specific
+                   * implementation to normal implementation */
+
+    if (value) {
+      char buf[32];
+      snprintf(buf, sizeof(buf), "%d", value);
+      return WriteFile(NEED_FWUPDATE_PATH, buf, strlen(buf));
+    } else {
+      /* No update tries, so remove file if it exists. */
+      unlink(NEED_FWUPDATE_PATH);
+      return 0;
+    }
+  }
+
   return -1;
 }