xref: /linux/tools/testing/selftests/vfio/vfio_pci_driver_test.c (revision 4f38da1f027ea2c9f01bb71daa7a299c191b6940)
1fded8da4SDavid Matlack // SPDX-License-Identifier: GPL-2.0-only
2fded8da4SDavid Matlack #include <sys/ioctl.h>
3fded8da4SDavid Matlack #include <sys/mman.h>
4fded8da4SDavid Matlack 
5fded8da4SDavid Matlack #include <linux/sizes.h>
6fded8da4SDavid Matlack #include <linux/vfio.h>
7fded8da4SDavid Matlack 
8fded8da4SDavid Matlack #include <vfio_util.h>
9fded8da4SDavid Matlack 
10fded8da4SDavid Matlack #include "../kselftest_harness.h"
11fded8da4SDavid Matlack 
12fded8da4SDavid Matlack static const char *device_bdf;
13fded8da4SDavid Matlack 
14fded8da4SDavid Matlack #define ASSERT_NO_MSI(_eventfd) do {			\
15fded8da4SDavid Matlack 	u64 __value;					\
16fded8da4SDavid Matlack 							\
17fded8da4SDavid Matlack 	ASSERT_EQ(-1, read(_eventfd, &__value, 8));	\
18fded8da4SDavid Matlack 	ASSERT_EQ(EAGAIN, errno);			\
19fded8da4SDavid Matlack } while (0)
20fded8da4SDavid Matlack 
21fded8da4SDavid Matlack static void region_setup(struct vfio_pci_device *device,
22fded8da4SDavid Matlack 			 struct vfio_dma_region *region, u64 size)
23fded8da4SDavid Matlack {
24fded8da4SDavid Matlack 	const int flags = MAP_SHARED | MAP_ANONYMOUS;
25fded8da4SDavid Matlack 	const int prot = PROT_READ | PROT_WRITE;
26fded8da4SDavid Matlack 	void *vaddr;
27fded8da4SDavid Matlack 
28fded8da4SDavid Matlack 	vaddr = mmap(NULL, size, prot, flags, -1, 0);
29fded8da4SDavid Matlack 	VFIO_ASSERT_NE(vaddr, MAP_FAILED);
30fded8da4SDavid Matlack 
31fded8da4SDavid Matlack 	region->vaddr = vaddr;
32fded8da4SDavid Matlack 	region->iova = (u64)vaddr;
33fded8da4SDavid Matlack 	region->size = size;
34fded8da4SDavid Matlack 
35fded8da4SDavid Matlack 	vfio_pci_dma_map(device, region);
36fded8da4SDavid Matlack }
37fded8da4SDavid Matlack 
38fded8da4SDavid Matlack static void region_teardown(struct vfio_pci_device *device,
39fded8da4SDavid Matlack 			    struct vfio_dma_region *region)
40fded8da4SDavid Matlack {
41fded8da4SDavid Matlack 	vfio_pci_dma_unmap(device, region);
42fded8da4SDavid Matlack 	VFIO_ASSERT_EQ(munmap(region->vaddr, region->size), 0);
43fded8da4SDavid Matlack }
44fded8da4SDavid Matlack 
45fded8da4SDavid Matlack FIXTURE(vfio_pci_driver_test) {
46fded8da4SDavid Matlack 	struct vfio_pci_device *device;
47fded8da4SDavid Matlack 	struct vfio_dma_region memcpy_region;
48fded8da4SDavid Matlack 	void *vaddr;
49fded8da4SDavid Matlack 	int msi_fd;
50fded8da4SDavid Matlack 
51fded8da4SDavid Matlack 	u64 size;
52fded8da4SDavid Matlack 	void *src;
53fded8da4SDavid Matlack 	void *dst;
54fded8da4SDavid Matlack 	iova_t src_iova;
55fded8da4SDavid Matlack 	iova_t dst_iova;
56fded8da4SDavid Matlack 	iova_t unmapped_iova;
57fded8da4SDavid Matlack };
58fded8da4SDavid Matlack 
59*892aff14SDavid Matlack FIXTURE_VARIANT(vfio_pci_driver_test) {
60*892aff14SDavid Matlack 	const char *iommu_mode;
61*892aff14SDavid Matlack };
62*892aff14SDavid Matlack 
63*892aff14SDavid Matlack #define FIXTURE_VARIANT_ADD_IOMMU_MODE(_iommu_mode)		\
64*892aff14SDavid Matlack FIXTURE_VARIANT_ADD(vfio_pci_driver_test, _iommu_mode) {	\
65*892aff14SDavid Matlack 	.iommu_mode = #_iommu_mode,				\
66*892aff14SDavid Matlack }
67*892aff14SDavid Matlack 
68*892aff14SDavid Matlack FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES();
69*892aff14SDavid Matlack 
70fded8da4SDavid Matlack FIXTURE_SETUP(vfio_pci_driver_test)
71fded8da4SDavid Matlack {
72fded8da4SDavid Matlack 	struct vfio_pci_driver *driver;
73fded8da4SDavid Matlack 
74*892aff14SDavid Matlack 	self->device = vfio_pci_device_init(device_bdf, variant->iommu_mode);
75fded8da4SDavid Matlack 
76fded8da4SDavid Matlack 	driver = &self->device->driver;
77fded8da4SDavid Matlack 
78fded8da4SDavid Matlack 	region_setup(self->device, &self->memcpy_region, SZ_1G);
79fded8da4SDavid Matlack 	region_setup(self->device, &driver->region, SZ_2M);
80fded8da4SDavid Matlack 
81fded8da4SDavid Matlack 	/* Any IOVA that doesn't overlap memcpy_region and driver->region. */
82fded8da4SDavid Matlack 	self->unmapped_iova = 8UL * SZ_1G;
83fded8da4SDavid Matlack 
84fded8da4SDavid Matlack 	vfio_pci_driver_init(self->device);
85fded8da4SDavid Matlack 	self->msi_fd = self->device->msi_eventfds[driver->msi];
86fded8da4SDavid Matlack 
87fded8da4SDavid Matlack 	/*
88fded8da4SDavid Matlack 	 * Use the maximum size supported by the device for memcpy operations,
89fded8da4SDavid Matlack 	 * slimmed down to fit into the memcpy region (divided by 2 so src and
90fded8da4SDavid Matlack 	 * dst regions do not overlap).
91fded8da4SDavid Matlack 	 */
92fded8da4SDavid Matlack 	self->size = self->device->driver.max_memcpy_size;
93fded8da4SDavid Matlack 	self->size = min(self->size, self->memcpy_region.size / 2);
94fded8da4SDavid Matlack 
95fded8da4SDavid Matlack 	self->src = self->memcpy_region.vaddr;
96fded8da4SDavid Matlack 	self->dst = self->src + self->size;
97fded8da4SDavid Matlack 
98fded8da4SDavid Matlack 	self->src_iova = to_iova(self->device, self->src);
99fded8da4SDavid Matlack 	self->dst_iova = to_iova(self->device, self->dst);
100fded8da4SDavid Matlack }
101fded8da4SDavid Matlack 
102fded8da4SDavid Matlack FIXTURE_TEARDOWN(vfio_pci_driver_test)
103fded8da4SDavid Matlack {
104fded8da4SDavid Matlack 	struct vfio_pci_driver *driver = &self->device->driver;
105fded8da4SDavid Matlack 
106fded8da4SDavid Matlack 	vfio_pci_driver_remove(self->device);
107fded8da4SDavid Matlack 
108fded8da4SDavid Matlack 	region_teardown(self->device, &self->memcpy_region);
109fded8da4SDavid Matlack 	region_teardown(self->device, &driver->region);
110fded8da4SDavid Matlack 
111fded8da4SDavid Matlack 	vfio_pci_device_cleanup(self->device);
112fded8da4SDavid Matlack }
113fded8da4SDavid Matlack 
114fded8da4SDavid Matlack TEST_F(vfio_pci_driver_test, init_remove)
115fded8da4SDavid Matlack {
116fded8da4SDavid Matlack 	int i;
117fded8da4SDavid Matlack 
118fded8da4SDavid Matlack 	for (i = 0; i < 10; i++) {
119fded8da4SDavid Matlack 		vfio_pci_driver_remove(self->device);
120fded8da4SDavid Matlack 		vfio_pci_driver_init(self->device);
121fded8da4SDavid Matlack 	}
122fded8da4SDavid Matlack }
123fded8da4SDavid Matlack 
124fded8da4SDavid Matlack TEST_F(vfio_pci_driver_test, memcpy_success)
125fded8da4SDavid Matlack {
126fded8da4SDavid Matlack 	fcntl_set_nonblock(self->msi_fd);
127fded8da4SDavid Matlack 
128fded8da4SDavid Matlack 	memset(self->src, 'x', self->size);
129fded8da4SDavid Matlack 	memset(self->dst, 'y', self->size);
130fded8da4SDavid Matlack 
131fded8da4SDavid Matlack 	ASSERT_EQ(0, vfio_pci_driver_memcpy(self->device,
132fded8da4SDavid Matlack 					    self->src_iova,
133fded8da4SDavid Matlack 					    self->dst_iova,
134fded8da4SDavid Matlack 					    self->size));
135fded8da4SDavid Matlack 
136fded8da4SDavid Matlack 	ASSERT_EQ(0, memcmp(self->src, self->dst, self->size));
137fded8da4SDavid Matlack 	ASSERT_NO_MSI(self->msi_fd);
138fded8da4SDavid Matlack }
139fded8da4SDavid Matlack 
140fded8da4SDavid Matlack TEST_F(vfio_pci_driver_test, memcpy_from_unmapped_iova)
141fded8da4SDavid Matlack {
142fded8da4SDavid Matlack 	fcntl_set_nonblock(self->msi_fd);
143fded8da4SDavid Matlack 
144fded8da4SDavid Matlack 	/*
145fded8da4SDavid Matlack 	 * Ignore the return value since not all devices will detect and report
146fded8da4SDavid Matlack 	 * accesses to unmapped IOVAs as errors.
147fded8da4SDavid Matlack 	 */
148fded8da4SDavid Matlack 	vfio_pci_driver_memcpy(self->device, self->unmapped_iova,
149fded8da4SDavid Matlack 			       self->dst_iova, self->size);
150fded8da4SDavid Matlack 
151fded8da4SDavid Matlack 	ASSERT_NO_MSI(self->msi_fd);
152fded8da4SDavid Matlack }
153fded8da4SDavid Matlack 
154fded8da4SDavid Matlack TEST_F(vfio_pci_driver_test, memcpy_to_unmapped_iova)
155fded8da4SDavid Matlack {
156fded8da4SDavid Matlack 	fcntl_set_nonblock(self->msi_fd);
157fded8da4SDavid Matlack 
158fded8da4SDavid Matlack 	/*
159fded8da4SDavid Matlack 	 * Ignore the return value since not all devices will detect and report
160fded8da4SDavid Matlack 	 * accesses to unmapped IOVAs as errors.
161fded8da4SDavid Matlack 	 */
162fded8da4SDavid Matlack 	vfio_pci_driver_memcpy(self->device, self->src_iova,
163fded8da4SDavid Matlack 			       self->unmapped_iova, self->size);
164fded8da4SDavid Matlack 
165fded8da4SDavid Matlack 	ASSERT_NO_MSI(self->msi_fd);
166fded8da4SDavid Matlack }
167fded8da4SDavid Matlack 
168fded8da4SDavid Matlack TEST_F(vfio_pci_driver_test, send_msi)
169fded8da4SDavid Matlack {
170fded8da4SDavid Matlack 	u64 value;
171fded8da4SDavid Matlack 
172fded8da4SDavid Matlack 	vfio_pci_driver_send_msi(self->device);
173fded8da4SDavid Matlack 	ASSERT_EQ(8, read(self->msi_fd, &value, 8));
174fded8da4SDavid Matlack 	ASSERT_EQ(1, value);
175fded8da4SDavid Matlack }
176fded8da4SDavid Matlack 
177fded8da4SDavid Matlack TEST_F(vfio_pci_driver_test, mix_and_match)
178fded8da4SDavid Matlack {
179fded8da4SDavid Matlack 	u64 value;
180fded8da4SDavid Matlack 	int i;
181fded8da4SDavid Matlack 
182fded8da4SDavid Matlack 	for (i = 0; i < 10; i++) {
183fded8da4SDavid Matlack 		memset(self->src, 'x', self->size);
184fded8da4SDavid Matlack 		memset(self->dst, 'y', self->size);
185fded8da4SDavid Matlack 
186fded8da4SDavid Matlack 		ASSERT_EQ(0, vfio_pci_driver_memcpy(self->device,
187fded8da4SDavid Matlack 						    self->src_iova,
188fded8da4SDavid Matlack 						    self->dst_iova,
189fded8da4SDavid Matlack 						    self->size));
190fded8da4SDavid Matlack 
191fded8da4SDavid Matlack 		ASSERT_EQ(0, memcmp(self->src, self->dst, self->size));
192fded8da4SDavid Matlack 
193fded8da4SDavid Matlack 		vfio_pci_driver_memcpy(self->device,
194fded8da4SDavid Matlack 				       self->unmapped_iova,
195fded8da4SDavid Matlack 				       self->dst_iova,
196fded8da4SDavid Matlack 				       self->size);
197fded8da4SDavid Matlack 
198fded8da4SDavid Matlack 		vfio_pci_driver_send_msi(self->device);
199fded8da4SDavid Matlack 		ASSERT_EQ(8, read(self->msi_fd, &value, 8));
200fded8da4SDavid Matlack 		ASSERT_EQ(1, value);
201fded8da4SDavid Matlack 	}
202fded8da4SDavid Matlack }
203fded8da4SDavid Matlack 
204fded8da4SDavid Matlack TEST_F_TIMEOUT(vfio_pci_driver_test, memcpy_storm, 60)
205fded8da4SDavid Matlack {
206fded8da4SDavid Matlack 	struct vfio_pci_driver *driver = &self->device->driver;
207fded8da4SDavid Matlack 	u64 total_size;
208fded8da4SDavid Matlack 	u64 count;
209fded8da4SDavid Matlack 
210fded8da4SDavid Matlack 	fcntl_set_nonblock(self->msi_fd);
211fded8da4SDavid Matlack 
212fded8da4SDavid Matlack 	/*
213fded8da4SDavid Matlack 	 * Perform up to 250GiB worth of DMA reads and writes across several
214fded8da4SDavid Matlack 	 * memcpy operations. Some devices can support even more but the test
215fded8da4SDavid Matlack 	 * will take too long.
216fded8da4SDavid Matlack 	 */
217fded8da4SDavid Matlack 	total_size = 250UL * SZ_1G;
218fded8da4SDavid Matlack 	count = min(total_size / self->size, driver->max_memcpy_count);
219fded8da4SDavid Matlack 
220fded8da4SDavid Matlack 	printf("Kicking off %lu memcpys of size 0x%lx\n", count, self->size);
221fded8da4SDavid Matlack 	vfio_pci_driver_memcpy_start(self->device,
222fded8da4SDavid Matlack 				     self->src_iova,
223fded8da4SDavid Matlack 				     self->dst_iova,
224fded8da4SDavid Matlack 				     self->size, count);
225fded8da4SDavid Matlack 
226fded8da4SDavid Matlack 	ASSERT_EQ(0, vfio_pci_driver_memcpy_wait(self->device));
227fded8da4SDavid Matlack 	ASSERT_NO_MSI(self->msi_fd);
228fded8da4SDavid Matlack }
229fded8da4SDavid Matlack 
230fded8da4SDavid Matlack int main(int argc, char *argv[])
231fded8da4SDavid Matlack {
232fded8da4SDavid Matlack 	struct vfio_pci_device *device;
233fded8da4SDavid Matlack 
234fded8da4SDavid Matlack 	device_bdf = vfio_selftests_get_bdf(&argc, argv);
235fded8da4SDavid Matlack 
2365df9bd62SDavid Matlack 	device = vfio_pci_device_init(device_bdf, default_iommu_mode);
237fded8da4SDavid Matlack 	if (!device->driver.ops) {
238fded8da4SDavid Matlack 		fprintf(stderr, "No driver found for device %s\n", device_bdf);
239fded8da4SDavid Matlack 		return KSFT_SKIP;
240fded8da4SDavid Matlack 	}
241fded8da4SDavid Matlack 	vfio_pci_device_cleanup(device);
242fded8da4SDavid Matlack 
243fded8da4SDavid Matlack 	return test_harness_run(argc, argv);
244fded8da4SDavid Matlack }
245