|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v5 11/11] vpci/msix: add MSI-X handlers
>>> On 14.08.17 at 16:28, <roger.pau@xxxxxxxxxx> wrote:
> +void vpci_msix_arch_mask(struct vpci_arch_msix_entry *arch,
> + struct pci_dev *pdev, bool mask)
> +{
> + if ( arch->pirq == INVALID_PIRQ )
> + return;
How come no similar guard is needed in vpci_msi_arch_mask()?
> +int vpci_msix_arch_enable(struct vpci_arch_msix_entry *arch,
Considering the first parameter's type, is the name actually
appropriate? From the parameter (and the code) I would
conclude you enable a single entry here only, not the entire
device. This may apply to functions further below, too.
> + struct pci_dev *pdev, uint64_t address,
> + uint32_t data, unsigned int entry_nr,
> + paddr_t table_base)
> +{
> + struct domain *d = pdev->domain;
> + struct msi_info msi_info = {
> + .seg = pdev->seg,
> + .bus = pdev->bus,
> + .devfn = pdev->devfn,
> + .table_base = table_base,
> + .entry_nr = entry_nr,
> + };
> + xen_domctl_bind_pt_irq_t bind = {
> + .irq_type = PT_IRQ_TYPE_MSI,
> + .u.msi.gvec = msi_vector(data),
> + .u.msi.gflags = msi_flags(data, address),
> + };
> + int rc;
> +
> + ASSERT(arch->pirq == INVALID_PIRQ);
> +
> + /*
> + * Simple sanity check before trying to setup the interrupt.
> + * According to the Intel SDM, bits [31, 20] must contain the
> + * value 0xfee. This avoids needlessly setting up pirqs for entries
> + * the guest has not actually configured.
> + */
> + if ( (address & 0xfff00000) != MSI_ADDR_HEADER )
> + return -EINVAL;
What about the upper half of the address? That needs to be zero,
I think.
> + rc = allocate_and_map_msi_pirq(d, -1, &arch->pirq,
> + MAP_PIRQ_TYPE_MSI, &msi_info);
> + if ( rc )
> + {
> + gdprintk(XENLOG_ERR,
> + "%04x:%02x:%02x.%u: unable to map MSI-X PIRQ entry %u:
> %d\n",
> + pdev->seg, pdev->bus, PCI_SLOT(pdev->devfn),
> + PCI_FUNC(pdev->devfn), entry_nr, rc);
> + return rc;
> + }
> +
> + bind.machine_irq = arch->pirq;
> + pcidevs_lock();
> + rc = pt_irq_create_bind(d, &bind);
> + if ( rc )
> + {
> + gdprintk(XENLOG_ERR,
> + "%04x:%02x:%02x.%u: unable to create MSI-X bind %u: %d\n",
> + pdev->seg, pdev->bus, PCI_SLOT(pdev->devfn),
> + PCI_FUNC(pdev->devfn), entry_nr, rc);
> + spin_lock(&d->event_lock);
> + unmap_domain_pirq(d, arch->pirq);
> + spin_unlock(&d->event_lock);
> + pcidevs_unlock();
> + arch->pirq = INVALID_PIRQ;
> + return rc;
> + }
> + pcidevs_unlock();
> +
> + return 0;
> +}
Much of this looks pretty similar to the MSI counterpart. Can any
reasonable portion of this be factored out?
> --- a/xen/drivers/vpci/header.c
> +++ b/xen/drivers/vpci/header.c
> @@ -20,6 +20,7 @@
> #include <xen/sched.h>
> #include <xen/vpci.h>
> #include <xen/p2m-common.h>
> +#include <asm/p2m.h>
No need to include xen/p2m-common.h then anymore, although for
a common source file it's probably not entirely right to include this
header at all.
> @@ -89,11 +90,45 @@ static int vpci_map_range(unsigned long s, unsigned long
> e, void *data)
> return modify_mmio(map->d, _gfn(s), _mfn(s), e - s + 1, map->map);
> }
>
> +static int vpci_unmap_msix(struct domain *d, struct vpci_msix_mem *msix)
> +{
> + unsigned long gfn;
> +
> + for ( gfn = PFN_DOWN(msix->addr); gfn <= PFN_UP(msix->addr + msix->size);
Missing a subtraction of 1 in the PFN_UP() invocation? And why <= ?
> @@ -102,6 +137,42 @@ static int vpci_modify_bar(struct domain *d, const
> struct vpci_bar *bar,
> if ( IS_ERR(mem) )
> return PTR_ERR(mem);
>
> + for ( i = 0; i < ARRAY_SIZE(bar->msix); i++ )
> + {
> + struct vpci_msix_mem *msix = bar->msix[i];
> +
> + if ( !msix || msix->addr == INVALID_PADDR )
> + continue;
> +
> + if ( map )
> + {
> + /*
> + * Make sure the MSI-X regions of the BAR are not mapped into the
> + * domain p2m, or else the MSI-X handlers are useless. Only do
> this
> + * when mapping, since that's when the memory decoding on the
> + * device is enabled.
> + *
> + * This is required because iommu_inclusive_mapping might have
> + * mapped MSI-X regions into the guest p2m.
> + */
> + rc = vpci_unmap_msix(d, msix);
> + if ( rc )
> + {
> + rangeset_destroy(mem);
> + return rc;
> + }
> + }
> +
> + rc = rangeset_remove_range(mem, PFN_DOWN(msix->addr),
> + PFN_DOWN(msix->addr + msix->size));
> + if ( rc )
> + {
> + rangeset_destroy(mem);
> + return rc;
> + }
> +
> + }
Why do you do this for the PBA regardless of whether it's shared
with a table page?
> @@ -255,6 +327,11 @@ static void vpci_bar_write(struct pci_dev *pdev,
> unsigned int reg,
> bar->addr &= ~(0xffffffffull << (hi ? 32 : 0));
> bar->addr |= (uint64_t)val << (hi ? 32 : 0);
>
> + /* Update any MSI-X areas contained in this BAR. */
> + for ( i = 0; i < ARRAY_SIZE(bar->msix); i++ )
> + if ( bar->msix[i] )
> + bar->msix[i]->addr = bar->addr + bar->msix[i]->offset;
Is it really necessary to store this information separately? It
can be re-calculated easily from BIR.
> +#define MSIX_SIZE(num) offsetof(struct vpci_msix, entries[num])
Wouldn't this better be VMSIX_SIZE()?
> +#define MSIX_ADDR_IN_RANGE(a, table) \
> + ((table)->addr != INVALID_PADDR && (a) >= (table)->addr && \
> + (a) < (table)->addr + (table)->size)
> +
> +static uint32_t vpci_msix_control_read(struct pci_dev *pdev, unsigned int
> reg,
> + const void *data)
> +{
> + const struct vpci_msix *msix = data;
> + uint16_t val;
> +
> + val = (msix->max_entries - 1) & PCI_MSIX_FLAGS_QSIZE;
Is the explicit masking really necessary? And if so, it would look to
be more correct to use MASK_INSR().
> +static struct vpci_msix *vpci_msix_find(struct domain *d, unsigned long addr)
d can be const as it looks.
> +static int vpci_msix_access_check(struct pci_dev *pdev, unsigned long addr,
> + unsigned int len)
> +{
> + uint8_t seg = pdev->seg, bus = pdev->bus;
> + uint8_t slot = PCI_SLOT(pdev->devfn), func = PCI_FUNC(pdev->devfn);
> +
> + /* Only allow 32/64b accesses. */
> + if ( len != 4 && len != 8 )
> + {
> + gdprintk(XENLOG_ERR,
> + "%04x:%02x:%02x.%u: invalid MSI-X table access size: %u\n",
> + seg, bus, slot, func, len);
> + return -EINVAL;
> + }
> +
> + /* Only allow aligned accesses. */
> + if ( (addr & (len - 1)) != 0 )
> + {
> + gdprintk(XENLOG_ERR,
> + "%04x:%02x:%02x.%u: MSI-X only allows aligned accesses\n",
> + seg, bus, slot, func);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
Wouldn't this function better return bool?
> +static int vpci_msix_read(struct vcpu *v, unsigned long addr,
> + unsigned int len, unsigned long *data)
> +{
> + struct domain *d = v->domain;
> + struct vpci_msix *msix;
> + const struct vpci_msix_entry *entry;
> + unsigned int offset;
> +
> + vpci_rlock(d);
> + msix = vpci_msix_find(d, addr);
> + if ( !msix )
> + {
> + vpci_runlock(d);
> + *data = ~0ul;
> + return X86EMUL_OKAY;
> + }
> +
> + if ( vpci_msix_access_check(msix->pdev, addr, len) )
> + {
> + vpci_runlock(d);
> + *data = ~0ul;
> + return X86EMUL_OKAY;
> + }
Please fold the two if()s.
> + if ( MSIX_ADDR_IN_RANGE(addr, &msix->pba) )
> + {
> + /* Access to PBA. */
> + switch ( len )
> + {
> + case 4:
> + *data = readl(addr);
> + break;
> + case 8:
> + *data = readq(addr);
> + break;
This is strictly only valid for Dom0, so perhaps worth a comment.
> + default:
> + ASSERT_UNREACHABLE();
> + *data = ~0ul;
> + break;
> + }
> +
> + vpci_runlock(d);
> + return X86EMUL_OKAY;
> + }
> +
> + entry = vpci_msix_get_entry(msix, addr);
> + offset = addr & (PCI_MSIX_ENTRY_SIZE - 1);
> +
> + switch ( offset )
> + {
> + case PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET:
> + /*
> + * NB: do explicit truncation to the size of the access. This
> shouldn't
> + * be required here, since the caller of the handler should already
> + * take the appropriate measures to truncate the value before
> returning
> + * to the guest, but better be safe than sorry.
> + */
> + *data = len == 8 ? entry->addr : (uint32_t)entry->addr;
I don't see the need for it - if there's really a bug up the call stack,
it'll be better to fix it there. There's no security implication here
afaics.
> --- a/xen/include/asm-x86/hvm/io.h
> +++ b/xen/include/asm-x86/hvm/io.h
> @@ -144,6 +144,25 @@ void vpci_msi_arch_init(struct vpci_arch_msi *arch);
> void vpci_msi_arch_print(const struct vpci_arch_msi *arch, uint16_t data,
> uint64_t addr);
>
> +/* Arch-specific MSI-X entry data for vPCI. */
> +struct vpci_arch_msix_entry {
> + int pirq;
> +};
> +
> +/* Arch-specific vPCI MSI-X helpers. */
> +void vpci_msix_arch_mask(struct vpci_arch_msix_entry *arch,
> + struct pci_dev *pdev, bool mask);
> +int vpci_msix_arch_enable(struct vpci_arch_msix_entry *arch,
> + struct pci_dev *pdev, uint64_t address,
> + uint32_t data, unsigned int entry_nr,
> + paddr_t table_base);
> +int vpci_msix_arch_disable(struct vpci_arch_msix_entry *arch,
> + struct pci_dev *pdev);
> +int vpci_msix_arch_init(struct vpci_arch_msix_entry *arch);
> +void vpci_msix_arch_print(const struct vpci_arch_msix_entry *entry,
> + uint32_t data, uint64_t addr, bool masked,
> + unsigned int pos);
Actually such helpers, when they are supposed to be called from
common code, and when it is not expected for them to be inlined,
would better be declared in a common header, so they won't need
repeating (and later updating) in multiple places. Obviously this
applies to the earlier MSI ones too.
Jan
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |