Add cros_debug option to crossystem

Change-Id: Ie62364a87f7f144ee647054d2a9ef83522cdbe7d

BUG=12904
TEST=manual

Expected output of `crossystem mainfw_type cros_debug` under each of the following scenarios:

* Boot Alex with dev switch off: normal 0
* Boot Alex with dev switch on (and dev firmware): developer 1
* Boot Alex with dev switch on (and normal firmware): normal 1
* Boot Alex with recovery firmware: recovery 0
* Boot Alex with dev switch off, then turn the dev switch on after booting: normal 0
* Boot Cr-48 with dev switch off: normal 0
* Boot Cr-48 with dev switch on: developer 1
* Boot Cr-48 with recovery firmware: recovery 0

* Boot Alex with dev switch off and kernel command line changed to include "cros_debug": normal 1
* Boot Alex with dev switch off and kernel command line changed to include "cros_debugg": normal 0
* Boot Alex with dev switch off and kernel command line changed to include "ccros_debug": normal 0

* Boot H2O BIOS with kernel command line changed to include "cros_debug": nonchrome 1
* Boot H2O BIOS with kernel command line changed to include "cros_debugg": nonchrome 0
* Boot H2O BIOS with kernel command line changed to include "ccros_debug": nonchrome 0

Review URL: http://codereview.chromium.org/6659021
diff --git a/host/lib/crossystem.c b/host/lib/crossystem.c
index 5951e10..17f4f50 100644
--- a/host/lib/crossystem.c
+++ b/host/lib/crossystem.c
@@ -54,6 +54,10 @@
 #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
 
 /* Base name for ACPI files */
 #define ACPI_BASE_PATH "/sys/devices/platform/chromeos_acpi"
@@ -69,9 +73,12 @@
 #define GPIO_BASE_PATH "/sys/class/gpio"
 #define GPIO_EXPORT_PATH GPIO_BASE_PATH "/export"
 
-/* Base name for NVRAM file */
+/* Filename for NVRAM file */
 #define NVRAM_PATH "/dev/nvram"
 
+/* Filename for kernel command line */
+#define KERNEL_CMDLINE_PATH "/proc/cmdline"
+
 
 /* Copy up to dest_size-1 characters from src to dest, ensuring null
    termination (which strncpy() doesn't do).  Returns the destination
@@ -414,11 +421,11 @@
 
   /* Try reading type from BINF.3 */
   switch(ReadFileInt(ACPI_BINF_PATH ".3")) {
-    case 0:
+    case BINF3_RECOVERY:
       return StrCopy(dest, "recovery", size);
-    case 1:
+    case BINF3_NORMAL:
       return StrCopy(dest, "normal", size);
-    case 2:
+    case BINF3_DEVELOPER:
       return StrCopy(dest, "developer", size);
     default:
       break;  /* Fall through to legacy handling */
@@ -450,6 +457,43 @@
 }
 
 
+/* Determine whether OS-level debugging should be allowed.  Passed the
+ * destination and its size.  Returns 1 if yes, 0 if no, -1 if error. */
+int VbGetCrosDebug(void) {
+  FILE* f = NULL;
+  char buf[4096] = "";
+  int binf3;
+  char *t, *saveptr;
+
+  /* Try reading firmware type from BINF.3. */
+  binf3 = ReadFileInt(ACPI_BINF_PATH ".3");
+  if (BINF3_RECOVERY == binf3)
+    return 0;  /* Recovery mode never allows debug. */
+  else if (BINF3_DEVELOPER == binf3)
+    return 1;  /* Developer firmware always allows debug. */
+
+  /* Normal new firmware, older ChromeOS firmware, or non-Chrome firmware.
+   * For all these cases, check /proc/cmdline for cros_debug. */
+  f = fopen(KERNEL_CMDLINE_PATH, "rt");
+  if (f) {
+    if (NULL == fgets(buf, sizeof(buf), f))
+      *buf = 0;
+    fclose(f);
+  }
+  for (t = strtok_r(buf, " ", &saveptr); t; t=strtok_r(NULL, " ", &saveptr)) {
+    if (0 == strcmp(t, "cros_debug"))
+      return 1;
+  }
+
+  /* Normal new firmware or older Chrome OS firmware allows debug if the
+   * dev switch is on. */
+  if (1 == ReadFileBit(ACPI_CHSW_PATH, CHSW_DEV_BOOT))
+    return 1;
+
+  /* All other cases disallow debug. */
+  return 0;
+}
+
 
 /* Read a system property integer.
  *
@@ -512,6 +556,8 @@
     return VbGetRecoveryReason();
   } else if (!strcasecmp(name,"fmap_base")) {
     value = ReadFileInt(ACPI_FMAP_PATH);
+  } else if (!strcasecmp(name,"cros_debug")) {
+    value = VbGetCrosDebug();
   }
 
   return value;