Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

vfio: selftests: Introduce vfio_pci_device_test

Introduce a basic VFIO selftest called vfio_pci_device_test to
demonstrate the functionality of the VFIO selftest library and provide
some test coverage of basic VFIO operations, including:

- Mapping and unmapping DMA
- Mapping and unmapping BARs
- Enabling, triggering, and disabling MSI and MSI-x
- Reading and writing to PCI config space

This test should work with most PCI devices, as long as they are bound
to vfio-pci.

Acked-by: Shuah Khan <skhan@linuxfoundation.org>
Signed-off-by: David Matlack <dmatlack@google.com>
Link: https://lore.kernel.org/r/20250822212518.4156428-4-dmatlack@google.com
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>

authored by

David Matlack and committed by
Alex Williamson
16eadd7c 19faf6fd

+181
+1
tools/testing/selftests/vfio/Makefile
··· 1 1 CFLAGS = $(KHDR_INCLUDES) 2 + TEST_GEN_PROGS += vfio_pci_device_test 2 3 include ../lib.mk 3 4 include lib/libvfio.mk 4 5
+180
tools/testing/selftests/vfio/vfio_pci_device_test.c
··· 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 <vfio_util.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 vfio_pci_device *device; 27 + }; 28 + 29 + FIXTURE_SETUP(vfio_pci_device_test) 30 + { 31 + self->device = vfio_pci_device_init(device_bdf, VFIO_TYPE1_IOMMU); 32 + } 33 + 34 + FIXTURE_TEARDOWN(vfio_pci_device_test) 35 + { 36 + vfio_pci_device_cleanup(self->device); 37 + } 38 + 39 + TEST_F(vfio_pci_device_test, dma_map_unmap) 40 + { 41 + const u64 size = SZ_2M; 42 + void *mem; 43 + u64 iova; 44 + 45 + mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); 46 + ASSERT_NE(mem, MAP_FAILED); 47 + 48 + iova = (u64)mem; 49 + 50 + vfio_pci_dma_map(self->device, iova, size, mem); 51 + printf("Mapped HVA %p (size 0x%lx) at IOVA 0x%lx\n", mem, size, iova); 52 + vfio_pci_dma_unmap(self->device, iova, size); 53 + 54 + ASSERT_TRUE(!munmap(mem, SZ_2M)); 55 + } 56 + 57 + #define read_pci_id_from_sysfs(_file) ({ \ 58 + char __sysfs_path[PATH_MAX]; \ 59 + char __buf[32]; \ 60 + int __fd; \ 61 + \ 62 + snprintf(__sysfs_path, PATH_MAX, "/sys/bus/pci/devices/%s/%s", device_bdf, _file); \ 63 + ASSERT_GT((__fd = open(__sysfs_path, O_RDONLY)), 0); \ 64 + ASSERT_GT(read(__fd, __buf, ARRAY_SIZE(__buf)), 0); \ 65 + ASSERT_EQ(0, close(__fd)); \ 66 + (u16)strtoul(__buf, NULL, 0); \ 67 + }) 68 + 69 + TEST_F(vfio_pci_device_test, config_space_read_write) 70 + { 71 + u16 vendor, device; 72 + u16 command; 73 + 74 + /* Check that Vendor and Device match what the kernel reports. */ 75 + vendor = read_pci_id_from_sysfs("vendor"); 76 + device = read_pci_id_from_sysfs("device"); 77 + 78 + ASSERT_EQ(vendor, vfio_pci_config_readw(self->device, PCI_VENDOR_ID)); 79 + ASSERT_EQ(device, vfio_pci_config_readw(self->device, PCI_DEVICE_ID)); 80 + 81 + printf("Vendor: %04x, Device: %04x\n", vendor, device); 82 + 83 + command = vfio_pci_config_readw(self->device, PCI_COMMAND); 84 + ASSERT_FALSE(command & PCI_COMMAND_MASTER); 85 + 86 + vfio_pci_config_writew(self->device, PCI_COMMAND, command | PCI_COMMAND_MASTER); 87 + command = vfio_pci_config_readw(self->device, PCI_COMMAND); 88 + ASSERT_TRUE(command & PCI_COMMAND_MASTER); 89 + printf("Enabled Bus Mastering (command: %04x)\n", command); 90 + 91 + vfio_pci_config_writew(self->device, PCI_COMMAND, command & ~PCI_COMMAND_MASTER); 92 + command = vfio_pci_config_readw(self->device, PCI_COMMAND); 93 + ASSERT_FALSE(command & PCI_COMMAND_MASTER); 94 + printf("Disabled Bus Mastering (command: %04x)\n", command); 95 + } 96 + 97 + TEST_F(vfio_pci_device_test, validate_bars) 98 + { 99 + struct vfio_pci_bar *bar; 100 + int i; 101 + 102 + for (i = 0; i < PCI_STD_NUM_BARS; i++) { 103 + bar = &self->device->bars[i]; 104 + 105 + if (!(bar->info.flags & VFIO_REGION_INFO_FLAG_MMAP)) { 106 + printf("BAR %d does not support mmap()\n", i); 107 + ASSERT_EQ(NULL, bar->vaddr); 108 + continue; 109 + } 110 + 111 + /* 112 + * BARs that support mmap() should be automatically mapped by 113 + * vfio_pci_device_init(). 114 + */ 115 + ASSERT_NE(NULL, bar->vaddr); 116 + ASSERT_NE(0, bar->info.size); 117 + printf("BAR %d mapped at %p (size 0x%llx)\n", i, bar->vaddr, bar->info.size); 118 + } 119 + } 120 + 121 + FIXTURE(vfio_pci_irq_test) { 122 + struct vfio_pci_device *device; 123 + }; 124 + 125 + FIXTURE_VARIANT(vfio_pci_irq_test) { 126 + int irq_index; 127 + }; 128 + 129 + FIXTURE_VARIANT_ADD(vfio_pci_irq_test, msi) { 130 + .irq_index = VFIO_PCI_MSI_IRQ_INDEX, 131 + }; 132 + 133 + FIXTURE_VARIANT_ADD(vfio_pci_irq_test, msix) { 134 + .irq_index = VFIO_PCI_MSIX_IRQ_INDEX, 135 + }; 136 + 137 + FIXTURE_SETUP(vfio_pci_irq_test) 138 + { 139 + self->device = vfio_pci_device_init(device_bdf, VFIO_TYPE1_IOMMU); 140 + } 141 + 142 + FIXTURE_TEARDOWN(vfio_pci_irq_test) 143 + { 144 + vfio_pci_device_cleanup(self->device); 145 + } 146 + 147 + TEST_F(vfio_pci_irq_test, enable_trigger_disable) 148 + { 149 + bool msix = variant->irq_index == VFIO_PCI_MSIX_IRQ_INDEX; 150 + u32 count; 151 + u64 value; 152 + int i; 153 + 154 + if (msix) 155 + count = self->device->msix_info.count; 156 + else 157 + count = self->device->msi_info.count; 158 + 159 + count = min(count, MAX_TEST_MSI); 160 + 161 + if (!count) 162 + SKIP(return, "MSI%s: not supported\n", msix ? "-x" : ""); 163 + 164 + vfio_pci_irq_enable(self->device, variant->irq_index, 0, count); 165 + printf("MSI%s: enabled %d interrupts\n", msix ? "-x" : "", count); 166 + 167 + for (i = 0; i < count; i++) { 168 + vfio_pci_irq_trigger(self->device, variant->irq_index, i); 169 + ASSERT_EQ(8, read(self->device->msi_eventfds[i], &value, 8)); 170 + ASSERT_EQ(1, value); 171 + } 172 + 173 + vfio_pci_irq_disable(self->device, variant->irq_index); 174 + } 175 + 176 + int main(int argc, char *argv[]) 177 + { 178 + device_bdf = vfio_selftests_get_bdf(&argc, argv); 179 + return test_harness_run(argc, argv); 180 + }