+vlib_pci_device_info_t *
+vlib_pci_get_device_info (vlib_main_t * vm, vlib_pci_addr_t * addr,
+ clib_error_t ** error)
+{
+ clib_error_t *err;
+ vlib_pci_device_info_t *di;
+ u8 *f = 0;
+ u32 tmp;
+ int fd;
+ u8 *tmpstr;
+ clib_bitmap_t *bmp = 0;
+
+ di = clib_mem_alloc (sizeof (vlib_pci_device_info_t));
+ clib_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->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)
+ clib_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, "%d", &di->numa_node);
+ if (err)
+ {
+ di->numa_node = -1;
+ clib_error_free (err);
+ }
+ if (di->numa_node == -1)
+ {
+ /* if '/sys/bus/pci/devices/<device id>/numa_node' returns -1 and
+ it is a SMP system, set numa_node to 0. */
+ if ((err = clib_sysfs_read ("/sys/devices/system/node/online", "%U",
+ unformat_bitmap_list, &bmp)))
+ clib_error_free (err);
+ if (clib_bitmap_count_set_bits (bmp) == 1)
+ di->numa_node = 0;
+ }
+
+ 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);
+ if (!di->driver_name)
+ di->driver_name = format (0, "<NONE>%c", 0);
+
+ di->iommu_group = -1;
+ 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/iommu_group/name%c", dev_dir_name, 0);
+ err = clib_sysfs_read ((char *) f, "%s", &tmpstr);
+ if (err == 0)
+ {
+ if (strncmp ((char *) tmpstr, "vfio-noiommu", 12) == 0)
+ di->flags |= VLIB_PCI_DEVICE_INFO_F_NOIOMMU;
+ vec_free (tmpstr);
+ }
+ else
+ clib_error_free (err);
+
+ close (fd);
+
+ 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;
+ uword 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 (bmp);
+ vec_free (f);
+ vec_free (dev_dir_name);
+ if (error)
+ *error = err;
+ else
+ clib_error_free (err);
+ return di;
+}
+
+static int
+directory_exists (char *path)
+{
+ struct stat s = { 0 };
+ if (stat (path, &s) == -1)
+ return 0;
+
+ return S_ISDIR (s.st_mode);
+}
+