--- /dev/null
+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 $($*)
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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 ]);
+ }
+}
--- /dev/null
+/*
+ * 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 */
+