diff mbox

[kvm-unit-tests,v7,12/13] pci: Add pci-testdev PCI bus test device

Message ID 15b9e7ffcf6f978c58e44e45502331949d61d19f.1471434672.git.agordeev@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Alexander Gordeev Aug. 17, 2016, 12:07 p.m. UTC
Cc: Thomas Huth <thuth@redhat.com>
Cc: Andrew Jones <drjones@redhat.com>
Signed-off-by: Alexander Gordeev <agordeev@redhat.com>
---
 lib/pci-testdev.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/pci.h         |   7 ++
 2 files changed, 199 insertions(+)
 create mode 100644 lib/pci-testdev.c

Comments

Andrew Jones Aug. 17, 2016, 2:03 p.m. UTC | #1
On Wed, Aug 17, 2016 at 02:07:13PM +0200, Alexander Gordeev wrote:
> Cc: Thomas Huth <thuth@redhat.com>
> Cc: Andrew Jones <drjones@redhat.com>
> Signed-off-by: Alexander Gordeev <agordeev@redhat.com>
> ---
>  lib/pci-testdev.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  lib/pci.h         |   7 ++
>  2 files changed, 199 insertions(+)
>  create mode 100644 lib/pci-testdev.c
> 

From a quick skim this looks like the same as v6, so you must have just
forgotten to pick up my r-b. Anyway, here it is again

Reviewed-by: Andrew Jones <drjones@redhat.com>
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Peter Xu Sept. 23, 2016, 7:25 a.m. UTC | #2
Some nit-picks inline...

(btw, this looks more like a test case shared by platforms rather than
 a library, so shall we move it outside of lib/? I don't know.)

On Wed, Aug 17, 2016 at 02:07:13PM +0200, Alexander Gordeev wrote:

[...]

> +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;

IIUC we only have 1?

> +
> +	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;

Here as well. Could I ask why we are handling 2/4?

[...]

> +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;

Since we have defined PCI_TESTDEV_NUM_TESTS, shall we use it here to
stop the loop rather than depending on a failure code from
pci_testdev_one()?

[...]

> +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;

x86/vmexit.c is using pci-testdev as well. Maybe we can generalize the
init part and share it? (Actually there is patch in my local tree for
this, but haven't posted :)

Thanks!

-- peterx
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andrew Jones Sept. 23, 2016, 8:55 a.m. UTC | #3
On Fri, Sep 23, 2016 at 03:25:42PM +0800, Peter Xu wrote:
> Some nit-picks inline...
> 
> (btw, this looks more like a test case shared by platforms rather than
>  a library, so shall we move it outside of lib/? I don't know.)

We only have lib/ for shared code right now. I don't think we need
to add a new directory for shared testcode vs. libcode yet, as this
is the first case. If it becomes a more common thing to do, though,
then I agree that it may be nicer to have a new directory for it.

Thanks,
drew

> 
> On Wed, Aug 17, 2016 at 02:07:13PM +0200, Alexander Gordeev wrote:
> 
> [...]
> 
> > +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;
> 
> IIUC we only have 1?
> 
> > +
> > +	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;
> 
> Here as well. Could I ask why we are handling 2/4?
> 
> [...]
> 
> > +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;
> 
> Since we have defined PCI_TESTDEV_NUM_TESTS, shall we use it here to
> stop the loop rather than depending on a failure code from
> pci_testdev_one()?
> 
> [...]
> 
> > +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;
> 
> x86/vmexit.c is using pci-testdev as well. Maybe we can generalize the
> init part and share it? (Actually there is patch in my local tree for
> this, but haven't posted :)
> 
> Thanks!
> 
> -- peterx
> --
> To unsubscribe from this list: send the line "unsubscribe kvm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Alexander Gordeev Oct. 12, 2016, 4:54 p.m. UTC | #4
On Fri, Sep 23, 2016 at 03:25:42PM +0800, Peter Xu wrote:
> > +	width = ops->io_readb(&test->width);
> > +	if (width != 1 && width != 2 && width != 4)
> > +		return false;
> 
> IIUC we only have 1?

I guess it boils what *have* does mean here.

pci-testdev protocol allows it to be any, but hw/misc/pci-testdev.c
implements just 1 (yet?).

> > +
> > +	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;
> 
> Here as well. Could I ask why we are handling 2/4?

Basically, because x86 had it and this implementation mimics it.

> [...]
> 
> > +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;
> 
> Since we have defined PCI_TESTDEV_NUM_TESTS, shall we use it here to
> stop the loop rather than depending on a failure code from
> pci_testdev_one()?

No, I think we indeed need to inquiry the device this way and
go ahead and test if it reported the size is supported.

> [...]

> x86/vmexit.c is using pci-testdev as well. Maybe we can generalize the
> init part and share it? (Actually there is patch in my local tree for
> this, but haven't posted :)

Yep, I have x86 enabler and it is very simple. But x86 is just too
different to try to generalize and we're not pursuing it right now.

> Thanks!
> 
> -- peterx
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Peter Xu Oct. 13, 2016, 6:52 a.m. UTC | #5
On Wed, Oct 12, 2016 at 06:54:28PM +0200, Alexander Gordeev wrote:
> On Fri, Sep 23, 2016 at 03:25:42PM +0800, Peter Xu wrote:
> > > +	width = ops->io_readb(&test->width);
> > > +	if (width != 1 && width != 2 && width != 4)
> > > +		return false;
> > 
> > IIUC we only have 1?
> 
> I guess it boils what *have* does mean here.
> 
> pci-testdev protocol allows it to be any, but hw/misc/pci-testdev.c
> implements just 1 (yet?).

Do we have other possible implementations for pci-testdev protocol?

> 
> > > +
> > > +	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;
> > 
> > Here as well. Could I ask why we are handling 2/4?
> 
> Basically, because x86 had it and this implementation mimics it.

Yes, actually I didn't notice that before. So I have the same question
for vmexit.c. But of course I don't think this question is a blocker
for the series.

[...]

> > x86/vmexit.c is using pci-testdev as well. Maybe we can generalize the
> > init part and share it? (Actually there is patch in my local tree for
> > this, but haven't posted :)
> 
> Yep, I have x86 enabler and it is very simple. But x86 is just too
> different to try to generalize and we're not pursuing it right now.

Could I ask what's the difficulties? Again this is not a block for
sure, so, looking forward to your next version.

Thanks,

-- peterx
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Alexander Gordeev Oct. 13, 2016, 1:16 p.m. UTC | #6
On Thu, Oct 13, 2016 at 02:52:49PM +0800, Peter Xu wrote:
> On Wed, Oct 12, 2016 at 06:54:28PM +0200, Alexander Gordeev wrote:
> > On Fri, Sep 23, 2016 at 03:25:42PM +0800, Peter Xu wrote:
> > > > +	width = ops->io_readb(&test->width);
> > > > +	if (width != 1 && width != 2 && width != 4)
> > > > +		return false;
> > > 
> > > IIUC we only have 1?
> > 
> > I guess it boils what *have* does mean here.
> > 
> > pci-testdev protocol allows it to be any, but hw/misc/pci-testdev.c
> > implements just 1 (yet?).
> 
> Do we have other possible implementations for pci-testdev protocol?

I typed answer twice, but realized I do not get the question. :)
Could you paraphrase, please?

> > > > +	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;
> > > 
> > > Here as well. Could I ask why we are handling 2/4?
> > 
> > Basically, because x86 had it and this implementation mimics it.
> 
> Yes, actually I didn't notice that before. So I have the same question
> for vmexit.c. But of course I don't think this question is a blocker
> for the series.

I am not sure about x86, but I do not see any problem either.

> > > x86/vmexit.c is using pci-testdev as well. Maybe we can generalize the
> > > init part and share it? (Actually there is patch in my local tree for
> > > this, but haven't posted :)
> > 
> > Yep, I have x86 enabler and it is very simple. But x86 is just too
> > different to try to generalize and we're not pursuing it right now.
> 
> Could I ask what's the difficulties? Again this is not a block for
> sure, so, looking forward to your next version.

Well, x86 is a series of tests, very self-contained and hence very
implementation-oriented. By contrast, this version is rather stand-
alone and does not really fit.

So if one embarks to generalize, then it would be x86 wide, not this
test alone, AFAICT.

> Thanks,
> 
> -- peterx
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Peter Xu Oct. 14, 2016, 5:01 a.m. UTC | #7
On Thu, Oct 13, 2016 at 03:16:49PM +0200, Alexander Gordeev wrote:
> On Thu, Oct 13, 2016 at 02:52:49PM +0800, Peter Xu wrote:
> > On Wed, Oct 12, 2016 at 06:54:28PM +0200, Alexander Gordeev wrote:
> > > On Fri, Sep 23, 2016 at 03:25:42PM +0800, Peter Xu wrote:
> > > > > +	width = ops->io_readb(&test->width);
> > > > > +	if (width != 1 && width != 2 && width != 4)
> > > > > +		return false;
> > > > 
> > > > IIUC we only have 1?
> > > 
> > > I guess it boils what *have* does mean here.
> > > 
> > > pci-testdev protocol allows it to be any, but hw/misc/pci-testdev.c
> > > implements just 1 (yet?).
> > 
> > Do we have other possible implementations for pci-testdev protocol?
> 
> I typed answer twice, but realized I do not get the question. :)
> Could you paraphrase, please?

Sorry for not being clear. I am just wondering whether there is other
implementation for pci-testdev besides the one in QEMU. It looks like
a special device to test QEMU only.

Thanks,

-- peterx
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andrew Jones Oct. 14, 2016, 7:07 a.m. UTC | #8
On Fri, Oct 14, 2016 at 01:01:48PM +0800, Peter Xu wrote:
> On Thu, Oct 13, 2016 at 03:16:49PM +0200, Alexander Gordeev wrote:
> > On Thu, Oct 13, 2016 at 02:52:49PM +0800, Peter Xu wrote:
> > > On Wed, Oct 12, 2016 at 06:54:28PM +0200, Alexander Gordeev wrote:
> > > > On Fri, Sep 23, 2016 at 03:25:42PM +0800, Peter Xu wrote:
> > > > > > +	width = ops->io_readb(&test->width);
> > > > > > +	if (width != 1 && width != 2 && width != 4)
> > > > > > +		return false;
> > > > > 
> > > > > IIUC we only have 1?
> > > > 
> > > > I guess it boils what *have* does mean here.
> > > > 
> > > > pci-testdev protocol allows it to be any, but hw/misc/pci-testdev.c
> > > > implements just 1 (yet?).
> > > 
> > > Do we have other possible implementations for pci-testdev protocol?
> > 
> > I typed answer twice, but realized I do not get the question. :)
> > Could you paraphrase, please?
> 
> Sorry for not being clear. I am just wondering whether there is other
> implementation for pci-testdev besides the one in QEMU. It looks like
> a special device to test QEMU only.

Yes. pci-testdev (most likely) has never been implemented anywhere other
than in QEMU. It was written specifically for kvm-unit-tests. See

http://www.linux-kvm.org/page/KVM-unit-tests#Testdevs

for a synopsis of all testdevs. Please feel free to add more details to
the pci-testdev section. Feel free to help maintain the document in any
other ways too, of course :-)

drew
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Peter Xu Oct. 14, 2016, 9:14 a.m. UTC | #9
On Fri, Oct 14, 2016 at 09:07:07AM +0200, Andrew Jones wrote:
> On Fri, Oct 14, 2016 at 01:01:48PM +0800, Peter Xu wrote:
> > On Thu, Oct 13, 2016 at 03:16:49PM +0200, Alexander Gordeev wrote:
> > > On Thu, Oct 13, 2016 at 02:52:49PM +0800, Peter Xu wrote:
> > > > On Wed, Oct 12, 2016 at 06:54:28PM +0200, Alexander Gordeev wrote:
> > > > > On Fri, Sep 23, 2016 at 03:25:42PM +0800, Peter Xu wrote:
> > > > > > > +	width = ops->io_readb(&test->width);
> > > > > > > +	if (width != 1 && width != 2 && width != 4)
> > > > > > > +		return false;
> > > > > > 
> > > > > > IIUC we only have 1?
> > > > > 
> > > > > I guess it boils what *have* does mean here.
> > > > > 
> > > > > pci-testdev protocol allows it to be any, but hw/misc/pci-testdev.c
> > > > > implements just 1 (yet?).
> > > > 
> > > > Do we have other possible implementations for pci-testdev protocol?
> > > 
> > > I typed answer twice, but realized I do not get the question. :)
> > > Could you paraphrase, please?
> > 
> > Sorry for not being clear. I am just wondering whether there is other
> > implementation for pci-testdev besides the one in QEMU. It looks like
> > a special device to test QEMU only.
> 
> Yes. pci-testdev (most likely) has never been implemented anywhere other
> than in QEMU. It was written specifically for kvm-unit-tests. See
> 
> http://www.linux-kvm.org/page/KVM-unit-tests#Testdevs
> 
> for a synopsis of all testdevs. Please feel free to add more details to
> the pci-testdev section. Feel free to help maintain the document in any
> other ways too, of course :-)

Thanks, Drew.

I am trying to use edu device for testing Intel IOMMU. I'll try to
post RFC patches first though. When needed, I can update the document
for edu device (though I still don't know how to do that).

-- peterx
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/lib/pci-testdev.c b/lib/pci-testdev.c
new file mode 100644
index 000000000000..ad482d3291c7
--- /dev/null
+++ b/lib/pci-testdev.c
@@ -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;
+}
diff --git a/lib/pci.h b/lib/pci.h
index 7acbbdf2eb16..40e11a892783 100644
--- a/lib/pci.h
+++ b/lib/pci.h
@@ -41,6 +41,8 @@  extern bool pci_bar_is64(pcidevaddr_t dev, int bar_num);
 extern bool pci_bar_is_memory(pcidevaddr_t dev, int bar_num);
 extern bool pci_bar_is_valid(pcidevaddr_t dev, int bar_num);
 
+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
@@ -49,7 +51,12 @@  extern bool pci_bar_is_valid(pcidevaddr_t dev, int bar_num);
 #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;