diff --git a/xen/drivers/vpci/header.c b/xen/drivers/vpci/header.c index 92c00b68aa..43cac3f7d3 100644 --- a/xen/drivers/vpci/header.c +++ b/xen/drivers/vpci/header.c @@ -89,6 +89,17 @@ static void modify_decoding(const struct pci_dev *pdev, uint16_t cmd, bool map = cmd & PCI_COMMAND_MEMORY; unsigned int i; + /* + * Make sure there are no mappings in the MSIX MMIO areas, so that accesses + * can be trapped (and emulated) by Xen when the memory decoding bit is + * enabled. + * + * FIXME: punching holes after the p2m has been set up might be racy for + * DomU usage, needs to be revisited. + */ + if ( map && !rom_only && vpci_make_msix_hole(pdev) ) + return; + for ( i = 0; i < ARRAY_SIZE(header->bars); i++ ) { if ( !MAPPABLE_BAR(&header->bars[i]) ) diff --git a/xen/drivers/vpci/msix.c b/xen/drivers/vpci/msix.c index 1960dae123..af3ffa087d 100644 --- a/xen/drivers/vpci/msix.c +++ b/xen/drivers/vpci/msix.c @@ -21,6 +21,7 @@ #include #include +#include #define VMSIX_SIZE(num) offsetof(struct vpci_msix, entries[num]) @@ -395,6 +396,54 @@ static const struct hvm_mmio_ops vpci_msix_table_ops = { .write = msix_write, }; +int vpci_make_msix_hole(const struct pci_dev *pdev) +{ + struct domain *d = pdev->domain; + unsigned int i; + + if ( !pdev->vpci->msix ) + return 0; + + /* Make sure there's a hole for the MSIX table/PBA in the p2m. */ + for ( i = 0; i < ARRAY_SIZE(pdev->vpci->msix->tables); i++ ) + { + unsigned long start = PFN_DOWN(vmsix_table_addr(pdev->vpci, i)); + unsigned long end = PFN_DOWN(vmsix_table_addr(pdev->vpci, i) + + vmsix_table_size(pdev->vpci, i) - 1); + + for ( ; start <= end; start++ ) + { + p2m_type_t t; + mfn_t mfn = get_gfn_query(d, start, &t); + + switch ( t ) + { + case p2m_mmio_dm: + case p2m_invalid: + break; + case p2m_mmio_direct: + if ( mfn_x(mfn) == start ) + { + clear_identity_p2m_entry(d, start); + break; + } + /* fallthrough. */ + default: + put_gfn(d, start); + gprintk(XENLOG_WARNING, + "%04x:%02x:%02x.%u: existing mapping (mfn: %" PRI_mfn + "type: %d) at %#lx clobbers MSIX MMIO area\n", + pdev->seg, pdev->bus, PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn), mfn_x(mfn), t, start); + return -EEXIST; + } + put_gfn(d, start); + } + } + + return 0; +} + static int init_msix(struct pci_dev *pdev) { struct domain *d = pdev->domain; diff --git a/xen/include/xen/vpci.h b/xen/include/xen/vpci.h index 44104b75b6..4cf233c779 100644 --- a/xen/include/xen/vpci.h +++ b/xen/include/xen/vpci.h @@ -152,6 +152,9 @@ struct vpci_vcpu { #ifdef __XEN__ void vpci_dump_msi(void); +/* Make sure there's a hole in the p2m for the MSIX mmio areas. */ +int vpci_make_msix_hole(const struct pci_dev *pdev); + /* Arch-specific vPCI MSI helpers. */ void vpci_msi_arch_mask(struct vpci_msi *msi, const struct pci_dev *pdev, unsigned int entry, bool mask);