--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <linux/limits.h>
+
+#include <libvfio.h>
+
+#define readlink_safe(_path, _buf) ({ \
+ int __ret; \
+ \
+ _Static_assert(!__builtin_types_compatible_p( \
+ __typeof__(_buf), char *), \
+ "readlink_safe: _buf must be an array, not a pointer"); \
+ \
+ __ret = readlink(_path, _buf, sizeof(_buf) - 1); \
+ if (__ret != -1) \
+ _buf[__ret] = 0; \
+ __ret; \
+})
+
+static void readlink_base(const char *path, const char *data_fmt, void *out_data)
+{
+ char rl_path[PATH_MAX];
+ int ret;
+
+ ret = readlink_safe(path, rl_path);
+ VFIO_ASSERT_NE(ret, -1);
+
+ ret = sscanf(basename(rl_path), data_fmt, out_data);
+ VFIO_ASSERT_EQ(ret, 1);
+}
+
+static int sysfs_val_get_int(const char *component, const char *name,
+ const char *file)
+{
+ char path[PATH_MAX];
+ char buf[32];
+ int ret;
+ int fd;
+
+ snprintf_assert(path, PATH_MAX, "/sys/bus/pci/%s/%s/%s", component, name, file);
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ VFIO_ASSERT_GT(read(fd, buf, ARRAY_SIZE(buf)), 0);
+ VFIO_ASSERT_EQ(close(fd), 0);
+
+ errno = 0;
+ ret = strtol(buf, NULL, 0);
+ VFIO_ASSERT_EQ(errno, 0, "sysfs path \"%s\" is not an integer: \"%s\"\n", path, buf);
+
+ return ret;
+}
+
+static void sysfs_val_set(const char *component, const char *name,
+ const char *file, const char *val)
+{
+ char path[PATH_MAX];
+ int fd;
+
+ snprintf_assert(path, PATH_MAX, "/sys/bus/pci/%s/%s/%s", component, name, file);
+ VFIO_ASSERT_GT(fd = open(path, O_WRONLY), 0);
+
+ VFIO_ASSERT_EQ(write(fd, val, strlen(val)), strlen(val));
+ VFIO_ASSERT_EQ(close(fd), 0);
+}
+
+static int sysfs_device_val_get(const char *bdf, const char *file)
+{
+ return sysfs_val_get_int("devices", bdf, file);
+}
+
+static void sysfs_device_val_set(const char *bdf, const char *file, const char *val)
+{
+ sysfs_val_set("devices", bdf, file, val);
+}
+
+static void sysfs_device_val_set_int(const char *bdf, const char *file, int val)
+{
+ char val_str[32];
+
+ snprintf_assert(val_str, sizeof(val_str), "%d", val);
+ sysfs_device_val_set(bdf, file, val_str);
+}
+
+int sysfs_sriov_totalvfs_get(const char *bdf)
+{
+ return sysfs_device_val_get(bdf, "sriov_totalvfs");
+}
+
+int sysfs_sriov_numvfs_get(const char *bdf)
+{
+ return sysfs_device_val_get(bdf, "sriov_numvfs");
+}
+
+void sysfs_sriov_numvfs_set(const char *bdf, int numvfs)
+{
+ sysfs_device_val_set_int(bdf, "sriov_numvfs", numvfs);
+}
+
+char *sysfs_sriov_vf_bdf_get(const char *pf_bdf, int i)
+{
+ char path[PATH_MAX];
+ char *out_vf_bdf;
+
+ /* Fit "0000:00:00.0" */
+ out_vf_bdf = calloc(16, sizeof(char));
+ VFIO_ASSERT_NOT_NULL(out_vf_bdf);
+
+ snprintf_assert(path, PATH_MAX, "/sys/bus/pci/devices/%s/virtfn%d", pf_bdf, i);
+ readlink_base(path, "%s", out_vf_bdf);
+
+ return out_vf_bdf;
+}
+
+int sysfs_iommu_group_get(const char *bdf)
+{
+ char path[PATH_MAX];
+ int group;
+
+ snprintf_assert(path, PATH_MAX, "/sys/bus/pci/devices/%s/iommu_group", bdf);
+ readlink_base(path, "%d", &group);
+
+ return group;
+}
+
+char *sysfs_driver_get(const char *bdf)
+{
+ char driver_path[PATH_MAX];
+ char path[PATH_MAX];
+ char *out_driver;
+ int ret;
+
+ snprintf_assert(path, PATH_MAX, "/sys/bus/pci/devices/%s/driver", bdf);
+ ret = readlink_safe(path, driver_path);
+ if (ret == -1) {
+ if (errno == ENOENT)
+ return NULL;
+
+ VFIO_FAIL("Failed to read %s\n", path);
+ }
+
+ out_driver = strdup(basename(driver_path));
+ VFIO_ASSERT_NOT_NULL(out_driver);
+
+ return out_driver;
+}
#include "kselftest.h"
#include <libvfio.h>
-#define PCI_SYSFS_PATH "/sys/bus/pci/devices"
-
static void vfio_pci_irq_set(struct vfio_pci_device *device,
u32 index, u32 vector, u32 count, int *fds)
{
ioctl_assert(device->fd, VFIO_DEVICE_RESET, NULL);
}
-static unsigned int vfio_pci_get_group_from_dev(const char *bdf)
-{
- char dev_iommu_group_path[PATH_MAX] = {0};
- char sysfs_path[PATH_MAX] = {0};
- unsigned int group;
- int ret;
-
- snprintf_assert(sysfs_path, PATH_MAX, "%s/%s/iommu_group", PCI_SYSFS_PATH, bdf);
-
- ret = readlink(sysfs_path, dev_iommu_group_path, sizeof(dev_iommu_group_path));
- VFIO_ASSERT_NE(ret, -1, "Failed to get the IOMMU group for device: %s\n", bdf);
-
- ret = sscanf(basename(dev_iommu_group_path), "%u", &group);
- VFIO_ASSERT_EQ(ret, 1, "Failed to get the IOMMU group for device: %s\n", bdf);
-
- return group;
-}
-
static void vfio_pci_group_setup(struct vfio_pci_device *device, const char *bdf)
{
struct vfio_group_status group_status = {
char group_path[32];
int group;
- group = vfio_pci_get_group_from_dev(bdf);
+ group = sysfs_iommu_group_get(bdf);
snprintf_assert(group_path, sizeof(group_path), "/dev/vfio/%d", group);
device->group_fd = open(group_path, O_RDWR);