root/tools/testing/selftests/vfio/lib/sysfs.c
// 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;
}