mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 08:33:17 +02:00
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>
This commit is contained in:
parent
19faf6fd96
commit
16eadd7c12
|
|
@ -1,4 +1,5 @@
|
|||
CFLAGS = $(KHDR_INCLUDES)
|
||||
TEST_GEN_PROGS += vfio_pci_device_test
|
||||
include ../lib.mk
|
||||
include lib/libvfio.mk
|
||||
|
||||
|
|
|
|||
180
tools/testing/selftests/vfio/vfio_pci_device_test.c
Normal file
180
tools/testing/selftests/vfio/vfio_pci_device_test.c
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
// 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, VFIO_TYPE1_IOMMU);
|
||||
}
|
||||
|
||||
FIXTURE_TEARDOWN(vfio_pci_device_test)
|
||||
{
|
||||
vfio_pci_device_cleanup(self->device);
|
||||
}
|
||||
|
||||
TEST_F(vfio_pci_device_test, dma_map_unmap)
|
||||
{
|
||||
const u64 size = SZ_2M;
|
||||
void *mem;
|
||||
u64 iova;
|
||||
|
||||
mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
ASSERT_NE(mem, MAP_FAILED);
|
||||
|
||||
iova = (u64)mem;
|
||||
|
||||
vfio_pci_dma_map(self->device, iova, size, mem);
|
||||
printf("Mapped HVA %p (size 0x%lx) at IOVA 0x%lx\n", mem, size, iova);
|
||||
vfio_pci_dma_unmap(self->device, iova, size);
|
||||
|
||||
ASSERT_TRUE(!munmap(mem, SZ_2M));
|
||||
}
|
||||
|
||||
#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_EQ(vendor, vfio_pci_config_readw(self->device, PCI_VENDOR_ID));
|
||||
ASSERT_EQ(device, vfio_pci_config_readw(self->device, PCI_DEVICE_ID));
|
||||
|
||||
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, VFIO_TYPE1_IOMMU);
|
||||
}
|
||||
|
||||
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;
|
||||
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++) {
|
||||
vfio_pci_irq_trigger(self->device, variant->irq_index, i);
|
||||
ASSERT_EQ(8, read(self->device->msi_eventfds[i], &value, 8));
|
||||
ASSERT_EQ(1, value);
|
||||
}
|
||||
|
||||
vfio_pci_irq_disable(self->device, variant->irq_index);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
device_bdf = vfio_selftests_get_bdf(&argc, argv);
|
||||
return test_harness_run(argc, argv);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user