diff --git a/xen/arch/x86/msi.c b/xen/arch/x86/msi.c index 76d4034c4f..c239a00fb1 100644 --- a/xen/arch/x86/msi.c +++ b/xen/arch/x86/msi.c @@ -1249,6 +1249,28 @@ void pci_cleanup_msi(struct pci_dev *pdev) msi_free_irqs(pdev); } +int pci_reset_msix_state(struct pci_dev *pdev) +{ + unsigned int pos = pci_find_cap_offset(pdev->seg, pdev->bus, pdev->sbdf.dev, + pdev->sbdf.fn, PCI_CAP_ID_MSIX); + + ASSERT(pos); + /* + * Xen expects the device state to be the after reset one, and hence + * host_maskall = guest_maskall = false and all entries should have the + * mask bit set. Test that the maskall bit is not set, having it set could + * signal that the device hasn't been reset properly. + */ + if ( pci_conf_read16(pdev->sbdf, msix_control_reg(pos)) & + PCI_MSIX_FLAGS_MASKALL ) + return -EBUSY; + + pdev->msix->host_maskall = false; + pdev->msix->guest_maskall = false; + + return 0; +} + int pci_msi_conf_write_intercept(struct pci_dev *pdev, unsigned int reg, unsigned int size, uint32_t *data) { diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c index 90ccb8370b..bdcc482d81 100644 --- a/xen/drivers/passthrough/pci.c +++ b/xen/drivers/passthrough/pci.c @@ -1505,7 +1505,12 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) } if ( pdev->msix ) + { + rc = pci_reset_msix_state(pdev); + if ( rc ) + goto done; msixtbl_init(d); + } pdev->fault.count = 0; diff --git a/xen/include/asm-x86/msi.h b/xen/include/asm-x86/msi.h index d0b0045d0d..6e35713ec7 100644 --- a/xen/include/asm-x86/msi.h +++ b/xen/include/asm-x86/msi.h @@ -92,6 +92,7 @@ extern int __setup_msi_irq(struct irq_desc *, struct msi_desc *, extern void teardown_msi_irq(int irq); extern int msi_free_vector(struct msi_desc *entry); extern int pci_restore_msi_state(struct pci_dev *pdev); +extern int pci_reset_msix_state(struct pci_dev *pdev); struct msi_desc { struct msi_attrib {