xref: /linux/tools/testing/selftests/vfio/vfio_pci_device_test.c (revision 91ee1738a9d358d3e0120a783cc052c275dea5e5)
1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <fcntl.h>
3 #include <stdlib.h>
4 
5 #include <sys/ioctl.h>
6 #include <sys/mman.h>
7 
8 #include <linux/limits.h>
9 #include <linux/pci_regs.h>
10 #include <linux/sizes.h>
11 #include <linux/vfio.h>
12 
13 #include <libvfio.h>
14 
15 #include "kselftest_harness.h"
16 
17 static const char *device_bdf;
18 
19 /*
20  * Limit the number of MSIs enabled/disabled by the test regardless of the
21  * number of MSIs the device itself supports, e.g. to avoid hitting IRTE limits.
22  */
23 #define MAX_TEST_MSI 16U
24 
25 FIXTURE(vfio_pci_device_test) {
26 	struct iommu *iommu;
27 	struct vfio_pci_device *device;
28 };
29 
30 FIXTURE_SETUP(vfio_pci_device_test)
31 {
32 	self->iommu = iommu_init(default_iommu_mode);
33 	self->device = vfio_pci_device_init(device_bdf, self->iommu);
34 }
35 
36 FIXTURE_TEARDOWN(vfio_pci_device_test)
37 {
38 	vfio_pci_device_cleanup(self->device);
39 	iommu_cleanup(self->iommu);
40 }
41 
42 #define read_pci_id_from_sysfs(_file) ({					\
43 	char __sysfs_path[PATH_MAX];						\
44 	char __buf[32];								\
45 	int __fd;								\
46 										\
47 	snprintf_assert(__sysfs_path, PATH_MAX, "/sys/bus/pci/devices/%s/%s",	\
48 			device_bdf, _file);					\
49 	ASSERT_GT((__fd = open(__sysfs_path, O_RDONLY)), 0);			\
50 	ASSERT_GT(read(__fd, __buf, ARRAY_SIZE(__buf)), 0);			\
51 	ASSERT_EQ(0, close(__fd));						\
52 	(u16)strtoul(__buf, NULL, 0);						\
53 })
54 
55 TEST_F(vfio_pci_device_test, config_space_read_write)
56 {
57 	u16 vendor, device;
58 	u16 command;
59 
60 	/* Check that Vendor and Device match what the kernel reports. */
61 	vendor = read_pci_id_from_sysfs("vendor");
62 	device = read_pci_id_from_sysfs("device");
63 	ASSERT_TRUE(vfio_pci_device_match(self->device, vendor, device));
64 
65 	printf("Vendor: %04x, Device: %04x\n", vendor, device);
66 
67 	command = vfio_pci_config_readw(self->device, PCI_COMMAND);
68 	ASSERT_FALSE(command & PCI_COMMAND_MASTER);
69 
70 	vfio_pci_config_writew(self->device, PCI_COMMAND, command | PCI_COMMAND_MASTER);
71 	command = vfio_pci_config_readw(self->device, PCI_COMMAND);
72 	ASSERT_TRUE(command & PCI_COMMAND_MASTER);
73 	printf("Enabled Bus Mastering (command: %04x)\n", command);
74 
75 	vfio_pci_config_writew(self->device, PCI_COMMAND, command & ~PCI_COMMAND_MASTER);
76 	command = vfio_pci_config_readw(self->device, PCI_COMMAND);
77 	ASSERT_FALSE(command & PCI_COMMAND_MASTER);
78 	printf("Disabled Bus Mastering (command: %04x)\n", command);
79 }
80 
81 TEST_F(vfio_pci_device_test, validate_bars)
82 {
83 	struct vfio_pci_bar *bar;
84 	int i;
85 
86 	for (i = 0; i < PCI_STD_NUM_BARS; i++) {
87 		bar = &self->device->bars[i];
88 
89 		if (!(bar->info.flags & VFIO_REGION_INFO_FLAG_MMAP)) {
90 			printf("BAR %d does not support mmap()\n", i);
91 			ASSERT_EQ(NULL, bar->vaddr);
92 			continue;
93 		}
94 
95 		/*
96 		 * BARs that support mmap() should be automatically mapped by
97 		 * vfio_pci_device_init().
98 		 */
99 		ASSERT_NE(NULL, bar->vaddr);
100 		ASSERT_NE(0, bar->info.size);
101 		printf("BAR %d mapped at %p (size 0x%llx)\n", i, bar->vaddr, bar->info.size);
102 	}
103 }
104 
105 FIXTURE(vfio_pci_irq_test) {
106 	struct iommu *iommu;
107 	struct vfio_pci_device *device;
108 };
109 
110 FIXTURE_VARIANT(vfio_pci_irq_test) {
111 	int irq_index;
112 };
113 
114 FIXTURE_VARIANT_ADD(vfio_pci_irq_test, msi) {
115 	.irq_index = VFIO_PCI_MSI_IRQ_INDEX,
116 };
117 
118 FIXTURE_VARIANT_ADD(vfio_pci_irq_test, msix) {
119 	.irq_index = VFIO_PCI_MSIX_IRQ_INDEX,
120 };
121 
122 FIXTURE_SETUP(vfio_pci_irq_test)
123 {
124 	self->iommu = iommu_init(default_iommu_mode);
125 	self->device = vfio_pci_device_init(device_bdf, self->iommu);
126 }
127 
128 FIXTURE_TEARDOWN(vfio_pci_irq_test)
129 {
130 	vfio_pci_device_cleanup(self->device);
131 	iommu_cleanup(self->iommu);
132 }
133 
134 TEST_F(vfio_pci_irq_test, enable_trigger_disable)
135 {
136 	bool msix = variant->irq_index == VFIO_PCI_MSIX_IRQ_INDEX;
137 	int msi_eventfd;
138 	u32 count;
139 	u64 value;
140 	int i;
141 
142 	if (msix)
143 		count = self->device->msix_info.count;
144 	else
145 		count = self->device->msi_info.count;
146 
147 	count = min(count, MAX_TEST_MSI);
148 
149 	if (!count)
150 		SKIP(return, "MSI%s: not supported\n", msix ? "-x" : "");
151 
152 	vfio_pci_irq_enable(self->device, variant->irq_index, 0, count);
153 	printf("MSI%s: enabled %d interrupts\n", msix ? "-x" : "", count);
154 
155 	for (i = 0; i < count; i++) {
156 		msi_eventfd = self->device->msi_eventfds[i];
157 
158 		fcntl_set_nonblock(msi_eventfd);
159 		ASSERT_EQ(-1, read(msi_eventfd, &value, 8));
160 		ASSERT_EQ(EAGAIN, errno);
161 
162 		vfio_pci_irq_trigger(self->device, variant->irq_index, i);
163 
164 		ASSERT_EQ(8, read(msi_eventfd, &value, 8));
165 		ASSERT_EQ(1, value);
166 	}
167 
168 	vfio_pci_irq_disable(self->device, variant->irq_index);
169 }
170 
171 TEST_F(vfio_pci_device_test, reset)
172 {
173 	if (!(self->device->info.flags & VFIO_DEVICE_FLAGS_RESET))
174 		SKIP(return, "Device does not support reset\n");
175 
176 	vfio_pci_device_reset(self->device);
177 }
178 
179 int main(int argc, char *argv[])
180 {
181 	device_bdf = vfio_selftests_get_bdf(&argc, argv);
182 	return test_harness_run(argc, argv);
183 }
184