From 29c6e6df492d81b1843e5dd999171bb84c6effea Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Fri, 9 Jan 2015 08:50:53 -0700 Subject: [PATCH 1/2] vfio-pci: Fix BAR size overflow We use an unsigned int when working with the PCI BAR size, which can obviously overflow if the BAR is 4GB or larger. This needs to change to a fixed length uint64_t. A similar issue is possible, though even more unlikely, when mapping the region above an MSI-X table. The start of the MSI-X vector table must be below 4GB, but the end, and therefore the start of the next mapping region, could still land at 4GB. Suggested-by: Nishank Trivedi Signed-off-by: Alex Williamson Reviewed-by: Don Slutz Tested-by: Alexey Kardashevskiy --- hw/vfio/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index b4e73d1f35..b6703c7d37 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2301,7 +2301,7 @@ static void vfio_unmap_bar(VFIOPCIDevice *vdev, int nr) static void vfio_map_bar(VFIOPCIDevice *vdev, int nr) { VFIOBAR *bar = &vdev->bars[nr]; - unsigned size = bar->region.size; + uint64_t size = bar->region.size; char name[64]; uint32_t pci_bar; uint8_t type; @@ -2351,7 +2351,7 @@ static void vfio_map_bar(VFIOPCIDevice *vdev, int nr) } if (vdev->msix && vdev->msix->table_bar == nr) { - unsigned start; + uint64_t start; start = HOST_PAGE_ALIGN(vdev->msix->table_offset + (vdev->msix->entries * PCI_MSIX_ENTRY_SIZE)); From b3e27c3aee8f5a96debfe0346e9c0e3a641a8516 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Fri, 9 Jan 2015 08:50:53 -0700 Subject: [PATCH 2/2] vfio-pci: Fix interrupt disabling When disabling MSI/X interrupts the disable functions will leave the device in INTx mode (when available). This matches how hardware operates, INTx is enabled unless MSI/X is enabled (DisINTx is handled separately). Therefore when we really want to disable all interrupts, such as when removing the device, and we start with the device in MSI/X mode, we need to pass through INTx on our way to being completely quiesced. In well behaved situations, the guest driver will have shutdown the device and it will start vfio_exitfn() in INTx mode, producing the desired result. If hot-unplug causes the guest to crash, we may get the device in MSI/X state, which will leave QEMU with a bogus handler installed. Fix this by re-ordering our disable routine so that it should always finish in VFIO_INT_NONE state, which is what all callers expect. Signed-off-by: Alex Williamson --- hw/vfio/pci.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index b6703c7d37..014a92ce5f 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2129,16 +2129,19 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, */ static void vfio_disable_interrupts(VFIOPCIDevice *vdev) { - switch (vdev->interrupt) { - case VFIO_INT_INTx: - vfio_disable_intx(vdev); - break; - case VFIO_INT_MSI: - vfio_disable_msi(vdev); - break; - case VFIO_INT_MSIX: + /* + * More complicated than it looks. Disabling MSI/X transitions the + * device to INTx mode (if supported). Therefore we need to first + * disable MSI/X and then cleanup by disabling INTx. + */ + if (vdev->interrupt == VFIO_INT_MSIX) { vfio_disable_msix(vdev); - break; + } else if (vdev->interrupt == VFIO_INT_MSI) { + vfio_disable_msi(vdev); + } + + if (vdev->interrupt == VFIO_INT_INTx) { + vfio_disable_intx(vdev); } }