Patchwork [RFC,1/8] qtest: add libqos

login
register
mail settings
Submitter Anthony Liguori
Date March 5, 2013, 1:53 p.m.
Message ID <1362491612-19226-2-git-send-email-aliguori@us.ibm.com>
Download mbox | patch
Permalink /patch/225049/
State New
Headers show

Comments

Anthony Liguori - March 5, 2013, 1:53 p.m.
This includes basic PCI support.

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
---
 configure             |   2 +-
 tests/Makefile        |   2 +-
 tests/libqos/pci-pc.c | 219 ++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/libqos/pci-pc.h |   8 ++
 tests/libqos/pci.c    | 108 +++++++++++++++++++++++++
 tests/libqos/pci.h    |  65 +++++++++++++++
 6 files changed, 402 insertions(+), 2 deletions(-)
 create mode 100644 tests/libqos/pci-pc.c
 create mode 100644 tests/libqos/pci-pc.h
 create mode 100644 tests/libqos/pci.c
 create mode 100644 tests/libqos/pci.h
Kevin Wolf - March 13, 2013, 11:26 a.m.
Am 05.03.2013 um 14:53 hat Anthony Liguori geschrieben:
> This includes basic PCI support.
> 
> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>

> +static void *qpci_pc_iomap(QPCIBus *bus, QPCIDevice *dev, int barno)
> +{
> +    QPCIBusPC *s = container_of(bus, QPCIBusPC, bus);
> +    static const int bar_reg_map[] = {
> +        PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2,
> +        PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5,
> +    };
> +    int bar_reg;
> +    uint32_t addr;
> +    uint64_t size;
> +
> +    g_assert(barno >= 0 && barno <= 5);
> +    bar_reg = bar_reg_map[barno];
> +
> +    qpci_config_writel(dev, bar_reg, 0xFFFFFFFF);
> +    addr = qpci_config_readl(dev, bar_reg);
> +
> +    size = (1ULL << ctol(addr));

This doesn't look right. It should be something like:

    size = (1ULL << ctzl(addr & ~0x3));

In fact, what must be masked out differs for I/O (0x3) and memory (0xf).

> --- /dev/null
> +++ b/tests/libqos/pci.c
> @@ -0,0 +1,108 @@
> +#include "libqos/pci.h"
> +
> +#include "hw/pci/pci_regs.h"
> +#include <glib.h>
> +
> +#include <stdio.h>
> +
> +QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn)
> +{
> +    QPCIDevice *dev;
> +
> +    dev = g_malloc0(sizeof(*dev));

Where is the matching free? I can't seem to destroy a device I
once queried.

> +    dev->bus = bus;
> +    dev->devfn = devfn;
> +
> +    if (qpci_config_readw(dev, PCI_VENDOR_ID) == 0xFFFF) {
> +        printf("vendor id is %x\n", qpci_config_readw(dev, PCI_VENDOR_ID));
> +        g_free(dev);
> +        return NULL;
> +    }
> +
> +    return dev;
> +}
> +
> +void qpci_device_enable(QPCIDevice *dev)
> +{
> +    uint16_t cmd;
> +
> +    /* FIXME -- does this need to be a bus callout? */
> +    cmd = qpci_config_readw(dev, PCI_COMMAND);
> +    cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
> +    qpci_config_writew(dev, PCI_COMMAND, cmd);
> +}

Wouldn't it make sense to enable bus mastering here as well? Forgetting
to do this manually is a trap that's easy to fall in...

Kevin
Andreas Färber - March 13, 2013, 12:11 p.m.
Am 05.03.2013 14:53, schrieb Anthony Liguori:
> This includes basic PCI support.
> 
> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
> ---
>  configure             |   2 +-
>  tests/Makefile        |   2 +-
>  tests/libqos/pci-pc.c | 219 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  tests/libqos/pci-pc.h |   8 ++
>  tests/libqos/pci.c    | 108 +++++++++++++++++++++++++
>  tests/libqos/pci.h    |  65 +++++++++++++++
>  6 files changed, 402 insertions(+), 2 deletions(-)
>  create mode 100644 tests/libqos/pci-pc.c
>  create mode 100644 tests/libqos/pci-pc.h
>  create mode 100644 tests/libqos/pci.c
>  create mode 100644 tests/libqos/pci.h

This is missing a refactoring of existing I2C libqos:

tests/libi2c.[hc] -> tests/libqos/i2c.[hc]
tests/libi2c-omap.c -> tests/libqos/i2c-omap.c

Andreas
Anthony Liguori - March 13, 2013, 2:03 p.m.
Kevin Wolf <kwolf@redhat.com> writes:

> Am 05.03.2013 um 14:53 hat Anthony Liguori geschrieben:
>> This includes basic PCI support.
>> 
>> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
>
>> +static void *qpci_pc_iomap(QPCIBus *bus, QPCIDevice *dev, int barno)
>> +{
>> +    QPCIBusPC *s = container_of(bus, QPCIBusPC, bus);
>> +    static const int bar_reg_map[] = {
>> +        PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2,
>> +        PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5,
>> +    };
>> +    int bar_reg;
>> +    uint32_t addr;
>> +    uint64_t size;
>> +
>> +    g_assert(barno >= 0 && barno <= 5);
>> +    bar_reg = bar_reg_map[barno];
>> +
>> +    qpci_config_writel(dev, bar_reg, 0xFFFFFFFF);
>> +    addr = qpci_config_readl(dev, bar_reg);
>> +
>> +    size = (1ULL << ctol(addr));
>
> This doesn't look right. It should be something like:
>
>     size = (1ULL << ctzl(addr & ~0x3));
>
> In fact, what must be masked out differs for I/O (0x3) and memory
> (0xf).

You are correct, it should look something like:

if (addr & PCI_BASE_ADDRESS_SPACE_IO) {
    size = (1ULL << ctzl(addr & PCI_BASE_ADDRESS_IO_MASK));
} else {
    size = (1ULL << ctzl(addr & PCI_BASE_ADDRESS_MEM_MASK));
}

This doesn't deal with 64-bit bars though.

I'll update in the next round.  I think this has worked for me because I
have only done single device testing.  I did switch from ffz when I
rebased but I think ffz would still have this problem.

>> +QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn)
>> +{
>> +    QPCIDevice *dev;
>> +
>> +    dev = g_malloc0(sizeof(*dev));
>
> Where is the matching free? I can't seem to destroy a device I
> once queried.

It's missing, I'll add it in the next round.

>> +    dev->bus = bus;
>> +    dev->devfn = devfn;
>> +
>> +    if (qpci_config_readw(dev, PCI_VENDOR_ID) == 0xFFFF) {
>> +        printf("vendor id is %x\n", qpci_config_readw(dev, PCI_VENDOR_ID));
>> +        g_free(dev);
>> +        return NULL;
>> +    }
>> +
>> +    return dev;
>> +}
>> +
>> +void qpci_device_enable(QPCIDevice *dev)
>> +{
>> +    uint16_t cmd;
>> +
>> +    /* FIXME -- does this need to be a bus callout? */
>> +    cmd = qpci_config_readw(dev, PCI_COMMAND);
>> +    cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
>> +    qpci_config_writew(dev, PCI_COMMAND, cmd);
>> +}
>
> Wouldn't it make sense to enable bus mastering here as well? Forgetting
> to do this manually is a trap that's easy to fall in...

Indeed, thanks for pointing it out.

Regards,

Anthony Liguori

>
> Kevin
Kevin Wolf - March 28, 2013, 11:41 a.m.
Am 13.03.2013 um 15:03 hat Anthony Liguori geschrieben:
> You are correct, it should look something like:
> 
> if (addr & PCI_BASE_ADDRESS_SPACE_IO) {
>     size = (1ULL << ctzl(addr & PCI_BASE_ADDRESS_IO_MASK));
> } else {
>     size = (1ULL << ctzl(addr & PCI_BASE_ADDRESS_MEM_MASK));
> }
> 
> This doesn't deal with 64-bit bars though.
> 
> I'll update in the next round.  I think this has worked for me because I
> have only done single device testing.  I did switch from ffz when I
> rebased but I think ffz would still have this problem.

For when are you planning the next round? Will we get this in time to
make use of it in 1.5?

Kevin
Anthony Liguori - March 29, 2013, 2:26 a.m.
Kevin Wolf <kwolf@redhat.com> writes:

> Am 13.03.2013 um 15:03 hat Anthony Liguori geschrieben:
>> You are correct, it should look something like:
>> 
>> if (addr & PCI_BASE_ADDRESS_SPACE_IO) {
>>     size = (1ULL << ctzl(addr & PCI_BASE_ADDRESS_IO_MASK));
>> } else {
>>     size = (1ULL << ctzl(addr & PCI_BASE_ADDRESS_MEM_MASK));
>> }
>> 
>> This doesn't deal with 64-bit bars though.
>> 
>> I'll update in the next round.  I think this has worked for me because I
>> have only done single device testing.  I did switch from ffz when I
>> rebased but I think ffz would still have this problem.
>
> For when are you planning the next round?

I'll send one out soon.  Perhaps tomorrow if all goes well.

Regards,

Anthony Liguori

> Will we get this in time to make use of it in 1.5?

>
> Kevin
Kevin Wolf - April 9, 2013, 9:15 a.m.
Am 29.03.2013 um 03:26 hat Anthony Liguori geschrieben:
> Kevin Wolf <kwolf@redhat.com> writes:
> 
> > Am 13.03.2013 um 15:03 hat Anthony Liguori geschrieben:
> >> You are correct, it should look something like:
> >> 
> >> if (addr & PCI_BASE_ADDRESS_SPACE_IO) {
> >>     size = (1ULL << ctzl(addr & PCI_BASE_ADDRESS_IO_MASK));
> >> } else {
> >>     size = (1ULL << ctzl(addr & PCI_BASE_ADDRESS_MEM_MASK));
> >> }
> >> 
> >> This doesn't deal with 64-bit bars though.
> >> 
> >> I'll update in the next round.  I think this has worked for me because I
> >> have only done single device testing.  I did switch from ffz when I
> >> rebased but I think ffz would still have this problem.
> >
> > For when are you planning the next round?
> 
> I'll send one out soon.  Perhaps tomorrow if all goes well.

I know, it's getting boring, but... which tomorrow?

Please just add the license headers and get the RFC committed. We'll get
the rest fixed up on top. We've been waiting for over a year now. This is
blocking any progress on the qtest coverage and probably already hurting
the quality of our releases.

Kevin

Patch

diff --git a/configure b/configure
index 19738ac..5d3e49e 100755
--- a/configure
+++ b/configure
@@ -4354,7 +4354,7 @@  if [ "$pixman" = "internal" ]; then
 fi
 
 # build tree in object directory in case the source is not in the current directory
-DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32"
+DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos"
 DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas"
 DIRS="$DIRS roms/seabios roms/vgabios"
 DIRS="$DIRS qapi-generated"
diff --git a/tests/Makefile b/tests/Makefile
index 567e36e..cbb4188 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -89,6 +89,7 @@  test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \
 test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o
 
 $(test-obj-y): QEMU_INCLUDES += -Itests
+QEMU_CFLAGS += -I$(SRC_PATH)/tests
 
 tests/test-x86-cpuid.o: QEMU_INCLUDES += -I$(SRC_PATH)/target-i386
 
@@ -117,7 +118,6 @@  tests/test-qmp-commands.h tests/test-qmp-marshal.c :\
 $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -o tests -p "test-" < $<, "  GEN   $@")
 
-
 tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
 tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
 tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c
new file mode 100644
index 0000000..da80f81
--- /dev/null
+++ b/tests/libqos/pci-pc.c
@@ -0,0 +1,219 @@ 
+#include "libqtest.h"
+#include "libqos/pci-pc.h"
+
+#include "hw/pci/pci_regs.h"
+
+#include "qemu-common.h"
+#include "qemu/host-utils.h"
+
+#include <glib.h>
+
+typedef struct QPCIBusPC
+{
+    QPCIBus bus;
+
+    uint32_t pci_hole_start;
+    uint32_t pci_hole_size;
+    uint32_t pci_hole_alloc;
+
+    uint16_t pci_iohole_start;
+    uint16_t pci_iohole_size;
+    uint16_t pci_iohole_alloc;
+} QPCIBusPC;
+
+static uint8_t qpci_pc_io_readb(QPCIBus *bus, void *addr)
+{
+    uintptr_t port = (uintptr_t)addr;
+    uint8_t value;
+
+    if (port < 0x10000) {
+        value = inb(port);
+    } else {
+        memread(port, &value, sizeof(value));
+    }
+
+    return value;
+}
+
+static uint16_t qpci_pc_io_readw(QPCIBus *bus, void *addr)
+{
+    uintptr_t port = (uintptr_t)addr;
+    uint16_t value;
+
+    if (port < 0x10000) {
+        value = inw(port);
+    } else {
+        memread(port, &value, sizeof(value));
+    }
+
+    return value;
+}
+
+static uint32_t qpci_pc_io_readl(QPCIBus *bus, void *addr)
+{
+    uintptr_t port = (uintptr_t)addr;
+    uint32_t value;
+
+    if (port < 0x10000) {
+        value = inl(port);
+    } else {
+        memread(port, &value, sizeof(value));
+    }
+
+    return value;
+}
+
+static void qpci_pc_io_writeb(QPCIBus *bus, void *addr, uint8_t value)
+{
+    uintptr_t port = (uintptr_t)addr;
+
+    if (port < 0x10000) {
+        outb(port, value);
+    } else {
+        memwrite(port, &value, sizeof(value));
+    }
+}
+
+static void qpci_pc_io_writew(QPCIBus *bus, void *addr, uint16_t value)
+{
+    uintptr_t port = (uintptr_t)addr;
+
+    if (port < 0x10000) {
+        outw(port, value);
+    } else {
+        memwrite(port, &value, sizeof(value));
+    }
+}
+
+static void qpci_pc_io_writel(QPCIBus *bus, void *addr, uint32_t value)
+{
+    uintptr_t port = (uintptr_t)addr;
+
+    if (port < 0x10000) {
+        outl(port, value);
+    } else {
+        memwrite(port, &value, sizeof(value));
+    }
+}
+
+static uint8_t qpci_pc_config_readb(QPCIBus *bus, int devfn, uint8_t offset)
+{
+    outl(0xcf8, (1 << 31) | (devfn << 8) | offset);
+    return inb(0xcfc);
+}
+
+static uint16_t qpci_pc_config_readw(QPCIBus *bus, int devfn, uint8_t offset)
+{
+    outl(0xcf8, (1 << 31) | (devfn << 8) | offset);
+    return inw(0xcfc);
+}
+
+static uint32_t qpci_pc_config_readl(QPCIBus *bus, int devfn, uint8_t offset)
+{
+    outl(0xcf8, (1 << 31) | (devfn << 8) | offset);
+    return inl(0xcfc);
+}
+
+static void qpci_pc_config_writeb(QPCIBus *bus, int devfn, uint8_t offset, uint8_t value)
+{
+    outl(0xcf8, (1 << 31) | (devfn << 8) | offset);
+    outb(0xcfc, value);
+}
+
+static void qpci_pc_config_writew(QPCIBus *bus, int devfn, uint8_t offset, uint16_t value)
+{
+    outl(0xcf8, (1 << 31) | (devfn << 8) | offset);
+    outw(0xcfc, value);
+}
+
+static void qpci_pc_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint32_t value)
+{
+    outl(0xcf8, (1 << 31) | (devfn << 8) | offset);
+    outl(0xcfc, value);
+}
+
+static void *qpci_pc_iomap(QPCIBus *bus, QPCIDevice *dev, int barno)
+{
+    QPCIBusPC *s = container_of(bus, QPCIBusPC, bus);
+    static const int bar_reg_map[] = {
+        PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2,
+        PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5,
+    };
+    int bar_reg;
+    uint32_t addr;
+    uint64_t size;
+
+    g_assert(barno >= 0 && barno <= 5);
+    bar_reg = bar_reg_map[barno];
+
+    qpci_config_writel(dev, bar_reg, 0xFFFFFFFF);
+    addr = qpci_config_readl(dev, bar_reg);
+
+    size = (1ULL << ctol(addr));
+    if (size == 0) {
+        return NULL;
+    }
+
+    if (addr & PCI_BASE_ADDRESS_SPACE_IO) {
+        uint16_t loc;
+
+        g_assert((s->pci_iohole_alloc + size) <= s->pci_iohole_size);
+        loc = s->pci_iohole_start + s->pci_iohole_alloc;
+        s->pci_iohole_alloc += size;
+
+        qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO);
+
+        return (void *)(intptr_t)loc;
+    } else {
+        uint64_t loc;
+
+        g_assert((s->pci_hole_alloc + size) <= s->pci_hole_size);
+        loc = s->pci_hole_start + s->pci_hole_alloc;
+        s->pci_hole_alloc += size;
+
+        qpci_config_writel(dev, bar_reg, loc);
+
+        return (void *)(intptr_t)loc;
+    }
+}
+
+static void qpci_pc_iounmap(QPCIBus *bus, void *data)
+{
+    /* FIXME */
+}
+
+QPCIBus *qpci_init_pc(void)
+{
+    QPCIBusPC *ret;
+
+    ret = g_malloc(sizeof(*ret));
+
+    ret->bus.io_readb = qpci_pc_io_readb;
+    ret->bus.io_readw = qpci_pc_io_readw;
+    ret->bus.io_readl = qpci_pc_io_readl;
+
+    ret->bus.io_writeb = qpci_pc_io_writeb;
+    ret->bus.io_writew = qpci_pc_io_writew;
+    ret->bus.io_writel = qpci_pc_io_writel;
+
+    ret->bus.config_readb = qpci_pc_config_readb;
+    ret->bus.config_readw = qpci_pc_config_readw;
+    ret->bus.config_readl = qpci_pc_config_readl;
+
+    ret->bus.config_writeb = qpci_pc_config_writeb;
+    ret->bus.config_writew = qpci_pc_config_writew;
+    ret->bus.config_writel = qpci_pc_config_writel;
+
+    ret->bus.iomap = qpci_pc_iomap;
+    ret->bus.iounmap = qpci_pc_iounmap;
+
+    ret->pci_hole_start = 0xE0000000;
+    ret->pci_hole_size = 0x20000000;
+    ret->pci_hole_alloc = 0;
+
+    ret->pci_iohole_start = 0xc000;
+    ret->pci_iohole_size = 0x4000;
+    ret->pci_iohole_alloc = 0;
+
+    return &ret->bus;
+}
diff --git a/tests/libqos/pci-pc.h b/tests/libqos/pci-pc.h
new file mode 100644
index 0000000..bedd5c3
--- /dev/null
+++ b/tests/libqos/pci-pc.h
@@ -0,0 +1,8 @@ 
+#ifndef LIBQOS_PCI_PC_H
+#define LIBQOS_PCI_PC_H
+
+#include "libqos/pci.h"
+
+QPCIBus *qpci_init_pc(void);
+
+#endif
diff --git a/tests/libqos/pci.c b/tests/libqos/pci.c
new file mode 100644
index 0000000..78d0bc0
--- /dev/null
+++ b/tests/libqos/pci.c
@@ -0,0 +1,108 @@ 
+#include "libqos/pci.h"
+
+#include "hw/pci/pci_regs.h"
+#include <glib.h>
+
+#include <stdio.h>
+
+QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn)
+{
+    QPCIDevice *dev;
+
+    dev = g_malloc0(sizeof(*dev));
+    dev->bus = bus;
+    dev->devfn = devfn;
+
+    if (qpci_config_readw(dev, PCI_VENDOR_ID) == 0xFFFF) {
+        printf("vendor id is %x\n", qpci_config_readw(dev, PCI_VENDOR_ID));
+        g_free(dev);
+        return NULL;
+    }
+
+    return dev;
+}
+
+void qpci_device_enable(QPCIDevice *dev)
+{
+    uint16_t cmd;
+
+    /* FIXME -- does this need to be a bus callout? */
+    cmd = qpci_config_readw(dev, PCI_COMMAND);
+    cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+    qpci_config_writew(dev, PCI_COMMAND, cmd);
+}
+
+uint8_t qpci_config_readb(QPCIDevice *dev, uint8_t offset)
+{
+    return dev->bus->config_readb(dev->bus, dev->devfn, offset);
+}
+
+uint16_t qpci_config_readw(QPCIDevice *dev, uint8_t offset)
+{
+    return dev->bus->config_readw(dev->bus, dev->devfn, offset);
+}
+
+uint32_t qpci_config_readl(QPCIDevice *dev, uint8_t offset)
+{
+    return dev->bus->config_readl(dev->bus, dev->devfn, offset);
+}
+
+
+void qpci_config_writeb(QPCIDevice *dev, uint8_t offset, uint8_t value)
+{
+    dev->bus->config_writeb(dev->bus, dev->devfn, offset, value);
+}
+
+void qpci_config_writew(QPCIDevice *dev, uint8_t offset, uint16_t value)
+{
+    dev->bus->config_writew(dev->bus, dev->devfn, offset, value);
+}
+
+void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value)
+{
+    dev->bus->config_writew(dev->bus, dev->devfn, offset, value);
+}
+
+
+uint8_t qpci_io_readb(QPCIDevice *dev, void *data)
+{
+    return dev->bus->io_readb(dev->bus, data);
+}
+
+uint16_t qpci_io_readw(QPCIDevice *dev, void *data)
+{
+    return dev->bus->io_readw(dev->bus, data);
+}
+
+uint32_t qpci_io_readl(QPCIDevice *dev, void *data)
+{
+    return dev->bus->io_readl(dev->bus, data);
+}
+
+
+void qpci_io_writeb(QPCIDevice *dev, void *data, uint8_t value)
+{
+    dev->bus->io_writeb(dev->bus, data, value);
+}
+
+void qpci_io_writew(QPCIDevice *dev, void *data, uint16_t value)
+{
+    dev->bus->io_writew(dev->bus, data, value);
+}
+
+void qpci_io_writel(QPCIDevice *dev, void *data, uint32_t value)
+{
+    dev->bus->io_writel(dev->bus, data, value);
+}
+
+void *qpci_iomap(QPCIDevice *dev, int barno)
+{
+    return dev->bus->iomap(dev->bus, dev, barno);
+}
+
+void qpci_iounmap(QPCIDevice *dev, void *data)
+{
+    dev->bus->iounmap(dev->bus, data);
+}
+
+
diff --git a/tests/libqos/pci.h b/tests/libqos/pci.h
new file mode 100644
index 0000000..5162a79
--- /dev/null
+++ b/tests/libqos/pci.h
@@ -0,0 +1,65 @@ 
+#ifndef LIBQOS_PCI_H
+#define LIBQOS_PCI_H
+
+#include <stdint.h>
+
+#define QPCI_DEVFN(dev, fn) (((dev) << 3) | (fn))
+
+typedef struct QPCIDevice QPCIDevice;
+typedef struct QPCIBus QPCIBus;
+
+struct QPCIBus
+{
+    uint8_t (*io_readb)(QPCIBus *bus, void *addr);
+    uint16_t (*io_readw)(QPCIBus *bus, void *addr);
+    uint32_t (*io_readl)(QPCIBus *bus, void *addr);
+
+    void (*io_writeb)(QPCIBus *bus, void *addr, uint8_t value);
+    void (*io_writew)(QPCIBus *bus, void *addr, uint16_t value);
+    void (*io_writel)(QPCIBus *bus, void *addr, uint32_t value);
+
+    uint8_t (*config_readb)(QPCIBus *bus, int devfn, uint8_t offset);
+    uint16_t (*config_readw)(QPCIBus *bus, int devfn, uint8_t offset);
+    uint32_t (*config_readl)(QPCIBus *bus, int devfn, uint8_t offset);
+
+    void (*config_writeb)(QPCIBus *bus, int devfn,
+                          uint8_t offset, uint8_t value);
+    void (*config_writew)(QPCIBus *bus, int devfn,
+                          uint8_t offset, uint16_t value);
+    void (*config_writel)(QPCIBus *bus, int devfn,
+                          uint8_t offset, uint32_t value);
+
+    void *(*iomap)(QPCIBus *bus, QPCIDevice *dev, int barno);
+    void (*iounmap)(QPCIBus *bus, void *data);
+};
+
+struct QPCIDevice
+{
+    QPCIBus *bus;
+    int devfn;
+};
+
+QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn);
+
+void qpci_device_enable(QPCIDevice *dev);
+
+uint8_t qpci_config_readb(QPCIDevice *dev, uint8_t offset);
+uint16_t qpci_config_readw(QPCIDevice *dev, uint8_t offset);
+uint32_t qpci_config_readl(QPCIDevice *dev, uint8_t offset);
+
+void qpci_config_writeb(QPCIDevice *dev, uint8_t offset, uint8_t value);
+void qpci_config_writew(QPCIDevice *dev, uint8_t offset, uint16_t value);
+void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value);
+
+uint8_t qpci_io_readb(QPCIDevice *dev, void *data);
+uint16_t qpci_io_readw(QPCIDevice *dev, void *data);
+uint32_t qpci_io_readl(QPCIDevice *dev, void *data);
+
+void qpci_io_writeb(QPCIDevice *dev, void *data, uint8_t value);
+void qpci_io_writew(QPCIDevice *dev, void *data, uint16_t value);
+void qpci_io_writel(QPCIDevice *dev, void *data, uint32_t value);
+
+void *qpci_iomap(QPCIDevice *dev, int barno);
+void qpci_iounmap(QPCIDevice *dev, void *data);
+
+#endif