aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--xen/arch/x86/msi.c198
-rw-r--r--xen/arch/x86/physdev.c14
-rw-r--r--xen/include/asm-x86/msi.h1
-rw-r--r--xen/include/public/physdev.h15
-rw-r--r--xen/include/xen/pci.h1
5 files changed, 160 insertions, 69 deletions
diff --git a/xen/arch/x86/msi.c b/xen/arch/x86/msi.c
index 1579dfc870..ef332fb11e 100644
--- a/xen/arch/x86/msi.c
+++ b/xen/arch/x86/msi.c
@@ -602,8 +602,8 @@ static u64 read_pci_mem_bar(u8 bus, u8 slot, u8 func, u8 bir, int vf)
* @entries: pointer to an array of struct msix_entry entries
* @nvec: number of @entries
*
- * Setup the MSI-X capability structure of device function with a
- * single MSI-X irq. A return of zero indicates the successful setup of
+ * Setup the MSI-X capability structure of device function with the requested
+ * number MSI-X irqs. A return of zero indicates the successful setup of
* requested MSI-X entries with allocated irqs or non-zero for otherwise.
**/
static int msix_capability_init(struct pci_dev *dev,
@@ -611,84 +611,67 @@ static int msix_capability_init(struct pci_dev *dev,
struct msi_desc **desc,
unsigned int nr_entries)
{
- struct msi_desc *entry;
- int pos;
+ struct msi_desc *entry = NULL;
+ int pos, vf;
u16 control;
- u64 table_paddr, entry_paddr;
- u32 table_offset, entry_offset;
- u8 bir;
- void __iomem *base;
- int idx;
+ u64 table_paddr;
+ u32 table_offset;
+ u8 bir, pbus, pslot, pfunc;
u8 bus = dev->bus;
u8 slot = PCI_SLOT(dev->devfn);
u8 func = PCI_FUNC(dev->devfn);
ASSERT(spin_is_locked(&pcidevs_lock));
- ASSERT(desc);
pos = pci_find_cap_offset(bus, slot, func, PCI_CAP_ID_MSIX);
control = pci_conf_read16(bus, slot, func, msix_control_reg(pos));
msix_set_enable(dev, 0);/* Ensure msix is disabled as I set it up */
- /* MSI-X Table Initialization */
- entry = alloc_msi_entry();
- if ( !entry )
- return -ENOMEM;
+ if ( desc )
+ {
+ entry = alloc_msi_entry();
+ if ( !entry )
+ return -ENOMEM;
+ ASSERT(msi);
+ }
- /* Request & Map MSI-X table region */
+ /* Locate MSI-X table region */
table_offset = pci_conf_read32(bus, slot, func, msix_table_offset_reg(pos));
bir = (u8)(table_offset & PCI_MSIX_BIRMASK);
table_offset &= ~PCI_MSIX_BIRMASK;
- entry_offset = msi->entry_nr * PCI_MSIX_ENTRY_SIZE;
- table_paddr = msi->table_base + table_offset;
- entry_paddr = table_paddr + entry_offset;
- idx = msix_get_fixmap(dev, table_paddr, entry_paddr);
- if ( idx < 0 )
+ if ( !dev->info.is_virtfn )
{
- xfree(entry);
- return idx;
+ pbus = bus;
+ pslot = slot;
+ pfunc = func;
+ vf = -1;
}
- base = (void *)(fix_to_virt(idx) +
- ((unsigned long)entry_paddr & ((1UL << PAGE_SHIFT) - 1)));
-
- entry->msi_attrib.type = PCI_CAP_ID_MSIX;
- entry->msi_attrib.is_64 = 1;
- entry->msi_attrib.entry_nr = msi->entry_nr;
- entry->msi_attrib.maskbit = 1;
- entry->msi_attrib.masked = 1;
- entry->msi_attrib.pos = pos;
- entry->irq = msi->irq;
- entry->dev = dev;
- entry->mask_base = base;
-
- list_add_tail(&entry->list, &dev->msi_list);
-
- if ( !dev->msix_nr_entries )
+ else
{
- u8 pbus, pslot, pfunc;
- int vf;
- u64 pba_paddr;
- u32 pba_offset;
+ pbus = dev->info.physfn.bus;
+ pslot = PCI_SLOT(dev->info.physfn.devfn);
+ pfunc = PCI_FUNC(dev->info.physfn.devfn);
+ vf = PCI_BDF2(dev->bus, dev->devfn);
+ }
- if ( !dev->info.is_virtfn )
- {
- pbus = bus;
- pslot = slot;
- pfunc = func;
- vf = -1;
- }
- else
+ table_paddr = read_pci_mem_bar(pbus, pslot, pfunc, bir, vf);
+ WARN_ON(msi && msi->table_base != table_paddr);
+ if ( !table_paddr )
+ {
+ if ( !msi || !msi->table_base )
{
- pbus = dev->info.physfn.bus;
- pslot = PCI_SLOT(dev->info.physfn.devfn);
- pfunc = PCI_FUNC(dev->info.physfn.devfn);
- vf = PCI_BDF2(dev->bus, dev->devfn);
+ xfree(entry);
+ return -ENXIO;
}
+ table_paddr = msi->table_base;
+ }
+ table_paddr += table_offset;
- ASSERT(!dev->msix_used_entries);
- WARN_ON(msi->table_base !=
- read_pci_mem_bar(pbus, pslot, pfunc, bir, vf));
+ if ( !dev->msix_used_entries )
+ {
+ u64 pba_paddr;
+ u32 pba_offset;
dev->msix_nr_entries = nr_entries;
dev->msix_table.first = PFN_DOWN(table_paddr);
@@ -709,7 +692,42 @@ static int msix_capability_init(struct pci_dev *dev,
BITS_TO_LONGS(nr_entries) - 1);
WARN_ON(rangeset_overlaps_range(mmio_ro_ranges, dev->msix_pba.first,
dev->msix_pba.last));
+ }
+
+ if ( entry )
+ {
+ /* Map MSI-X table region */
+ u64 entry_paddr = table_paddr + msi->entry_nr * PCI_MSIX_ENTRY_SIZE;
+ int idx = msix_get_fixmap(dev, table_paddr, entry_paddr);
+ void __iomem *base;
+
+ if ( idx < 0 )
+ {
+ xfree(entry);
+ return idx;
+ }
+ base = (void *)(fix_to_virt(idx) +
+ ((unsigned long)entry_paddr & (PAGE_SIZE - 1)));
+
+ /* Mask interrupt here */
+ writel(1, base + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
+
+ entry->msi_attrib.type = PCI_CAP_ID_MSIX;
+ entry->msi_attrib.is_64 = 1;
+ entry->msi_attrib.entry_nr = msi->entry_nr;
+ entry->msi_attrib.maskbit = 1;
+ entry->msi_attrib.masked = 1;
+ entry->msi_attrib.pos = pos;
+ entry->irq = msi->irq;
+ entry->dev = dev;
+ entry->mask_base = base;
+
+ list_add_tail(&entry->list, &dev->msi_list);
+ *desc = entry;
+ }
+ if ( !dev->msix_used_entries )
+ {
if ( rangeset_add_range(mmio_ro_ranges, dev->msix_table.first,
dev->msix_table.last) )
WARN();
@@ -720,7 +738,7 @@ static int msix_capability_init(struct pci_dev *dev,
if ( dev->domain )
p2m_change_entry_type_global(p2m_get_hostp2m(dev->domain),
p2m_mmio_direct, p2m_mmio_direct);
- if ( !dev->domain || !paging_mode_translate(dev->domain) )
+ if ( desc && (!dev->domain || !paging_mode_translate(dev->domain)) )
{
struct domain *d = dev->domain;
@@ -734,6 +752,13 @@ static int msix_capability_init(struct pci_dev *dev,
break;
if ( d )
{
+ if ( !IS_PRIV(d) && dev->msix_warned != d->domain_id )
+ {
+ dev->msix_warned = d->domain_id;
+ printk(XENLOG_ERR
+ "Potentially insecure use of MSI-X on %02x:%02x.%u by Dom%d\n",
+ bus, slot, func, d->domain_id);
+ }
/* XXX How to deal with existing mappings? */
}
}
@@ -742,10 +767,6 @@ static int msix_capability_init(struct pci_dev *dev,
WARN_ON(dev->msix_table.first != (table_paddr >> PAGE_SHIFT));
++dev->msix_used_entries;
- /* Mask interrupt here */
- writel(1, entry->mask_base + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
-
- *desc = entry;
/* Restore MSI-X enabled bits */
pci_conf_write16(bus, slot, func, msix_control_reg(pos), control);
@@ -876,6 +897,19 @@ static int __pci_enable_msix(struct msi_info *msi, struct msi_desc **desc)
return status;
}
+static void _pci_cleanup_msix(struct pci_dev *dev)
+{
+ if ( !--dev->msix_used_entries )
+ {
+ if ( rangeset_remove_range(mmio_ro_ranges, dev->msix_table.first,
+ dev->msix_table.last) )
+ WARN();
+ if ( rangeset_remove_range(mmio_ro_ranges, dev->msix_pba.first,
+ dev->msix_pba.last) )
+ WARN();
+ }
+}
+
static void __pci_disable_msix(struct msi_desc *entry)
{
struct pci_dev *dev;
@@ -898,15 +932,41 @@ static void __pci_disable_msix(struct msi_desc *entry)
pci_conf_write16(bus, slot, func, msix_control_reg(pos), control);
- if ( !--dev->msix_used_entries )
+ _pci_cleanup_msix(dev);
+}
+
+int pci_prepare_msix(u8 bus, u8 devfn, bool_t off)
+{
+ int rc;
+ struct pci_dev *pdev;
+ u8 slot = PCI_SLOT(devfn), func = PCI_FUNC(devfn);
+ unsigned int pos = pci_find_cap_offset(bus, slot, func,
+ PCI_CAP_ID_MSIX);
+
+ if ( !pos )
+ return -ENODEV;
+
+ spin_lock(&pcidevs_lock);
+ pdev = pci_get_pdev(bus, devfn);
+ if ( !pdev )
+ rc = -ENODEV;
+ else if ( pdev->msix_used_entries != !!off )
+ rc = -EBUSY;
+ else if ( off )
{
- if ( rangeset_remove_range(mmio_ro_ranges, dev->msix_table.first,
- dev->msix_table.last) )
- WARN();
- if ( rangeset_remove_range(mmio_ro_ranges, dev->msix_pba.first,
- dev->msix_pba.last) )
- WARN();
+ _pci_cleanup_msix(pdev);
+ rc = 0;
}
+ else
+ {
+ u16 control = pci_conf_read16(bus, slot, func, msix_control_reg(pos));
+
+ rc = msix_capability_init(pdev, NULL, NULL,
+ multi_msix_capable(control));
+ }
+ spin_unlock(&pcidevs_lock);
+
+ return rc;
}
/*
diff --git a/xen/arch/x86/physdev.c b/xen/arch/x86/physdev.c
index de364f0c8d..8feb84aa67 100644
--- a/xen/arch/x86/physdev.c
+++ b/xen/arch/x86/physdev.c
@@ -548,6 +548,20 @@ ret_t do_physdev_op(int cmd, XEN_GUEST_HANDLE(void) arg)
break;
}
+ case PHYSDEVOP_prepare_msix:
+ case PHYSDEVOP_release_msix: {
+ struct physdev_pci_device dev;
+
+ if ( copy_from_guest(&dev, arg, 1) )
+ ret = -EFAULT;
+ else if ( dev.seg )
+ ret = -EOPNOTSUPP;
+ else
+ ret = pci_prepare_msix(dev.bus, dev.devfn,
+ cmd != PHYSDEVOP_prepare_msix);
+ break;
+ }
+
case PHYSDEVOP_restore_msi: {
struct physdev_restore_msi restore_msi;
struct pci_dev *pdev;
diff --git a/xen/include/asm-x86/msi.h b/xen/include/asm-x86/msi.h
index dc402b9702..bd83a577fd 100644
--- a/xen/include/asm-x86/msi.h
+++ b/xen/include/asm-x86/msi.h
@@ -75,6 +75,7 @@ extern void unmask_msi_irq(unsigned int irq);
extern void set_msi_affinity(unsigned int vector, cpumask_t mask);
extern int pci_enable_msi(struct msi_info *msi, struct msi_desc **desc);
extern void pci_disable_msi(struct msi_desc *desc);
+extern int pci_prepare_msix(u8 bus, u8 devfn, bool_t off);
extern void pci_cleanup_msi(struct pci_dev *pdev);
extern int setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc, int irq);
extern void teardown_msi_irq(int irq);
diff --git a/xen/include/public/physdev.h b/xen/include/public/physdev.h
index 6f792ee92f..d65f72b40a 100644
--- a/xen/include/public/physdev.h
+++ b/xen/include/public/physdev.h
@@ -264,6 +264,21 @@ typedef struct physdev_get_free_pirq physdev_get_free_pirq_t;
DEFINE_XEN_GUEST_HANDLE(physdev_get_free_pirq_t);
/*
+ * Dom0 should use these two to announce MMIO resources assigned to
+ * MSI-X capable devices won't (prepare) or may (release) change.
+ */
+#define PHYSDEVOP_prepare_msix 30
+#define PHYSDEVOP_release_msix 31
+struct physdev_pci_device {
+ /* IN */
+ uint16_t seg;
+ uint8_t bus;
+ uint8_t devfn;
+};
+typedef struct physdev_pci_device physdev_pci_device_t;
+DEFINE_XEN_GUEST_HANDLE(physdev_pci_device_t);
+
+/*
* Notify that some PIRQ-bound event channels have been unmasked.
* ** This command is obsolete since interface version 0x00030202 and is **
* ** unsupported by newer versions of Xen. **
diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h
index e1b9cdd3c9..3b8067ebbe 100644
--- a/xen/include/xen/pci.h
+++ b/xen/include/xen/pci.h
@@ -57,6 +57,7 @@ struct pci_dev {
int msix_table_refcnt[MAX_MSIX_TABLE_PAGES];
int msix_table_idx[MAX_MSIX_TABLE_PAGES];
spinlock_t msix_table_lock;
+ domid_t msix_warned;
struct domain *domain;
const u8 bus;