| // 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; |
| } |