xref: /linux/tools/testing/selftests/vfio/lib/sysfs.c (revision 3dc7c001169d112b3e514cacff6c93091c57af9a)
1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <fcntl.h>
3 #include <unistd.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <linux/limits.h>
7 
8 #include <libvfio.h>
9 
10 #define readlink_safe(_path, _buf) ({					\
11 	int __ret;							\
12 									\
13 	_Static_assert(!__builtin_types_compatible_p(			\
14 		__typeof__(_buf), char *),				\
15 	    "readlink_safe: _buf must be an array, not a pointer");	\
16 									\
17 	__ret = readlink(_path, _buf, sizeof(_buf) - 1);		\
18 	if (__ret != -1)						\
19 		_buf[__ret] = 0;					\
20 	__ret;								\
21 })
22 
23 static void readlink_base(const char *path, const char *data_fmt, void *out_data)
24 {
25 	char rl_path[PATH_MAX];
26 	int ret;
27 
28 	ret = readlink_safe(path, rl_path);
29 	VFIO_ASSERT_NE(ret, -1);
30 
31 	ret = sscanf(basename(rl_path), data_fmt, out_data);
32 	VFIO_ASSERT_EQ(ret, 1);
33 }
34 
35 static int sysfs_val_get_int(const char *component, const char *name,
36 			     const char *file)
37 {
38 	char path[PATH_MAX];
39 	char buf[32];
40 	int ret;
41 	int fd;
42 
43 	snprintf_assert(path, PATH_MAX, "/sys/bus/pci/%s/%s/%s", component, name, file);
44 	fd = open(path, O_RDONLY);
45 	if (fd < 0)
46 		return fd;
47 
48 	VFIO_ASSERT_GT(read(fd, buf, ARRAY_SIZE(buf)), 0);
49 	VFIO_ASSERT_EQ(close(fd), 0);
50 
51 	errno = 0;
52 	ret = strtol(buf, NULL, 0);
53 	VFIO_ASSERT_EQ(errno, 0, "sysfs path \"%s\" is not an integer: \"%s\"\n", path, buf);
54 
55 	return ret;
56 }
57 
58 static void sysfs_val_set(const char *component, const char *name,
59 			  const char *file, const char *val)
60 {
61 	char path[PATH_MAX];
62 	int fd;
63 
64 	snprintf_assert(path, PATH_MAX, "/sys/bus/pci/%s/%s/%s", component, name, file);
65 	VFIO_ASSERT_GT(fd = open(path, O_WRONLY), 0);
66 
67 	VFIO_ASSERT_EQ(write(fd, val, strlen(val)), strlen(val));
68 	VFIO_ASSERT_EQ(close(fd), 0);
69 }
70 
71 static int sysfs_device_val_get(const char *bdf, const char *file)
72 {
73 	return sysfs_val_get_int("devices", bdf, file);
74 }
75 
76 static void sysfs_device_val_set(const char *bdf, const char *file, const char *val)
77 {
78 	sysfs_val_set("devices", bdf, file, val);
79 }
80 
81 static void sysfs_device_val_set_int(const char *bdf, const char *file, int val)
82 {
83 	char val_str[32];
84 
85 	snprintf_assert(val_str, sizeof(val_str), "%d", val);
86 	sysfs_device_val_set(bdf, file, val_str);
87 }
88 
89 int sysfs_sriov_totalvfs_get(const char *bdf)
90 {
91 	return sysfs_device_val_get(bdf, "sriov_totalvfs");
92 }
93 
94 int sysfs_sriov_numvfs_get(const char *bdf)
95 {
96 	return sysfs_device_val_get(bdf, "sriov_numvfs");
97 }
98 
99 void sysfs_sriov_numvfs_set(const char *bdf, int numvfs)
100 {
101 	sysfs_device_val_set_int(bdf, "sriov_numvfs", numvfs);
102 }
103 
104 char *sysfs_sriov_vf_bdf_get(const char *pf_bdf, int i)
105 {
106 	char path[PATH_MAX];
107 	char *out_vf_bdf;
108 
109 	/* Fit "0000:00:00.0" */
110 	out_vf_bdf = calloc(16, sizeof(char));
111 	VFIO_ASSERT_NOT_NULL(out_vf_bdf);
112 
113 	snprintf_assert(path, PATH_MAX, "/sys/bus/pci/devices/%s/virtfn%d", pf_bdf, i);
114 	readlink_base(path, "%s", out_vf_bdf);
115 
116 	return out_vf_bdf;
117 }
118 
119 int sysfs_iommu_group_get(const char *bdf)
120 {
121 	char path[PATH_MAX];
122 	int group;
123 
124 	snprintf_assert(path, PATH_MAX, "/sys/bus/pci/devices/%s/iommu_group", bdf);
125 	readlink_base(path, "%d", &group);
126 
127 	return group;
128 }
129 
130 char *sysfs_driver_get(const char *bdf)
131 {
132 	char driver_path[PATH_MAX];
133 	char path[PATH_MAX];
134 	char *out_driver;
135 	int ret;
136 
137 	snprintf_assert(path, PATH_MAX, "/sys/bus/pci/devices/%s/driver", bdf);
138 	ret = readlink_safe(path, driver_path);
139 	if (ret == -1) {
140 		if (errno == ENOENT)
141 			return NULL;
142 
143 		VFIO_FAIL("Failed to read %s\n", path);
144 	}
145 
146 	out_driver = strdup(basename(driver_path));
147 	VFIO_ASSERT_NOT_NULL(out_driver);
148 
149 	return out_driver;
150 }
151