new file mode 100644
@@ -0,0 +1,192 @@
+/*
+ * QEMU "pci-testdev" PCI test device
+ *
+ * Copyright (C) 2016, Red Hat Inc, Alexander Gordeev <agordeev@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+#include "pci.h"
+#include "asm/io.h"
+
+struct pci_testdev_ops {
+ u8 (*io_readb)(const volatile void *addr);
+ u16 (*io_readw)(const volatile void *addr);
+ u32 (*io_readl)(const volatile void *addr);
+ void (*io_writeb)(u8 value, volatile void *addr);
+ void (*io_writew)(u16 value, volatile void *addr);
+ void (*io_writel)(u32 value, volatile void *addr);
+};
+
+static u8 pio_readb(const volatile void *addr)
+{
+ return inb((unsigned long)addr);
+}
+
+static u16 pio_readw(const volatile void *addr)
+{
+ return inw((unsigned long)addr);
+}
+
+static u32 pio_readl(const volatile void *addr)
+{
+ return inl((unsigned long)addr);
+}
+
+static void pio_writeb(u8 value, volatile void *addr)
+{
+ outb(value, (unsigned long)addr);
+}
+
+static void pio_writew(u16 value, volatile void *addr)
+{
+ outw(value, (unsigned long)addr);
+}
+
+static void pio_writel(u32 value, volatile void *addr)
+{
+ outl(value, (unsigned long)addr);
+}
+
+static struct pci_testdev_ops pci_testdev_io_ops = {
+ .io_readb = pio_readb,
+ .io_readw = pio_readw,
+ .io_readl = pio_readl,
+ .io_writeb = pio_writeb,
+ .io_writew = pio_writew,
+ .io_writel = pio_writel
+};
+
+static u8 mmio_readb(const volatile void *addr)
+{
+ return *(const volatile u8 __force *)addr;
+}
+
+static u16 mmio_readw(const volatile void *addr)
+{
+ return *(const volatile u16 __force *)addr;
+}
+
+static u32 mmio_readl(const volatile void *addr)
+{
+ return *(const volatile u32 __force *)addr;
+}
+
+static void mmio_writeb(u8 value, volatile void *addr)
+{
+ *(volatile u8 __force *)addr = value;
+}
+
+static void mmio_writew(u16 value, volatile void *addr)
+{
+ *(volatile u16 __force *)addr = value;
+}
+
+static void mmio_writel(u32 value, volatile void *addr)
+{
+ *(volatile u32 __force *)addr = value;
+}
+
+static struct pci_testdev_ops pci_testdev_mem_ops = {
+ .io_readb = mmio_readb,
+ .io_readw = mmio_readw,
+ .io_readl = mmio_readl,
+ .io_writeb = mmio_writeb,
+ .io_writew = mmio_writew,
+ .io_writel = mmio_writel
+};
+
+static bool pci_testdev_one(struct pci_test_dev_hdr *test,
+ int test_nr,
+ struct pci_testdev_ops *ops)
+{
+ u8 width;
+ u32 count, sig, off;
+ const int nr_writes = 16;
+ int i;
+
+ ops->io_writeb(test_nr, &test->test);
+ count = ops->io_readl(&test->count);
+ if (count != 0)
+ return false;
+
+ width = ops->io_readb(&test->width);
+ if (width != 1 && width != 2 && width != 4)
+ return false;
+
+ sig = ops->io_readl(&test->data);
+ off = ops->io_readl(&test->offset);
+
+ for (i = 0; i < nr_writes; i++) {
+ switch (width) {
+ case 1: ops->io_writeb(sig, (void *)test + off); break;
+ case 2: ops->io_writew(sig, (void *)test + off); break;
+ case 4: ops->io_writel(sig, (void *)test + off); break;
+ }
+ }
+
+ count = ops->io_readl(&test->count);
+ if (!count)
+ return true;
+
+ return (int)count == nr_writes;
+}
+
+void pci_testdev_print(struct pci_test_dev_hdr *test,
+ struct pci_testdev_ops *ops)
+{
+ bool io = (ops == &pci_testdev_io_ops);
+ int i;
+
+ printf("pci-testdev %3s: ", io ? "io" : "mem");
+ for (i = 0;; ++i) {
+ char c = ops->io_readb(&test->name[i]);
+ if (!c)
+ break;
+ printf("%c", c);
+ }
+ printf("\n");
+}
+
+static int pci_testdev_all(struct pci_test_dev_hdr *test,
+ struct pci_testdev_ops *ops)
+{
+ int i;
+
+ for (i = 0;; i++) {
+ if (!pci_testdev_one(test, i, ops))
+ break;
+ pci_testdev_print(test, ops);
+ }
+
+ return i;
+}
+
+int pci_testdev(void)
+{
+ phys_addr_t addr;
+ void __iomem *mem, *io;
+ pcidevaddr_t dev;
+ int nr_tests = 0;
+ bool ret;
+
+ dev = pci_find_dev(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_TEST);
+ if (dev == PCIDEVADDR_INVALID) {
+ printf("'pci-testdev' device is not found, "
+ "check QEMU '-device pci-testdev' parameter\n");
+ return -1;
+ }
+
+ ret = pci_bar_is_valid(dev, 0) && pci_bar_is_valid(dev, 1);
+ assert(ret);
+
+ addr = pci_bar_get_addr(dev, 0);
+ mem = ioremap(addr, PAGE_SIZE);
+
+ addr = pci_bar_get_addr(dev, 1);
+ io = (void *)(unsigned long)addr;
+
+ nr_tests += pci_testdev_all(mem, &pci_testdev_mem_ops);
+ nr_tests += pci_testdev_all(io, &pci_testdev_io_ops);
+
+ return nr_tests;
+}
@@ -43,6 +43,8 @@ extern bool pci_bar_is_valid(pcidevaddr_t dev, int bar_num);
extern void pci_bar_print(pcidevaddr_t dev, int bar_num);
extern void pci_dev_print_id(pcidevaddr_t dev);
+int pci_testdev(void);
+
/*
* pci-testdev is a driver for the pci-testdev qemu pci device. The
* device enables testing mmio and portio exits, and measuring their
@@ -51,7 +53,12 @@ extern void pci_dev_print_id(pcidevaddr_t dev);
#define PCI_VENDOR_ID_REDHAT 0x1b36
#define PCI_DEVICE_ID_REDHAT_TEST 0x0005
+/*
+ * pci-testdev supports at least three types of tests (via mmio and
+ * portio BARs): no-eventfd, wildcard-eventfd and datamatch-eventfd
+ */
#define PCI_TESTDEV_NUM_BARS 2
+#define PCI_TESTDEV_NUM_TESTS 3
struct pci_test_dev_hdr {
uint8_t test;