Add in a platform_family value to crossystem

This implements a platform_family value within the crossystem utility,
as the platform (particularly for ARM) is not easily accessable elsewhere at
runtime.

For the ARM side this contains a table which is used to determine the platform
family based on the /proc/device-tree/compatible entry. Similarly on x86 the
table is used to check against PCI entries. Additional entries can be made
as new platform families emerge.

BUG=chromium-os:24669
TEST=Manual, verified that crossystem runs properly and returns a valid
platform_family value on various platforms (mario, alex, z600, x220, etc).

Change-Id: Id0e973902d27ead471c1243bcc6c3292acc8479d
Reviewed-on: https://gerrit.chromium.org/gerrit/13520
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
Commit-Ready: Olof Johansson <olofj@chromium.org>
Reviewed-by: Olof Johansson <olofj@chromium.org>
Tested-by: Olof Johansson <olofj@chromium.org>
diff --git a/host/arch/arm/lib/crossystem_arch.c b/host/arch/arm/lib/crossystem_arch.c
index 7e6095e..aa740b0 100644
--- a/host/arch/arm/lib/crossystem_arch.c
+++ b/host/arch/arm/lib/crossystem_arch.c
@@ -32,6 +32,21 @@
 #define SECTOR_SIZE 512
 #define MAX_NMMCBLK 9
 
+typedef struct PlatformFamily {
+  const char* compatible_string; /* Last string in FDT compatible entry */
+  const char* platform_string;   /* String to return */
+} PlatformFamily;
+
+/* Array of platform family names, terminated with a NULL entry */
+const PlatformFamily platform_family_array[] = {
+  {"nvidia,tegra250", "Tegra2"},
+  {"nvidia,tegra20", "Tegra2"},
+  {"ti,omap4", "OMAP4"},
+  {"ti,omap3", "OMAP3"},
+  /* Terminate with NULL entry */
+  {NULL, NULL}
+};
+
 static int FindEmmcDev(void) {
   int mmcblk;
   char filename[FNAME_SIZE];
@@ -137,6 +152,38 @@
   return (char *)str;
 }
 
+static char * ReadFdtPlatformFamily(void) {
+  char *compat = NULL;
+  char *s;
+  FILE *file;
+  const PlatformFamily* p;
+  size_t size = 0;
+  int slen;
+
+  /* TODO: Allow this to be a more direct path by modifying ReadFdtBlock */
+  ReadFdtBlock("../../compatible", &compat, &size);
+
+  if (size > 0)
+    compat[size-1] = 0;
+
+  /* Check each null separated string in compatible against the family array */
+  s = compat;
+  while ((s-compat) < size) {
+    slen = strlen(s);
+    for (p = platform_family_array; p->compatible_string; p++) {
+      if (!strcmp(s, p->compatible_string)) {
+        free(compat);
+        return strdup(p->platform_string);
+      }
+    }
+    s += slen + 1;
+  }
+
+  /* No recognized 'compatible' entry found */
+  free(compat);
+  return NULL;
+}
+
 static int VbGetGpioStatus(unsigned gpio_number) {
   char const *gpio_name_format = "/sys/class/gpio/gpio%d/value";
   char gpio_name[FNAME_SIZE];
@@ -330,6 +377,9 @@
   if (prop)
     str = ReadFdtString(prop);
 
+  if (!strcasecmp(name, "platform_family"))
+    str = ReadFdtPlatformFamily();
+
   if (str) {
       rv = StrCopy(dest, str, size);
       free(str);
diff --git a/host/arch/x86/lib/crossystem_arch.c b/host/arch/x86/lib/crossystem_arch.c
index 1042c66..6ef9063 100644
--- a/host/arch/x86/lib/crossystem_arch.c
+++ b/host/arch/x86/lib/crossystem_arch.c
@@ -87,6 +87,27 @@
 /* Filename for legacy firmware update tries */
 #define NEED_FWUPDATE_PATH "/mnt/stateful_partition/.need_firmware_update"
 
+/* Filenames for PCI Vendor and Device IDs */
+#define PCI_VENDOR_ID_PATH "/sys/bus/pci/devices/0000:00:00.0/vendor"
+#define PCI_DEVICE_ID_PATH "/sys/bus/pci/devices/0000:00:00.0/device"
+
+typedef struct PlatformFamily {
+  unsigned int vendor;          /* Vendor id value */
+  unsigned int device;          /* Device id value */
+  const char* platform_string; /* String to return */
+} PlatformFamily;
+
+/* Array of platform family names, terminated with a NULL entry */
+const PlatformFamily platform_family_array[] = {
+  {0x8086, 0xA010, "PineTrail"},
+  {0x8086, 0x3406, "Westmere"},
+  {0x8086, 0x0104, "SandyBridge"}, /* mobile */
+  {0x8086, 0x0100, "SandyBridge"}, /* desktop */
+  {0x8086, 0x0154, "IvyBridge"},   /* mobile */
+  {0x8086, 0x0150, "IvyBridge"},   /* desktop */
+  /* Terminate with NULL entry */
+  {NULL, NULL, NULL}
+};
 
 static void VbFixCmosChecksum(FILE* file) {
   int fd = fileno(file);
@@ -444,6 +465,39 @@
   }
 }
 
+/* Determine the platform family and return it in the dest string.
+ * This uses the PCI Bus 0, Device 0, Function 0 vendor and device id values
+ * taken from sysfs to determine the platform family. This assumes there will
+ * be a unique pair of values here for any given platform.
+ */
+static char* ReadPlatformFamilyString(char* dest, int size) {
+  FILE* f;
+  const PlatformFamily* p;
+  unsigned int v = 0xFFFF;
+  unsigned int d = 0xFFFF;
+
+  f = fopen(PCI_VENDOR_ID_PATH, "rt");
+  if (!f)
+    return NULL;
+  if(fscanf(f, "0x%4x", &v) != 1)
+    return NULL;
+  fclose(f);
+
+  f = fopen(PCI_DEVICE_ID_PATH, "rt");
+  if (!f)
+    return NULL;
+  if(fscanf(f, "0x%4x", &d) != 1)
+    return NULL;
+  fclose(f);
+
+  for (p = platform_family_array; p->vendor; p++) {
+    if((v == p->vendor) && (d == p->device))
+      return StrCopy(dest, p->platform_string, size);
+  }
+
+  /* No recognized platform family was found */
+  return NULL;
+}
 
 /* Physical GPIO number <N> may be accessed through /sys/class/gpio/gpio<M>/,
  * but <N> and <M> may differ by some offset <O>. To determine that constant,
@@ -642,6 +696,8 @@
       default:
         return NULL;
     }
+  } else if (!strcasecmp(name,"platform_family")) {
+    return ReadPlatformFamilyString(dest, size);
   }
 
   return NULL;
diff --git a/utility/crossystem_main.c b/utility/crossystem_main.c
index b9c540e..0a2e6de 100644
--- a/utility/crossystem_main.c
+++ b/utility/crossystem_main.c
@@ -56,6 +56,7 @@
   {"mainfw_act", IS_STRING, "Active main firmware"},
   {"mainfw_type", IS_STRING, "Active main firmware type"},
   {"nvram_cleared", CAN_WRITE, "Have NV settings been lost?  Write 0 to clear"},
+  {"platform_family", IS_STRING, "Platform family type"},
   {"recovery_reason", 0, "Recovery mode reason for current boot"},
   {"recovery_request", CAN_WRITE, "Recovery mode request (writable)"},
   {"recoverysw_boot", 0, "Recovery switch position at boot"},