116eadd7cSDavid Matlack // SPDX-License-Identifier: GPL-2.0-only 216eadd7cSDavid Matlack #include <fcntl.h> 316eadd7cSDavid Matlack #include <stdlib.h> 416eadd7cSDavid Matlack 516eadd7cSDavid Matlack #include <sys/ioctl.h> 616eadd7cSDavid Matlack #include <sys/mman.h> 716eadd7cSDavid Matlack 816eadd7cSDavid Matlack #include <linux/limits.h> 916eadd7cSDavid Matlack #include <linux/pci_regs.h> 1016eadd7cSDavid Matlack #include <linux/sizes.h> 1116eadd7cSDavid Matlack #include <linux/vfio.h> 1216eadd7cSDavid Matlack 1316eadd7cSDavid Matlack #include <vfio_util.h> 1416eadd7cSDavid Matlack 1516eadd7cSDavid Matlack #include "../kselftest_harness.h" 1616eadd7cSDavid Matlack 1716eadd7cSDavid Matlack static const char *device_bdf; 1816eadd7cSDavid Matlack 1916eadd7cSDavid Matlack /* 2016eadd7cSDavid Matlack * Limit the number of MSIs enabled/disabled by the test regardless of the 2116eadd7cSDavid Matlack * number of MSIs the device itself supports, e.g. to avoid hitting IRTE limits. 2216eadd7cSDavid Matlack */ 2316eadd7cSDavid Matlack #define MAX_TEST_MSI 16U 2416eadd7cSDavid Matlack 2516eadd7cSDavid Matlack FIXTURE(vfio_pci_device_test) { 2616eadd7cSDavid Matlack struct vfio_pci_device *device; 2716eadd7cSDavid Matlack }; 2816eadd7cSDavid Matlack 2916eadd7cSDavid Matlack FIXTURE_SETUP(vfio_pci_device_test) 3016eadd7cSDavid Matlack { 31*5df9bd62SDavid Matlack self->device = vfio_pci_device_init(device_bdf, default_iommu_mode); 3216eadd7cSDavid Matlack } 3316eadd7cSDavid Matlack 3416eadd7cSDavid Matlack FIXTURE_TEARDOWN(vfio_pci_device_test) 3516eadd7cSDavid Matlack { 3616eadd7cSDavid Matlack vfio_pci_device_cleanup(self->device); 3716eadd7cSDavid Matlack } 3816eadd7cSDavid Matlack 3916eadd7cSDavid Matlack #define read_pci_id_from_sysfs(_file) ({ \ 4016eadd7cSDavid Matlack char __sysfs_path[PATH_MAX]; \ 4116eadd7cSDavid Matlack char __buf[32]; \ 4216eadd7cSDavid Matlack int __fd; \ 4316eadd7cSDavid Matlack \ 4416eadd7cSDavid Matlack snprintf(__sysfs_path, PATH_MAX, "/sys/bus/pci/devices/%s/%s", device_bdf, _file); \ 4516eadd7cSDavid Matlack ASSERT_GT((__fd = open(__sysfs_path, O_RDONLY)), 0); \ 4616eadd7cSDavid Matlack ASSERT_GT(read(__fd, __buf, ARRAY_SIZE(__buf)), 0); \ 4716eadd7cSDavid Matlack ASSERT_EQ(0, close(__fd)); \ 4816eadd7cSDavid Matlack (u16)strtoul(__buf, NULL, 0); \ 4916eadd7cSDavid Matlack }) 5016eadd7cSDavid Matlack 5116eadd7cSDavid Matlack TEST_F(vfio_pci_device_test, config_space_read_write) 5216eadd7cSDavid Matlack { 5316eadd7cSDavid Matlack u16 vendor, device; 5416eadd7cSDavid Matlack u16 command; 5516eadd7cSDavid Matlack 5616eadd7cSDavid Matlack /* Check that Vendor and Device match what the kernel reports. */ 5716eadd7cSDavid Matlack vendor = read_pci_id_from_sysfs("vendor"); 5816eadd7cSDavid Matlack device = read_pci_id_from_sysfs("device"); 5950d8fe80SDavid Matlack ASSERT_TRUE(vfio_pci_device_match(self->device, vendor, device)); 6016eadd7cSDavid Matlack 6116eadd7cSDavid Matlack printf("Vendor: %04x, Device: %04x\n", vendor, device); 6216eadd7cSDavid Matlack 6316eadd7cSDavid Matlack command = vfio_pci_config_readw(self->device, PCI_COMMAND); 6416eadd7cSDavid Matlack ASSERT_FALSE(command & PCI_COMMAND_MASTER); 6516eadd7cSDavid Matlack 6616eadd7cSDavid Matlack vfio_pci_config_writew(self->device, PCI_COMMAND, command | PCI_COMMAND_MASTER); 6716eadd7cSDavid Matlack command = vfio_pci_config_readw(self->device, PCI_COMMAND); 6816eadd7cSDavid Matlack ASSERT_TRUE(command & PCI_COMMAND_MASTER); 6916eadd7cSDavid Matlack printf("Enabled Bus Mastering (command: %04x)\n", command); 7016eadd7cSDavid Matlack 7116eadd7cSDavid Matlack vfio_pci_config_writew(self->device, PCI_COMMAND, command & ~PCI_COMMAND_MASTER); 7216eadd7cSDavid Matlack command = vfio_pci_config_readw(self->device, PCI_COMMAND); 7316eadd7cSDavid Matlack ASSERT_FALSE(command & PCI_COMMAND_MASTER); 7416eadd7cSDavid Matlack printf("Disabled Bus Mastering (command: %04x)\n", command); 7516eadd7cSDavid Matlack } 7616eadd7cSDavid Matlack 7716eadd7cSDavid Matlack TEST_F(vfio_pci_device_test, validate_bars) 7816eadd7cSDavid Matlack { 7916eadd7cSDavid Matlack struct vfio_pci_bar *bar; 8016eadd7cSDavid Matlack int i; 8116eadd7cSDavid Matlack 8216eadd7cSDavid Matlack for (i = 0; i < PCI_STD_NUM_BARS; i++) { 8316eadd7cSDavid Matlack bar = &self->device->bars[i]; 8416eadd7cSDavid Matlack 8516eadd7cSDavid Matlack if (!(bar->info.flags & VFIO_REGION_INFO_FLAG_MMAP)) { 8616eadd7cSDavid Matlack printf("BAR %d does not support mmap()\n", i); 8716eadd7cSDavid Matlack ASSERT_EQ(NULL, bar->vaddr); 8816eadd7cSDavid Matlack continue; 8916eadd7cSDavid Matlack } 9016eadd7cSDavid Matlack 9116eadd7cSDavid Matlack /* 9216eadd7cSDavid Matlack * BARs that support mmap() should be automatically mapped by 9316eadd7cSDavid Matlack * vfio_pci_device_init(). 9416eadd7cSDavid Matlack */ 9516eadd7cSDavid Matlack ASSERT_NE(NULL, bar->vaddr); 9616eadd7cSDavid Matlack ASSERT_NE(0, bar->info.size); 9716eadd7cSDavid Matlack printf("BAR %d mapped at %p (size 0x%llx)\n", i, bar->vaddr, bar->info.size); 9816eadd7cSDavid Matlack } 9916eadd7cSDavid Matlack } 10016eadd7cSDavid Matlack 10116eadd7cSDavid Matlack FIXTURE(vfio_pci_irq_test) { 10216eadd7cSDavid Matlack struct vfio_pci_device *device; 10316eadd7cSDavid Matlack }; 10416eadd7cSDavid Matlack 10516eadd7cSDavid Matlack FIXTURE_VARIANT(vfio_pci_irq_test) { 10616eadd7cSDavid Matlack int irq_index; 10716eadd7cSDavid Matlack }; 10816eadd7cSDavid Matlack 10916eadd7cSDavid Matlack FIXTURE_VARIANT_ADD(vfio_pci_irq_test, msi) { 11016eadd7cSDavid Matlack .irq_index = VFIO_PCI_MSI_IRQ_INDEX, 11116eadd7cSDavid Matlack }; 11216eadd7cSDavid Matlack 11316eadd7cSDavid Matlack FIXTURE_VARIANT_ADD(vfio_pci_irq_test, msix) { 11416eadd7cSDavid Matlack .irq_index = VFIO_PCI_MSIX_IRQ_INDEX, 11516eadd7cSDavid Matlack }; 11616eadd7cSDavid Matlack 11716eadd7cSDavid Matlack FIXTURE_SETUP(vfio_pci_irq_test) 11816eadd7cSDavid Matlack { 119*5df9bd62SDavid Matlack self->device = vfio_pci_device_init(device_bdf, default_iommu_mode); 12016eadd7cSDavid Matlack } 12116eadd7cSDavid Matlack 12216eadd7cSDavid Matlack FIXTURE_TEARDOWN(vfio_pci_irq_test) 12316eadd7cSDavid Matlack { 12416eadd7cSDavid Matlack vfio_pci_device_cleanup(self->device); 12516eadd7cSDavid Matlack } 12616eadd7cSDavid Matlack 12716eadd7cSDavid Matlack TEST_F(vfio_pci_irq_test, enable_trigger_disable) 12816eadd7cSDavid Matlack { 12916eadd7cSDavid Matlack bool msix = variant->irq_index == VFIO_PCI_MSIX_IRQ_INDEX; 13092494780SDavid Matlack int msi_eventfd; 13116eadd7cSDavid Matlack u32 count; 13216eadd7cSDavid Matlack u64 value; 13316eadd7cSDavid Matlack int i; 13416eadd7cSDavid Matlack 13516eadd7cSDavid Matlack if (msix) 13616eadd7cSDavid Matlack count = self->device->msix_info.count; 13716eadd7cSDavid Matlack else 13816eadd7cSDavid Matlack count = self->device->msi_info.count; 13916eadd7cSDavid Matlack 14016eadd7cSDavid Matlack count = min(count, MAX_TEST_MSI); 14116eadd7cSDavid Matlack 14216eadd7cSDavid Matlack if (!count) 14316eadd7cSDavid Matlack SKIP(return, "MSI%s: not supported\n", msix ? "-x" : ""); 14416eadd7cSDavid Matlack 14516eadd7cSDavid Matlack vfio_pci_irq_enable(self->device, variant->irq_index, 0, count); 14616eadd7cSDavid Matlack printf("MSI%s: enabled %d interrupts\n", msix ? "-x" : "", count); 14716eadd7cSDavid Matlack 14816eadd7cSDavid Matlack for (i = 0; i < count; i++) { 14992494780SDavid Matlack msi_eventfd = self->device->msi_eventfds[i]; 15092494780SDavid Matlack 15192494780SDavid Matlack fcntl_set_nonblock(msi_eventfd); 15292494780SDavid Matlack ASSERT_EQ(-1, read(msi_eventfd, &value, 8)); 15392494780SDavid Matlack ASSERT_EQ(EAGAIN, errno); 15492494780SDavid Matlack 15516eadd7cSDavid Matlack vfio_pci_irq_trigger(self->device, variant->irq_index, i); 15692494780SDavid Matlack 15792494780SDavid Matlack ASSERT_EQ(8, read(msi_eventfd, &value, 8)); 15816eadd7cSDavid Matlack ASSERT_EQ(1, value); 15916eadd7cSDavid Matlack } 16016eadd7cSDavid Matlack 16116eadd7cSDavid Matlack vfio_pci_irq_disable(self->device, variant->irq_index); 16216eadd7cSDavid Matlack } 16316eadd7cSDavid Matlack 164a0fd0af5SJosh Hilke TEST_F(vfio_pci_device_test, reset) 165a0fd0af5SJosh Hilke { 166a0fd0af5SJosh Hilke if (!(self->device->info.flags & VFIO_DEVICE_FLAGS_RESET)) 167a0fd0af5SJosh Hilke SKIP(return, "Device does not support reset\n"); 168a0fd0af5SJosh Hilke 169a0fd0af5SJosh Hilke vfio_pci_device_reset(self->device); 170a0fd0af5SJosh Hilke } 171a0fd0af5SJosh Hilke 17216eadd7cSDavid Matlack int main(int argc, char *argv[]) 17316eadd7cSDavid Matlack { 17416eadd7cSDavid Matlack device_bdf = vfio_selftests_get_bdf(&argc, argv); 17516eadd7cSDavid Matlack return test_harness_run(argc, argv); 17616eadd7cSDavid Matlack } 177