vlib: Add support for PCI VPD parsing

This allows better detection of various NICs, including the ones
where different sub-models are sharing same PCI VID:PID.

This change also extends output of "show pci" debug cli command.

Change-Id: I06f78e8376307e88b0252a99c203c696437a6e35
Signed-off-by: Damjan Marion <damarion@cisco.com>
diff --git a/vlib/vlib/pci/pci.c b/vlib/vlib/pci/pci.c
index 07d89fd..7100064 100644
--- a/vlib/vlib/pci/pci.c
+++ b/vlib/vlib/pci/pci.c
@@ -52,6 +52,19 @@
 
 vlib_pci_main_t pci_main;
 
+vlib_pci_device_t *
+vlib_get_pci_device (vlib_pci_addr_t * addr)
+{
+  vlib_pci_main_t *pm = &pci_main;
+  uword *p;
+  p = hash_get (pm->pci_dev_index_by_pci_addr, addr->as_u32);
+
+  if (p == 0)
+    return 0;
+
+  return vec_elt_at_index (pm->pci_devs, p[0]);
+}
+
 static clib_error_t *
 show_pci_fn (vlib_main_t * vm,
 	     unformat_input_t * input, vlib_cli_command_t * cmd)
@@ -70,9 +83,9 @@
 				  format_unformat_error, input);
     }
 
-  vlib_cli_output (vm, "%-13s%-7s%-12s%-15s%-20s%-40s",
-		   "Address", "Socket", "VID:PID", "Link Speed", "Driver",
-		   "Product Name");
+  vlib_cli_output (vm, "%-13s%-5s%-12s%-13s%-16s%-32s%s",
+		   "Address", "Sock", "VID:PID", "Link Speed", "Driver",
+		   "Product Name", "Vital Product Data");
 
   /* *INDENT-OFF* */
   pool_foreach (d, pm->pci_devs, ({
@@ -85,12 +98,13 @@
     if (d->numa_node >= 0)
       s = format (s, "  %d", d->numa_node);
 
-    vlib_cli_output (vm, "%-13U%-7v%04x:%04x   %-15U%-20s%-40v",
+    vlib_cli_output (vm, "%-13U%-5v%04x:%04x   %-13U%-16s%-32v%U",
 		     format_vlib_pci_addr, &d->bus_address, s,
 		     d->vendor_id, d->device_id,
 		     format_vlib_pci_link_speed, d,
 		     d->driver_name ? (char *) d->driver_name : "",
-		     d->product_name);
+		     d->product_name,
+		     format_vlib_pci_vpd, d->vpd_r, 0);
   }));
 /* *INDENT-ON* */
 
@@ -152,6 +166,78 @@
   return format (s, "unknown");
 }
 
+u8 *
+format_vlib_pci_vpd (u8 * s, va_list * args)
+{
+  u8 *data = va_arg (*args, u8 *);
+  u8 *id = va_arg (*args, u8 *);
+  uword indent = format_get_indent (s);
+  char *string_types[] = { "PN", "EC", "SN", "MN", 0 };
+  uword p = 0;
+  int first_line = 1;
+
+  if (vec_len (data) < 3)
+    return s;
+
+  while (p + 3 < vec_len (data))
+    {
+
+      if (data[p] == 0 && data[p + 1] == 0)
+	return s;
+
+      if (p + data[p + 2] > vec_len (data))
+	return s;
+
+      if (id == 0)
+	{
+	  int is_string = 0;
+	  char **c = string_types;
+
+	  while (c[0])
+	    {
+	      if (*(u16 *) & data[p] == *(u16 *) c[0])
+		is_string = 1;
+	      c++;
+	    }
+
+	  if (data[p + 2])
+	    {
+	      if (!first_line)
+		s = format (s, "\n%U", format_white_space, indent);
+	      else
+		{
+		  first_line = 0;
+		  s = format (s, " ");
+		}
+
+	      s = format (s, "%c%c: ", data[p], data[p + 1]);
+	      if (is_string)
+		vec_add (s, data + p + 3, data[p + 2]);
+	      else
+		{
+		  int i;
+		  const int max_bytes = 8;
+		  s = format (s, "0x");
+		  for (i = 0; i < clib_min (data[p + 2], max_bytes); i++)
+		    s = format (s, " %02x", data[p + 3 + i]);
+
+		  if (data[p + 2] > max_bytes)
+		    s = format (s, " ...");
+		}
+	    }
+	}
+      else if (*(u16 *) & data[p] == *(u16 *) id)
+	{
+	  vec_add (s, data + p + 3, data[p + 2]);
+	  return s;
+	}
+
+      p += 3 + data[p + 2];
+    }
+
+  return s;
+}
+
 
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (show_pci_command, static) = {
diff --git a/vlib/vlib/pci/pci.h b/vlib/vlib/pci/pci.h
index 3b83b89..811a6ff 100644
--- a/vlib/vlib/pci/pci.h
+++ b/vlib/vlib/pci/pci.h
@@ -230,6 +230,7 @@
 					   u32 resource, u8 * addr,
 					   void **result);
 
+vlib_pci_device_t *vlib_get_pci_device (vlib_pci_addr_t * addr);
 /* Free's device. */
 void vlib_pci_free_device (vlib_pci_device_t * dev);
 
@@ -237,6 +238,7 @@
 format_function_t format_vlib_pci_addr;
 format_function_t format_vlib_pci_handle;
 format_function_t format_vlib_pci_link_speed;
+format_function_t format_vlib_pci_vpd;
 
 #endif /* included_vlib_pci_h */