+vlib_pci_device_info_t *
+vlib_pci_get_device_info (vlib_pci_addr_t * addr, clib_error_t ** error)
+{
+ linux_pci_main_t *lpm = &linux_pci_main;
+ clib_error_t *err;
+ vlib_pci_device_info_t *di;
+ u8 *f = 0;
+ u32 tmp;
+ int fd;
+
+ di = clib_mem_alloc (sizeof (vlib_pci_device_info_t));
+ memset (di, 0, sizeof (vlib_pci_device_info_t));
+ di->addr.as_u32 = addr->as_u32;
+
+ u8 *dev_dir_name = format (0, "%s/%U", sysfs_pci_dev_path,
+ format_vlib_pci_addr, addr);
+
+ f = format (0, "%v/config%c", dev_dir_name, 0);
+ fd = open ((char *) f, O_RDWR);
+
+ /* Try read-only access if write fails. */
+ if (fd < 0)
+ fd = open ((char *) f, O_RDONLY);
+
+ if (fd < 0)
+ {
+ err = clib_error_return_unix (0, "open `%s'", f);
+ goto error;
+ }
+
+ /* You can only read more that 64 bytes of config space as root; so we try to
+ read the full space but fall back to just the first 64 bytes. */
+ if (read (fd, &di->config_data, sizeof (di->config_data)) !=
+ sizeof (di->config_data)
+ && read (fd, &di->config0,
+ sizeof (di->config0)) != sizeof (di->config0))
+ {
+ err = clib_error_return_unix (0, "read `%s'", f);
+ close (fd);
+ goto error;
+ }
+
+ {
+ static pci_config_header_t all_ones;
+ if (all_ones.vendor_id == 0)
+ memset (&all_ones, ~0, sizeof (all_ones));
+
+ if (!memcmp (&di->config0.header, &all_ones, sizeof (all_ones)))
+ {
+ err = clib_error_return (0, "invalid PCI config for `%s'", f);
+ close (fd);
+ goto error;
+ }
+ }
+
+ if (di->config0.header.header_type == 0)
+ pci_config_type0_little_to_host (&di->config0);
+ else
+ pci_config_type1_little_to_host (&di->config1);
+
+ di->numa_node = -1;
+ vec_reset_length (f);
+ f = format (f, "%v/numa_node%c", dev_dir_name, 0);
+ err = clib_sysfs_read ((char *) f, "%u", &di->numa_node);
+ if (err)
+ {
+ di->numa_node = -1;
+ clib_error_free (err);
+ }
+
+ vec_reset_length (f);
+ f = format (f, "%v/class%c", dev_dir_name, 0);
+ err = clib_sysfs_read ((char *) f, "0x%x", &tmp);
+ if (err)
+ goto error;
+ di->device_class = tmp >> 8;
+
+ vec_reset_length (f);
+ f = format (f, "%v/vendor%c", dev_dir_name, 0);
+ err = clib_sysfs_read ((char *) f, "0x%x", &tmp);
+ if (err)
+ goto error;
+ di->vendor_id = tmp;
+
+ vec_reset_length (f);
+ f = format (f, "%v/device%c", dev_dir_name, 0);
+ err = clib_sysfs_read ((char *) f, "0x%x", &tmp);
+ if (err)
+ goto error;
+ di->device_id = tmp;
+
+ vec_reset_length (f);
+ f = format (f, "%v/driver%c", dev_dir_name, 0);
+ di->driver_name = clib_sysfs_link_to_name ((char *) f);
+
+ di->iommu_group = -1;
+ if (lpm->vfio_container_fd != -1)
+ {
+ u8 *tmpstr;
+ vec_reset_length (f);
+ f = format (f, "%v/iommu_group%c", dev_dir_name, 0);
+ tmpstr = clib_sysfs_link_to_name ((char *) f);
+ if (tmpstr)
+ {
+ di->iommu_group = atoi ((char *) tmpstr);
+ vec_free (tmpstr);
+ }
+ }
+
+ vec_reset_length (f);
+ f = format (f, "%v/vpd%c", dev_dir_name, 0);
+ fd = open ((char *) f, O_RDONLY);
+ if (fd >= 0)
+ {
+ while (1)
+ {
+ u8 tag[3];
+ u8 *data = 0;
+ int len;
+
+ if (read (fd, &tag, 3) != 3)
+ break;
+
+ if (tag[0] != 0x82 && tag[0] != 0x90 && tag[0] != 0x91)
+ break;
+
+ len = (tag[2] << 8) | tag[1];
+ vec_validate (data, len);
+
+ if (read (fd, data, len) != len)
+ {
+ vec_free (data);
+ break;
+ }
+ if (tag[0] == 0x82)
+ di->product_name = data;
+ else if (tag[0] == 0x90)
+ di->vpd_r = data;
+ else if (tag[0] == 0x91)
+ di->vpd_w = data;
+
+ data = 0;
+ }
+ close (fd);
+ }
+
+ goto done;
+
+error:
+ vlib_pci_free_device_info (di);
+ di = 0;
+
+done:
+ vec_free (f);
+ vec_free (dev_dir_name);
+ if (error)
+ *error = err;
+ else
+ clib_error_free (err);
+ return di;
+}
+