All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH for-next 0/6] vpci: initial PCI config space traps
@ 2017-04-11 10:03 Roger Pau Monne
  2017-04-11 10:03 ` [PATCH for-next 1/6] xen/pci: split code to size BARs from pci_add_device Roger Pau Monne
                   ` (6 more replies)
  0 siblings, 7 replies; 8+ messages in thread
From: Roger Pau Monne @ 2017-04-11 10:03 UTC (permalink / raw)
  To: xen-devel; +Cc: julien.grall, boris.ostrovsky

Hello,

The following series contain an implementation of handlers for the PCI
configuration space inside of Xen. This allows Xen to detect accesses to the
PCI configuration space and react accordingly.

I'm still not sure that the interface for the handlers is the final one, right
now only some basic handlers are implemented (header and capability list),
other maybe more complex handlers (MSI/MSI-X) might need to expand the
interface.

Patches #1 and #2 are preparatory, and are not specific to vpci (mostly code
movement or split). The meat is certainly in patch #3 that implements the
generic handlers for accesses to the PCI configuration space. Currently a
per-device red-back tree is used in order to store the list of handlers, and
they are indexed based on their offset inside of the configuration space. Patch
#3 also adds the x86 port IO traps and wires them into the newly introduced
vPCI dispatchers. Patch #4 adds handlers for the ECAM areas (as found on the
MMCFG ACPI table). Patch #5 adds handlers to the PCI header in order to detect
BAR related operations (size, position change) and reacts accordingly to them.
Finally patch #6 allows Xen to mask certain features listed in the
capabilities list of each PCI device, allowing Xen to mask the MSI/MSI-X
features, so that the panic in the PVH Dom0 builder can be replaced with a
printk instead. Note that PVH Dom0 is still missing a lot of functionality, but
this should hopefully be enough to boot on simple hardware.

This series is based on top of my previous "x86/dpci: bind legacy PCI
interrupts to PVHv2 Dom0", which has been rebased on top of the clang fix. The
full dependency can be seen at:

http://xenbits.xen.org/gitweb/?p=people/royger/xen.git;a=shortlog;h=refs/heads/vpci_v1
git://xenbits.xen.org/people/royger/xen.git vpci_v1

Note that this is only safe to use for the hardware domain (that's trusted),
any non-trusted domain will need a lot more of traps before it can freely
access the PCI configuration space.

Thanks, Roger.

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH for-next 1/6] xen/pci: split code to size BARs from pci_add_device
  2017-04-11 10:03 [PATCH for-next 0/6] vpci: initial PCI config space traps Roger Pau Monne
@ 2017-04-11 10:03 ` Roger Pau Monne
  2017-04-11 10:03 ` [PATCH for-next 2/6] xen/mm: move modify_identity_mmio to global file Roger Pau Monne
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Roger Pau Monne @ 2017-04-11 10:03 UTC (permalink / raw)
  To: xen-devel; +Cc: julien.grall, Roger Pau Monne, boris.ostrovsky, Jan Beulich

So that it can be called from outside in order to get the size of regular PCI
BARs. This will be required in order to map the BARs from PCI devices into PVH
Dom0 p2m.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
Cc: Jan Beulich <jbeulich@suse.com>
---
 xen/drivers/passthrough/pci.c | 86 ++++++++++++++++++++++++++-----------------
 xen/include/xen/pci.h         |  3 ++
 2 files changed, 56 insertions(+), 33 deletions(-)

diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c
index c8e2d2d9a9..4e33442976 100644
--- a/xen/drivers/passthrough/pci.c
+++ b/xen/drivers/passthrough/pci.c
@@ -587,6 +587,51 @@ static void pci_enable_acs(struct pci_dev *pdev)
     pci_conf_write16(seg, bus, dev, func, pos + PCI_ACS_CTRL, ctrl);
 }
 
+int pci_size_bar(unsigned int seg, unsigned int bus, unsigned int slot,
+                 unsigned int func, unsigned int base, unsigned int max_bars,
+                 unsigned int *index, uint64_t *addr, uint64_t *size)
+{
+    unsigned int idx = base + *index * 4;
+    u32 bar = pci_conf_read32(seg, bus, slot, func, idx);
+    u32 hi = 0;
+
+    *addr = *size = 0;
+
+    ASSERT((bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY);
+    pci_conf_write32(seg, bus, slot, func, idx, ~0);
+    if ( (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
+         PCI_BASE_ADDRESS_MEM_TYPE_64 )
+    {
+        if ( *index >= max_bars )
+        {
+            dprintk(XENLOG_WARNING,
+                    "device %04x:%02x:%02x.%u with 64-bit BAR in last slot\n",
+                    seg, bus, slot, func);
+            return -EINVAL;
+        }
+        hi = pci_conf_read32(seg, bus, slot, func, idx + 4);
+        pci_conf_write32(seg, bus, slot, func, idx + 4, ~0);
+    }
+    *size = pci_conf_read32(seg, bus, slot, func, idx) &
+            PCI_BASE_ADDRESS_MEM_MASK;
+    if ( (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
+         PCI_BASE_ADDRESS_MEM_TYPE_64 )
+    {
+        *size |= (u64)pci_conf_read32(seg, bus, slot, func, idx + 4) << 32;
+        pci_conf_write32(seg, bus, slot, func, idx + 4, hi);
+    }
+    else if ( *size )
+        *size |= (u64)~0 << 32;
+    pci_conf_write32(seg, bus, slot, func, idx, bar);
+    *size = -(*size);
+    *addr = (bar & PCI_BASE_ADDRESS_MEM_MASK) | ((u64)hi << 32);
+    if ( (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
+         PCI_BASE_ADDRESS_MEM_TYPE_64 )
+        ++*index;
+
+    return 0;
+}
+
 int pci_add_device(u16 seg, u8 bus, u8 devfn,
                    const struct pci_dev_info *info, nodeid_t node)
 {
@@ -651,7 +696,7 @@ int pci_add_device(u16 seg, u8 bus, u8 devfn,
             {
                 unsigned int idx = pos + PCI_SRIOV_BAR + i * 4;
                 u32 bar = pci_conf_read32(seg, bus, slot, func, idx);
-                u32 hi = 0;
+                uint64_t addr;
 
                 if ( (bar & PCI_BASE_ADDRESS_SPACE) ==
                      PCI_BASE_ADDRESS_SPACE_IO )
@@ -662,38 +707,13 @@ int pci_add_device(u16 seg, u8 bus, u8 devfn,
                            seg, bus, slot, func, i);
                     continue;
                 }
-                pci_conf_write32(seg, bus, slot, func, idx, ~0);
-                if ( (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
-                     PCI_BASE_ADDRESS_MEM_TYPE_64 )
-                {
-                    if ( i >= PCI_SRIOV_NUM_BARS )
-                    {
-                        printk(XENLOG_WARNING
-                               "SR-IOV device %04x:%02x:%02x.%u with 64-bit"
-                               " vf BAR in last slot\n",
-                               seg, bus, slot, func);
-                        break;
-                    }
-                    hi = pci_conf_read32(seg, bus, slot, func, idx + 4);
-                    pci_conf_write32(seg, bus, slot, func, idx + 4, ~0);
-                }
-                pdev->vf_rlen[i] = pci_conf_read32(seg, bus, slot, func, idx) &
-                                   PCI_BASE_ADDRESS_MEM_MASK;
-                if ( (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
-                     PCI_BASE_ADDRESS_MEM_TYPE_64 )
-                {
-                    pdev->vf_rlen[i] |= (u64)pci_conf_read32(seg, bus,
-                                                             slot, func,
-                                                             idx + 4) << 32;
-                    pci_conf_write32(seg, bus, slot, func, idx + 4, hi);
-                }
-                else if ( pdev->vf_rlen[i] )
-                    pdev->vf_rlen[i] |= (u64)~0 << 32;
-                pci_conf_write32(seg, bus, slot, func, idx, bar);
-                pdev->vf_rlen[i] = -pdev->vf_rlen[i];
-                if ( (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
-                     PCI_BASE_ADDRESS_MEM_TYPE_64 )
-                    ++i;
+                ret = pci_size_bar(seg, bus, slot, func, pos + PCI_SRIOV_BAR,
+                                   PCI_SRIOV_NUM_BARS, &i, &addr,
+                                   &pdev->vf_rlen[i]);
+                if ( ret )
+                    dprintk(XENLOG_WARNING,
+                            "%04x:%02x:%02x.%u: failed to size SR-IOV BAR%u\n",
+                            seg, bus, slot, func, i);
             }
         }
         else
diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h
index 59b6e8a81c..74d828db38 100644
--- a/xen/include/xen/pci.h
+++ b/xen/include/xen/pci.h
@@ -161,6 +161,9 @@ const char *parse_pci(const char *, unsigned int *seg, unsigned int *bus,
                       unsigned int *dev, unsigned int *func);
 const char *parse_pci_seg(const char *, unsigned int *seg, unsigned int *bus,
                           unsigned int *dev, unsigned int *func, bool *def_seg);
+int pci_size_bar(unsigned int seg, unsigned int bus, unsigned int slot,
+                 unsigned int func, unsigned int base, unsigned int max_bars,
+                 unsigned int *index, uint64_t *addr, uint64_t *size);
 
 
 bool_t pcie_aer_get_firmware_first(const struct pci_dev *);
-- 
2.11.0 (Apple Git-81)


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH for-next 2/6] xen/mm: move modify_identity_mmio to global file
  2017-04-11 10:03 [PATCH for-next 0/6] vpci: initial PCI config space traps Roger Pau Monne
  2017-04-11 10:03 ` [PATCH for-next 1/6] xen/pci: split code to size BARs from pci_add_device Roger Pau Monne
@ 2017-04-11 10:03 ` Roger Pau Monne
  2017-04-11 10:03 ` [PATCH for-next 3/6] xen/vpci: introduce basic handlers to trap accesses to the PCI config space Roger Pau Monne
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Roger Pau Monne @ 2017-04-11 10:03 UTC (permalink / raw)
  To: xen-devel
  Cc: Andrew Cooper, julien.grall, Jan Beulich, boris.ostrovsky,
	Roger Pau Monne

Mostly code motion, this function will be needed in other parts apart from PVH
Dom0 build. Also note that the __init attribute is dropped.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
Cc: Jan Beulich <jbeulich@suse.com>
Cc: Andrew Cooper <andrew.cooper3@citrix.com>
---
 xen/arch/x86/hvm/dom0_build.c | 26 --------------------------
 xen/common/memory.c           | 32 ++++++++++++++++++++++++++++++++
 xen/include/xen/p2m-common.h  |  4 ++++
 3 files changed, 36 insertions(+), 26 deletions(-)

diff --git a/xen/arch/x86/hvm/dom0_build.c b/xen/arch/x86/hvm/dom0_build.c
index db9be87612..07bfa0be46 100644
--- a/xen/arch/x86/hvm/dom0_build.c
+++ b/xen/arch/x86/hvm/dom0_build.c
@@ -59,32 +59,6 @@ static struct acpi_madt_interrupt_override __initdata *intsrcovr;
 static unsigned int __initdata acpi_nmi_sources;
 static struct acpi_madt_nmi_source __initdata *nmisrc;
 
-static int __init modify_identity_mmio(struct domain *d, unsigned long pfn,
-                                       unsigned long nr_pages, const bool map)
-{
-    int rc;
-
-    for ( ; ; )
-    {
-        rc = (map ? map_mmio_regions : unmap_mmio_regions)
-             (d, _gfn(pfn), nr_pages, _mfn(pfn));
-        if ( rc == 0 )
-            break;
-        if ( rc < 0 )
-        {
-            printk(XENLOG_WARNING
-                   "Failed to identity %smap [%#lx,%#lx) for d%d: %d\n",
-                   map ? "" : "un", pfn, pfn + nr_pages, d->domain_id, rc);
-            break;
-        }
-        nr_pages -= rc;
-        pfn += rc;
-        process_pending_softirqs();
-    }
-
-    return rc;
-}
-
 /* Populate a HVM memory range using the biggest possible order. */
 static int __init pvh_populate_memory_range(struct domain *d,
                                             unsigned long start,
diff --git a/xen/common/memory.c b/xen/common/memory.c
index 52879e7438..66a5232fd1 100644
--- a/xen/common/memory.c
+++ b/xen/common/memory.c
@@ -1438,6 +1438,38 @@ int prepare_ring_for_helper(
     return 0;
 }
 
+int modify_identity_mmio(struct domain *d, unsigned long pfn,
+                         unsigned long nr_pages, const bool map)
+{
+    int rc;
+
+    /*
+     * Make sure this function is only used by the hardware domain, because it
+     * can take an arbitrary long time, and could DoS the whole system.
+     */
+    ASSERT(is_hardware_domain(d));
+
+    for ( ; ; )
+    {
+        rc = (map ? map_mmio_regions : unmap_mmio_regions)
+             (d, _gfn(pfn), nr_pages, _mfn(pfn));
+        if ( rc == 0 )
+            break;
+        if ( rc < 0 )
+        {
+            printk(XENLOG_WARNING
+                   "Failed to identity %smap [%#lx,%#lx) for d%d: %d\n",
+                   map ? "" : "un", pfn, pfn + nr_pages, d->domain_id, rc);
+            break;
+        }
+        nr_pages -= rc;
+        pfn += rc;
+        process_pending_softirqs();
+    }
+
+    return rc;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/include/xen/p2m-common.h b/xen/include/xen/p2m-common.h
index 8cd5a6b503..869630231c 100644
--- a/xen/include/xen/p2m-common.h
+++ b/xen/include/xen/p2m-common.h
@@ -13,4 +13,8 @@ int unmap_mmio_regions(struct domain *d,
                        unsigned long nr,
                        mfn_t mfn);
 
+
+int modify_identity_mmio(struct domain *d, unsigned long pfn,
+                         unsigned long nr_pages, const bool map);
+
 #endif /* _XEN_P2M_COMMON_H */
-- 
2.11.0 (Apple Git-81)


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH for-next 3/6] xen/vpci: introduce basic handlers to trap accesses to the PCI config space
  2017-04-11 10:03 [PATCH for-next 0/6] vpci: initial PCI config space traps Roger Pau Monne
  2017-04-11 10:03 ` [PATCH for-next 1/6] xen/pci: split code to size BARs from pci_add_device Roger Pau Monne
  2017-04-11 10:03 ` [PATCH for-next 2/6] xen/mm: move modify_identity_mmio to global file Roger Pau Monne
@ 2017-04-11 10:03 ` Roger Pau Monne
  2017-04-11 10:04 ` [PATCH for-next 4/6] x86/ecam: add handlers for the PVH Dom0 MMCFG areas Roger Pau Monne
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Roger Pau Monne @ 2017-04-11 10:03 UTC (permalink / raw)
  To: xen-devel
  Cc: Wei Liu, Andrew Cooper, Ian Jackson, julien.grall, Paul Durrant,
	Jan Beulich, boris.ostrovsky, Roger Pau Monne

This functionality is going to reside in vpci.c (and the corresponding vpci.h
header), and should be arch-agnostic. The handlers introduced in this patch
setup the basic functionality required in order to trap accesses to the PCI
config space, and allow decoding the address and finding the corresponding
handler that should handle the access (although no handlers are implemented).

Note that the traps to the PCI IO ports registers (0xcf8/0xcfc) are setup
inside of a x86 HVM file, since that's not shared with other arches.

A new XEN_X86_EMU_VPCI x86 domain flag is added in order to signal Xen whether
a domain should use the newly introduced vPCI handlers, this is only enabled
for PVH Dom0 at the moment.

A very simple user-space test is also provided, so that the basic functionality
of the vPCI traps can be asserted. This has been proven quite helpful during
development, since the logic to handle partial accesses or accesses that expand
across multiple registers is not trivial.

The handlers for the registers are added to a red-black tree, that indexes them
based on their offset. Since Xen needs to handle partial accesses to the
registers and access that expand across multiple registers the logic in
xen_vpci_{read/write} is kind of convoluted, I've tried to properly comment it
in order to make it easier to understand.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
Cc: Ian Jackson <ian.jackson@eu.citrix.com>
Cc: Wei Liu <wei.liu2@citrix.com>
Cc: Jan Beulich <jbeulich@suse.com>
Cc: Andrew Cooper <andrew.cooper3@citrix.com>
Cc: Paul Durrant <paul.durrant@citrix.com>
---
 .gitignore                        |   4 +
 tools/libxl/libxl_x86.c           |   2 +-
 tools/tests/Makefile              |   1 +
 tools/tests/vpci/Makefile         |  45 ++++
 tools/tests/vpci/emul.h           | 102 +++++++++
 tools/tests/vpci/main.c           | 206 ++++++++++++++++++
 xen/arch/arm/xen.lds.S            |   3 +
 xen/arch/x86/domain.c             |   3 +-
 xen/arch/x86/hvm/hvm.c            |   2 +
 xen/arch/x86/hvm/io.c             | 118 ++++++++++
 xen/arch/x86/setup.c              |   3 +-
 xen/arch/x86/xen.lds.S            |   3 +
 xen/drivers/Makefile              |   2 +-
 xen/drivers/passthrough/pci.c     |   3 +
 xen/drivers/vpci/Makefile         |   1 +
 xen/drivers/vpci/vpci.c           | 446 ++++++++++++++++++++++++++++++++++++++
 xen/include/asm-x86/domain.h      |   1 +
 xen/include/asm-x86/hvm/io.h      |   3 +
 xen/include/public/arch-x86/xen.h |   5 +-
 xen/include/xen/pci.h             |   4 +
 xen/include/xen/vpci.h            |  62 ++++++
 21 files changed, 1014 insertions(+), 5 deletions(-)
 create mode 100644 tools/tests/vpci/Makefile
 create mode 100644 tools/tests/vpci/emul.h
 create mode 100644 tools/tests/vpci/main.c
 create mode 100644 xen/drivers/vpci/Makefile
 create mode 100644 xen/drivers/vpci/vpci.c
 create mode 100644 xen/include/xen/vpci.h

diff --git a/.gitignore b/.gitignore
index 74747cb7e7..ebafba25b5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -236,6 +236,10 @@ tools/tests/regression/build/*
 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
diff --git a/tools/libxl/libxl_x86.c b/tools/libxl/libxl_x86.c
index 455f6f0bed..dd7fc78a99 100644
--- a/tools/libxl/libxl_x86.c
+++ b/tools/libxl/libxl_x86.c
@@ -11,7 +11,7 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc,
     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
diff --git a/tools/tests/Makefile b/tools/tests/Makefile
index 639776130b..5cfe781e62 100644
--- a/tools/tests/Makefile
+++ b/tools/tests/Makefile
@@ -13,6 +13,7 @@ endif
 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-%
diff --git a/tools/tests/vpci/Makefile b/tools/tests/vpci/Makefile
new file mode 100644
index 0000000000..7969fcbd82
--- /dev/null
+++ b/tools/tests/vpci/Makefile
@@ -0,0 +1,45 @@
+
+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"\
+	             ;' <$< >$@
+
diff --git a/tools/tests/vpci/emul.h b/tools/tests/vpci/emul.h
new file mode 100644
index 0000000000..c090b6d6aa
--- /dev/null
+++ b/tools/tests/vpci/emul.h
@@ -0,0 +1,102 @@
+/*
+ * 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 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:
+ */
+
diff --git a/tools/tests/vpci/main.c b/tools/tests/vpci/main.c
new file mode 100644
index 0000000000..0fc63de038
--- /dev/null
+++ b/tools/tests/vpci/main.c
@@ -0,0 +1,206 @@
+/*
+ * 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:
+ */
+
diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S
index 44bd3bf0ce..41bf9dfaf3 100644
--- a/xen/arch/arm/xen.lds.S
+++ b/xen/arch/arm/xen.lds.S
@@ -79,6 +79,9 @@ SECTIONS
        __start_schedulers_array = .;
        *(.data.schedulers)
        __end_schedulers_array = .;
+       __start_vpci_array = .;
+       *(.data.vpci)
+       __end_vpci_array = .;
        *(.data.rel)
        *(.data.rel.*)
        CONSTRUCTORS
diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index 90e2b1f82a..d516b46f67 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -500,7 +500,8 @@ static bool emulation_flags_ok(const struct domain *d, uint32_t emflags)
     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 )
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index f50d15ff50..c2a9b76a81 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -37,6 +37,7 @@
 #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>
@@ -655,6 +656,7 @@ int hvm_domain_initialise(struct domain *d)
         d->arch.hvm_domain.io_bitmap = hvm_io_bitmap;
 
     register_g2m_portio_handler(d);
+    register_vpci_portio_handler(d);
 
     hvm_ioreq_init(d);
 
diff --git a/xen/arch/x86/hvm/io.c b/xen/arch/x86/hvm/io.c
index 9e004098cc..11d561e861 100644
--- a/xen/arch/x86/hvm/io.c
+++ b/xen/arch/x86/hvm/io.c
@@ -25,6 +25,7 @@
 #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>
@@ -257,6 +258,123 @@ void register_g2m_portio_handler(struct domain *d)
     handler->ops = &g2m_portio_ops;
 }
 
+/* Do some sanity checks. */
+static int vpci_access_check(unsigned int reg, unsigned int len)
+{
+    /* Check read size */
+    if ( len != 1 && len != 2 && len != 4 )
+    {
+        gdprintk(XENLOG_WARNING, "invalid length (reg: %#x, len: %u)\n",
+                 reg, len);
+        return -EINVAL;
+    }
+
+    /* Check offset alignment */
+    if ( reg & (len - 1) )
+    {
+        gdprintk(XENLOG_WARNING,
+                 "invalid access size alignment (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;
+
+    if ( addr == 0xcf8 )
+    {
+        ASSERT(size == 4);
+        *data = d->arch.hvm_domain.pci_cf8;
+        return X86EMUL_OKAY;
+    }
+
+    /* Decode the PCI address. */
+    vpci_decode_addr(d->arch.hvm_domain.pci_cf8, addr, &bus, &devfn, &reg);
+
+    if ( vpci_access_check(reg, size) || reg >= 0xff )
+        return X86EMUL_UNHANDLEABLE;
+
+    rc = xen_vpci_read(0, bus, devfn, reg, size, &data32);
+    if ( !rc )
+        *data = data32;
+
+     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;
+
+    if ( addr == 0xcf8 )
+    {
+        ASSERT(size == 4);
+        d->arch.hvm_domain.pci_cf8 = data;
+        return X86EMUL_OKAY;
+    }
+
+    /* Decode the PCI address. */
+    vpci_decode_addr(d->arch.hvm_domain.pci_cf8, addr, &bus, &devfn, &reg);
+
+    if ( vpci_access_check(reg, size) || reg >= 0xff )
+        return X86EMUL_UNHANDLEABLE;
+
+    rc = xen_vpci_write(0, bus, devfn, reg, size, data);
+
+    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;
+
+    handler->type = IOREQ_TYPE_PIO;
+    handler->ops = &vpci_portio_ops;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c
index c3d99e9b54..b4592311bf 100644
--- a/xen/arch/x86/setup.c
+++ b/xen/arch/x86/setup.c
@@ -1564,7 +1564,8 @@ void __init noreturn __start_xen(unsigned long mbi_p)
         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. */
diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
index 8289a1bf09..f5cc8e2b8d 100644
--- a/xen/arch/x86/xen.lds.S
+++ b/xen/arch/x86/xen.lds.S
@@ -224,6 +224,9 @@ SECTIONS
        __start_schedulers_array = .;
        *(.data.schedulers)
        __end_schedulers_array = .;
+       __start_vpci_array = .;
+       *(.data.vpci)
+       __end_vpci_array = .;
        *(.data.rel.ro)
        *(.data.rel.ro.*)
   } :text
diff --git a/xen/drivers/Makefile b/xen/drivers/Makefile
index 19391802a8..d51c766453 100644
--- a/xen/drivers/Makefile
+++ b/xen/drivers/Makefile
@@ -1,6 +1,6 @@
 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
diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c
index 4e33442976..7710c41533 100644
--- a/xen/drivers/passthrough/pci.c
+++ b/xen/drivers/passthrough/pci.c
@@ -30,6 +30,7 @@
 #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"
@@ -1061,6 +1062,8 @@ static void setup_one_hwdom_device(const struct setup_hwdom *ctxt,
         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)
diff --git a/xen/drivers/vpci/Makefile b/xen/drivers/vpci/Makefile
new file mode 100644
index 0000000000..840a906470
--- /dev/null
+++ b/xen/drivers/vpci/Makefile
@@ -0,0 +1 @@
+obj-y += vpci.o
diff --git a/xen/drivers/vpci/vpci.c b/xen/drivers/vpci/vpci.c
new file mode 100644
index 0000000000..8648de5899
--- /dev/null
+++ b/xen/drivers/vpci/vpci.c
@@ -0,0 +1,446 @@
+/*
+ * 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
+
+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;
+    }
+
+    return rc;
+}
+
+/* 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;
+};
+
+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,
+                                                unsigned int reg,
+                                                unsigned int size)
+{
+    struct rb_node *node;
+    struct vpci_register r = {
+        .offset = reg,
+        .size = size,
+    };
+
+    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;
+
+    /*
+     * In order to avoid using locks to protect the RB tree that contains the
+     * handlers prevent adding them unless the domain is paused (and then
+     * calls to xen_vpci_add_register should also be serialized).
+     */
+    ASSERT(atomic_read(&pdev->domain->pause_count));
+
+    /* 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;
+
+    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);
+            return -EEXIST;
+        }
+    }
+
+    rb_link_node(&r->node, parent, new);
+    rb_insert_color(&r->node, &pdev->vpci->handlers);
+
+    return 0;
+}
+
+int xen_vpci_remove_register(struct pci_dev *pdev, unsigned int offset)
+{
+    struct vpci_register *r;
+
+    r = vpci_find_register(pdev, offset, 1 /* size doesn't matter here. */);
+    if ( !r )
+        return -ENOENT;
+
+    rb_erase(&r->node, &pdev->vpci->handlers);
+    xfree(r);
+
+    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;
+
+    *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;
+    struct vpci_register *r;
+    unsigned int data_size, data_offset = 0;
+    int rc;
+
+    /* 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:
+ */
+
diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h
index 6ab987f231..f0741917ed 100644
--- a/xen/include/asm-x86/domain.h
+++ b/xen/include/asm-x86/domain.h
@@ -426,6 +426,7 @@ struct arch_domain
 #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))
 
diff --git a/xen/include/asm-x86/hvm/io.h b/xen/include/asm-x86/hvm/io.h
index 2484eb1c75..2dbf92f13e 100644
--- a/xen/include/asm-x86/hvm/io.h
+++ b/xen/include/asm-x86/hvm/io.h
@@ -155,6 +155,9 @@ extern void hvm_dpci_msi_eoi(struct domain *d, int vector);
  */
 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__ */
 
 
diff --git a/xen/include/public/arch-x86/xen.h b/xen/include/public/arch-x86/xen.h
index 8a9ba7982b..c00f8cda93 100644
--- a/xen/include/public/arch-x86/xen.h
+++ b/xen/include/public/arch-x86/xen.h
@@ -295,12 +295,15 @@ struct xen_arch_domainconfig {
 #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;
 };
 
diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h
index 74d828db38..3d3853fd6f 100644
--- a/xen/include/xen/pci.h
+++ b/xen/include/xen/pci.h
@@ -13,6 +13,7 @@
 #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>
@@ -88,6 +89,9 @@ struct pci_dev {
 #define PT_FAULT_THRESHOLD 10
     } fault;
     u64 vf_rlen[6];
+
+    /* Data for vPCI. */
+    struct vpci *vpci;
 };
 
 #define for_each_pdev(domain, pdev) \
diff --git a/xen/include/xen/vpci.h b/xen/include/xen/vpci.h
new file mode 100644
index 0000000000..2f54919310
--- /dev/null
+++ b/xen/include/xen/vpci.h
@@ -0,0 +1,62 @@
+#ifndef _VPCI_
+#define _VPCI_
+
+#include <xen/pci.h>
+#include <xen/types.h>
+
+/*
+ * Guest handlers for PCI register accesses.
+ *
+ * sval: internal stored value (usage up to the handler).
+ * {r/w}val: read/write value from the guest.
+ */
+
+union vpci_val {
+    uint8_t half_word;
+    uint16_t word;
+    uint32_t double_word;
+};
+
+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:
+ */
+
-- 
2.11.0 (Apple Git-81)


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH for-next 4/6] x86/ecam: add handlers for the PVH Dom0 MMCFG areas
  2017-04-11 10:03 [PATCH for-next 0/6] vpci: initial PCI config space traps Roger Pau Monne
                   ` (2 preceding siblings ...)
  2017-04-11 10:03 ` [PATCH for-next 3/6] xen/vpci: introduce basic handlers to trap accesses to the PCI config space Roger Pau Monne
@ 2017-04-11 10:04 ` Roger Pau Monne
  2017-04-11 10:04 ` [PATCH for-next 5/6] xen/vpci: add handlers to map the BARs Roger Pau Monne
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Roger Pau Monne @ 2017-04-11 10:04 UTC (permalink / raw)
  To: xen-devel
  Cc: Andrew Cooper, julien.grall, Paul Durrant, Jan Beulich,
	boris.ostrovsky, Roger Pau Monne

Introduce a set of handlers for the accesses to the ECAM areas. Those areas are
setup based on the contents of the hardware MMCFG tables, and the list of
handled ECAM areas is stored inside of the hvm_domain struct.

The read/writes are forwarded to the generic vpci handlers once the address is
decoded in order to obtain the device and register the guest is trying to
access.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
Cc: Jan Beulich <jbeulich@suse.com>
Cc: Andrew Cooper <andrew.cooper3@citrix.com>
Cc: Paul Durrant <paul.durrant@citrix.com>
---
 xen/arch/x86/hvm/dom0_build.c    |  27 +++++++++++
 xen/arch/x86/hvm/hvm.c           |   1 +
 xen/arch/x86/hvm/io.c            | 101 +++++++++++++++++++++++++++++++++++++++
 xen/include/asm-x86/hvm/domain.h |  10 ++++
 xen/include/asm-x86/hvm/io.h     |   4 ++
 5 files changed, 143 insertions(+)

diff --git a/xen/arch/x86/hvm/dom0_build.c b/xen/arch/x86/hvm/dom0_build.c
index 07bfa0be46..47600345ab 100644
--- a/xen/arch/x86/hvm/dom0_build.c
+++ b/xen/arch/x86/hvm/dom0_build.c
@@ -38,6 +38,8 @@
 #include <public/hvm/hvm_info_table.h>
 #include <public/hvm/hvm_vcpu.h>
 
+#include "../x86_64/mmconfig.h"
+
 /*
  * Have the TSS cover the ISA port range, which makes it
  * - 104 bytes base structure
@@ -1022,6 +1024,24 @@ static int __init pvh_setup_acpi(struct domain *d, paddr_t start_info)
     return 0;
 }
 
+int __init pvh_setup_ecam(struct domain *d)
+{
+    unsigned int i;
+    int rc;
+
+    for ( i = 0; i < pci_mmcfg_config_num; i++ )
+    {
+        size_t size = (pci_mmcfg_config[i].end_bus_number + 1) << 20;
+
+        rc = register_vpci_ecam_handler(d, pci_mmcfg_config[i].address, size,
+                                        pci_mmcfg_config[i].pci_segment);
+        if ( rc )
+            return rc;
+    }
+
+    return 0;
+}
+
 int __init dom0_construct_pvh(struct domain *d, const module_t *image,
                               unsigned long image_headroom,
                               module_t *initrd,
@@ -1064,6 +1084,13 @@ int __init dom0_construct_pvh(struct domain *d, const module_t *image,
         return rc;
     }
 
+    rc = pvh_setup_ecam(d);
+    if ( rc )
+    {
+        printk("Failed to setup Dom0 PCI ECAM areas: %d\n", rc);
+        return rc;
+    }
+
     panic("Building a PVHv2 Dom0 is not yet supported.");
     return 0;
 }
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index c2a9b76a81..d8f3dcdc7c 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -613,6 +613,7 @@ int hvm_domain_initialise(struct domain *d)
     spin_lock_init(&d->arch.hvm_domain.write_map.lock);
     INIT_LIST_HEAD(&d->arch.hvm_domain.write_map.list);
     INIT_LIST_HEAD(&d->arch.hvm_domain.g2m_ioport_list);
+    INIT_LIST_HEAD(&d->arch.hvm_domain.ecam_regions);
 
     hvm_init_cacheattr_region_list(d);
 
diff --git a/xen/arch/x86/hvm/io.c b/xen/arch/x86/hvm/io.c
index 11d561e861..b78d668fc1 100644
--- a/xen/arch/x86/hvm/io.c
+++ b/xen/arch/x86/hvm/io.c
@@ -375,6 +375,107 @@ void register_vpci_portio_handler(struct domain *d)
     handler->ops = &vpci_portio_ops;
 }
 
+/* Handlers to trap PCI ECAM config accesses. */
+static struct hvm_ecam *vpci_ecam_find(struct domain *d, unsigned long addr)
+{
+    struct hvm_ecam *ecam = NULL;
+
+    list_for_each_entry ( ecam, &d->arch.hvm_domain.ecam_regions, next )
+        if ( addr >= ecam->addr && addr < ecam->addr + ecam->size )
+            return ecam;
+
+    return NULL;
+}
+
+static void vpci_ecam_decode_addr(unsigned long addr, unsigned int *bus,
+                                  unsigned int *devfn, unsigned int *reg)
+{
+    *bus = (addr >> 20) & 0xff;
+    *devfn = (addr >> 12) & 0xff;
+    *reg = addr & 0xfff;
+}
+
+static int vpci_ecam_accept(struct vcpu *v, unsigned long addr)
+{
+
+    return !!vpci_ecam_find(v->domain, addr);
+}
+
+static int vpci_ecam_read(struct vcpu *v, unsigned long addr,
+                          unsigned int len, unsigned long *data)
+{
+    struct domain *d = v->domain;
+    struct hvm_ecam *ecam = vpci_ecam_find(d, addr);
+    unsigned int bus, devfn, reg;
+    uint32_t data32;
+    int rc;
+
+    ASSERT(ecam);
+
+    vpci_ecam_decode_addr(addr - ecam->addr, &bus, &devfn, &reg);
+
+    if ( vpci_access_check(reg, len) || reg >= 0xfff )
+        return X86EMUL_UNHANDLEABLE;
+
+    rc = xen_vpci_read(ecam->segment, bus, devfn, reg, len, &data32);
+    if ( !rc )
+        *data = data32;
+
+    return rc ? X86EMUL_UNHANDLEABLE : X86EMUL_OKAY;
+}
+
+static int vpci_ecam_write(struct vcpu *v, unsigned long addr,
+                           unsigned int len, unsigned long data)
+{
+    struct domain *d = v->domain;
+    struct hvm_ecam *ecam = vpci_ecam_find(d, addr);
+    unsigned int bus, devfn, reg;
+    int rc;
+
+    ASSERT(ecam);
+
+    vpci_ecam_decode_addr(addr - ecam->addr, &bus, &devfn, &reg);
+
+    if ( vpci_access_check(reg, len) || reg >= 0xfff )
+        return X86EMUL_UNHANDLEABLE;
+
+    rc = xen_vpci_write(ecam->segment, bus, devfn, reg, len, data);
+
+    return rc ? X86EMUL_UNHANDLEABLE : X86EMUL_OKAY;
+}
+
+static const struct hvm_mmio_ops vpci_ecam_ops = {
+    .check = vpci_ecam_accept,
+    .read = vpci_ecam_read,
+    .write = vpci_ecam_write,
+};
+
+int register_vpci_ecam_handler(struct domain *d, paddr_t addr, size_t size,
+                               unsigned int seg)
+{
+    struct hvm_ecam *ecam;
+
+    ASSERT(is_hardware_domain(d));
+    ASSERT(atomic_read(&d->pause_count));
+
+    if ( vpci_ecam_find(d, addr) )
+        return -EEXIST;
+
+    ecam = xzalloc(struct hvm_ecam);
+    if ( !ecam )
+        return -ENOMEM;
+
+    if ( list_empty(&d->arch.hvm_domain.ecam_regions) )
+        register_mmio_handler(d, &vpci_ecam_ops);
+
+    ecam->addr = addr;
+    ecam->segment = seg;
+    ecam->size = size;
+    list_add(&ecam->next,  &d->arch.hvm_domain.ecam_regions);
+
+    return 0;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/include/asm-x86/hvm/domain.h b/xen/include/asm-x86/hvm/domain.h
index d2899c9bb2..9b5425f0a4 100644
--- a/xen/include/asm-x86/hvm/domain.h
+++ b/xen/include/asm-x86/hvm/domain.h
@@ -100,6 +100,13 @@ struct hvm_pi_ops {
     void (*do_resume)(struct vcpu *v);
 };
 
+struct hvm_ecam {
+    paddr_t addr;
+    size_t size;
+    unsigned int segment;
+    struct list_head next;
+};
+
 struct hvm_domain {
     /* Guest page range used for non-default ioreq servers */
     struct {
@@ -184,6 +191,9 @@ struct hvm_domain {
     /* List of guest to machine IO ports mapping. */
     struct list_head g2m_ioport_list;
 
+    /* List of ECAM regions. */
+    struct list_head ecam_regions;
+
     /* List of permanently write-mapped pages. */
     struct {
         spinlock_t lock;
diff --git a/xen/include/asm-x86/hvm/io.h b/xen/include/asm-x86/hvm/io.h
index 2dbf92f13e..0434aca706 100644
--- a/xen/include/asm-x86/hvm/io.h
+++ b/xen/include/asm-x86/hvm/io.h
@@ -158,6 +158,10 @@ void register_g2m_portio_handler(struct domain *d);
 /* HVM port IO handler for PCI accesses. */
 void register_vpci_portio_handler(struct domain *d);
 
+/* HVM MMIO handler for PCI ECAM accesses. */
+int register_vpci_ecam_handler(struct domain *d, paddr_t addr, size_t size,
+                               unsigned int seg);
+
 #endif /* __ASM_X86_HVM_IO_H__ */
 
 
-- 
2.11.0 (Apple Git-81)


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH for-next 5/6] xen/vpci: add handlers to map the BARs
  2017-04-11 10:03 [PATCH for-next 0/6] vpci: initial PCI config space traps Roger Pau Monne
                   ` (3 preceding siblings ...)
  2017-04-11 10:04 ` [PATCH for-next 4/6] x86/ecam: add handlers for the PVH Dom0 MMCFG areas Roger Pau Monne
@ 2017-04-11 10:04 ` Roger Pau Monne
  2017-04-11 10:04 ` [PATCH for-next 6/6] xen/vpci: trap access to the list of PCI capabilities Roger Pau Monne
  2017-04-18 12:44 ` [PATCH for-next 0/6] vpci: initial PCI config space traps Roger Pau Monne
  6 siblings, 0 replies; 8+ messages in thread
From: Roger Pau Monne @ 2017-04-11 10:04 UTC (permalink / raw)
  To: xen-devel
  Cc: Stefano Stabellini, Wei Liu, George Dunlap, Andrew Cooper,
	Ian Jackson, Tim Deegan, julien.grall, Jan Beulich,
	boris.ostrovsky, Roger Pau Monne

Introduce a set of handlers that trap accesses to the PCI BARs and the command
register, in order to emulate BAR sizing and BAR relocation.

The command handler is used to detect changes to bit 2 (response to memory
space accesses), and maps/unmaps the BARs of the device into the guest p2m.

The BAR register handlers are used to detect attempts by the guest to size or
relocate the BARs.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
Cc: Andrew Cooper <andrew.cooper3@citrix.com>
Cc: George Dunlap <George.Dunlap@eu.citrix.com>
Cc: Ian Jackson <ian.jackson@eu.citrix.com>
Cc: Jan Beulich <jbeulich@suse.com>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: Stefano Stabellini <sstabellini@kernel.org>
Cc: Tim Deegan <tim@xen.org>
Cc: Wei Liu <wei.liu2@citrix.com>
---
 xen/drivers/vpci/Makefile |   2 +-
 xen/drivers/vpci/header.c | 244 ++++++++++++++++++++++++++++++++++++++++++++++
 xen/include/xen/vpci.h    |  23 +++++
 3 files changed, 268 insertions(+), 1 deletion(-)
 create mode 100644 xen/drivers/vpci/header.c

diff --git a/xen/drivers/vpci/Makefile b/xen/drivers/vpci/Makefile
index 840a906470..241467212f 100644
--- a/xen/drivers/vpci/Makefile
+++ b/xen/drivers/vpci/Makefile
@@ -1 +1 @@
-obj-y += vpci.o
+obj-y += vpci.o header.o
diff --git a/xen/drivers/vpci/header.c b/xen/drivers/vpci/header.c
new file mode 100644
index 0000000000..a5f36ea4ab
--- /dev/null
+++ b/xen/drivers/vpci/header.c
@@ -0,0 +1,244 @@
+/*
+ * Generic functionality for handling accesses to the PCI header from the
+ * configuration space.
+ *
+ * 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>
+#include <xen/p2m-common.h>
+
+static int vpci_cmd_read(struct pci_dev *pdev, unsigned int reg,
+                         union vpci_val *val, void *data)
+{
+    uint16_t *cmd = data;
+
+    val->word = *cmd;
+    return 0;
+}
+
+static int vpci_cmd_write(struct pci_dev *pdev, unsigned int reg,
+                          union vpci_val val, void *data)
+{
+    uint16_t new_cmd = val.word, *saved_cmd = data;
+    struct vpci_bar *bars = pdev->vpci->header.bars;
+    uint8_t seg = pdev->seg, bus = pdev->bus;
+    uint8_t slot = PCI_SLOT(pdev->devfn), func = PCI_FUNC(pdev->devfn);
+    unsigned int i;
+    int rc;
+
+
+    if ( !((new_cmd ^ *saved_cmd) & PCI_COMMAND_MEMORY) )
+        goto out;
+
+    /* Memory space access change. */
+    for ( i = 0; i < ARRAY_SIZE(pdev->vpci->header.bars); i++ )
+    {
+        bool map = !!(new_cmd & PCI_COMMAND_MEMORY);
+
+        if ( bars[i].type != VPCI_BAR_MEM &&
+             bars[i].type != VPCI_BAR_MEM64_LO )
+            continue;
+
+        if ( !map && !bars[i].mapped_addr )
+            continue;
+
+        rc = modify_identity_mmio(pdev->domain, PFN_DOWN(bars[i].addr),
+                                  PFN_UP(bars[i].size), map);
+        if ( rc )
+            continue;
+
+        bars[i].mapped_addr = map ? bars[i].addr : 0;
+    }
+
+ out:
+    pci_conf_write16(seg, bus, slot, func, reg, new_cmd);
+    *saved_cmd = pci_conf_read16(seg, bus, slot, func, reg);
+    return 0;
+}
+
+static int vpci_bar_read(struct pci_dev *pdev, unsigned int reg,
+                         union vpci_val *val, void *data)
+{
+    struct vpci_bar *bar = data;
+    bool hi = false;
+
+    ASSERT(bar->type == VPCI_BAR_MEM || bar->type == VPCI_BAR_MEM64_LO ||
+           bar->type == VPCI_BAR_MEM64_HI);
+
+    if ( bar->type == VPCI_BAR_MEM64_HI )
+    {
+        ASSERT(reg - PCI_BASE_ADDRESS_0 > 0);
+        bar--;
+        hi = true;
+    }
+
+    if ( bar->sizing )
+        val->double_word = ~(bar->size - 1) >> (hi ? 32 : 0);
+    else
+        val->double_word = bar->addr >> (hi ? 32 : 0);
+
+    val->double_word |= hi ? 0 : bar->attributes;
+
+    return 0;
+}
+
+static int vpci_bar_write(struct pci_dev *pdev, unsigned int reg,
+                          union vpci_val val, void *data)
+{
+    struct vpci_bar *bar = data;
+    uint32_t wdata = val.double_word;
+    bool hi = false;
+
+    ASSERT(bar->type == VPCI_BAR_MEM || bar->type == VPCI_BAR_MEM64_LO ||
+           bar->type == VPCI_BAR_MEM64_HI);
+
+    if ( wdata == GENMASK(31, 0) )
+    {
+        /* Next reads from this register are going to return the BAR size. */
+        bar->sizing = true;
+        return 0;
+    }
+
+    /* End previous sizing cycle if any. */
+    bar->sizing = false;
+
+    if ( bar->type == VPCI_BAR_MEM64_HI )
+    {
+        ASSERT(reg - PCI_BASE_ADDRESS_0 > 0);
+        bar--;
+        hi = true;
+    }
+
+    /* Update the relevant part of the BAR address. */
+    bar->addr &= hi ? ~GENMASK(63, 32) : ~GENMASK(31, 0);
+    wdata &= hi ? GENMASK(31, 0) : PCI_BASE_ADDRESS_MEM_MASK;
+    bar->addr |= (uint64_t)wdata << (hi ? 32 : 0);
+
+    return 0;
+}
+
+static int vpci_init_bars(struct pci_dev *pdev)
+{
+    uint8_t seg = pdev->seg, bus = pdev->bus;
+    uint8_t slot = PCI_SLOT(pdev->devfn), func = PCI_FUNC(pdev->devfn);
+    uint8_t header_type;
+    unsigned int i, num_bars;
+    struct vpci_header *header = &pdev->vpci->header;
+    struct vpci_bar *bars = header->bars;
+    int rc;
+
+    header_type = pci_conf_read8(seg, bus, slot, func, PCI_HEADER_TYPE) & 0x7f;
+    if ( header_type == PCI_HEADER_TYPE_NORMAL )
+        num_bars = 6;
+    else if ( header_type == PCI_HEADER_TYPE_BRIDGE )
+        num_bars = 2;
+    else
+        return -ENOSYS;
+
+    /* Setup a handler for the control register. */
+    header->command = pci_conf_read16(seg, bus, slot, func, PCI_COMMAND);
+    rc = xen_vpci_add_register(pdev, vpci_cmd_read, vpci_cmd_write,
+                               PCI_COMMAND, 2, &header->command);
+    if ( rc )
+    {
+        dprintk(XENLOG_ERR,
+                "%04x:%02x:%02x.%u: failed to add handler register %#x: %d\n",
+                seg, bus, slot, func, PCI_COMMAND, rc);
+        return rc;
+    }
+
+
+    for ( i = 0; i < num_bars; i++ )
+    {
+        uint8_t reg = PCI_BASE_ADDRESS_0 + i * 4;
+        uint32_t val = pci_conf_read32(seg, bus, slot, func, reg);
+        uint64_t addr, size;
+        unsigned int index;
+
+        if ( i && bars[i - 1].type == VPCI_BAR_MEM64_LO )
+        {
+            bars[i].type = VPCI_BAR_MEM64_HI;
+            continue;
+        }
+        else if ( (val & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO )
+        {
+            bars[i].type = VPCI_BAR_IO;
+            continue;
+        }
+        else if ( (val & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
+                  PCI_BASE_ADDRESS_MEM_TYPE_64 )
+            bars[i].type = VPCI_BAR_MEM64_LO;
+        else
+            bars[i].type = VPCI_BAR_MEM;
+
+        /* Size the BAR and map it. */
+        index = i;
+        rc = pci_size_bar(seg, bus, slot, func, PCI_BASE_ADDRESS_0, num_bars,
+                          &index, &addr, &size);
+        if ( rc )
+        {
+            dprintk(XENLOG_ERR,
+                    "%04x:%02x:%02x.%u: unable to size BAR#%u: %d\n",
+                    seg, bus, slot, func, i, rc);
+            return rc;
+        }
+
+        if ( size == 0 )
+        {
+            bars[i].type = VPCI_BAR_EMPTY;
+            continue;
+        }
+
+        bars[i].addr = addr;
+        bars[i].size = size;
+        bars[i].attributes = val & ~PCI_BASE_ADDRESS_MEM_MASK;
+
+        if ( header->command & PCI_COMMAND_MEMORY )
+        {
+            /* Memory space accesses are active, map BARs. */
+            rc = modify_identity_mmio(pdev->domain, PFN_DOWN(bars[i].addr),
+                                      PFN_UP(bars[i].size), true);
+            if ( !rc )
+                bars[i].mapped_addr = addr;
+        }
+
+        rc = xen_vpci_add_register(pdev, vpci_bar_read, vpci_bar_write, reg,
+                                   4, &bars[i]);
+        if ( rc )
+        {
+            dprintk(XENLOG_ERR,
+                    "%04x:%02x:%02x.%u: failed to add handler for BAR#%u: %d\n",
+                    seg, bus, slot, func, i, rc);
+            return rc;
+        }
+    }
+
+    return 0;
+}
+
+REGISTER_VPCI_INIT(vpci_init_bars);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
+
diff --git a/xen/include/xen/vpci.h b/xen/include/xen/vpci.h
index 2f54919310..205d121a92 100644
--- a/xen/include/xen/vpci.h
+++ b/xen/include/xen/vpci.h
@@ -46,6 +46,29 @@ int xen_vpci_write(unsigned int seg, unsigned int bus, unsigned int devfn,
 struct vpci {
     /* Root pointer for the tree of vPCI handlers. */
     struct rb_root handlers;
+
+#ifdef __XEN__
+    /* Hide the rest of the vpci struct from the user-space test harness. */
+    struct vpci_header {
+        /* Cached value of the command register. */
+        uint16_t command;
+        /* Information about the PCI BARs of this device. */
+        struct vpci_bar {
+            enum {
+                VPCI_BAR_EMPTY,
+                VPCI_BAR_IO,
+                VPCI_BAR_MEM,
+                VPCI_BAR_MEM64_LO,
+                VPCI_BAR_MEM64_HI,
+            } type;
+            paddr_t addr;
+            paddr_t mapped_addr;
+            size_t size;
+            unsigned int attributes:4;
+            bool sizing;
+        } bars[6];
+    } header;
+#endif
 };
 
 #endif
-- 
2.11.0 (Apple Git-81)


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH for-next 6/6] xen/vpci: trap access to the list of PCI capabilities
  2017-04-11 10:03 [PATCH for-next 0/6] vpci: initial PCI config space traps Roger Pau Monne
                   ` (4 preceding siblings ...)
  2017-04-11 10:04 ` [PATCH for-next 5/6] xen/vpci: add handlers to map the BARs Roger Pau Monne
@ 2017-04-11 10:04 ` Roger Pau Monne
  2017-04-18 12:44 ` [PATCH for-next 0/6] vpci: initial PCI config space traps Roger Pau Monne
  6 siblings, 0 replies; 8+ messages in thread
From: Roger Pau Monne @ 2017-04-11 10:04 UTC (permalink / raw)
  To: xen-devel
  Cc: Andrew Cooper, julien.grall, Jan Beulich, boris.ostrovsky,
	Roger Pau Monne

Add traps to each capability PCI_CAP_LIST_NEXT field in order to mask them on
request.

All capabilities from the device are fetched and stored in an internal list,
that's later used in order to return the next capability to the guest. Note
that this only removes the capability from the linked list as seen by the
guest, but the actual capability structure could still be accessed by the
guest, provided that it's position can be found using another mechanism.
Finally the MSI and MSI-X capabilities are masked until Xen knows how to
properly handle accesses to them.

This should allow a PVH Dom0 to boot on some hardware, provided that the
hardware doesn't require MSI/MSI-X and that there are no SR-IOV devices in the
system, so the panic at the end of the PVH Dom0 build is replaced by a
warning.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
Cc: Jan Beulich <jbeulich@suse.com>
Cc: Andrew Cooper <andrew.cooper3@citrix.com>
---
 xen/arch/x86/hvm/dom0_build.c   |   2 +-
 xen/drivers/vpci/Makefile       |   2 +-
 xen/drivers/vpci/capabilities.c | 153 ++++++++++++++++++++++++++++++++++++++++
 xen/include/xen/vpci.h          |   3 +
 4 files changed, 158 insertions(+), 2 deletions(-)
 create mode 100644 xen/drivers/vpci/capabilities.c

diff --git a/xen/arch/x86/hvm/dom0_build.c b/xen/arch/x86/hvm/dom0_build.c
index 47600345ab..23ab20167e 100644
--- a/xen/arch/x86/hvm/dom0_build.c
+++ b/xen/arch/x86/hvm/dom0_build.c
@@ -1091,7 +1091,7 @@ int __init dom0_construct_pvh(struct domain *d, const module_t *image,
         return rc;
     }
 
-    panic("Building a PVHv2 Dom0 is not yet supported.");
+    printk("WARNING: PVH is an experimental mode with limited functionality\n");
     return 0;
 }
 
diff --git a/xen/drivers/vpci/Makefile b/xen/drivers/vpci/Makefile
index 241467212f..c3f3085c93 100644
--- a/xen/drivers/vpci/Makefile
+++ b/xen/drivers/vpci/Makefile
@@ -1 +1 @@
-obj-y += vpci.o header.o
+obj-y += vpci.o header.o capabilities.o
diff --git a/xen/drivers/vpci/capabilities.c b/xen/drivers/vpci/capabilities.c
new file mode 100644
index 0000000000..69ecd22537
--- /dev/null
+++ b/xen/drivers/vpci/capabilities.c
@@ -0,0 +1,153 @@
+/*
+ * Generic functionality for handling accesses to the PCI capabilities from
+ * the configuration space.
+ *
+ * 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>
+
+struct vpci_capability {
+    struct list_head next;
+    uint8_t offset;
+    bool masked;
+};
+
+static int vpci_cap_read(struct pci_dev *dev, unsigned int reg,
+                         union vpci_val *val, void *data)
+{
+    struct vpci_capability *next, *curr = data;
+
+    val->half_word = 0;
+
+    /* Return the position of the next non-masked capability. */
+    while ( (next = list_next_entry(curr, next)) != NULL )
+    {
+        if ( !next->masked )
+        {
+            val->half_word = next->offset;
+            break;
+        }
+        curr = next;
+    }
+
+    return 0;
+}
+static int vpci_cap_write(struct pci_dev *dev, unsigned int reg,
+                          union vpci_val val, void *data)
+{
+    /* Ignored. */
+    return 0;
+}
+
+static int vpci_index_capabilities(struct pci_dev *pdev)
+{
+    uint8_t seg = pdev->seg, bus = pdev->bus;
+    uint8_t slot = PCI_SLOT(pdev->devfn), func = PCI_FUNC(pdev->devfn);
+    uint8_t pos = PCI_CAPABILITY_LIST;
+    uint16_t status;
+    unsigned int max_cap = 48;
+    struct vpci_capability *cap;
+    int rc;
+
+    INIT_LIST_HEAD(&pdev->vpci->cap_list);
+
+    /* Check if device has capabilities. */
+    status = pci_conf_read16(seg, bus, slot, func, PCI_STATUS);
+    if ( (status & PCI_STATUS_CAP_LIST) == 0 )
+        return 0;
+
+    /* Add the root capability pointer. */
+    cap = xzalloc(struct vpci_capability);
+    if ( !cap )
+        return -ENOMEM;
+
+    cap->offset = pos;
+    list_add_tail(&cap->next, &pdev->vpci->cap_list);
+    rc = xen_vpci_add_register(pdev, vpci_cap_read, vpci_cap_write, pos,
+                               1, cap);
+    if ( rc )
+        return rc;
+
+    /*
+     * Iterate over the list of capabilities present in the device, and
+     * add a handler for each pointer to the next item (PCI_CAP_LIST_NEXT).
+     */
+    while ( max_cap-- )
+    {
+        pos = pci_conf_read8(seg, bus, slot, func, pos);
+        if ( pos < 0x40 )
+            break;
+
+        cap = xzalloc(struct vpci_capability);
+        if ( !cap )
+            return -ENOMEM;
+
+        cap->offset = pos;
+        list_add_tail(&cap->next, &pdev->vpci->cap_list);
+        pos += PCI_CAP_LIST_NEXT;
+        rc = xen_vpci_add_register(pdev, vpci_cap_read, vpci_cap_write, pos,
+                                   1, cap);
+        if ( rc )
+            return rc;
+    }
+
+    return 0;
+}
+
+static void vpci_mask_capability(struct pci_dev *pdev, uint8_t cap_id)
+{
+    struct vpci_capability *cap;
+    uint8_t cap_offset;
+
+    cap_offset = pci_find_cap_offset(pdev->seg, pdev->bus,
+                                     PCI_SLOT(pdev->devfn),
+                                     PCI_FUNC(pdev->devfn), cap_id);
+    if ( !cap_offset )
+        return;
+
+    list_for_each_entry ( cap, &pdev->vpci->cap_list, next )
+        if ( cap->offset == cap_offset )
+            cap->masked = true;
+}
+
+static int vpci_capabilities_init(struct pci_dev *pdev)
+{
+    int rc;
+
+    rc = vpci_index_capabilities(pdev);
+    if ( rc )
+        return rc;
+
+    /* Mask MSI and MSI-X capabilities until Xen handles them. */
+    vpci_mask_capability(pdev, PCI_CAP_ID_MSI);
+    vpci_mask_capability(pdev, PCI_CAP_ID_MSIX);
+
+    return 0;
+}
+
+REGISTER_VPCI_INIT(vpci_capabilities_init);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
+
diff --git a/xen/include/xen/vpci.h b/xen/include/xen/vpci.h
index 205d121a92..944f48d893 100644
--- a/xen/include/xen/vpci.h
+++ b/xen/include/xen/vpci.h
@@ -68,6 +68,9 @@ struct vpci {
             bool sizing;
         } bars[6];
     } header;
+
+    /* List of capabilities supported by the device. */
+    struct list_head cap_list;
 #endif
 };
 
-- 
2.11.0 (Apple Git-81)


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH for-next 0/6] vpci: initial PCI config space traps
  2017-04-11 10:03 [PATCH for-next 0/6] vpci: initial PCI config space traps Roger Pau Monne
                   ` (5 preceding siblings ...)
  2017-04-11 10:04 ` [PATCH for-next 6/6] xen/vpci: trap access to the list of PCI capabilities Roger Pau Monne
@ 2017-04-18 12:44 ` Roger Pau Monne
  6 siblings, 0 replies; 8+ messages in thread
From: Roger Pau Monne @ 2017-04-18 12:44 UTC (permalink / raw)
  To: xen-devel, julien.grall, boris.ostrovsky

On Tue, Apr 11, 2017 at 11:03:56AM +0100, Roger Pau Monne wrote:
> Hello,
> 
> The following series contain an implementation of handlers for the PCI
> configuration space inside of Xen. This allows Xen to detect accesses to the
> PCI configuration space and react accordingly.
> 
> I'm still not sure that the interface for the handlers is the final one, right
> now only some basic handlers are implemented (header and capability list),
> other maybe more complex handlers (MSI/MSI-X) might need to expand the
> interface.
> 
> Patches #1 and #2 are preparatory, and are not specific to vpci (mostly code
> movement or split). The meat is certainly in patch #3 that implements the
> generic handlers for accesses to the PCI configuration space. Currently a
> per-device red-back tree is used in order to store the list of handlers, and
> they are indexed based on their offset inside of the configuration space. Patch
> #3 also adds the x86 port IO traps and wires them into the newly introduced
> vPCI dispatchers. Patch #4 adds handlers for the ECAM areas (as found on the
> MMCFG ACPI table). Patch #5 adds handlers to the PCI header in order to detect
> BAR related operations (size, position change) and reacts accordingly to them.
> Finally patch #6 allows Xen to mask certain features listed in the
> capabilities list of each PCI device, allowing Xen to mask the MSI/MSI-X
> features, so that the panic in the PVH Dom0 builder can be replaced with a
> printk instead. Note that PVH Dom0 is still missing a lot of functionality, but
> this should hopefully be enough to boot on simple hardware.
> 
> This series is based on top of my previous "x86/dpci: bind legacy PCI
> interrupts to PVHv2 Dom0", which has been rebased on top of the clang fix. The
> full dependency can be seen at:
> 
> http://xenbits.xen.org/gitweb/?p=people/royger/xen.git;a=shortlog;h=refs/heads/vpci_v1
> git://xenbits.xen.org/people/royger/xen.git vpci_v1
> 
> Note that this is only safe to use for the hardware domain (that's trusted),
> any non-trusted domain will need a lot more of traps before it can freely
> access the PCI configuration space.

Hello,

Do not review this version, I have a new one coming up very soon with MSI/MSI-X
and some other fixes for issues that I've found while implementing MSI.

Thanks, Roger.

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2017-04-18 12:44 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-04-11 10:03 [PATCH for-next 0/6] vpci: initial PCI config space traps Roger Pau Monne
2017-04-11 10:03 ` [PATCH for-next 1/6] xen/pci: split code to size BARs from pci_add_device Roger Pau Monne
2017-04-11 10:03 ` [PATCH for-next 2/6] xen/mm: move modify_identity_mmio to global file Roger Pau Monne
2017-04-11 10:03 ` [PATCH for-next 3/6] xen/vpci: introduce basic handlers to trap accesses to the PCI config space Roger Pau Monne
2017-04-11 10:04 ` [PATCH for-next 4/6] x86/ecam: add handlers for the PVH Dom0 MMCFG areas Roger Pau Monne
2017-04-11 10:04 ` [PATCH for-next 5/6] xen/vpci: add handlers to map the BARs Roger Pau Monne
2017-04-11 10:04 ` [PATCH for-next 6/6] xen/vpci: trap access to the list of PCI capabilities Roger Pau Monne
2017-04-18 12:44 ` [PATCH for-next 0/6] vpci: initial PCI config space traps Roger Pau Monne

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.