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