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
FIXTURE(vfio_pci_device_test)25 FIXTURE(vfio_pci_device_test) {
26 struct iommu *iommu;
27 struct vfio_pci_device *device;
28 };
29
FIXTURE_SETUP(vfio_pci_device_test)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
FIXTURE_TEARDOWN(vfio_pci_device_test)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(__sysfs_path, PATH_MAX, "/sys/bus/pci/devices/%s/%s", device_bdf, _file); \
48 ASSERT_GT((__fd = open(__sysfs_path, O_RDONLY)), 0); \
49 ASSERT_GT(read(__fd, __buf, ARRAY_SIZE(__buf)), 0); \
50 ASSERT_EQ(0, close(__fd)); \
51 (u16)strtoul(__buf, NULL, 0); \
52 })
53
TEST_F(vfio_pci_device_test,config_space_read_write)54 TEST_F(vfio_pci_device_test, config_space_read_write)
55 {
56 u16 vendor, device;
57 u16 command;
58
59 /* Check that Vendor and Device match what the kernel reports. */
60 vendor = read_pci_id_from_sysfs("vendor");
61 device = read_pci_id_from_sysfs("device");
62 ASSERT_TRUE(vfio_pci_device_match(self->device, vendor, device));
63
64 printf("Vendor: %04x, Device: %04x\n", vendor, device);
65
66 command = vfio_pci_config_readw(self->device, PCI_COMMAND);
67 ASSERT_FALSE(command & PCI_COMMAND_MASTER);
68
69 vfio_pci_config_writew(self->device, PCI_COMMAND, command | PCI_COMMAND_MASTER);
70 command = vfio_pci_config_readw(self->device, PCI_COMMAND);
71 ASSERT_TRUE(command & PCI_COMMAND_MASTER);
72 printf("Enabled Bus Mastering (command: %04x)\n", command);
73
74 vfio_pci_config_writew(self->device, PCI_COMMAND, command & ~PCI_COMMAND_MASTER);
75 command = vfio_pci_config_readw(self->device, PCI_COMMAND);
76 ASSERT_FALSE(command & PCI_COMMAND_MASTER);
77 printf("Disabled Bus Mastering (command: %04x)\n", command);
78 }
79
TEST_F(vfio_pci_device_test,validate_bars)80 TEST_F(vfio_pci_device_test, validate_bars)
81 {
82 struct vfio_pci_bar *bar;
83 int i;
84
85 for (i = 0; i < PCI_STD_NUM_BARS; i++) {
86 bar = &self->device->bars[i];
87
88 if (!(bar->info.flags & VFIO_REGION_INFO_FLAG_MMAP)) {
89 printf("BAR %d does not support mmap()\n", i);
90 ASSERT_EQ(NULL, bar->vaddr);
91 continue;
92 }
93
94 /*
95 * BARs that support mmap() should be automatically mapped by
96 * vfio_pci_device_init().
97 */
98 ASSERT_NE(NULL, bar->vaddr);
99 ASSERT_NE(0, bar->info.size);
100 printf("BAR %d mapped at %p (size 0x%llx)\n", i, bar->vaddr, bar->info.size);
101 }
102 }
103
FIXTURE(vfio_pci_irq_test)104 FIXTURE(vfio_pci_irq_test) {
105 struct iommu *iommu;
106 struct vfio_pci_device *device;
107 };
108
FIXTURE_VARIANT(vfio_pci_irq_test)109 FIXTURE_VARIANT(vfio_pci_irq_test) {
110 int irq_index;
111 };
112
FIXTURE_VARIANT_ADD(vfio_pci_irq_test,msi)113 FIXTURE_VARIANT_ADD(vfio_pci_irq_test, msi) {
114 .irq_index = VFIO_PCI_MSI_IRQ_INDEX,
115 };
116
FIXTURE_VARIANT_ADD(vfio_pci_irq_test,msix)117 FIXTURE_VARIANT_ADD(vfio_pci_irq_test, msix) {
118 .irq_index = VFIO_PCI_MSIX_IRQ_INDEX,
119 };
120
FIXTURE_SETUP(vfio_pci_irq_test)121 FIXTURE_SETUP(vfio_pci_irq_test)
122 {
123 self->iommu = iommu_init(default_iommu_mode);
124 self->device = vfio_pci_device_init(device_bdf, self->iommu);
125 }
126
FIXTURE_TEARDOWN(vfio_pci_irq_test)127 FIXTURE_TEARDOWN(vfio_pci_irq_test)
128 {
129 vfio_pci_device_cleanup(self->device);
130 iommu_cleanup(self->iommu);
131 }
132
TEST_F(vfio_pci_irq_test,enable_trigger_disable)133 TEST_F(vfio_pci_irq_test, enable_trigger_disable)
134 {
135 bool msix = variant->irq_index == VFIO_PCI_MSIX_IRQ_INDEX;
136 int msi_eventfd;
137 u32 count;
138 u64 value;
139 int i;
140
141 if (msix)
142 count = self->device->msix_info.count;
143 else
144 count = self->device->msi_info.count;
145
146 count = min(count, MAX_TEST_MSI);
147
148 if (!count)
149 SKIP(return, "MSI%s: not supported\n", msix ? "-x" : "");
150
151 vfio_pci_irq_enable(self->device, variant->irq_index, 0, count);
152 printf("MSI%s: enabled %d interrupts\n", msix ? "-x" : "", count);
153
154 for (i = 0; i < count; i++) {
155 msi_eventfd = self->device->msi_eventfds[i];
156
157 fcntl_set_nonblock(msi_eventfd);
158 ASSERT_EQ(-1, read(msi_eventfd, &value, 8));
159 ASSERT_EQ(EAGAIN, errno);
160
161 vfio_pci_irq_trigger(self->device, variant->irq_index, i);
162
163 ASSERT_EQ(8, read(msi_eventfd, &value, 8));
164 ASSERT_EQ(1, value);
165 }
166
167 vfio_pci_irq_disable(self->device, variant->irq_index);
168 }
169
TEST_F(vfio_pci_device_test,reset)170 TEST_F(vfio_pci_device_test, reset)
171 {
172 if (!(self->device->info.flags & VFIO_DEVICE_FLAGS_RESET))
173 SKIP(return, "Device does not support reset\n");
174
175 vfio_pci_device_reset(self->device);
176 }
177
main(int argc,char * argv[])178 int main(int argc, char *argv[])
179 {
180 device_bdf = vfio_selftests_get_bdf(&argc, argv);
181 return test_harness_run(argc, argv);
182 }
183