[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v10 09/11] vpci/msi: add MSI handlers



Add handlers for the MSI control, address, data and mask fields in
order to detect accesses to them and setup the interrupts as requested
by the guest.

Note that the pending register is not trapped, and the guest can
freely read/write to it.

Signed-off-by: Roger Pau Monné <roger.pau@xxxxxxxxxx>
Reviewed-by: Jan Beulich <jbeulich@xxxxxxxx>
---
Cc: Jan Beulich <jbeulich@xxxxxxxx>
Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
Cc: George Dunlap <George.Dunlap@xxxxxxxxxxxxx>
Cc: Ian Jackson <ian.jackson@xxxxxxxxxxxxx>
Cc: Julien Grall <julien.grall@xxxxxxx>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@xxxxxxxxxx>
Cc: Stefano Stabellini <sstabellini@xxxxxxxxxx>
Cc: Tim Deegan <tim@xxxxxxx>
Cc: Wei Liu <wei.liu2@xxxxxxxxxx>
Cc: Paul Durrant <paul.durrant@xxxxxxxxxx>
---
Changes since v8:
 - Add a FIXME about the lack of testing and a comment regarding the
   lack of cleaning done in the init_msi error path.
 - Free msi struct when cleaning up if an init function failed.
 - Remove the 'error' label of init_msi, the caller will already
   perform the cleaning.

Changes since v7:
 - Don't store pci segment/bus on local variables.
 - Add an error label to init_msi.
 - Don't trap accesses to the PBA.
 - Fix msi_pending_bits_reg macro so it matches coding style.
 - Move the position of vectors in the vpci_msi struct.
 - Add a comment to clarify the expected state of vectors after
   pt_irq_create_bind and use XEN_DOMCTL_VMSI_X86_UNMASKED.

Changes since v6:
 - Use domain_spin_lock_irq_desc instead of open coding it.
 - Reduce the size of printed debug messages.
 - Constify domain in vpci_dump_msi.
 - Lock domlist_read_lock before iterating over the list of domains.
 - Make max_vectors and vectors uint8_t.
 - Drop the vpci_ prefix from the static functions in msi.c.
 - Turn the booleans in vpci_msi into bitfields.
 - Apply the mask bits to all vectors when enabling msi.
 - Remove the pos field.
 - Remove the usage of __msi_set_{enable/disable}.
 - Update the bindings when the message or data fields are updated.
 - Make vpci_msi_arch_disable return void, it wasn't returning any
   error.
 - Prevent the guest from writing to the pending bits field, it's read
   only as defined in the spec.
 - Add the must_check attribute to vpci_msi_arch_enable.

Changes since v5:
 - Update to new lock usage.
 - Change handlers to match the new type.
 - s/msi_flags/msi_gflags/, remove the local variables and use the new
   DOMCTL_VMSI_* defines.
 - Change the MSI arch function to take a vpci_msi instead of a
   vpci_arch_msi as parameter.
 - Fix the calculation of the guest vector for MSI injection to take
   into account the number of bits that can be modified.
 - Use INVALID_PIRQ everywhere.
 - Simplify exit path of vpci_msi_disable.
 - Remove the conditional when setting address64 and masking fields.
 - Add a process_pending_softirqs to the MSI dump loop.
 - Place the prototypes for the MSI arch-specific functions in
   xen/vpci.h.
 - Add parentheses around the INVALID_PIRQ definition.

Changes since v4:
 - Fix commit message.
 - Change the ASSERTs in vpci_msi_arch_mask into ifs.
 - Introduce INVALID_PIRQ.
 - Destroy the partially created bindings in case of failure in
   vpci_msi_arch_enable.
 - Just take the pcidevs lock once in vpci_msi_arch_disable.
 - Print an error message in case of failure of pt_irq_destroy_bind.
 - Make vpci_msi_arch_init return void.
 - Constify the arch parameter of vpci_msi_arch_print.
 - Use fixed instead of cpu for msi redirection.
 - Separate the header includes in vpci/msi.c between xen and asm.
 - Store the number of configured vectors even if MSI is not enabled
   and always return it in vpci_msi_control_read.
 - Fix/add comments in vpci_msi_control_write to clarify intended
   behavior.
 - Simplify usage of masks in vpci_msi_address_{upper_}write.
 - Add comment to vpci_msi_mask_{read/write}.
 - Don't use MASK_EXTR in vpci_msi_mask_write.
 - s/msi_offset/pos/ in vpci_init_msi.
 - Move control variable setup closer to it's usage.
 - Use d%d in vpci_dump_msi.
 - Fix printing of bitfield mask in vpci_dump_msi.
 - Fix definition of MSI_ADDR_REDIRECTION_MASK.
 - Shuffle the layout of vpci_msi to minimize gaps.
 - Remove the error label in vpci_init_msi.

Changes since v3:
 - Propagate changes from previous versions: drop xen_ prefix, drop
   return value from handlers, use the new vpci_val fields.
 - Use MASK_EXTR.
 - Remove the usage of GENMASK.
 - Add GFLAGS_SHIFT_DEST_ID and use it in msi_flags.
 - Add "arch" to the MSI arch specific functions.
 - Move the dumping of vPCI MSI information to dump_msi (key 'M').
 - Remove the guest_vectors field.
 - Allow the guest to change the number of active vectors without
   having to disable and enable MSI.
 - Check the number of active vectors when parsing the disable
   mask.
 - Remove the debug messages from vpci_init_msi.
 - Move the arch-specific part of the dump handler to x86/hvm/vmsi.c.
 - Use trylock in the dump handler to get the vpci lock.

Changes since v2:
 - Add an arch-specific abstraction layer. Note that this is only implemented
   for x86 currently.
 - Add a wrapper to detect MSI enabling for vPCI.
---
NB: I've only been able to test this with devices using a single MSI
interrupt and no mask register. I will try to find hardware that
supports the mask register and more than one vector, but I cannot make
any promises.

If there are doubts about the untested parts we could always force Xen
to report no per-vector masking support and only 1 available vector,
but I would rather avoid doing it.
---
 xen/arch/x86/hvm/vmsi.c      | 142 +++++++++++++++++++
 xen/arch/x86/msi.c           |   3 +
 xen/drivers/vpci/Makefile    |   2 +-
 xen/drivers/vpci/msi.c       | 324 +++++++++++++++++++++++++++++++++++++++++++
 xen/drivers/vpci/vpci.c      |   1 +
 xen/include/asm-x86/hvm/io.h |   5 +
 xen/include/asm-x86/msi.h    |   3 +
 xen/include/xen/irq.h        |   1 +
 xen/include/xen/vpci.h       |  38 +++++
 9 files changed, 518 insertions(+), 1 deletion(-)
 create mode 100644 xen/drivers/vpci/msi.c

diff --git a/xen/arch/x86/hvm/vmsi.c b/xen/arch/x86/hvm/vmsi.c
index 7126de7841..be59c56d43 100644
--- a/xen/arch/x86/hvm/vmsi.c
+++ b/xen/arch/x86/hvm/vmsi.c
@@ -31,6 +31,7 @@
 #include <xen/errno.h>
 #include <xen/sched.h>
 #include <xen/irq.h>
+#include <xen/vpci.h>
 #include <public/hvm/ioreq.h>
 #include <asm/hvm/io.h>
 #include <asm/hvm/vpic.h>
@@ -621,3 +622,144 @@ void msix_write_completion(struct vcpu *v)
     if ( msixtbl_write(v, ctrl_address, 4, 0) != X86EMUL_OKAY )
         gdprintk(XENLOG_WARNING, "MSI-X write completion failure\n");
 }
+
+static unsigned int msi_gflags(uint16_t data, uint64_t addr, bool masked)
+{
+    /*
+     * We need to use the DOMCTL constants here because the output of this
+     * function is used as input to pt_irq_create_bind, which also takes the
+     * input from the DOMCTL itself.
+     */
+    return MASK_INSR(MASK_EXTR(addr, MSI_ADDR_DEST_ID_MASK),
+                     XEN_DOMCTL_VMSI_X86_DEST_ID_MASK) |
+           MASK_INSR(MASK_EXTR(addr, MSI_ADDR_REDIRECTION_MASK),
+                     XEN_DOMCTL_VMSI_X86_RH_MASK) |
+           MASK_INSR(MASK_EXTR(addr, MSI_ADDR_DESTMODE_MASK),
+                     XEN_DOMCTL_VMSI_X86_DM_MASK) |
+           MASK_INSR(MASK_EXTR(data, MSI_DATA_DELIVERY_MODE_MASK),
+                     XEN_DOMCTL_VMSI_X86_DELIV_MASK) |
+           MASK_INSR(MASK_EXTR(data, MSI_DATA_TRIGGER_MASK),
+                     XEN_DOMCTL_VMSI_X86_TRIG_MASK) |
+           /* NB: by default MSI vectors are bound masked. */
+           (masked ? 0 : XEN_DOMCTL_VMSI_X86_UNMASKED);
+}
+
+void vpci_msi_arch_mask(struct vpci_msi *msi, const struct pci_dev *pdev,
+                        unsigned int entry, bool mask)
+{
+    unsigned long flags;
+    struct irq_desc *desc = domain_spin_lock_irq_desc(pdev->domain,
+                                                      msi->arch.pirq + entry,
+                                                      &flags);
+
+    if ( !desc )
+        return;
+    guest_mask_msi_irq(desc, mask);
+    spin_unlock_irqrestore(&desc->lock, flags);
+}
+
+int vpci_msi_arch_enable(struct vpci_msi *msi, const struct pci_dev *pdev,
+                         unsigned int vectors)
+{
+    struct msi_info msi_info = {
+        .seg = pdev->seg,
+        .bus = pdev->bus,
+        .devfn = pdev->devfn,
+        .entry_nr = vectors,
+    };
+    unsigned int i;
+    int rc;
+
+    ASSERT(msi->arch.pirq == INVALID_PIRQ);
+
+    /* Get a PIRQ. */
+    rc = allocate_and_map_msi_pirq(pdev->domain, -1, &msi->arch.pirq,
+                                   MAP_PIRQ_TYPE_MULTI_MSI, &msi_info);
+    if ( rc )
+    {
+        gdprintk(XENLOG_ERR, "%04x:%02x:%02x.%u: failed to map PIRQ: %d\n",
+                 pdev->seg, pdev->bus, PCI_SLOT(pdev->devfn),
+                 PCI_FUNC(pdev->devfn), rc);
+        return rc;
+    }
+
+    for ( i = 0; i < vectors; i++ )
+    {
+        uint8_t vector = MASK_EXTR(msi->data, MSI_DATA_VECTOR_MASK);
+        uint8_t vector_mask = 0xff >> (8 - fls(msi->vectors) + 1);
+        struct xen_domctl_bind_pt_irq bind = {
+            .machine_irq = msi->arch.pirq + i,
+            .irq_type = PT_IRQ_TYPE_MSI,
+            .u.msi.gvec = (vector & ~vector_mask) |
+                          ((vector + i) & vector_mask),
+            .u.msi.gflags = msi_gflags(msi->data, msi->address,
+                                       (msi->mask >> i) & 1),
+        };
+
+        pcidevs_lock();
+        rc = pt_irq_create_bind(pdev->domain, &bind);
+        if ( rc )
+        {
+            gdprintk(XENLOG_ERR,
+                     "%04x:%02x:%02x.%u: failed to bind PIRQ %u: %d\n",
+                     pdev->seg, pdev->bus, PCI_SLOT(pdev->devfn),
+                     PCI_FUNC(pdev->devfn), msi->arch.pirq + i, rc);
+            while ( bind.machine_irq-- )
+                pt_irq_destroy_bind(pdev->domain, &bind);
+            spin_lock(&pdev->domain->event_lock);
+            unmap_domain_pirq(pdev->domain, msi->arch.pirq);
+            spin_unlock(&pdev->domain->event_lock);
+            pcidevs_unlock();
+            msi->arch.pirq = INVALID_PIRQ;
+            return rc;
+        }
+        pcidevs_unlock();
+    }
+
+    return 0;
+}
+
+void vpci_msi_arch_disable(struct vpci_msi *msi, const struct pci_dev *pdev)
+{
+    unsigned int i;
+
+    ASSERT(msi->arch.pirq != INVALID_PIRQ);
+
+    pcidevs_lock();
+    for ( i = 0; i < msi->vectors; i++ )
+    {
+        struct xen_domctl_bind_pt_irq bind = {
+            .machine_irq = msi->arch.pirq + i,
+            .irq_type = PT_IRQ_TYPE_MSI,
+        };
+        int rc;
+
+        rc = pt_irq_destroy_bind(pdev->domain, &bind);
+        ASSERT(!rc);
+    }
+
+    spin_lock(&pdev->domain->event_lock);
+    unmap_domain_pirq(pdev->domain, msi->arch.pirq);
+    spin_unlock(&pdev->domain->event_lock);
+    pcidevs_unlock();
+
+    msi->arch.pirq = INVALID_PIRQ;
+}
+
+void vpci_msi_arch_init(struct vpci_msi *msi)
+{
+    msi->arch.pirq = INVALID_PIRQ;
+}
+
+void vpci_msi_arch_print(const struct vpci_msi *msi)
+{
+    printk("vec=%#02x%7s%6s%3sassert%5s%7s dest_id=%lu pirq: %d\n",
+           MASK_EXTR(msi->data, MSI_DATA_VECTOR_MASK),
+           msi->data & MSI_DATA_DELIVERY_LOWPRI ? "lowest" : "fixed",
+           msi->data & MSI_DATA_TRIGGER_LEVEL ? "level" : "edge",
+           msi->data & MSI_DATA_LEVEL_ASSERT ? "" : "de",
+           msi->address & MSI_ADDR_DESTMODE_LOGIC ? "log" : "phys",
+           msi->address & MSI_ADDR_REDIRECTION_LOWPRI ? "lowest" : "fixed",
+           MASK_EXTR(msi->address, MSI_ADDR_DEST_ID_MASK),
+           msi->arch.pirq);
+}
diff --git a/xen/arch/x86/msi.c b/xen/arch/x86/msi.c
index 8c89f072a8..5567990fbd 100644
--- a/xen/arch/x86/msi.c
+++ b/xen/arch/x86/msi.c
@@ -30,6 +30,7 @@
 #include <public/physdev.h>
 #include <xen/iommu.h>
 #include <xsm/xsm.h>
+#include <xen/vpci.h>
 
 static s8 __read_mostly use_msi = -1;
 boolean_param("msi", use_msi);
@@ -1527,6 +1528,8 @@ static void dump_msi(unsigned char key)
                attr.guest_masked ? 'G' : ' ',
                mask);
     }
+
+    vpci_dump_msi();
 }
 
 static int __init msi_setup_keyhandler(void)
diff --git a/xen/drivers/vpci/Makefile b/xen/drivers/vpci/Makefile
index 241467212f..62cec9e82b 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 msi.o
diff --git a/xen/drivers/vpci/msi.c b/xen/drivers/vpci/msi.c
new file mode 100644
index 0000000000..c3c69ec453
--- /dev/null
+++ b/xen/drivers/vpci/msi.c
@@ -0,0 +1,324 @@
+/*
+ * Handlers for accesses to the MSI capability structure.
+ *
+ * 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/softirq.h>
+#include <xen/vpci.h>
+
+#include <asm/msi.h>
+
+static uint32_t control_read(const struct pci_dev *pdev, unsigned int reg,
+                             void *data)
+{
+    const struct vpci_msi *msi = data;
+
+    return MASK_INSR(fls(msi->max_vectors) - 1, PCI_MSI_FLAGS_QMASK) |
+           MASK_INSR(fls(msi->vectors) - 1, PCI_MSI_FLAGS_QSIZE) |
+           (msi->enabled ? PCI_MSI_FLAGS_ENABLE : 0) |
+           (msi->masking ? PCI_MSI_FLAGS_MASKBIT : 0) |
+           (msi->address64 ? PCI_MSI_FLAGS_64BIT : 0);
+}
+
+static void control_write(const struct pci_dev *pdev, unsigned int reg,
+                          uint32_t val, void *data)
+{
+    struct vpci_msi *msi = data;
+    unsigned int vectors = min_t(uint8_t,
+                                 1u << MASK_EXTR(val, PCI_MSI_FLAGS_QSIZE),
+                                 msi->max_vectors);
+    bool new_enabled = val & PCI_MSI_FLAGS_ENABLE;
+
+    /*
+     * No change if the enable field and the number of vectors is
+     * the same or the device is not enabled, in which case the
+     * vectors field can be updated directly.
+     */
+    if ( new_enabled == msi->enabled &&
+         (vectors == msi->vectors || !msi->enabled) )
+    {
+        msi->vectors = vectors;
+        return;
+    }
+
+    if ( new_enabled )
+    {
+        /*
+         * If the device is already enabled it means the number of
+         * enabled messages has changed. Disable and re-enable the
+         * device in order to apply the change.
+         */
+        if ( msi->enabled )
+        {
+            vpci_msi_arch_disable(msi, pdev);
+            msi->enabled = false;
+        }
+
+        if ( vpci_msi_arch_enable(msi, pdev, vectors) )
+            return;
+    }
+    else
+        vpci_msi_arch_disable(msi, pdev);
+
+    msi->vectors = vectors;
+    msi->enabled = new_enabled;
+
+    pci_conf_write16(pdev->seg, pdev->bus, PCI_SLOT(pdev->devfn),
+                     PCI_FUNC(pdev->devfn), reg,
+                     control_read(pdev, reg, data));
+}
+
+static void update_msi(const struct pci_dev *pdev, struct vpci_msi *msi)
+{
+    if ( !msi->enabled )
+        return;
+
+    vpci_msi_arch_disable(msi, pdev);
+    if ( vpci_msi_arch_enable(msi, pdev, msi->vectors) )
+        msi->enabled = false;
+}
+
+/* Handlers for the address field (32bit or low part of a 64bit address). */
+static uint32_t address_read(const struct pci_dev *pdev, unsigned int reg,
+                             void *data)
+{
+    const struct vpci_msi *msi = data;
+
+    return msi->address;
+}
+
+static void address_write(const struct pci_dev *pdev, unsigned int reg,
+                          uint32_t val, void *data)
+{
+    struct vpci_msi *msi = data;
+
+    /* Clear low part. */
+    msi->address &= ~0xffffffffull;
+    msi->address |= val;
+
+    update_msi(pdev, msi);
+}
+
+/* Handlers for the high part of a 64bit address field. */
+static uint32_t address_hi_read(const struct pci_dev *pdev, unsigned int reg,
+                                void *data)
+{
+    const struct vpci_msi *msi = data;
+
+    return msi->address >> 32;
+}
+
+static void address_hi_write(const struct pci_dev *pdev, unsigned int reg,
+                             uint32_t val, void *data)
+{
+    struct vpci_msi *msi = data;
+
+    /* Clear and update high part. */
+    msi->address &= 0xffffffff;
+    msi->address |= (uint64_t)val << 32;
+
+    update_msi(pdev, msi);
+}
+
+/* Handlers for the data field. */
+static uint32_t data_read(const struct pci_dev *pdev, unsigned int reg,
+                          void *data)
+{
+    const struct vpci_msi *msi = data;
+
+    return msi->data;
+}
+
+static void data_write(const struct pci_dev *pdev, unsigned int reg,
+                       uint32_t val, void *data)
+{
+    struct vpci_msi *msi = data;
+
+    msi->data = val;
+
+    update_msi(pdev, msi);
+}
+
+/* Handlers for the MSI mask bits. */
+static uint32_t mask_read(const struct pci_dev *pdev, unsigned int reg,
+                          void *data)
+{
+    const struct vpci_msi *msi = data;
+
+    return msi->mask;
+}
+
+static void mask_write(const struct pci_dev *pdev, unsigned int reg,
+                       uint32_t val, void *data)
+{
+    struct vpci_msi *msi = data;
+    uint32_t dmask = msi->mask ^ val;
+
+    if ( !dmask )
+        return;
+
+    if ( msi->enabled )
+    {
+        unsigned int i;
+
+        for ( i = ffs(dmask) - 1; dmask && i < msi->vectors;
+              i = ffs(dmask) - 1 )
+        {
+            vpci_msi_arch_mask(msi, pdev, i, (val >> i) & 1);
+            __clear_bit(i, &dmask);
+        }
+    }
+
+    msi->mask = val;
+}
+
+static int init_msi(struct pci_dev *pdev)
+{
+    uint8_t slot = PCI_SLOT(pdev->devfn), func = PCI_FUNC(pdev->devfn);
+    unsigned int pos = pci_find_cap_offset(pdev->seg, pdev->bus, slot, func,
+                                           PCI_CAP_ID_MSI);
+    uint16_t control;
+    int ret;
+
+    if ( !pos )
+        return 0;
+
+    pdev->vpci->msi = xzalloc(struct vpci_msi);
+    if ( !pdev->vpci->msi )
+        return -ENOMEM;
+
+    ret = vpci_add_register(pdev->vpci, control_read, control_write,
+                            msi_control_reg(pos), 2, pdev->vpci->msi);
+    if ( ret )
+        /*
+         * NB: there's no need to free the msi struct or remove the register
+         * handlers form the config space, the caller will take care of the
+         * cleanup.
+         */
+        return ret;
+
+    /* Get the maximum number of vectors the device supports. */
+    control = pci_conf_read16(pdev->seg, pdev->bus, slot, func,
+                              msi_control_reg(pos));
+
+    /*
+     * FIXME: I've only been able to test this code with devices using a single
+     * MSI interrupt and no mask register.
+     */
+    pdev->vpci->msi->max_vectors = multi_msi_capable(control);
+    ASSERT(pdev->vpci->msi->max_vectors <= 32);
+
+    /* The multiple message enable is 0 after reset (1 message enabled). */
+    pdev->vpci->msi->vectors = 1;
+
+    /* No PIRQ bound yet. */
+    vpci_msi_arch_init(pdev->vpci->msi);
+
+    pdev->vpci->msi->address64 = is_64bit_address(control);
+    pdev->vpci->msi->masking = is_mask_bit_support(control);
+
+    ret = vpci_add_register(pdev->vpci, address_read, address_write,
+                            msi_lower_address_reg(pos), 4, pdev->vpci->msi);
+    if ( ret )
+        return ret;
+
+    ret = vpci_add_register(pdev->vpci, data_read, data_write,
+                            msi_data_reg(pos, pdev->vpci->msi->address64), 2,
+                            pdev->vpci->msi);
+    if ( ret )
+        return ret;
+
+    if ( pdev->vpci->msi->address64 )
+    {
+        ret = vpci_add_register(pdev->vpci, address_hi_read, address_hi_write,
+                                msi_upper_address_reg(pos), 4, 
pdev->vpci->msi);
+        if ( ret )
+            return ret;
+    }
+
+    if ( pdev->vpci->msi->masking )
+    {
+        ret = vpci_add_register(pdev->vpci, mask_read, mask_write,
+                                msi_mask_bits_reg(pos,
+                                                  pdev->vpci->msi->address64),
+                                4, pdev->vpci->msi);
+        if ( ret )
+            return ret;
+        /*
+         * FIXME: do not add any handler for the pending bits for the hardware
+         * domain, which means direct access. This will be revisited when
+         * adding unprivileged domain support.
+         */
+    }
+
+    return 0;
+}
+REGISTER_VPCI_INIT(init_msi);
+
+void vpci_dump_msi(void)
+{
+    const struct domain *d;
+
+    rcu_read_lock(&domlist_read_lock);
+    for_each_domain ( d )
+    {
+        const struct pci_dev *pdev;
+
+        if ( !has_vpci(d) )
+            continue;
+
+        printk("vPCI MSI d%d\n", d->domain_id);
+
+        list_for_each_entry ( pdev, &d->arch.pdev_list, domain_list )
+        {
+            const struct vpci_msi *msi;
+
+            if ( !pdev->vpci || !spin_trylock(&pdev->vpci->lock) )
+                continue;
+
+            msi = pdev->vpci->msi;
+            if ( msi && msi->enabled )
+            {
+                printk("%04x:%02x:%02x.%u MSI\n", pdev->seg, pdev->bus,
+                       PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+
+                printk("  enabled: %d 64-bit: %d",
+                       msi->enabled, msi->address64);
+                if ( msi->masking )
+                    printk(" mask=%08x", msi->mask);
+                printk(" vectors max: %u enabled: %u\n",
+                       msi->max_vectors, msi->vectors);
+
+                vpci_msi_arch_print(msi);
+            }
+
+            spin_unlock(&pdev->vpci->lock);
+            process_pending_softirqs();
+        }
+    }
+    rcu_read_unlock(&domlist_read_lock);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/drivers/vpci/vpci.c b/xen/drivers/vpci/vpci.c
index e5b49b9d82..3012b30013 100644
--- a/xen/drivers/vpci/vpci.c
+++ b/xen/drivers/vpci/vpci.c
@@ -47,6 +47,7 @@ void vpci_remove_device(struct pci_dev *pdev)
         xfree(r);
     }
     spin_unlock(&pdev->vpci->lock);
+    xfree(pdev->vpci->msi);
     xfree(pdev->vpci);
     pdev->vpci = NULL;
 }
diff --git a/xen/include/asm-x86/hvm/io.h b/xen/include/asm-x86/hvm/io.h
index 16465ceb30..0fedb3473c 100644
--- a/xen/include/asm-x86/hvm/io.h
+++ b/xen/include/asm-x86/hvm/io.h
@@ -127,6 +127,11 @@ void hvm_dpci_eoi(struct domain *d, unsigned int guest_irq,
 void msix_write_completion(struct vcpu *);
 void msixtbl_init(struct domain *d);
 
+/* Arch-specific MSI data for vPCI. */
+struct vpci_arch_msi {
+    int pirq;
+};
+
 enum stdvga_cache_state {
     STDVGA_CACHE_UNINITIALIZED,
     STDVGA_CACHE_ENABLED,
diff --git a/xen/include/asm-x86/msi.h b/xen/include/asm-x86/msi.h
index 37d37b820e..10387dce2e 100644
--- a/xen/include/asm-x86/msi.h
+++ b/xen/include/asm-x86/msi.h
@@ -48,6 +48,7 @@
 #define MSI_ADDR_REDIRECTION_SHIFT  3
 #define MSI_ADDR_REDIRECTION_CPU    (0 << MSI_ADDR_REDIRECTION_SHIFT)
 #define MSI_ADDR_REDIRECTION_LOWPRI (1 << MSI_ADDR_REDIRECTION_SHIFT)
+#define MSI_ADDR_REDIRECTION_MASK   (1 << MSI_ADDR_REDIRECTION_SHIFT)
 
 #define MSI_ADDR_DEST_ID_SHIFT         12
 #define         MSI_ADDR_DEST_ID_MASK          0x00ff000
@@ -152,6 +153,8 @@ int msi_free_irq(struct msi_desc *entry);
        ( (is64bit == 1) ? base+PCI_MSI_DATA_64 : base+PCI_MSI_DATA_32 )
 #define msi_mask_bits_reg(base, is64bit) \
        ( (is64bit == 1) ? base+PCI_MSI_MASK_BIT : base+PCI_MSI_MASK_BIT-4)
+#define msi_pending_bits_reg(base, is64bit) \
+       ((base) + PCI_MSI_MASK_BIT + ((is64bit) ? 4 : 0))
 #define msi_disable(control)           control &= ~PCI_MSI_FLAGS_ENABLE
 #define multi_msi_capable(control) \
        (1 << ((control & PCI_MSI_FLAGS_QMASK) >> 1))
diff --git a/xen/include/xen/irq.h b/xen/include/xen/irq.h
index 0aa817e266..586b78393a 100644
--- a/xen/include/xen/irq.h
+++ b/xen/include/xen/irq.h
@@ -133,6 +133,7 @@ struct pirq {
     struct arch_pirq arch;
 };
 
+#define INVALID_PIRQ (-1)
 #define pirq_info(d, p) ((struct pirq *)radix_tree_lookup(&(d)->pirq_tree, p))
 
 /* Use this instead of pirq_info() if the structure may need allocating. */
diff --git a/xen/include/xen/vpci.h b/xen/include/xen/vpci.h
index 6bf8b22b4f..116b93f519 100644
--- a/xen/include/xen/vpci.h
+++ b/xen/include/xen/vpci.h
@@ -87,6 +87,30 @@ struct vpci {
         /* FIXME: currently there's no support for SR-IOV. */
     } header;
 #endif
+
+    /* MSI data. */
+    struct vpci_msi {
+#ifdef __XEN__
+      /* Address. */
+        uint64_t address;
+        /* Mask bitfield. */
+        uint32_t mask;
+        /* Data. */
+        uint16_t data;
+        /* Maximum number of vectors supported by the device. */
+        uint8_t max_vectors : 5;
+        /* Enabled? */
+        bool enabled        : 1;
+        /* Supports per-vector masking? */
+        bool masking        : 1;
+        /* 64-bit address capable? */
+        bool address64      : 1;
+        /* Number of vectors configured. */
+        uint8_t vectors     : 5;
+        /* Arch-specific data. */
+        struct vpci_arch_msi arch;
+#endif
+    } *msi;
 };
 
 struct vpci_vcpu {
@@ -97,6 +121,20 @@ struct vpci_vcpu {
     bool rom_only : 1;
 };
 
+#ifdef __XEN__
+void vpci_dump_msi(void);
+
+/* Arch-specific vPCI MSI helpers. */
+void vpci_msi_arch_mask(struct vpci_msi *msi, const struct pci_dev *pdev,
+                        unsigned int entry, bool mask);
+int __must_check vpci_msi_arch_enable(struct vpci_msi *msi,
+                                      const struct pci_dev *pdev,
+                                      unsigned int vectors);
+void vpci_msi_arch_disable(struct vpci_msi *msi, const struct pci_dev *pdev);
+void vpci_msi_arch_init(struct vpci_msi *msi);
+void vpci_msi_arch_print(const struct vpci_msi *msi);
+#endif /* __XEN__ */
+
 #else /* !CONFIG_HAS_VPCI */
 struct vpci_vcpu {};
 #endif
-- 
2.16.2


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.