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

Re: [Xen-devel] [PATCH v11 09/12] vpci/msi: add MSI handlers



> -----Original Message-----
> From: Roger Pau Monne [mailto:roger.pau@xxxxxxxxxx]
> Sent: 20 March 2018 15:16
> To: xen-devel@xxxxxxxxxxxxxxxxxxxx
> Cc: Boris Ostrovsky <boris.ostrovsky@xxxxxxxxxx>; Konrad Rzeszutek Wilk
> <konrad.wilk@xxxxxxxxxx>; Roger Pau Monne <roger.pau@xxxxxxxxxx>; Jan
> Beulich <jbeulich@xxxxxxxx>; Andrew Cooper
> <Andrew.Cooper3@xxxxxxxxxx>; George Dunlap
> <George.Dunlap@xxxxxxxxxx>; Ian Jackson <Ian.Jackson@xxxxxxxxxx>; Julien
> Grall <julien.grall@xxxxxxx>; Stefano Stabellini <sstabellini@xxxxxxxxxx>;
> Tim (Xen.org) <tim@xxxxxxx>; Wei Liu <wei.liu2@xxxxxxxxxx>; Paul Durrant
> <Paul.Durrant@xxxxxxxxxx>
> Subject: [PATCH v11 09/12] 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>

io header changes...

Reviewed-by: Paul Durrant <paul.durrant@xxxxxxxxxx>

> ---
> 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®.