1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
|
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef SELFTESTS_VFIO_LIB_INCLUDE_VFIO_UTIL_H
#define SELFTESTS_VFIO_LIB_INCLUDE_VFIO_UTIL_H
#include <fcntl.h>
#include <string.h>
#include <linux/vfio.h>
#include <linux/list.h>
#include <linux/pci_regs.h>
#include "../../../kselftest.h"
#define VFIO_LOG_AND_EXIT(...) do { \
fprintf(stderr, " " __VA_ARGS__); \
fprintf(stderr, "\n"); \
exit(KSFT_FAIL); \
} while (0)
#define VFIO_ASSERT_OP(_lhs, _rhs, _op, ...) do { \
typeof(_lhs) __lhs = (_lhs); \
typeof(_rhs) __rhs = (_rhs); \
\
if (__lhs _op __rhs) \
break; \
\
fprintf(stderr, "%s:%u: Assertion Failure\n\n", __FILE__, __LINE__); \
fprintf(stderr, " Expression: " #_lhs " " #_op " " #_rhs "\n"); \
fprintf(stderr, " Observed: %#lx %s %#lx\n", \
(u64)__lhs, #_op, (u64)__rhs); \
fprintf(stderr, " [errno: %d - %s]\n", errno, strerror(errno)); \
VFIO_LOG_AND_EXIT(__VA_ARGS__); \
} while (0)
#define VFIO_ASSERT_EQ(_a, _b, ...) VFIO_ASSERT_OP(_a, _b, ==, ##__VA_ARGS__)
#define VFIO_ASSERT_NE(_a, _b, ...) VFIO_ASSERT_OP(_a, _b, !=, ##__VA_ARGS__)
#define VFIO_ASSERT_LT(_a, _b, ...) VFIO_ASSERT_OP(_a, _b, <, ##__VA_ARGS__)
#define VFIO_ASSERT_LE(_a, _b, ...) VFIO_ASSERT_OP(_a, _b, <=, ##__VA_ARGS__)
#define VFIO_ASSERT_GT(_a, _b, ...) VFIO_ASSERT_OP(_a, _b, >, ##__VA_ARGS__)
#define VFIO_ASSERT_GE(_a, _b, ...) VFIO_ASSERT_OP(_a, _b, >=, ##__VA_ARGS__)
#define VFIO_ASSERT_TRUE(_a, ...) VFIO_ASSERT_NE(false, (_a), ##__VA_ARGS__)
#define VFIO_ASSERT_FALSE(_a, ...) VFIO_ASSERT_EQ(false, (_a), ##__VA_ARGS__)
#define VFIO_ASSERT_NULL(_a, ...) VFIO_ASSERT_EQ(NULL, _a, ##__VA_ARGS__)
#define VFIO_ASSERT_NOT_NULL(_a, ...) VFIO_ASSERT_NE(NULL, _a, ##__VA_ARGS__)
#define VFIO_FAIL(_fmt, ...) do { \
fprintf(stderr, "%s:%u: FAIL\n\n", __FILE__, __LINE__); \
VFIO_LOG_AND_EXIT(_fmt, ##__VA_ARGS__); \
} while (0)
struct vfio_iommu_mode {
const char *name;
const char *container_path;
unsigned long iommu_type;
};
/*
* Generator for VFIO selftests fixture variants that replicate across all
* possible IOMMU modes. Tests must define FIXTURE_VARIANT_ADD_IOMMU_MODE()
* which should then use FIXTURE_VARIANT_ADD() to create the variant.
*/
#define FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES(...) \
FIXTURE_VARIANT_ADD_IOMMU_MODE(vfio_type1_iommu, ##__VA_ARGS__); \
FIXTURE_VARIANT_ADD_IOMMU_MODE(vfio_type1v2_iommu, ##__VA_ARGS__); \
FIXTURE_VARIANT_ADD_IOMMU_MODE(iommufd_compat_type1, ##__VA_ARGS__); \
FIXTURE_VARIANT_ADD_IOMMU_MODE(iommufd_compat_type1v2, ##__VA_ARGS__); \
FIXTURE_VARIANT_ADD_IOMMU_MODE(iommufd, ##__VA_ARGS__)
struct vfio_pci_bar {
struct vfio_region_info info;
void *vaddr;
};
typedef u64 iova_t;
#define INVALID_IOVA UINT64_MAX
struct vfio_dma_region {
struct list_head link;
void *vaddr;
iova_t iova;
u64 size;
};
struct vfio_pci_device;
struct vfio_pci_driver_ops {
const char *name;
/**
* @probe() - Check if the driver supports the given device.
*
* Return: 0 on success, non-0 on failure.
*/
int (*probe)(struct vfio_pci_device *device);
/**
* @init() - Initialize the driver for @device.
*
* Must be called after device->driver.region has been initialized.
*/
void (*init)(struct vfio_pci_device *device);
/**
* remove() - Deinitialize the driver for @device.
*/
void (*remove)(struct vfio_pci_device *device);
/**
* memcpy_start() - Kick off @count repeated memcpy operations from
* [@src, @src + @size) to [@dst, @dst + @size).
*
* Guarantees:
* - The device will attempt DMA reads on [src, src + size).
* - The device will attempt DMA writes on [dst, dst + size).
* - The device will not generate any interrupts.
*
* memcpy_start() returns immediately, it does not wait for the
* copies to complete.
*/
void (*memcpy_start)(struct vfio_pci_device *device,
iova_t src, iova_t dst, u64 size, u64 count);
/**
* memcpy_wait() - Wait until the memcpy operations started by
* memcpy_start() have finished.
*
* Guarantees:
* - All in-flight DMAs initiated by memcpy_start() are fully complete
* before memcpy_wait() returns.
*
* Returns non-0 if the driver detects that an error occurred during the
* memcpy, 0 otherwise.
*/
int (*memcpy_wait)(struct vfio_pci_device *device);
/**
* send_msi() - Make the device send the MSI device->driver.msi.
*
* Guarantees:
* - The device will send the MSI once.
*/
void (*send_msi)(struct vfio_pci_device *device);
};
struct vfio_pci_driver {
const struct vfio_pci_driver_ops *ops;
bool initialized;
bool memcpy_in_progress;
/* Region to be used by the driver (e.g. for in-memory descriptors) */
struct vfio_dma_region region;
/* The maximum size that can be passed to memcpy_start(). */
u64 max_memcpy_size;
/* The maximum count that can be passed to memcpy_start(). */
u64 max_memcpy_count;
/* The MSI vector the device will signal in ops->send_msi(). */
int msi;
};
struct vfio_pci_device {
int fd;
const struct vfio_iommu_mode *iommu_mode;
int group_fd;
int container_fd;
int iommufd;
u32 ioas_id;
struct vfio_device_info info;
struct vfio_region_info config_space;
struct vfio_pci_bar bars[PCI_STD_NUM_BARS];
struct vfio_irq_info msi_info;
struct vfio_irq_info msix_info;
struct list_head dma_regions;
/* eventfds for MSI and MSI-x interrupts */
int msi_eventfds[PCI_MSIX_FLAGS_QSIZE + 1];
struct vfio_pci_driver driver;
};
/*
* Return the BDF string of the device that the test should use.
*
* If a BDF string is provided by the user on the command line (as the last
* element of argv[]), then this function will return that and decrement argc
* by 1.
*
* Otherwise this function will attempt to use the environment variable
* $VFIO_SELFTESTS_BDF.
*
* If BDF cannot be determined then the test will exit with KSFT_SKIP.
*/
const char *vfio_selftests_get_bdf(int *argc, char *argv[]);
const char *vfio_pci_get_cdev_path(const char *bdf);
extern const char *default_iommu_mode;
struct vfio_pci_device *vfio_pci_device_init(const char *bdf, const char *iommu_mode);
void vfio_pci_device_cleanup(struct vfio_pci_device *device);
void vfio_pci_device_reset(struct vfio_pci_device *device);
void vfio_pci_dma_map(struct vfio_pci_device *device,
struct vfio_dma_region *region);
void vfio_pci_dma_unmap(struct vfio_pci_device *device,
struct vfio_dma_region *region);
void vfio_pci_config_access(struct vfio_pci_device *device, bool write,
size_t config, size_t size, void *data);
#define vfio_pci_config_read(_device, _offset, _type) ({ \
_type __data; \
vfio_pci_config_access((_device), false, _offset, sizeof(__data), &__data); \
__data; \
})
#define vfio_pci_config_readb(_d, _o) vfio_pci_config_read(_d, _o, u8)
#define vfio_pci_config_readw(_d, _o) vfio_pci_config_read(_d, _o, u16)
#define vfio_pci_config_readl(_d, _o) vfio_pci_config_read(_d, _o, u32)
#define vfio_pci_config_write(_device, _offset, _value, _type) do { \
_type __data = (_value); \
vfio_pci_config_access((_device), true, _offset, sizeof(_type), &__data); \
} while (0)
#define vfio_pci_config_writeb(_d, _o, _v) vfio_pci_config_write(_d, _o, _v, u8)
#define vfio_pci_config_writew(_d, _o, _v) vfio_pci_config_write(_d, _o, _v, u16)
#define vfio_pci_config_writel(_d, _o, _v) vfio_pci_config_write(_d, _o, _v, u32)
void vfio_pci_irq_enable(struct vfio_pci_device *device, u32 index,
u32 vector, int count);
void vfio_pci_irq_disable(struct vfio_pci_device *device, u32 index);
void vfio_pci_irq_trigger(struct vfio_pci_device *device, u32 index, u32 vector);
static inline void fcntl_set_nonblock(int fd)
{
int r;
r = fcntl(fd, F_GETFL, 0);
VFIO_ASSERT_NE(r, -1, "F_GETFL failed for fd %d\n", fd);
r = fcntl(fd, F_SETFL, r | O_NONBLOCK);
VFIO_ASSERT_NE(r, -1, "F_SETFL O_NONBLOCK failed for fd %d\n", fd);
}
static inline void vfio_pci_msi_enable(struct vfio_pci_device *device,
u32 vector, int count)
{
vfio_pci_irq_enable(device, VFIO_PCI_MSI_IRQ_INDEX, vector, count);
}
static inline void vfio_pci_msi_disable(struct vfio_pci_device *device)
{
vfio_pci_irq_disable(device, VFIO_PCI_MSI_IRQ_INDEX);
}
static inline void vfio_pci_msix_enable(struct vfio_pci_device *device,
u32 vector, int count)
{
vfio_pci_irq_enable(device, VFIO_PCI_MSIX_IRQ_INDEX, vector, count);
}
static inline void vfio_pci_msix_disable(struct vfio_pci_device *device)
{
vfio_pci_irq_disable(device, VFIO_PCI_MSIX_IRQ_INDEX);
}
iova_t __to_iova(struct vfio_pci_device *device, void *vaddr);
iova_t to_iova(struct vfio_pci_device *device, void *vaddr);
static inline bool vfio_pci_device_match(struct vfio_pci_device *device,
u16 vendor_id, u16 device_id)
{
return (vendor_id == vfio_pci_config_readw(device, PCI_VENDOR_ID)) &&
(device_id == vfio_pci_config_readw(device, PCI_DEVICE_ID));
}
void vfio_pci_driver_probe(struct vfio_pci_device *device);
void vfio_pci_driver_init(struct vfio_pci_device *device);
void vfio_pci_driver_remove(struct vfio_pci_device *device);
int vfio_pci_driver_memcpy(struct vfio_pci_device *device,
iova_t src, iova_t dst, u64 size);
void vfio_pci_driver_memcpy_start(struct vfio_pci_device *device,
iova_t src, iova_t dst, u64 size,
u64 count);
int vfio_pci_driver_memcpy_wait(struct vfio_pci_device *device);
void vfio_pci_driver_send_msi(struct vfio_pci_device *device);
#endif /* SELFTESTS_VFIO_LIB_INCLUDE_VFIO_UTIL_H */
|