tools/tests/regression/downloads/*
tools/tests/mem-sharing/memshrtool
tools/tests/mce-test/tools/xen-mceinj
+tools/tests/vpci/rbtree.[hc]
+tools/tests/vpci/vpci.[hc]
+tools/tests/vpci/test_vpci.out
+tools/tests/vpci/test_vpci
tools/xcutils/lsevtchn
tools/xcutils/readnotes
tools/xenbackendd/_paths.h
if (d_config->c_info.type == LIBXL_DOMAIN_TYPE_HVM) {
if (d_config->b_info.device_model_version !=
LIBXL_DEVICE_MODEL_VERSION_NONE) {
- xc_config->emulation_flags = XEN_X86_EMU_ALL;
+ xc_config->emulation_flags = (XEN_X86_EMU_ALL & ~XEN_X86_EMU_VPCI);
} else if (libxl_defbool_val(d_config->b_info.u.hvm.apic)) {
/*
* HVM guests without device model may want
SUBDIRS-$(CONFIG_X86) += x86_emulator
SUBDIRS-y += xen-access
SUBDIRS-y += xenstore
+SUBDIRS-$(CONFIG_HAS_PCI) += vpci
.PHONY: all clean install distclean
all clean distclean: %: subdirs-%
--- /dev/null
+
+XEN_ROOT=$(CURDIR)/../../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+TARGET := test_vpci
+
+.PHONY: all
+all: $(TARGET)
+
+.PHONY: run
+run: $(TARGET)
+ ./$(TARGET) > $(TARGET).out
+
+$(TARGET): vpci.c vpci.h rbtree.c rbtree.h
+ $(HOSTCC) -g -o $@ vpci.c main.c rbtree.c
+
+.PHONY: clean
+clean:
+ rm -rf $(TARGET) $(TARGET).out *.o *~ vpci.h vpci.c rbtree.c rbtree.h
+
+.PHONY: distclean
+distclean: clean
+
+.PHONY: install
+install:
+
+vpci.h: $(XEN_ROOT)/xen/include/xen/vpci.h
+ sed -e '/#include/d' <$< >$@
+
+vpci.c: $(XEN_ROOT)/xen/drivers/vpci/vpci.c
+ # Trick the compiler so it doesn't complain about missing symbols
+ sed -e '/#include/d' \
+ -e '1s;^;#include "emul.h"\
+ const vpci_register_init_t __start_vpci_array[1]\;\
+ const vpci_register_init_t __end_vpci_array[1]\;\
+ ;' <$< >$@
+
+rbtree.h: $(XEN_ROOT)/xen/include/xen/rbtree.h
+ sed -e '/#include/d' <$< >$@
+
+rbtree.c: $(XEN_ROOT)/xen/common/rbtree.c
+ sed -e "/#include/d" \
+ -e '1s;^;#include "emul.h"\
+ ;' <$< >$@
+
--- /dev/null
+/*
+ * Unit tests for the generic vPCI handler code.
+ *
+ * Copyright (C) 2017 Citrix Systems R&D
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms and conditions of the GNU General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _TEST_VPCI_
+#define _TEST_VPCI_
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <assert.h>
+
+#define container_of(ptr, type, member) ({ \
+ typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+#include "rbtree.h"
+
+struct pci_dev {
+ struct domain *domain;
+ struct vpci *vpci;
+};
+
+struct domain {
+ struct pci_dev pdev;
+};
+
+struct vcpu
+{
+ struct domain *domain;
+};
+
+extern struct vcpu v;
+
+#define spin_lock(x)
+#define spin_unlock(x)
+#define spin_is_locked(x) true
+
+#define current (&v)
+
+#define has_vpci(d) true
+
+#include "vpci.h"
+
+#define xzalloc(type) (type *)calloc(1, sizeof(type))
+#define xfree(p) free(p)
+
+#define EXPORT_SYMBOL(x)
+
+#define pci_get_pdev_by_domain(d, ...) &(d)->pdev
+
+#define atomic_read(x) 1
+
+/* Dummy native helpers. Writes are ignored, reads return 1's. */
+#define pci_conf_read8(...) (0xff)
+#define pci_conf_read16(...) (0xffff)
+#define pci_conf_read32(...) (0xffffffff)
+#define pci_conf_write8(...)
+#define pci_conf_write16(...)
+#define pci_conf_write32(...)
+
+#define BUG() assert(0)
+#define ASSERT_UNREACHABLE() assert(0)
+#define ASSERT(x) assert(x)
+
+#ifdef _LP64
+#define BITS_PER_LONG 64
+#else
+#define BITS_PER_LONG 32
+#endif
+#define GENMASK(h, l) \
+ (((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
+
+#define min(x,y) ({ \
+ const typeof(x) _x = (x); \
+ const typeof(y) _y = (y); \
+ (void) (&_x == &_y); \
+ _x < _y ? _x : _y; })
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
+
--- /dev/null
+/*
+ * Unit tests for the generic vPCI handler code.
+ *
+ * Copyright (C) 2017 Citrix Systems R&D
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms and conditions of the GNU General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "emul.h"
+
+/* Single vcpu (current), and single domain with a single PCI device. */
+static struct vpci vpci = {
+ .handlers = RB_ROOT,
+};
+
+static struct domain d = {
+ .pdev.domain = &d,
+ .pdev.vpci = &vpci,
+};
+
+struct vcpu v = { .domain = &d };
+
+/* Dummy hooks, write stores data, read fetches it. */
+static int vpci_read8(struct pci_dev *pdev, unsigned int reg,
+ union vpci_val *val, void *data)
+{
+ uint8_t *priv = data;
+
+ val->half_word = *priv;
+ return 0;
+}
+
+static int vpci_write8(struct pci_dev *pdev, unsigned int reg,
+ union vpci_val val, void *data)
+{
+ uint8_t *priv = data;
+
+ *priv = val.half_word;
+ return 0;
+}
+
+static int vpci_read16(struct pci_dev *pdev, unsigned int reg,
+ union vpci_val *val, void *data)
+{
+ uint16_t *priv = data;
+
+ val->word = *priv;
+ return 0;
+}
+
+static int vpci_write16(struct pci_dev *pdev, unsigned int reg,
+ union vpci_val val, void *data)
+{
+ uint16_t *priv = data;
+
+ *priv = val.word;
+ return 0;
+}
+
+static int vpci_read32(struct pci_dev *pdev, unsigned int reg,
+ union vpci_val *val, void *data)
+{
+ uint32_t *priv = data;
+
+ val->double_word = *priv;
+ return 0;
+}
+
+static int vpci_write32(struct pci_dev *pdev, unsigned int reg,
+ union vpci_val val, void *data)
+{
+ uint32_t *priv = data;
+
+ *priv = val.double_word;
+ return 0;
+}
+
+#define VPCI_READ(reg, size, data) \
+ assert(!xen_vpci_read(0, 0, 0, reg, size, data))
+
+#define VPCI_READ_CHECK(reg, size, expected) ({ \
+ uint32_t val; \
+ VPCI_READ(reg, size, &val); \
+ assert(val == expected); \
+ })
+
+#define VPCI_WRITE(reg, size, data) \
+ assert(!xen_vpci_write(0, 0, 0, reg, size, data))
+
+#define VPCI_CHECK_REG(reg, size, data) ({ \
+ VPCI_WRITE(reg, size, data); \
+ VPCI_READ_CHECK(reg, size, data); \
+ })
+
+#define VPCI_ADD_REG(fread, fwrite, off, size, store) \
+ assert(!xen_vpci_add_register(&d.pdev, fread, fwrite, off, size, &store)) \
+
+#define VPCI_ADD_INVALID_REG(fread, fwrite, off, size) \
+ assert(xen_vpci_add_register(&d.pdev, fread, fwrite, off, size, NULL)) \
+
+int
+main(int argc, char **argv)
+{
+ /* Index storage by offset. */
+ uint32_t r0 = 0xdeadbeef;
+ uint8_t r5 = 0xef;
+ uint8_t r6 = 0xbe;
+ uint8_t r7 = 0xef;
+ uint16_t r12 = 0x8696;
+ int rc;
+
+ VPCI_ADD_REG(vpci_read32, vpci_write32, 0, 4, r0);
+ VPCI_READ_CHECK(0, 4, 0xdeadbeef);
+ VPCI_CHECK_REG(0, 4, 0xbcbcbcbc);
+
+ VPCI_ADD_REG(vpci_read8, vpci_write8, 5, 1, r5);
+ VPCI_READ_CHECK(5, 1, 0xef);
+ VPCI_CHECK_REG(5, 1, 0xba);
+
+ VPCI_ADD_REG(vpci_read8, vpci_write8, 6, 1, r6);
+ VPCI_READ_CHECK(6, 1, 0xbe);
+ VPCI_CHECK_REG(6, 1, 0xba);
+
+ VPCI_ADD_REG(vpci_read8, vpci_write8, 7, 1, r7);
+ VPCI_READ_CHECK(7, 1, 0xef);
+ VPCI_CHECK_REG(7, 1, 0xbd);
+
+ VPCI_ADD_REG(vpci_read16, vpci_write16, 12, 2, r12);
+ VPCI_READ_CHECK(12, 2, 0x8696);
+ VPCI_READ_CHECK(12, 4, 0xffff8696);
+
+ /*
+ * At this point we have the following layout:
+ *
+ * 32 24 16 8 0
+ * +-----+-----+-----+-----+
+ * | r0 | 0
+ * +-----+-----+-----+-----+
+ * | r7 | r6 | r5 |/////| 32
+ * +-----+-----+-----+-----|
+ * |///////////////////////| 64
+ * +-----------+-----------+
+ * |///////////| r12 | 96
+ * +-----------+-----------+
+ * ...
+ * / = empty.
+ */
+
+ /* Try to add an overlapping register handler. */
+ VPCI_ADD_INVALID_REG(vpci_read32, vpci_write32, 4, 4);
+
+ /* Try to add a non-aligned register. */
+ VPCI_ADD_INVALID_REG(vpci_read16, vpci_write16, 15, 2);
+
+ /* Try to add a register with wrong size. */
+ VPCI_ADD_INVALID_REG(vpci_read16, vpci_write16, 8, 3);
+
+ /* Try to add a register with missing handlers. */
+ VPCI_ADD_INVALID_REG(vpci_read16, NULL, 8, 2);
+ VPCI_ADD_INVALID_REG(NULL, vpci_write16, 8, 2);
+
+ /* Read/write of unset register. */
+ VPCI_READ_CHECK(8, 4, 0xffffffff);
+ VPCI_READ_CHECK(8, 2, 0xffff);
+ VPCI_READ_CHECK(8, 1, 0xff);
+ VPCI_WRITE(10, 2, 0xbeef);
+ VPCI_READ_CHECK(10, 2, 0xffff);
+
+ /* Read of multiple registers */
+ VPCI_CHECK_REG(7, 1, 0xbd);
+ VPCI_READ_CHECK(4, 4, 0xbdbabaff);
+
+ /* Partial read of a register. */
+ VPCI_CHECK_REG(0, 4, 0x1a1b1c1d);
+ VPCI_READ_CHECK(2, 1, 0x1b);
+ VPCI_READ_CHECK(6, 2, 0xbdba);
+
+ /* Write of multiple registers. */
+ VPCI_CHECK_REG(4, 4, 0xaabbccff);
+
+ /* Partial write of a register. */
+ VPCI_CHECK_REG(2, 1, 0xfe);
+ VPCI_CHECK_REG(6, 2, 0xfebc);
+
+ return 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
+
__start_schedulers_array = .;
*(.data.schedulers)
__end_schedulers_array = .;
+ __start_vpci_array = .;
+ *(.data.vpci)
+ __end_vpci_array = .;
*(.data.rel)
*(.data.rel.*)
CONSTRUCTORS
if ( is_hvm_domain(d) )
{
if ( is_hardware_domain(d) &&
- emflags != (XEN_X86_EMU_LAPIC|XEN_X86_EMU_IOAPIC) )
+ emflags != (XEN_X86_EMU_LAPIC|XEN_X86_EMU_IOAPIC|
+ XEN_X86_EMU_VPCI) )
return false;
if ( !is_hardware_domain(d) && emflags &&
emflags != XEN_X86_EMU_ALL && emflags != XEN_X86_EMU_LAPIC )
#include <xen/vm_event.h>
#include <xen/monitor.h>
#include <xen/warning.h>
+#include <xen/vpci.h>
#include <asm/shadow.h>
#include <asm/hap.h>
#include <asm/current.h>
d->arch.hvm_domain.io_bitmap = hvm_io_bitmap;
register_g2m_portio_handler(d);
+ register_vpci_portio_handler(d);
hvm_ioreq_init(d);
#include <xen/trace.h>
#include <xen/event.h>
#include <xen/hypercall.h>
+#include <xen/vpci.h>
#include <asm/current.h>
#include <asm/cpufeature.h>
#include <asm/processor.h>
handler->ops = &g2m_portio_ops;
}
+/* Do some sanity checks. */
+static int vpci_access_check(unsigned int reg, unsigned int len)
+{
+ /* Check access size. */
+ if ( len != 1 && len != 2 && len != 4 )
+ {
+ gdprintk(XENLOG_WARNING, "invalid length (reg: %#x, len: %u)\n",
+ reg, len);
+ return -EINVAL;
+ }
+
+ /* Check if access crosses a double-word boundary. */
+ if ( (reg & 3) + len > 4 )
+ {
+ gdprintk(XENLOG_WARNING,
+ "invalid access across double-word boundary (reg: %#x, len: %u)\n",
+ reg, len);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Helper to decode a PCI address. */
+static void vpci_decode_addr(unsigned int cf8, unsigned int addr,
+ unsigned int *bus, unsigned int *devfn,
+ unsigned int *reg)
+{
+ unsigned long bdf;
+
+ bdf = CF8_BDF(cf8);
+ *bus = PCI_BUS(bdf);
+ *devfn = PCI_DEVFN(PCI_SLOT(bdf), PCI_FUNC(bdf));
+ /*
+ * NB: the lower 2 bits of the register address are fetched from the
+ * offset into the 0xcfc register when reading/writing to it.
+ */
+ *reg = (cf8 & 0xfc) | (addr & 3);
+}
+
+/* vPCI config space IO ports handlers (0xcf8/0xcfc). */
+static bool_t vpci_portio_accept(const struct hvm_io_handler *handler,
+ const ioreq_t *p)
+{
+ return (p->addr == 0xcf8 && p->size == 4) || (p->addr & 0xfffc) == 0xcfc;
+}
+
+static int vpci_portio_read(const struct hvm_io_handler *handler,
+ uint64_t addr, uint32_t size, uint64_t *data)
+{
+ struct domain *d = current->domain;
+ unsigned int bus, devfn, reg;
+ uint32_t data32;
+ int rc;
+
+ vpci_lock(d);
+ if ( addr == 0xcf8 )
+ {
+ ASSERT(size == 4);
+ *data = d->arch.hvm_domain.pci_cf8;
+ vpci_unlock(d);
+ return X86EMUL_OKAY;
+ }
+
+ /* Decode the PCI address. */
+ vpci_decode_addr(d->arch.hvm_domain.pci_cf8, addr, &bus, &devfn, ®);
+
+ if ( vpci_access_check(reg, size) || reg >= 0xff )
+ {
+ vpci_unlock(d);
+ return X86EMUL_UNHANDLEABLE;
+ }
+
+ rc = xen_vpci_read(0, bus, devfn, reg, size, &data32);
+ if ( !rc )
+ *data = data32;
+ vpci_unlock(d);
+
+ return rc ? X86EMUL_UNHANDLEABLE : X86EMUL_OKAY;
+}
+
+static int vpci_portio_write(const struct hvm_io_handler *handler,
+ uint64_t addr, uint32_t size, uint64_t data)
+{
+ struct domain *d = current->domain;
+ unsigned int bus, devfn, reg;
+ int rc;
+
+ vpci_lock(d);
+ if ( addr == 0xcf8 )
+ {
+ ASSERT(size == 4);
+ d->arch.hvm_domain.pci_cf8 = data;
+ vpci_unlock(d);
+ return X86EMUL_OKAY;
+ }
+
+ /* Decode the PCI address. */
+ vpci_decode_addr(d->arch.hvm_domain.pci_cf8, addr, &bus, &devfn, ®);
+
+ if ( vpci_access_check(reg, size) || reg >= 0xff )
+ {
+ vpci_unlock(d);
+ return X86EMUL_UNHANDLEABLE;
+ }
+
+ rc = xen_vpci_write(0, bus, devfn, reg, size, data);
+ vpci_unlock(d);
+
+ return rc ? X86EMUL_UNHANDLEABLE : X86EMUL_OKAY;
+}
+
+static const struct hvm_io_ops vpci_portio_ops = {
+ .accept = vpci_portio_accept,
+ .read = vpci_portio_read,
+ .write = vpci_portio_write,
+};
+
+void register_vpci_portio_handler(struct domain *d)
+{
+ struct hvm_io_handler *handler = hvm_next_io_handler(d);
+
+ if ( !has_vpci(d) || handler == NULL )
+ return;
+
+ spin_lock_init(&d->arch.hvm_domain.vpci_lock);
+ handler->type = IOREQ_TYPE_PIO;
+ handler->ops = &vpci_portio_ops;
+}
+
/*
* Local variables:
* mode: C
domcr_flags |= DOMCRF_hvm |
((hvm_funcs.hap_supported && !opt_dom0_shadow) ?
DOMCRF_hap : 0);
- config.emulation_flags = XEN_X86_EMU_LAPIC|XEN_X86_EMU_IOAPIC;
+ config.emulation_flags = XEN_X86_EMU_LAPIC|XEN_X86_EMU_IOAPIC|
+ XEN_X86_EMU_VPCI;
}
/* Create initial domain 0. */
__start_schedulers_array = .;
*(.data.schedulers)
__end_schedulers_array = .;
+ __start_vpci_array = .;
+ *(.data.vpci)
+ __end_vpci_array = .;
*(.data.rel.ro)
*(.data.rel.ro.*)
} :text
subdir-y += char
subdir-$(CONFIG_HAS_CPUFREQ) += cpufreq
-subdir-$(CONFIG_HAS_PCI) += pci
+subdir-$(CONFIG_HAS_PCI) += pci vpci
subdir-$(CONFIG_HAS_PASSTHROUGH) += passthrough
subdir-$(CONFIG_ACPI) += acpi
subdir-$(CONFIG_VIDEO) += video
#include <xen/radix-tree.h>
#include <xen/softirq.h>
#include <xen/tasklet.h>
+#include <xen/vpci.h>
#include <xsm/xsm.h>
#include <asm/msi.h>
#include "ats.h"
devfn += pdev->phantom_stride;
} while ( devfn != pdev->devfn &&
PCI_SLOT(devfn) == PCI_SLOT(pdev->devfn) );
+
+ xen_vpci_add_handlers(pdev);
}
static int __hwdom_init _setup_hwdom_pci_devices(struct pci_seg *pseg, void *arg)
--- /dev/null
+obj-y += vpci.o
--- /dev/null
+/*
+ * Generic functionality for handling accesses to the PCI configuration space
+ * from guests.
+ *
+ * Copyright (C) 2017 Citrix Systems R&D
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms and conditions of the GNU General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <xen/sched.h>
+#include <xen/vpci.h>
+
+extern const vpci_register_init_t __start_vpci_array[], __end_vpci_array[];
+#define NUM_VPCI_INIT (__end_vpci_array - __start_vpci_array)
+#define vpci_init __start_vpci_array
+
+/* Helpers for locking/unlocking. */
+#define vpci_lock(d) spin_lock(&(d)->arch.hvm_domain.vpci_lock)
+#define vpci_unlock(d) spin_unlock(&(d)->arch.hvm_domain.vpci_lock)
+#define vpci_locked(d) spin_is_locked(&(d)->arch.hvm_domain.vpci_lock)
+
+/* Internal struct to store the emulated PCI registers. */
+struct vpci_register {
+ vpci_read_t read;
+ vpci_write_t write;
+ unsigned int size;
+ unsigned int offset;
+ void *priv_data;
+ struct rb_node node;
+};
+
+int xen_vpci_add_handlers(struct pci_dev *pdev)
+{
+ int i, rc = 0;
+
+ if ( !has_vpci(pdev->domain) )
+ return 0;
+
+ pdev->vpci = xzalloc(struct vpci);
+ if ( !pdev->vpci )
+ return -ENOMEM;
+
+ pdev->vpci->handlers = RB_ROOT;
+
+ for ( i = 0; i < NUM_VPCI_INIT; i++ )
+ {
+ rc = vpci_init[i](pdev);
+ if ( rc )
+ break;
+ }
+
+ if ( rc )
+ {
+ struct rb_node *node = rb_first(&pdev->vpci->handlers);
+ struct vpci_register *r;
+
+ /* Iterate over the tree and cleanup. */
+ while ( node != NULL )
+ {
+ r = container_of(node, struct vpci_register, node);
+ node = rb_next(node);
+ rb_erase(&r->node, &pdev->vpci->handlers);
+ xfree(r);
+ }
+ xfree(pdev->vpci);
+ }
+
+ return rc;
+}
+
+static bool vpci_register_overlap(const struct vpci_register *r,
+ unsigned int offset)
+{
+ if ( offset >= r->offset && offset < r->offset + r->size )
+ return true;
+
+ return false;
+}
+
+
+static int vpci_register_cmp(const struct vpci_register *r1,
+ const struct vpci_register *r2)
+{
+ /* Make sure there's no overlap between registers. */
+ if ( vpci_register_overlap(r1, r2->offset) ||
+ vpci_register_overlap(r1, r2->offset + r2->size - 1) ||
+ vpci_register_overlap(r2, r1->offset) ||
+ vpci_register_overlap(r2, r1->offset + r1->size - 1) )
+ return 0;
+
+ if (r1->offset < r2->offset)
+ return -1;
+ else if (r1->offset > r2->offset)
+ return 1;
+
+ ASSERT_UNREACHABLE();
+ return 0;
+}
+
+static struct vpci_register *vpci_find_register(const struct pci_dev *pdev,
+ const unsigned int reg,
+ const unsigned int size)
+{
+ struct rb_node *node;
+ struct vpci_register r = {
+ .offset = reg,
+ .size = size,
+ };
+
+ ASSERT(vpci_locked(pdev->domain));
+
+ node = pdev->vpci->handlers.rb_node;
+ while ( node )
+ {
+ struct vpci_register *t =
+ container_of(node, struct vpci_register, node);
+
+ switch ( vpci_register_cmp(&r, t) )
+ {
+ case -1:
+ node = node->rb_left;
+ break;
+ case 1:
+ node = node->rb_right;
+ break;
+ default:
+ return t;
+ }
+ }
+
+ return NULL;
+}
+
+int xen_vpci_add_register(struct pci_dev *pdev, vpci_read_t read_handler,
+ vpci_write_t write_handler, unsigned int offset,
+ unsigned int size, void *data)
+{
+ struct rb_node **new, *parent;
+ struct vpci_register *r;
+
+ /* Some sanity checks. */
+ if ( (size != 1 && size != 2 && size != 4) || offset >= 0xFFF ||
+ offset & (size - 1) || read_handler == NULL || write_handler == NULL )
+ return -EINVAL;
+
+ r = xzalloc(struct vpci_register);
+ if ( !r )
+ return -ENOMEM;
+
+ r->read = read_handler;
+ r->write = write_handler;
+ r->size = size;
+ r->offset = offset;
+ r->priv_data = data;
+
+ vpci_lock(pdev->domain);
+ new = &pdev->vpci->handlers.rb_node;
+ parent = NULL;
+
+ while (*new) {
+ struct vpci_register *this =
+ container_of(*new, struct vpci_register, node);
+
+ parent = *new;
+ switch ( vpci_register_cmp(r, this) )
+ {
+ case -1:
+ new = &((*new)->rb_left);
+ break;
+ case 1:
+ new = &((*new)->rb_right);
+ break;
+ default:
+ xfree(r);
+ vpci_unlock(pdev->domain);
+ return -EEXIST;
+ }
+ }
+
+ rb_link_node(&r->node, parent, new);
+ rb_insert_color(&r->node, &pdev->vpci->handlers);
+ vpci_unlock(pdev->domain);
+
+ return 0;
+}
+
+int xen_vpci_remove_register(struct pci_dev *pdev, unsigned int offset)
+{
+ struct vpci_register *r;
+
+ vpci_lock(pdev->domain);
+ r = vpci_find_register(pdev, offset, 1 /* size doesn't matter here. */);
+ if ( !r )
+ {
+ vpci_unlock(pdev->domain);
+ return -ENOENT;
+ }
+
+ rb_erase(&r->node, &pdev->vpci->handlers);
+ xfree(r);
+ vpci_unlock(pdev->domain);
+
+ return 0;
+}
+
+/* Wrappers for performing reads/writes to the underlying hardware. */
+static void vpci_read_hw(unsigned int seg, unsigned int bus,
+ unsigned int devfn, unsigned int reg, uint32_t size,
+ uint32_t *data)
+{
+ switch ( size )
+ {
+ case 4:
+ *data = pci_conf_read32(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
+ reg);
+ break;
+ case 3:
+ /*
+ * This is possible because a 4byte read can have 1byte trapped and
+ * the rest passed-through.
+ */
+ *data = pci_conf_read16(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
+ reg + 1) << 8;
+ *data |= pci_conf_read8(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
+ reg);
+ break;
+ case 2:
+ *data = pci_conf_read16(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
+ reg);
+ break;
+ case 1:
+ *data = pci_conf_read8(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
+ reg);
+ break;
+ default:
+ BUG();
+ }
+}
+
+static void vpci_write_hw(unsigned int seg, unsigned int bus,
+ unsigned int devfn, unsigned int reg, uint32_t size,
+ uint32_t data)
+{
+ switch ( size )
+ {
+ case 4:
+ pci_conf_write32(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), reg,
+ data);
+ break;
+ case 3:
+ /*
+ * This is possible because a 4byte write can have 1byte trapped and
+ * the rest passed-through.
+ */
+ pci_conf_write8(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), reg, data);
+ pci_conf_write16(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), reg + 1,
+ data >> 8);
+ break;
+ case 2:
+ pci_conf_write16(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), reg,
+ data);
+ break;
+ case 1:
+ pci_conf_write8(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), reg, data);
+ break;
+ default:
+ BUG();
+ }
+}
+
+/* Helper macros for the read/write handlers. */
+#define GENMASK_BYTES(e, s) GENMASK((e) * 8, (s) * 8)
+#define SHIFT_RIGHT_BYTES(d, o) d >>= (o) * 8
+#define ADD_RESULT(r, d, s, o) r |= ((d) & GENMASK_BYTES(s, 0)) << ((o) * 8)
+
+int xen_vpci_read(unsigned int seg, unsigned int bus, unsigned int devfn,
+ unsigned int reg, uint32_t size, uint32_t *data)
+{
+ struct domain *d = current->domain;
+ struct pci_dev *pdev;
+ const struct vpci_register *r;
+ union vpci_val val = { .double_word = 0 };
+ unsigned int data_rshift = 0, data_lshift = 0, data_size;
+ uint32_t tmp_data;
+ int rc;
+
+ ASSERT(vpci_locked(d));
+
+ *data = 0;
+
+ /* Find the PCI dev matching the address. */
+ pdev = pci_get_pdev_by_domain(d, seg, bus, devfn);
+ if ( !pdev )
+ goto passthrough;
+
+ /* Find the vPCI register handler. */
+ r = vpci_find_register(pdev, reg, size);
+ if ( !r )
+ goto passthrough;
+
+ if ( r->offset > reg )
+ {
+ /*
+ * There's a heading gap into the emulated register.
+ * NB: it's possible for this recursive call to have a size of 3.
+ */
+ rc = xen_vpci_read(seg, bus, devfn, reg, r->offset - reg, &tmp_data);
+ if ( rc )
+ return rc;
+
+ /* Add the head read to the partial result. */
+ ADD_RESULT(*data, tmp_data, r->offset - reg, 0);
+ data_lshift = r->offset - reg;
+
+ /* Account for the read. */
+ size -= data_lshift;
+ reg += data_lshift;
+ }
+ else if ( r->offset < reg )
+ /* There's an offset into the emulated register */
+ data_rshift = reg - r->offset;
+
+ ASSERT(data_lshift == 0 || data_rshift == 0);
+ data_size = min(size, r->size - data_rshift);
+ ASSERT(data_size != 0);
+
+ /* Perform the read of the register. */
+ rc = r->read(pdev, r->offset, &val, r->priv_data);
+ if ( rc )
+ return rc;
+
+ val.double_word >>= data_rshift * 8;
+ ADD_RESULT(*data, val.double_word, data_size, data_lshift);
+
+ /* Account for the read */
+ size -= data_size;
+ reg += data_size;
+
+ /* Read the remaining, if any. */
+ if ( size > 0 )
+ {
+ /*
+ * Read tailing data.
+ * NB: it's possible for this recursive call to have a size of 3.
+ */
+ rc = xen_vpci_read(seg, bus, devfn, reg, size, &tmp_data);
+ if ( rc )
+ return rc;
+
+ /* Add the tail read to the partial result. */
+ ADD_RESULT(*data, tmp_data, size, data_size + data_lshift);
+ }
+
+ return 0;
+
+ passthrough:
+ vpci_read_hw(seg, bus, devfn, reg, size, data);
+ return 0;
+}
+
+/* Perform a maybe partial write to a register. */
+static int vpci_write_helper(struct pci_dev *pdev,
+ const struct vpci_register *r, unsigned int size,
+ unsigned int offset, uint32_t data)
+{
+ union vpci_val val = { .double_word = data };
+ int rc;
+
+ ASSERT(size <= r->size);
+ if ( size != r->size )
+ {
+ rc = r->read(pdev, r->offset, &val, r->priv_data);
+ if ( rc )
+ return rc;
+ val.double_word &= ~GENMASK_BYTES(size + offset, offset);
+ data &= GENMASK_BYTES(size, 0);
+ val.double_word |= data << (offset * 8);
+ }
+
+ return r->write(pdev, r->offset, val, r->priv_data);
+}
+
+int xen_vpci_write(unsigned int seg, unsigned int bus, unsigned int devfn,
+ unsigned int reg, uint32_t size, uint32_t data)
+{
+ struct domain *d = current->domain;
+ struct pci_dev *pdev;
+ const struct vpci_register *r;
+ unsigned int data_size, data_offset = 0;
+ int rc;
+
+ ASSERT(vpci_locked(d));
+
+ /* Find the PCI dev matching the address. */
+ pdev = pci_get_pdev_by_domain(d, seg, bus, devfn);
+ if ( !pdev )
+ goto passthrough;
+
+ /* Find the vPCI register handler. */
+ r = vpci_find_register(pdev, reg, size);
+ if ( !r )
+ goto passthrough;
+
+ else if ( r->offset > reg )
+ {
+ /*
+ * There's a heading gap into the emulated register found.
+ * NB: it's possible for this recursive call to have a size of 3.
+ */
+ rc = xen_vpci_write(seg, bus, devfn, reg, r->offset - reg, data);
+ if ( rc )
+ return rc;
+
+ /* Advance the data by the written size. */
+ SHIFT_RIGHT_BYTES(data, r->offset - reg);
+ size -= r->offset - reg;
+ reg += r->offset - reg;
+ }
+ else if ( r->offset < reg )
+ /* There's an offset into the emulated register. */
+ data_offset = reg - r->offset;
+
+ data_size = min(size, r->size - data_offset);
+
+ /* Perform the write of the register. */
+ ASSERT(data_size != 0);
+ rc = vpci_write_helper(pdev, r, data_size, data_offset, data);
+ if ( rc )
+ return rc;
+
+ /* Account for the read */
+ size -= data_size;
+ reg += data_size;
+ SHIFT_RIGHT_BYTES(data, data_size);
+
+ /* Write the remaining, if any. */
+ if ( size > 0 )
+ {
+ /*
+ * Write tailing data.
+ * NB: it's possible for this recursive call to have a size of 3.
+ */
+ rc = xen_vpci_write(seg, bus, devfn, reg, size, data);
+ if ( rc )
+ return rc;
+ }
+
+ return 0;
+
+ passthrough:
+ vpci_write_hw(seg, bus, devfn, reg, size, data);
+ return 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
+
#define has_vpit(d) (!!((d)->arch.emulation_flags & XEN_X86_EMU_PIT))
#define has_pirq(d) (!!((d)->arch.emulation_flags & \
XEN_X86_EMU_USE_PIRQ))
+#define has_vpci(d) (!!((d)->arch.emulation_flags & XEN_X86_EMU_VPCI))
#define has_arch_pdevs(d) (!list_empty(&(d)->arch.pdev_list))
/* List of guest to machine IO ports mapping. */
struct list_head g2m_ioport_list;
+ /* Lock for the PCI emulation layer (vPCI). */
+ spinlock_t vpci_lock;
+
/* List of permanently write-mapped pages. */
struct {
spinlock_t lock;
*/
void register_g2m_portio_handler(struct domain *d);
+/* HVM port IO handler for PCI accesses. */
+void register_vpci_portio_handler(struct domain *d);
+
#endif /* __ASM_X86_HVM_IO_H__ */
#define XEN_X86_EMU_PIT (1U<<_XEN_X86_EMU_PIT)
#define _XEN_X86_EMU_USE_PIRQ 9
#define XEN_X86_EMU_USE_PIRQ (1U<<_XEN_X86_EMU_USE_PIRQ)
+#define _XEN_X86_EMU_VPCI 10
+#define XEN_X86_EMU_VPCI (1U<<_XEN_X86_EMU_VPCI)
#define XEN_X86_EMU_ALL (XEN_X86_EMU_LAPIC | XEN_X86_EMU_HPET | \
XEN_X86_EMU_PM | XEN_X86_EMU_RTC | \
XEN_X86_EMU_IOAPIC | XEN_X86_EMU_PIC | \
XEN_X86_EMU_VGA | XEN_X86_EMU_IOMMU | \
- XEN_X86_EMU_PIT | XEN_X86_EMU_USE_PIRQ)
+ XEN_X86_EMU_PIT | XEN_X86_EMU_USE_PIRQ |\
+ XEN_X86_EMU_VPCI)
uint32_t emulation_flags;
};
#include <xen/irq.h>
#include <xen/pci_regs.h>
#include <xen/pfn.h>
+#include <xen/rbtree.h>
#include <asm/device.h>
#include <asm/numa.h>
#include <asm/pci.h>
#define PT_FAULT_THRESHOLD 10
} fault;
u64 vf_rlen[6];
+
+ /* Data for vPCI. */
+ struct vpci *vpci;
};
#define for_each_pdev(domain, pdev) \
--- /dev/null
+#ifndef _VPCI_
+#define _VPCI_
+
+#include <xen/pci.h>
+#include <xen/types.h>
+
+/* Helpers for locking/unlocking. */
+#define vpci_lock(d) spin_lock(&(d)->arch.hvm_domain.vpci_lock)
+#define vpci_unlock(d) spin_unlock(&(d)->arch.hvm_domain.vpci_lock)
+#define vpci_locked(d) spin_is_locked(&(d)->arch.hvm_domain.vpci_lock)
+
+/* Value read or written by the handlers. */
+union vpci_val {
+ uint8_t half_word;
+ uint16_t word;
+ uint32_t double_word;
+};
+
+/*
+ * The vPCI handlers will never be called concurrently for the same domain, ii
+ * is guaranteed that the vpci domain lock will always be locked when calling
+ * any handler.
+ */
+typedef int (*vpci_read_t)(struct pci_dev *pdev, unsigned int reg,
+ union vpci_val *val, void *data);
+
+typedef int (*vpci_write_t)(struct pci_dev *pdev, unsigned int reg,
+ union vpci_val val, void *data);
+
+typedef int (*vpci_register_init_t)(struct pci_dev *dev);
+
+#define REGISTER_VPCI_INIT(x) \
+ static const vpci_register_init_t x##_entry __used_section(".data.vpci") = x
+
+/* Add vPCI handlers to device. */
+int xen_vpci_add_handlers(struct pci_dev *dev);
+
+/* Add/remove a register handler. */
+int xen_vpci_add_register(struct pci_dev *pdev, vpci_read_t read_handler,
+ vpci_write_t write_handler, unsigned int offset,
+ unsigned int size, void *data);
+int xen_vpci_remove_register(struct pci_dev *pdev, unsigned int offset);
+
+/* Generic read/write handlers for the PCI config space. */
+int xen_vpci_read(unsigned int seg, unsigned int bus, unsigned int devfn,
+ unsigned int reg, uint32_t size, uint32_t *data);
+int xen_vpci_write(unsigned int seg, unsigned int bus, unsigned int devfn,
+ unsigned int reg, uint32_t size, uint32_t data);
+
+struct vpci {
+ /* Root pointer for the tree of vPCI handlers. */
+ struct rb_root handlers;
+};
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
+