aboutsummaryrefslogtreecommitdiffstats
path: root/main/xen/xsa337-2.patch
diff options
context:
space:
mode:
Diffstat (limited to 'main/xen/xsa337-2.patch')
-rw-r--r--main/xen/xsa337-2.patch184
1 files changed, 184 insertions, 0 deletions
diff --git a/main/xen/xsa337-2.patch b/main/xen/xsa337-2.patch
new file mode 100644
index 0000000000..8089e68a2c
--- /dev/null
+++ b/main/xen/xsa337-2.patch
@@ -0,0 +1,184 @@
+From: Jan Beulich <jbeulich@suse.com>
+Subject: x86/MSI-X: restrict reading of table/PBA bases from BARs
+
+When assigned to less trusted or un-trusted guests, devices may change
+state behind our backs (they may e.g. get reset by means we may not know
+about). Therefore we should avoid reading BARs from hardware once a
+device is no longer owned by Dom0. Furthermore when we can't read a BAR,
+or when we read zero, we shouldn't instead use the caller provided
+address unless that caller can be trusted.
+
+Re-arrange the logic in msix_capability_init() such that only Dom0 (and
+only if the device isn't DomU-owned yet) or calls through
+PHYSDEVOP_prepare_msix will actually result in the reading of the
+respective BAR register(s). Additionally do so only as long as in-use
+table entries are known (note that invocation of PHYSDEVOP_prepare_msix
+counts as a "pseudo" entry). In all other uses the value already
+recorded will get used instead.
+
+Clear the recorded values in _pci_cleanup_msix() as well as on the one
+affected error path. (Adjust this error path to also avoid blindly
+disabling MSI-X when it was enabled on entry to the function.)
+
+While moving around variable declarations (in many cases to reduce their
+scopes), also adjust some of their types.
+
+This is part of XSA-337.
+
+Signed-off-by: Jan Beulich <jbeulich@suse.com>
+Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
+---
+v2: Use "unsigned int" for moved bir, pbus, etc. Further restrict under
+ what conditions to read the BAR(s).
+
+--- a/xen/arch/x86/msi.c
++++ b/xen/arch/x86/msi.c
+@@ -771,16 +771,14 @@ static int msix_capability_init(struct p
+ {
+ struct arch_msix *msix = dev->msix;
+ struct msi_desc *entry = NULL;
+- int vf;
+ u16 control;
+ u64 table_paddr;
+ u32 table_offset;
+- u8 bir, pbus, pslot, pfunc;
+ u16 seg = dev->seg;
+ u8 bus = dev->bus;
+ u8 slot = PCI_SLOT(dev->devfn);
+ u8 func = PCI_FUNC(dev->devfn);
+- bool maskall = msix->host_maskall;
++ bool maskall = msix->host_maskall, zap_on_error = false;
+ unsigned int pos = pci_find_cap_offset(seg, bus, slot, func,
+ PCI_CAP_ID_MSIX);
+
+@@ -822,43 +820,45 @@ static int msix_capability_init(struct p
+
+ /* Locate MSI-X table region */
+ table_offset = pci_conf_read32(dev->sbdf, msix_table_offset_reg(pos));
+- bir = (u8)(table_offset & PCI_MSIX_BIRMASK);
+- table_offset &= ~PCI_MSIX_BIRMASK;
++ if ( !msix->used_entries &&
++ (!msi ||
++ (is_hardware_domain(current->domain) &&
++ (dev->domain == current->domain || dev->domain == dom_io))) )
++ {
++ unsigned int bir = table_offset & PCI_MSIX_BIRMASK, pbus, pslot, pfunc;
++ int vf;
++ paddr_t pba_paddr;
++ unsigned int pba_offset;
+
+- if ( !dev->info.is_virtfn )
+- {
+- pbus = bus;
+- pslot = slot;
+- pfunc = func;
+- vf = -1;
+- }
+- else
+- {
+- 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);
+- }
+-
+- table_paddr = read_pci_mem_bar(seg, pbus, pslot, pfunc, bir, vf);
+- WARN_ON(msi && msi->table_base != table_paddr);
+- if ( !table_paddr )
+- {
+- if ( !msi || !msi->table_base )
++ if ( !dev->info.is_virtfn )
+ {
+- pci_conf_write16(dev->sbdf, msix_control_reg(pos),
+- control & ~PCI_MSIX_FLAGS_ENABLE);
+- xfree(entry);
+- return -ENXIO;
++ pbus = bus;
++ pslot = slot;
++ pfunc = func;
++ vf = -1;
++ }
++ else
++ {
++ 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);
+ }
+- table_paddr = msi->table_base;
+- }
+- table_paddr += table_offset;
+
+- if ( !msix->used_entries )
+- {
+- u64 pba_paddr;
+- u32 pba_offset;
++ table_paddr = read_pci_mem_bar(seg, pbus, pslot, pfunc, bir, vf);
++ WARN_ON(msi && msi->table_base != table_paddr);
++ if ( !table_paddr )
++ {
++ if ( !msi || !msi->table_base )
++ {
++ pci_conf_write16(dev->sbdf, msix_control_reg(pos),
++ control & ~PCI_MSIX_FLAGS_ENABLE);
++ xfree(entry);
++ return -ENXIO;
++ }
++ table_paddr = msi->table_base;
++ }
++ table_paddr += table_offset & ~PCI_MSIX_BIRMASK;
+
+ msix->table.first = PFN_DOWN(table_paddr);
+ msix->table.last = PFN_DOWN(table_paddr +
+@@ -877,7 +877,18 @@ static int msix_capability_init(struct p
+ BITS_TO_LONGS(msix->nr_entries) - 1);
+ WARN_ON(rangeset_overlaps_range(mmio_ro_ranges, msix->pba.first,
+ msix->pba.last));
++
++ zap_on_error = true;
++ }
++ else if ( !msix->table.first )
++ {
++ pci_conf_write16(dev->sbdf, msix_control_reg(pos), control);
++ xfree(entry);
++ return -ENODATA;
+ }
++ else
++ table_paddr = (msix->table.first << PAGE_SHIFT) +
++ PAGE_OFFSET(table_offset & ~PCI_MSIX_BIRMASK);
+
+ if ( entry )
+ {
+@@ -888,8 +899,15 @@ static int msix_capability_init(struct p
+
+ if ( idx < 0 )
+ {
+- pci_conf_write16(dev->sbdf, msix_control_reg(pos),
+- control & ~PCI_MSIX_FLAGS_ENABLE);
++ if ( zap_on_error )
++ {
++ msix->table.first = 0;
++ msix->pba.first = 0;
++
++ control &= ~PCI_MSIX_FLAGS_ENABLE;
++ }
++
++ pci_conf_write16(dev->sbdf, msix_control_reg(pos), control);
+ xfree(entry);
+ return idx;
+ }
+@@ -1072,9 +1090,14 @@ static void _pci_cleanup_msix(struct arc
+ if ( rangeset_remove_range(mmio_ro_ranges, msix->table.first,
+ msix->table.last) )
+ WARN();
++ msix->table.first = 0;
++ msix->table.last = 0;
++
+ if ( rangeset_remove_range(mmio_ro_ranges, msix->pba.first,
+ msix->pba.last) )
+ WARN();
++ msix->pba.first = 0;
++ msix->pba.last = 0;
+ }
+ }
+