xref: /linux/tools/testing/selftests/vfio/vfio_pci_device_test.c (revision 4f38da1f027ea2c9f01bb71daa7a299c191b6940)
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