summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/vfio/vfio_pci_device_test.c
blob: 7a270698e4d24b89e3759b8cf037a9834ea06ba5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// SPDX-License-Identifier: GPL-2.0-only
#include <fcntl.h>
#include <stdlib.h>

#include <sys/ioctl.h>
#include <sys/mman.h>

#include <linux/limits.h>
#include <linux/pci_regs.h>
#include <linux/sizes.h>
#include <linux/vfio.h>

#include <vfio_util.h>

#include "../kselftest_harness.h"

static const char *device_bdf;

/*
 * Limit the number of MSIs enabled/disabled by the test regardless of the
 * number of MSIs the device itself supports, e.g. to avoid hitting IRTE limits.
 */
#define MAX_TEST_MSI 16U

FIXTURE(vfio_pci_device_test) {
	struct vfio_pci_device *device;
};

FIXTURE_SETUP(vfio_pci_device_test)
{
	self->device = vfio_pci_device_init(device_bdf, default_iommu_mode);
}

FIXTURE_TEARDOWN(vfio_pci_device_test)
{
	vfio_pci_device_cleanup(self->device);
}

#define read_pci_id_from_sysfs(_file) ({							\
	char __sysfs_path[PATH_MAX];								\
	char __buf[32];										\
	int __fd;										\
												\
	snprintf(__sysfs_path, PATH_MAX, "/sys/bus/pci/devices/%s/%s", device_bdf, _file);	\
	ASSERT_GT((__fd = open(__sysfs_path, O_RDONLY)), 0);					\
	ASSERT_GT(read(__fd, __buf, ARRAY_SIZE(__buf)), 0);					\
	ASSERT_EQ(0, close(__fd));								\
	(u16)strtoul(__buf, NULL, 0);								\
})

TEST_F(vfio_pci_device_test, config_space_read_write)
{
	u16 vendor, device;
	u16 command;

	/* Check that Vendor and Device match what the kernel reports. */
	vendor = read_pci_id_from_sysfs("vendor");
	device = read_pci_id_from_sysfs("device");
	ASSERT_TRUE(vfio_pci_device_match(self->device, vendor, device));

	printf("Vendor: %04x, Device: %04x\n", vendor, device);

	command = vfio_pci_config_readw(self->device, PCI_COMMAND);
	ASSERT_FALSE(command & PCI_COMMAND_MASTER);

	vfio_pci_config_writew(self->device, PCI_COMMAND, command | PCI_COMMAND_MASTER);
	command = vfio_pci_config_readw(self->device, PCI_COMMAND);
	ASSERT_TRUE(command & PCI_COMMAND_MASTER);
	printf("Enabled Bus Mastering (command: %04x)\n", command);

	vfio_pci_config_writew(self->device, PCI_COMMAND, command & ~PCI_COMMAND_MASTER);
	command = vfio_pci_config_readw(self->device, PCI_COMMAND);
	ASSERT_FALSE(command & PCI_COMMAND_MASTER);
	printf("Disabled Bus Mastering (command: %04x)\n", command);
}

TEST_F(vfio_pci_device_test, validate_bars)
{
	struct vfio_pci_bar *bar;
	int i;

	for (i = 0; i < PCI_STD_NUM_BARS; i++) {
		bar = &self->device->bars[i];

		if (!(bar->info.flags & VFIO_REGION_INFO_FLAG_MMAP)) {
			printf("BAR %d does not support mmap()\n", i);
			ASSERT_EQ(NULL, bar->vaddr);
			continue;
		}

		/*
		 * BARs that support mmap() should be automatically mapped by
		 * vfio_pci_device_init().
		 */
		ASSERT_NE(NULL, bar->vaddr);
		ASSERT_NE(0, bar->info.size);
		printf("BAR %d mapped at %p (size 0x%llx)\n", i, bar->vaddr, bar->info.size);
	}
}

FIXTURE(vfio_pci_irq_test) {
	struct vfio_pci_device *device;
};

FIXTURE_VARIANT(vfio_pci_irq_test) {
	int irq_index;
};

FIXTURE_VARIANT_ADD(vfio_pci_irq_test, msi) {
	.irq_index = VFIO_PCI_MSI_IRQ_INDEX,
};

FIXTURE_VARIANT_ADD(vfio_pci_irq_test, msix) {
	.irq_index = VFIO_PCI_MSIX_IRQ_INDEX,
};

FIXTURE_SETUP(vfio_pci_irq_test)
{
	self->device = vfio_pci_device_init(device_bdf, default_iommu_mode);
}

FIXTURE_TEARDOWN(vfio_pci_irq_test)
{
	vfio_pci_device_cleanup(self->device);
}

TEST_F(vfio_pci_irq_test, enable_trigger_disable)
{
	bool msix = variant->irq_index == VFIO_PCI_MSIX_IRQ_INDEX;
	int msi_eventfd;
	u32 count;
	u64 value;
	int i;

	if (msix)
		count = self->device->msix_info.count;
	else
		count = self->device->msi_info.count;

	count = min(count, MAX_TEST_MSI);

	if (!count)
		SKIP(return, "MSI%s: not supported\n", msix ? "-x" : "");

	vfio_pci_irq_enable(self->device, variant->irq_index, 0, count);
	printf("MSI%s: enabled %d interrupts\n", msix ? "-x" : "", count);

	for (i = 0; i < count; i++) {
		msi_eventfd = self->device->msi_eventfds[i];

		fcntl_set_nonblock(msi_eventfd);
		ASSERT_EQ(-1, read(msi_eventfd, &value, 8));
		ASSERT_EQ(EAGAIN, errno);

		vfio_pci_irq_trigger(self->device, variant->irq_index, i);

		ASSERT_EQ(8, read(msi_eventfd, &value, 8));
		ASSERT_EQ(1, value);
	}

	vfio_pci_irq_disable(self->device, variant->irq_index);
}

TEST_F(vfio_pci_device_test, reset)
{
	if (!(self->device->info.flags & VFIO_DEVICE_FLAGS_RESET))
		SKIP(return, "Device does not support reset\n");

	vfio_pci_device_reset(self->device);
}

int main(int argc, char *argv[])
{
	device_bdf = vfio_selftests_get_bdf(&argc, argv);
	return test_harness_run(argc, argv);
}