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