]> xenbits.xensource.com Git - people/pauldu/demu.git/commitdiff
Initial commit
authorPaul Durrant <paul.durrant@citrix.com>
Wed, 13 Nov 2013 16:23:45 +0000 (16:23 +0000)
committerPaul Durrant <paul.durrant@citrix.com>
Wed, 13 Nov 2013 16:23:45 +0000 (16:23 +0000)
Signed-off-by: Paul Durrant <paul.durrant@citrix.com>
Makefile [new file with mode: 0644]
debug.h [new file with mode: 0644]
device.c [new file with mode: 0644]
device.h [new file with mode: 0644]
main.c [new file with mode: 0644]
pci.c [new file with mode: 0644]
pci.h [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..eaddd3b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,51 @@
+TARGET = demu
+
+OBJS :=        device.o \
+       pci.o \
+       main.o
+
+CFLAGS  = -I$(shell pwd)/include
+
+# _GNU_SOURCE for asprintf.
+CFLAGS += -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_GNU_SOURCE 
+CFLAGS += -Wall -Werror -g -O1 
+
+ifeq ($(shell uname),Linux)
+LDLIBS := -lutil -lrt
+endif
+
+LDLIBS += -lxenstore -lxenctrl
+
+# Get gcc to generate the dependencies for us.
+CFLAGS   += -Wp,-MD,$(@D)/.$(@F).d
+
+SUBDIRS  = $(filter-out ./,$(dir $(OBJS) $(LIBS)))
+DEPS     = .*.d
+
+LDFLAGS := -g 
+
+all: $(TARGET)
+
+$(TARGET): $(LIBS) $(OBJS)
+       gcc -o $@ $(LDFLAGS) $(OBJS) $(LIBS) $(LDLIBS)
+
+%.o: %.c
+       gcc -o $@ $(CFLAGS) -c $<
+
+.PHONY: ALWAYS
+
+clean:
+       $(foreach dir,$(SUBDIRS),make -C $(dir) clean)
+       rm -f $(OBJS)
+       rm -f $(DEPS)
+       rm -f $(TARGET)
+       rm -f TAGS
+
+.PHONY: TAGS
+TAGS:
+       find . -name \*.[ch] | etags -
+
+-include $(DEPS)
+
+print-%:
+       echo $($*)
diff --git a/debug.h b/debug.h
new file mode 100644 (file)
index 0000000..816d778
--- /dev/null
+++ b/debug.h
@@ -0,0 +1,40 @@
+/*  
+ * Copyright (c) 2012, Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef  _DEBUG_H
+#define  _DEBUG_H
+
+#define DBG(...)                            \
+    do {                                    \
+        fprintf(stderr, "%s: ", __func__);  \
+        fprintf(stderr, __VA_ARGS__);       \
+        fflush(stderr);                     \
+    } while (FALSE)
+
+#endif  /* _DEBUG_H */
+
diff --git a/device.c b/device.c
new file mode 100644 (file)
index 0000000..5cc2df5
--- /dev/null
+++ b/device.c
@@ -0,0 +1,210 @@
+/*  
+ * Copyright (c) 2012, Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#include <err.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include <sys/types.h>
+
+#include <xenctrl.h>
+
+#include "debug.h"
+#include "pci.h"
+#include "device.h"
+
+#define FALSE 0
+
+typedef struct _device_io_state {
+    unsigned int    index;
+    unsigned int    order;
+} device_io_state_t;
+
+static  device_io_state_t   device_io_state;
+
+static void
+device_io_map(void *priv, uint64_t addr)
+{
+    device_io_state_t   *state = priv;
+
+    DBG("%d: %016"PRIx64" - %016"PRIx64"\n",
+        state->index,
+        addr,
+        addr + (1 << state->order) - 1);
+}
+
+static void
+device_io_unmap(void *priv)
+{
+    device_io_state_t   *state = priv;
+
+    DBG("%d\n", state->index);
+}
+
+static uint8_t
+device_io_readb(void *priv, uint64_t offset)
+{
+    return 0;
+}
+
+static void
+device_io_writeb(void *priv, uint64_t offset, uint8_t val)
+{
+}
+
+static bar_ops_t    device_io_ops = {
+    .map = device_io_map,
+    .unmap = device_io_unmap,
+    .readb = device_io_readb,
+    .writeb = device_io_writeb
+};
+
+typedef struct _device_memory_state {
+    unsigned int    index;
+    unsigned int    order;
+} device_memory_state_t;
+
+static  device_memory_state_t   device_memory_state;
+
+static void
+device_memory_map(void *priv, uint64_t addr)
+{
+    device_memory_state_t   *state = priv;
+
+    DBG("%d: %016"PRIx64" - %016"PRIx64"\n",
+        state->index,
+        addr,
+        addr + (1 << state->order) - 1);
+}
+
+static void
+device_memory_unmap(void *priv)
+{
+    device_memory_state_t   *state = priv;
+
+    DBG("%d\n", state->index);
+}
+
+static uint8_t
+device_memory_readb(void *priv, uint64_t offset)
+{
+    return 0;
+}
+
+static void
+device_memory_writeb(void *priv, uint64_t offset, uint8_t val)
+{
+}
+
+static bar_ops_t    device_memory_ops = {
+    .map = device_memory_map,
+    .unmap = device_memory_unmap,
+    .readb = device_memory_readb,
+    .writeb = device_memory_writeb
+};
+
+int
+device_initialize(xc_interface *xch, domid_t domid, ioservid_t ioservid,
+                  unsigned int bus, unsigned int device, unsigned int function)
+{
+    pci_info_t  info;
+    int         rc;
+
+    info.bus = bus;
+    info.device = device;
+    info.function = function;
+
+    info.vendor_id = 0x5853;
+    info.device_id = 0x0003;
+    info.subvendor_id = 0x5853;
+    info.subdevice_id = 0x0003;
+    info.revision = 0x01;
+    info.class = 0x01;
+    info.subclass = 0x00;
+    info.prog_if = 0x00;
+    info.header_type = 0;
+    info.command = PCI_COMMAND_IO;
+    info.interrupt_pin = 1;
+
+    rc = pci_device_register(xch, domid, ioservid, &info);
+    if (rc < 0)
+        goto fail1;
+
+    device_io_state.index = 0;
+    device_io_state.order = 8;
+
+    rc = pci_bar_register(device_io_state.index,
+                          PCI_BASE_ADDRESS_SPACE_IO,
+                          device_io_state.order,
+                          &device_io_ops,
+                          &device_io_state);
+    if (rc < 0)
+        goto fail2;
+
+    device_memory_state.index = 1;
+    device_memory_state.order = 24;
+
+    rc = pci_bar_register(device_memory_state.index,
+                          PCI_BASE_ADDRESS_SPACE_MEMORY |
+                          PCI_BASE_ADDRESS_MEM_PREFETCH,
+                          device_memory_state.order,
+                          &device_memory_ops,
+                          &device_memory_state);
+    if (rc < 0)
+        goto fail3;
+
+    return 0;
+
+fail3:
+    DBG("fail3\n");
+
+    pci_bar_deregister(0);
+
+fail2:
+    DBG("fail2\n");
+
+    pci_device_deregister();
+
+fail1:
+    DBG("fail1\n");
+
+    warn("fail");
+    return -1;
+}
+
+void
+device_teardown(void)
+{
+    pci_bar_deregister(1);
+    pci_bar_deregister(0);
+    pci_device_deregister();
+}
diff --git a/device.h b/device.h
new file mode 100644 (file)
index 0000000..8e4622e
--- /dev/null
+++ b/device.h
@@ -0,0 +1,39 @@
+/*  
+ * Copyright (c) 2012, Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef  _DEVICE_H
+#define  _DEVICE_H
+
+int     device_initialize(xc_interface *xch, domid_t domid, ioservid_t ioservid,
+                          unsigned int bus, unsigned int device,
+                          unsigned int function);
+
+void    device_teardown(void);
+
+#endif  /* _DEVICE_H */
+
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..cbb833f
--- /dev/null
+++ b/main.c
@@ -0,0 +1,829 @@
+/*  
+ * Copyright (c) 2012, Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <netinet/in.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sched.h>
+#include <assert.h>
+
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+
+#include <locale.h>
+
+#include <xenctrl.h>
+#include <xen/hvm/ioreq.h>
+
+#include "debug.h"
+#include "device.h"
+#include "pci.h"
+
+#define FALSE 0
+
+#define mb() asm volatile ("" : : : "memory")
+
+enum {
+    DEMU_OPT_DOMAIN,
+    DEMU_OPT_VCPUS,
+    DEMU_OPT_BUS,
+    DEMU_OPT_DEVICE,
+    DEMU_OPT_FUNCTION,
+    DEMU_NR_OPTS
+    };
+
+static struct option demu_option[] = {
+    {"domain", 1, NULL, 0},
+    {"vcpus", 1, NULL, 0},
+    {"bus", 1, NULL, 0},
+    {"device", 1, NULL, 0},
+    {"function", 1, NULL, 0},
+    {NULL, 0, NULL, 0}
+};
+
+static const char *demu_option_text[] = {
+    "<domid>",
+    "<vcpu count>",
+    "<bus>",
+    "<device>",
+    "<function>",
+    NULL
+};
+
+static const char *prog;
+
+static void
+usage(void)
+{
+    int i;
+
+    fprintf(stderr, "Usage: %s <options>\n\n", prog);
+
+    for (i = 0; i < DEMU_NR_OPTS; i++)
+        fprintf(stderr, "\t--%s %s\n",
+                demu_option[i].name,
+                demu_option_text[i]);
+
+    fprintf(stderr, "\n");
+
+    exit(2);
+}
+
+typedef enum {
+    DEMU_SEQ_UNINITIALIZED = 0,
+    DEMU_SEQ_INTERFACE_OPEN,
+    DEMU_SEQ_SERVER_REGISTERED,
+    DEMU_SEQ_SHARED_IOPAGE_MAPPED,
+    DEMU_SEQ_BUFFERED_IOPAGE_MAPPED,
+    DEMU_SEQ_PORT_ARRAY_ALLOCATED,
+    DEMU_SEQ_EVTCHN_OPEN,
+    DEMU_SEQ_PORTS_BOUND,
+    DEMU_SEQ_BUF_PORT_BOUND,
+    DEMU_SEQ_INITIALIZED,
+    DEMU_NR_SEQS
+} demu_seq_t;
+
+typedef struct demu_state {
+    demu_seq_t          seq;
+    xc_interface        *xch;
+    xc_interface        *xceh;
+    domid_t             domid;
+    unsigned int        vcpus;
+    ioservid_t          ioservid;
+    shared_iopage_t     *iopage;
+    evtchn_port_t       *ioreq_local_port;
+    buffered_iopage_t   *buffered_iopage;
+    evtchn_port_t       buf_ioreq_port;
+    evtchn_port_t       buf_ioreq_local_port;
+} demu_state_t;
+
+static demu_state_t demu_state;
+
+static void
+handle_pio(ioreq_t *ioreq)
+{
+    if (ioreq->dir == IOREQ_READ) {
+        if (!ioreq->data_is_ptr) {
+            ioreq->data = (uint64_t)pci_bar_read(0, ioreq->addr, ioreq->size);
+        } else {
+            assert(FALSE);
+        }
+    } else if (ioreq->dir == IOREQ_WRITE) {
+        if (!ioreq->data_is_ptr) {
+            pci_bar_write(0, ioreq->addr, ioreq->size, (uint32_t)ioreq->data);
+        } else {
+            assert(FALSE);
+        }
+    }
+}
+
+static void
+handle_copy(ioreq_t *ioreq)
+{
+    if (ioreq->dir == IOREQ_READ) {
+        if (!ioreq->data_is_ptr) {
+            ioreq->data = (uint64_t)pci_bar_read(1, ioreq->addr, ioreq->size);
+        } else {
+            assert(FALSE);
+        }
+    } else if (ioreq->dir == IOREQ_WRITE) {
+        if (!ioreq->data_is_ptr) {
+            pci_bar_write(1, ioreq->addr, ioreq->size, (uint32_t)ioreq->data);
+        } else {
+            assert(FALSE);
+        }
+    }
+}
+
+static void
+handle_pci_config(ioreq_t *ioreq)
+{
+    if (ioreq->dir == IOREQ_READ) {
+        if (!ioreq->data_is_ptr) {
+            ioreq->data = (uint32_t)pci_config_read(ioreq->addr, ioreq->size);
+        } else {
+            assert(FALSE);
+        }
+    } else if (ioreq->dir == IOREQ_WRITE) {
+        if (!ioreq->data_is_ptr) {
+            pci_config_write(ioreq->addr, ioreq->size, (uint32_t)ioreq->data);
+        } else {
+            assert(FALSE);
+        }
+    }
+}
+
+static void
+handle_ioreq(ioreq_t *ioreq)
+{
+    switch (ioreq->type) {
+    case IOREQ_TYPE_PIO:
+        handle_pio(ioreq);
+        break;
+
+    case IOREQ_TYPE_COPY:
+        handle_copy(ioreq);
+        break;
+
+    case IOREQ_TYPE_PCI_CONFIG:
+        handle_pci_config(ioreq);
+        break;
+
+    case IOREQ_TYPE_TIMEOFFSET:
+        break;
+
+    case IOREQ_TYPE_INVALIDATE:
+        break;
+
+    default:
+        DBG("UNKNOWN (%02x)", ioreq->type);
+        break;
+    }
+}
+
+static void
+demu_seq_next(void)
+{
+    assert(demu_state.seq < DEMU_SEQ_INITIALIZED);
+
+    switch (++demu_state.seq) {
+    case DEMU_SEQ_INTERFACE_OPEN:
+        DBG(">INTERFACE_OPEN\n");
+        break;
+
+    case DEMU_SEQ_SERVER_REGISTERED:
+        DBG(">SERVER_REGISTERED\n");
+        DBG("ioservid = %u\n", demu_state.ioservid);
+        break;
+
+    case DEMU_SEQ_SHARED_IOPAGE_MAPPED:
+        DBG(">SHARED_IOPAGE_MAPPED\n");
+        DBG("iopage = %p\n", demu_state.iopage);
+        break;
+
+    case DEMU_SEQ_BUFFERED_IOPAGE_MAPPED:
+        DBG(">BUFFERED_IOPAGE_MAPPED\n");
+        DBG("buffered_iopage = %p\n", demu_state.buffered_iopage);
+        break;
+
+    case DEMU_SEQ_PORT_ARRAY_ALLOCATED:
+        DBG(">PORT_ARRAY_ALLOCATED\n");
+        break;
+
+    case DEMU_SEQ_EVTCHN_OPEN:
+        DBG(">EVTCHN_OPEN\n");
+        break;
+
+    case DEMU_SEQ_PORTS_BOUND: {
+        int i;
+
+        DBG(">EVTCHN_PORTS_BOUND\n");
+
+        for (i = 0; i < demu_state.vcpus; i++)
+            DBG("VCPU%d: %u -> %u\n", i,
+                demu_state.iopage->vcpu_ioreq[i].vp_eport,
+                demu_state.ioreq_local_port[i]);
+
+        break;
+    }
+
+    case DEMU_SEQ_BUF_PORT_BOUND:
+        DBG(">EVTCHN_BUF_PORT_BOUND\n");
+
+        DBG("%u -> %u\n",
+            demu_state.buf_ioreq_port,
+            demu_state.buf_ioreq_local_port);
+        break;
+
+    case DEMU_SEQ_INITIALIZED:
+        DBG(">INITIALIZED\n");
+        break;
+
+    default:
+        assert(FALSE);
+        break;
+    }
+}
+
+static void
+demu_teardown(void)
+{
+    if (demu_state.seq == DEMU_SEQ_INITIALIZED) {
+        DBG("<INITIALIZED\n");
+        device_teardown();
+
+        demu_state.seq = DEMU_SEQ_PORTS_BOUND;
+    }
+
+    if (demu_state.seq >= DEMU_SEQ_PORTS_BOUND) {
+        DBG("<EVTCHN_BUF_PORT_BOUND\n");
+        evtchn_port_t   port;
+
+        port = demu_state.buf_ioreq_local_port;
+
+        DBG("%u\n", port);
+        (void) xc_evtchn_unbind(demu_state.xceh, port);
+
+        demu_state.seq = DEMU_SEQ_PORTS_BOUND;
+    }
+
+    if (demu_state.seq >= DEMU_SEQ_PORTS_BOUND) {
+        DBG("<EVTCHN_PORTS_BOUND\n");
+
+        demu_state.seq = DEMU_SEQ_EVTCHN_OPEN;
+    }
+
+    if (demu_state.seq >= DEMU_SEQ_EVTCHN_OPEN) {
+        int i;
+
+        DBG("<EVTCHN_OPEN\n");
+
+        for (i = 0; i < demu_state.vcpus; i++) {
+            evtchn_port_t   port;
+
+            port = demu_state.ioreq_local_port[i];
+
+            if (port >= 0) {
+                DBG("VCPU%d: %u\n", i, port);
+                (void) xc_evtchn_unbind(demu_state.xceh, port);
+            }
+        }
+
+        xc_evtchn_close(demu_state.xceh);
+
+        demu_state.seq = DEMU_SEQ_PORT_ARRAY_ALLOCATED;
+    }
+
+    if (demu_state.seq >= DEMU_SEQ_PORT_ARRAY_ALLOCATED) {
+        DBG("<PORT_ARRAY_ALLOCATED\n");
+
+        free(demu_state.ioreq_local_port);
+
+        demu_state.seq = DEMU_SEQ_BUFFERED_IOPAGE_MAPPED;
+    }
+
+    if (demu_state.seq >= DEMU_SEQ_BUFFERED_IOPAGE_MAPPED) {
+        DBG("<BUFFERED_IOPAGE_MAPPED\n");
+
+        munmap(demu_state.buffered_iopage, XC_PAGE_SIZE);
+
+        demu_state.seq = DEMU_SEQ_SHARED_IOPAGE_MAPPED;
+    }
+
+    if (demu_state.seq >= DEMU_SEQ_SHARED_IOPAGE_MAPPED) {
+        DBG("<SHARED_IOPAGE_MAPPED\n");
+
+        munmap(demu_state.iopage, XC_PAGE_SIZE);
+
+        demu_state.seq = DEMU_SEQ_SERVER_REGISTERED;
+    }
+
+    if (demu_state.seq >= DEMU_SEQ_SERVER_REGISTERED) {
+        DBG("<SERVER_REGISTERED\n");
+
+        (void) xc_hvm_destroy_ioreq_server(demu_state.xch,
+                                           demu_state.domid,
+                                           demu_state.ioservid);
+        demu_state.seq = DEMU_SEQ_INTERFACE_OPEN;
+    }
+
+    if (demu_state.seq >= DEMU_SEQ_INTERFACE_OPEN) {
+        DBG("<INTERFACE_OPEN\n");
+
+        xc_interface_close(demu_state.xch);
+
+        demu_state.seq = DEMU_SEQ_UNINITIALIZED;
+    }
+}
+
+static struct sigaction sigterm_handler;
+
+static void
+demu_sigterm(int num)
+{
+    DBG("%s\n", strsignal(num));
+
+    demu_teardown();
+
+    exit(0);
+}
+
+static struct sigaction sigusr1_handler;
+
+static void
+demu_sigusr1(int num)
+{
+    DBG("%s\n", strsignal(num));
+
+    sigaction(SIGHUP, &sigusr1_handler, NULL);
+
+    pci_config_dump();
+}
+
+static int
+demu_initialize(domid_t domid, unsigned int vcpus, unsigned int bus,
+                unsigned int device, unsigned int function)
+{
+    int             rc;
+    xc_dominfo_t    dominfo;
+    unsigned long   pfn;
+    evtchn_port_t   port;
+    int             i;
+
+    demu_state.domid = domid;
+    demu_state.vcpus = vcpus;
+
+    demu_state.xch = xc_interface_open(NULL, NULL, 0);
+    if (demu_state.xch == NULL)
+        goto fail1;
+
+    demu_seq_next();
+
+    rc = xc_domain_getinfo(demu_state.xch, demu_state.domid, 1, &dominfo);
+    if (rc < 0 || dominfo.domid != demu_state.domid)
+        goto fail2;
+
+    rc = xc_hvm_create_ioreq_server(demu_state.xch, demu_state.domid, &demu_state.ioservid);
+    if (rc < 0)
+        goto fail3;
+    
+    demu_seq_next();
+
+    rc = xc_hvm_get_ioreq_server_pfn(demu_state.xch, demu_state.domid,
+                                    demu_state.ioservid, &pfn);
+    if (rc < 0)
+        goto fail4;
+
+    demu_state.iopage = xc_map_foreign_range(demu_state.xch,
+                                             demu_state.domid,
+                                             XC_PAGE_SIZE,
+                                             PROT_READ | PROT_WRITE,
+                                             pfn);
+    if (demu_state.iopage == NULL)
+        goto fail5;
+
+    demu_seq_next();
+
+    rc = xc_hvm_get_ioreq_server_buf_pfn(demu_state.xch, demu_state.domid,
+                                        demu_state.ioservid, &pfn);
+    if (rc < 0)
+        goto fail6;
+    demu_state.buffered_iopage = xc_map_foreign_range(demu_state.xch,
+                                                      demu_state.domid,
+                                                      XC_PAGE_SIZE,
+                                                      PROT_READ | PROT_WRITE,
+                                                      pfn);
+    if (demu_state.buffered_iopage == NULL)
+        goto fail7;
+
+    demu_seq_next();
+
+    demu_state.ioreq_local_port = malloc(sizeof (evtchn_port_t) *
+                                         demu_state.vcpus);
+    if (demu_state.ioreq_local_port == NULL)
+        goto fail8;
+
+    for (i = 0; i < demu_state.vcpus; i++)
+        demu_state.ioreq_local_port[i] = -1;
+
+    demu_seq_next();
+
+    demu_state.xceh = xc_evtchn_open(NULL, 0);
+    if (demu_state.xceh == NULL)
+        goto fail9;
+
+    demu_seq_next();
+
+    for (i = 0; i < demu_state.vcpus; i++) {
+        evtchn_port_t port = demu_state.iopage->vcpu_ioreq[i].vp_eport;
+
+        rc = xc_evtchn_bind_interdomain(demu_state.xceh, demu_state.domid,
+                                        port);
+        if (rc < 0)
+            goto fail10;
+
+        demu_state.ioreq_local_port[i] = rc;
+    }
+
+    demu_seq_next();
+
+    rc = xc_hvm_get_ioreq_server_buf_port(demu_state.xch, demu_state.domid,
+                                          demu_state.ioservid, &port);
+    if (rc < 0)
+           goto fail11;
+
+    rc = xc_evtchn_bind_interdomain(demu_state.xceh, demu_state.domid,
+                                    port);
+    if (rc < 0)
+        goto fail12;
+
+    demu_state.buf_ioreq_local_port = rc;
+
+    demu_seq_next();
+
+    rc = device_initialize(demu_state.xch, demu_state.domid,
+                           demu_state.ioservid, bus, device, function);
+    if (rc < 0)
+        goto fail13;
+
+    demu_seq_next();
+
+    assert(demu_state.seq == DEMU_SEQ_INITIALIZED);
+    return 0;
+
+fail13:
+    DBG("fail13\n");
+
+fail12:
+    DBG("fail12\n");
+
+fail11:
+    DBG("fail11\n");
+
+fail10:
+    DBG("fail10\n");
+
+fail9:
+    DBG("fail9\n");
+
+fail8:
+    DBG("fail8\n");
+
+fail7:
+    DBG("fail7\n");
+
+fail6:
+    DBG("fail6\n");
+
+fail5:
+    DBG("fail5\n");
+
+fail4:
+    DBG("fail4\n");
+
+fail3:
+    DBG("fail3\n");
+
+fail2:
+    DBG("fail2\n");
+
+fail1:
+    DBG("fail1\n");
+
+    warn("fail");
+    return -1;
+}
+
+static void
+demu_poll_buffered_iopage(void)
+{
+    if (demu_state.seq != DEMU_SEQ_INITIALIZED)
+        return;
+
+    for (;;) {
+        unsigned int    read_pointer;
+        unsigned int    write_pointer;
+        
+        read_pointer = demu_state.buffered_iopage->read_pointer;
+        write_pointer = demu_state.buffered_iopage->write_pointer;
+
+        if (read_pointer == write_pointer)
+            break;
+
+        while (read_pointer != write_pointer) {
+            unsigned int    slot;
+            buf_ioreq_t     *buf_ioreq;
+            ioreq_t         ioreq;
+
+            slot = read_pointer % IOREQ_BUFFER_SLOT_NUM;
+
+            buf_ioreq = &demu_state.buffered_iopage->buf_ioreq[slot];
+
+            ioreq.size = 1UL << buf_ioreq->size;
+            ioreq.count = 1;
+            ioreq.addr = buf_ioreq->addr;
+            ioreq.data = buf_ioreq->data;
+            ioreq.state = STATE_IOREQ_READY;
+            ioreq.dir = buf_ioreq->dir;
+            ioreq.df = 1;
+            ioreq.type = buf_ioreq->type;
+            ioreq.data_is_ptr = 0;
+
+            read_pointer++;
+
+            if (ioreq.size == 8) {
+                slot = read_pointer % IOREQ_BUFFER_SLOT_NUM;
+                buf_ioreq = &demu_state.buffered_iopage->buf_ioreq[slot];
+
+                ioreq.data |= ((uint64_t)buf_ioreq->data) << 32;
+
+                read_pointer++;
+            }
+
+            handle_ioreq(&ioreq);
+            mb();
+        }
+
+        demu_state.buffered_iopage->read_pointer = read_pointer;
+        mb();
+    }
+}
+
+static void
+demu_poll_iopage(unsigned int i)
+{
+    ioreq_t         *ioreq;
+
+    if (demu_state.seq != DEMU_SEQ_INITIALIZED)
+        return;
+
+    ioreq = &demu_state.iopage->vcpu_ioreq[i];
+    if (ioreq->state != STATE_IOREQ_READY) {
+        fprintf(stderr, "IO request not ready\n");
+        return;
+    }
+    mb();
+
+    ioreq->state = STATE_IOREQ_INPROCESS;
+
+    handle_ioreq(ioreq);
+    mb();
+
+    ioreq->state = STATE_IORESP_READY;
+    mb();
+
+    xc_evtchn_notify(demu_state.xceh, demu_state.ioreq_local_port[i]);
+}
+
+static void
+demu_poll_iopages(void)
+{
+    evtchn_port_t   port;
+    int             i;
+
+    if (demu_state.seq != DEMU_SEQ_INITIALIZED)
+        return;
+
+    port = xc_evtchn_pending(demu_state.xceh);
+    if (port < 0)
+        return;
+
+    if (port == demu_state.buf_ioreq_local_port) {
+        xc_evtchn_unmask(demu_state.xceh, port);
+        demu_poll_buffered_iopage();
+    } else {
+        for (i = 0; i < demu_state.vcpus; i++) {
+            if (port == demu_state.ioreq_local_port[i]) {
+                xc_evtchn_unmask(demu_state.xceh, port);
+                demu_poll_iopage(i);
+            }
+        }
+    }
+}
+
+int
+main(int argc, char **argv, char **envp)
+{
+    char            *domain_str;
+    char            *vcpus_str;
+    char            *bus_str;
+    char            *device_str;
+    char            *function_str;
+    int             index;
+    char            *end;
+    domid_t         domid;
+    unsigned int    vcpus;
+    unsigned int    bus;
+    unsigned int    device;
+    unsigned int    function;
+    sigset_t        block;
+    struct pollfd   pfd;
+    int             rc;
+
+    prog = basename(argv[0]);
+
+    domain_str = NULL;
+    vcpus_str = NULL;
+    bus_str = NULL;
+    device_str = NULL;
+    function_str = NULL;
+
+    for (;;) {
+        char    c;
+
+        c = getopt_long(argc, argv, "", demu_option, &index);
+        if (c == -1)
+            break;
+
+        DBG("--%s = '%s'\n", demu_option[index].name, optarg);
+
+        assert(c == 0);
+        switch (index) {
+        case DEMU_OPT_DOMAIN:
+            domain_str = optarg;
+            break;
+
+        case DEMU_OPT_VCPUS:
+            vcpus_str = optarg;
+            break;
+
+        case DEMU_OPT_BUS:
+            bus_str = optarg;
+            break;
+
+        case DEMU_OPT_DEVICE:
+            device_str = optarg;
+            break;
+
+        case DEMU_OPT_FUNCTION:
+            function_str = optarg;
+            break;
+
+        default:
+            assert(FALSE);
+            break;
+        }
+    }
+
+    if (domain_str == NULL ||
+        vcpus_str == NULL ||
+        bus_str == NULL ||
+        device_str == NULL ||
+        function_str == NULL) {
+        usage();
+        /*NOTREACHED*/
+    }
+
+    domid = (domid_t)strtol(domain_str, &end, 0);
+    if (*end != '\0') {
+        fprintf(stderr, "invalid domain '%s'\n", domain_str);
+        exit(1);
+    }
+
+    vcpus = (unsigned int)strtol(vcpus_str, &end, 0);
+    if (*end != '\0') {
+        fprintf(stderr, "invalid vcpu count '%s'\n", vcpus_str);
+        exit(1);
+    }
+
+    bus = (unsigned int)strtol(bus_str, &end, 0);
+    if (*end != '\0') {
+        fprintf(stderr, "invalid bus '%s'\n", bus_str);
+        exit(1);
+    }
+
+    device = (unsigned int)strtol(device_str, &end, 0);
+    if (*end != '\0') {
+        fprintf(stderr, "invalid vcpu count '%s'\n", device_str);
+        exit(1);
+    }
+
+    function = (unsigned int)strtol(function_str, &end, 0);
+    if (*end != '\0') {
+        fprintf(stderr, "invalid vcpu count '%s'\n", function_str);
+        exit(1);
+    }
+
+    sigfillset(&block);
+
+    memset(&sigterm_handler, 0, sizeof (struct sigaction));
+    sigterm_handler.sa_handler = demu_sigterm;
+
+    sigaction(SIGTERM, &sigterm_handler, NULL);
+    sigdelset(&block, SIGTERM);
+
+    sigaction(SIGINT, &sigterm_handler, NULL);
+    sigdelset(&block, SIGINT);
+
+    sigaction(SIGHUP, &sigterm_handler, NULL);
+    sigdelset(&block, SIGHUP);
+
+    sigaction(SIGABRT, &sigterm_handler, NULL);
+    sigdelset(&block, SIGABRT);
+
+    memset(&sigusr1_handler, 0, sizeof (struct sigaction));
+    sigusr1_handler.sa_handler = demu_sigusr1;
+
+    sigaction(SIGUSR1, &sigusr1_handler, NULL);
+    sigdelset(&block, SIGUSR1);
+
+    sigprocmask(SIG_BLOCK, &block, NULL);
+
+    rc = demu_initialize(domid, vcpus, bus, device, function);
+    if (rc < 0) {
+        demu_teardown();
+        exit(1);
+    }
+
+    pfd.fd = xc_evtchn_fd(demu_state.xceh);
+    pfd.events = POLLIN | POLLERR | POLLHUP;
+    pfd.revents = 0;
+
+    for (;;) {
+        rc = poll(&pfd, 1, 5000);
+
+        if (rc > 0 && pfd.revents & POLLIN)
+            demu_poll_iopages();
+
+        if (rc < 0 && errno != EINTR)
+            break;
+    }
+
+    return 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * c-tab-always-indent: nil
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/pci.c b/pci.c
new file mode 100644 (file)
index 0000000..584e94e
--- /dev/null
+++ b/pci.c
@@ -0,0 +1,484 @@
+/*  
+ * Copyright (c) 2012, Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#include <err.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <sys/types.h>
+
+#include <xenctrl.h>
+
+#include "debug.h"
+#include "pci.h"
+
+#define FALSE 0
+
+typedef struct pci_bar {
+    const bar_ops_t *ops;
+    int             is_mmio;
+    int             enable;
+    uint32_t        addr;
+    uint32_t        size;
+    void            *priv;
+} pci_bar_t;
+
+typedef struct pci {
+    xc_interface    *xch;
+    domid_t         domid;
+    ioservid_t      ioservid;
+    uint16_t        bdf;
+    uint8_t         config[PCI_CONFIG_SIZE];
+    uint8_t         mask[PCI_CONFIG_SIZE];
+    pci_bar_t       bar[PCI_NUM_BAR];
+    uint32_t        irq_pin;
+    uint32_t        irq_state;
+} pci_t;
+
+static pci_t    pci;
+
+int
+pci_device_register(xc_interface *xch, domid_t domid, ioservid_t ioservid,
+                    const pci_info_t *info)
+{
+    int     rc;
+
+    pci.xch = xch;
+    pci.domid = domid;
+    pci.ioservid = ioservid;
+
+    if (info->bus & ~0xff ||
+        info->device & ~0x1f ||
+        info->function & ~0x07)
+        goto fail1;
+
+    pci.bdf = (info->bus << 8) | (info->device << 3) | (info->function);
+
+    *(uint16_t *)&pci.config[PCI_VENDOR_ID] = info->vendor_id;
+    *(uint16_t *)&pci.config[PCI_DEVICE_ID] = info->device_id;
+    pci.config[PCI_REVISION_ID] = info->revision;
+    pci.config[PCI_CLASS_PROG] = info->prog_if;
+    pci.config[PCI_CLASS_DEVICE + 1] = info->class;
+    pci.config[PCI_CLASS_DEVICE] = info->subclass;
+    pci.config[PCI_HEADER_TYPE] = info->header_type;
+    *(uint16_t *)&pci.config[PCI_SUBSYSTEM_VENDOR_ID] = info->subvendor_id;
+    *(uint16_t *)&pci.config[PCI_SUBSYSTEM_ID] = info->subdevice_id;
+    *(uint16_t *)&pci.config[PCI_COMMAND] = info->command;
+    pci.config[PCI_INTERRUPT_PIN] = info->interrupt_pin;
+
+    pci.mask[PCI_CACHE_LINE_SIZE] = 0xff;
+    pci.mask[PCI_INTERRUPT_LINE] = 0xff;
+    *(uint16_t *)&pci.mask[PCI_COMMAND] = PCI_COMMAND_IO |
+                                          PCI_COMMAND_MEMORY |
+                                          PCI_COMMAND_MASTER |
+                                          PCI_COMMAND_INTX_DISABLE;
+    memset(&pci.mask[PCI_CONFIG_HEADER_SIZE], 0xff,
+           PCI_CONFIG_SIZE - PCI_CONFIG_HEADER_SIZE);
+
+    DBG("%02x:%02x:%02x\n", info->bus, info->device, info->function);
+
+    rc = xc_hvm_map_pcidev_to_ioreq_server(xch, domid, ioservid, pci.bdf);
+    if (rc < 0)
+        goto fail2;
+
+    return 0;
+
+fail2:
+    DBG("fail2\n");
+
+fail1:
+    DBG("fail1\n");
+
+    warn("fail");
+    return -1;
+}
+
+void
+pci_device_deregister(void)
+{
+    DBG("%02x:%02x:%02x\n",
+        (pci.bdf >> 8) & 0xff,
+        (pci.bdf >> 5) & 0x1f,
+        (pci.bdf     ) & 0x07);
+
+    (void) xc_hvm_unmap_pcidev_from_ioreq_server(pci.xch, pci.domid,
+                                                pci.ioservid, pci.bdf);
+}
+
+int
+pci_bar_register(unsigned int index, uint8_t type, unsigned int order,
+                 const bar_ops_t *ops, void *priv)
+{
+    pci_bar_t   *bar;
+
+    DBG("%d: %08x\n", index, 1u << order);
+
+    if (index >= PCI_NUM_BAR)
+        goto fail;
+
+    bar = &pci.bar[index];
+
+    if (bar->enable)
+        goto fail;
+
+    if (ops == NULL ||
+        ops->readb == NULL ||
+        ops->writeb == NULL)
+        goto fail;
+
+    bar->ops = ops;
+    bar->is_mmio = !(type & PCI_BASE_ADDRESS_SPACE_IO);
+    bar->size = 1u << order;
+
+    *(uint32_t *)&pci.config[PCI_BASE_ADDRESS_0 + (index * 4)] = type;
+    *(uint32_t *)&pci.mask[PCI_BASE_ADDRESS_0 + (index * 4)] = ~(bar->size - 1);
+
+    bar->enable = 1;
+    bar->addr = PCI_BAR_UNMAPPED;
+
+    bar->priv = priv;
+
+    return 0;
+
+fail:
+    return -1;
+}
+
+void
+pci_bar_deregister(unsigned int index)
+{
+    pci_bar_t *bar = &pci.bar[index];
+
+    DBG("%d\n", index);
+
+    if (bar->addr == PCI_BAR_UNMAPPED)
+        return;
+
+    (void) xc_hvm_unmap_io_range_from_ioreq_server(pci.xch,
+                                                  pci.domid,
+                                                  pci.ioservid,
+                                                  bar->is_mmio,
+                                                  bar->addr);
+}
+
+static pci_bar_t *
+pci_bar_get(int is_mmio, uint64_t addr)
+{
+    int i;
+
+    for (i = 0; i < PCI_NUM_BAR; i++)
+    {
+        pci_bar_t *bar = &pci.bar[i];
+
+        if (!bar->enable || bar->is_mmio != is_mmio)
+            continue;
+
+        if (bar->addr <= addr && addr < (bar->addr + bar->size))
+            return bar;
+    }
+
+    return NULL;
+}
+
+#define PCI_BAR_READ(_fn, _priv, _addr, _size, _count, _val)    \
+do {                                                            \
+    int             _i = 0;                                     \
+    unsigned int    _shift = 0;                                 \
+                                                                \
+    (_val) = 0;                                                 \
+    for (_i = 0; _i < (_count); _i++)                           \
+    {                                                           \
+        (_val) |= (_fn)((_priv), (_addr)) << _shift;            \
+        _shift += 8 * (_size);                                  \
+        (_addr) += (_size);                                     \
+    }                                                           \
+} while (FALSE)
+
+uint32_t
+pci_bar_read(int is_mmio, uint64_t addr, uint64_t size)
+{
+    pci_bar_t   *bar = pci_bar_get(is_mmio, addr);
+    uint32_t    val = 0;
+
+    assert(bar != NULL);
+
+    addr -= bar->addr;
+
+    switch (size) {
+    case 1:
+        val = bar->ops->readb(bar->priv, addr);
+        break;
+
+    case 2:
+        if (bar->ops->readw == NULL)
+            PCI_BAR_READ(bar->ops->readb, bar->priv, addr, 1, 2, val);
+        else
+            val = bar->ops->readw(bar->priv, addr);
+        break;
+
+    case 4:
+        if (bar->ops->readl == NULL) {
+            if (bar->ops->readw == NULL)
+                PCI_BAR_READ(bar->ops->readb, bar->priv, addr, 1, 4, val);
+            else
+                PCI_BAR_READ(bar->ops->readw, bar->priv, addr, 2, 2, val);
+        } else {
+            val = bar->ops->readl(bar->priv, addr);
+        }
+        break;
+
+    default:
+        assert(FALSE);
+    }
+
+    return val;
+}
+
+#define PCI_BAR_WRITE(_fn, _priv, _addr, _size, _count, _val)   \
+do {                                                            \
+    int             _i = 0;                                     \
+    unsigned int    _shift = 0;                                 \
+                                                                \
+    (_val) = 0;                                                 \
+    for (_i = 0; _i < (_count); _i++)                           \
+    {                                                           \
+        (_fn)((_priv), (_addr), (_val) >> _shift);              \
+        _shift += 8 * (_size);                                  \
+        (_addr) += (_size);                                     \
+    }                                                           \
+} while (FALSE)
+
+void
+pci_bar_write(int is_mmio, uint64_t addr, uint64_t size, uint32_t val)
+{
+    pci_bar_t   *bar = pci_bar_get(is_mmio, addr);
+
+    assert(bar != NULL);
+
+    addr -= bar->addr;
+
+    switch (size) {
+    case 1:
+        bar->ops->writeb(bar->priv, addr, val);
+        break;
+
+    case 2:
+        if (bar->ops->writew == NULL)
+            PCI_BAR_WRITE(bar->ops->writeb, bar->priv, addr, 1, 2, val);
+        else
+            bar->ops->writew(bar->priv, addr, val);
+        break;
+
+    case 4:
+        if (bar->ops->writel == NULL) {
+            if (bar->ops->writew == NULL)
+                PCI_BAR_WRITE(bar->ops->writeb, bar->priv, addr, 1, 4, val);
+            else
+                PCI_BAR_WRITE(bar->ops->writew, bar->priv, addr, 2, 2, val);
+        } else {
+            bar->ops->writel(bar->priv, addr, val);
+        }
+        break;
+
+    default:
+        assert(FALSE);
+    }
+}
+
+static void
+pci_map_bar(unsigned int index)
+{
+    pci_bar_t *bar = &pci.bar[index];
+
+    if (bar->ops->map)
+        bar->ops->map(bar->priv, bar->addr);
+
+    (void) xc_hvm_map_io_range_to_ioreq_server(pci.xch,
+                                               pci.domid,
+                                               pci.ioservid,
+                                               bar->is_mmio,
+                                               bar->addr,
+                                               bar->addr + bar->size - 1);
+}
+
+static void
+pci_unmap_bar(unsigned int index)
+{
+    pci_bar_t *bar = &pci.bar[index];
+
+    DBG("%d\n", index);
+
+    (void) xc_hvm_unmap_io_range_from_ioreq_server(pci.xch,
+                                                   pci.domid,
+                                                   pci.ioservid,
+                                                   bar->is_mmio,
+                                                   bar->addr);
+
+    if (bar->ops->unmap)
+        bar->ops->unmap(bar->priv);
+}
+
+static void
+pci_update_bar(unsigned int index)
+{
+    pci_bar_t *bar = &pci.bar[index];
+    uint32_t addr = *(uint32_t *)&pci.config[PCI_BASE_ADDRESS_0 + (index * 4)];
+    uint16_t cmd = *(uint16_t *)&pci.config[PCI_COMMAND];
+    uint32_t mask = ~(bar->size - 1);
+
+    if (!bar->enable)
+        return;
+
+    if (bar->is_mmio)
+        addr &= PCI_BASE_ADDRESS_MEM_MASK;
+    else
+        addr &= PCI_BASE_ADDRESS_IO_MASK;
+
+    if ((!(cmd & PCI_COMMAND_IO) && !bar->is_mmio)
+        || (!(cmd & PCI_COMMAND_MEMORY) && bar->is_mmio))
+        addr = PCI_BAR_UNMAPPED;
+
+    if (addr == 0 || addr == mask)
+        addr = PCI_BAR_UNMAPPED;
+
+    if (bar->addr == addr)
+        return;
+
+    if (bar->addr != PCI_BAR_UNMAPPED) {
+        pci_unmap_bar(index);
+        bar->addr = PCI_BAR_UNMAPPED;
+    }
+
+    if (addr != PCI_BAR_UNMAPPED) {
+        bar->addr = addr;
+        pci_map_bar(index);
+    }
+}
+
+static void
+pci_update_config()
+{
+    int i;
+
+    for (i = 0; i < PCI_NUM_BAR; i++)
+        pci_update_bar(i);
+}
+
+uint32_t
+pci_config_read(uint64_t addr, uint64_t size)
+{
+    uint64_t    i;
+    uint32_t    val;
+    uint16_t    bdf;
+
+    val = ~(0u);
+
+    if (!(addr & (1ull << 31)))
+        goto done;
+
+    bdf = (uint16_t)(addr >> 8);
+
+    if (bdf != pci.bdf)
+        goto done;
+
+    addr &= 0xff;
+    addr += size >> 16;
+    size &= 0xffff;
+
+    val = 0;
+    for (i = 0; i < size; i++) {
+        if ((addr + i) < PCI_CONFIG_SIZE)
+            val |= (uint32_t)pci.config[addr + i] << (i * 8);
+        else
+            val |= (uint32_t)0xff << (i + 8);
+    }
+
+    DBG("%02x (%02x) -> %08x\n",
+        (uint8_t)addr,
+        (uint8_t)size,
+        val);
+
+done:
+    return val;
+}
+
+void
+pci_config_write(uint64_t addr, uint64_t size, uint32_t val)
+{
+    uint64_t    i;
+    uint8_t     mask;
+    uint16_t    bdf;
+
+    if (!(addr & (1ull << 31)))
+        return;
+
+    bdf = (uint16_t)(addr >> 8);
+
+    if (bdf != pci.bdf)
+        return;
+
+    addr &= 0xff;
+    addr += size >> 16;
+    size &= 0xffff;
+
+    DBG("%02x (%02x) <- %08x\n",
+        (uint8_t)addr,
+        (uint8_t)size,
+        val);
+
+    for (i = 0; i < size; i++) {
+        if ((addr + i) < PCI_CONFIG_SIZE) {
+            mask = pci.mask[addr + i];
+            pci.config[addr + i] &= ~mask;
+            pci.config[addr + i] |= (uint8_t)(val >> (i * 8)) & mask;
+        }
+    }
+
+    pci_update_config();
+}
+
+void
+pci_config_dump(void)
+{
+    int i;
+
+    fprintf(stderr, "    3  2  1  0\n");
+    fprintf(stderr, "--------------\n");
+
+    for (i = 0; i < PCI_CONFIG_HEADER_SIZE; i += 4) {
+        fprintf(stderr, "%02x |%02x %02x %02x %02x\n",
+                i,
+                pci.config[i + 3],
+                pci.config[i + 2],
+                pci.config[i + 1],
+                pci.config[i ]);
+    }
+}
diff --git a/pci.h b/pci.h
new file mode 100644 (file)
index 0000000..6b76927
--- /dev/null
+++ b/pci.h
@@ -0,0 +1,83 @@
+/*  
+ * Copyright (c) 2012, Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#include <linux/pci_regs.h>
+
+#ifndef  _PCI_H
+#define  _PCI_H
+
+#define PCI_NUM_BAR             6
+#define PCI_CONFIG_HEADER_SIZE  0x40
+#define PCI_CONFIG_SIZE         0x100
+#define PCI_BAR_UNMAPPED        (~(0u))
+
+typedef struct pci_info {
+        uint8_t         bus;
+        uint8_t         device:5;
+        uint8_t         function:3;
+        uint16_t        vendor_id;
+        uint16_t        device_id;
+        uint16_t        subvendor_id;
+        uint16_t        subdevice_id;
+        uint8_t         revision;
+        uint8_t         class;
+        uint8_t         subclass;
+        uint8_t         prog_if;
+        uint8_t         header_type;
+        uint16_t        command;
+        uint8_t         interrupt_pin;
+} pci_info_t;
+
+int     pci_device_register(xc_interface *xch, domid_t domid, ioservid_t ioservid,
+                            const pci_info_t *info);
+void    pci_device_deregister(void);
+
+typedef struct bar_ops {
+        void            (*map)(void *priv, uint64_t addr);
+        void            (*unmap)(void *priv);
+        uint8_t         (*readb)(void *priv, uint64_t offset);
+        uint16_t        (*readw)(void *priv, uint64_t offset);
+        uint32_t        (*readl)(void *priv, uint64_t offset);
+        void            (*writeb)(void *priv, uint64_t offset, uint8_t val);
+        void            (*writew)(void *priv, uint64_t offset, uint16_t val);
+        void            (*writel)(void *priv, uint64_t offset, uint32_t val);
+} bar_ops_t;
+
+int     pci_bar_register(unsigned int index, uint8_t type, unsigned int order,
+                         const bar_ops_t *ops, void *priv);
+void    pci_bar_deregister(unsigned int index);
+
+uint32_t        pci_bar_read(int is_mmio, uint64_t addr, uint64_t size);
+void            pci_bar_write(int is_mmio, uint64_t addr, uint64_t size, uint32_t val);
+
+uint32_t        pci_config_read(uint64_t addr, uint64_t size);
+void            pci_config_write(uint64_t addr, uint64_t size, uint32_t val);
+void            pci_config_dump();
+
+#endif  /* _PCI_H */
+