diff options
-rw-r--r-- | hw/msix.c | 64 |
1 files changed, 46 insertions, 18 deletions
@@ -20,6 +20,7 @@ #define PCI_MSIX_FLAGS 2 /* Table at lower 11 bits */ #define PCI_MSIX_FLAGS_QSIZE 0x7FF #define PCI_MSIX_FLAGS_ENABLE (1 << 15) +#define PCI_MSIX_FLAGS_MASKALL (1 << 14) #define PCI_MSIX_FLAGS_BIRMASK (7 << 0) /* MSI-X capability structure */ @@ -30,6 +31,7 @@ /* MSI enable bit and maskall bit are in byte 1 in FLAGS register */ #define MSIX_CONTROL_OFFSET (PCI_MSIX_FLAGS + 1) #define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8) +#define MSIX_MASKALL_MASK (PCI_MSIX_FLAGS_MASKALL >> 8) /* MSI-X table format */ #define MSIX_MSG_ADDR 0 @@ -101,22 +103,11 @@ static int msix_add_config(struct PCIDevice *pdev, unsigned short nentries, bar_nr); pdev->msix_cap = config_offset; /* Make flags bit writeable. */ - pdev->wmask[config_offset + MSIX_CONTROL_OFFSET] |= MSIX_ENABLE_MASK; + pdev->wmask[config_offset + MSIX_CONTROL_OFFSET] |= MSIX_ENABLE_MASK | + MSIX_MASKALL_MASK; return 0; } -/* Handle MSI-X capability config write. */ -void msix_write_config(PCIDevice *dev, uint32_t addr, - uint32_t val, int len) -{ - unsigned enable_pos = dev->msix_cap + MSIX_CONTROL_OFFSET; - if (addr + len <= enable_pos || addr > enable_pos) - return; - - if (msix_enabled(dev)) - qemu_set_irq(dev->irq[0], 0); -} - static uint32_t msix_mmio_readl(void *opaque, target_phys_addr_t addr) { PCIDevice *dev = opaque; @@ -157,10 +148,50 @@ static void msix_clr_pending(PCIDevice *dev, int vector) *msix_pending_byte(dev, vector) &= ~msix_pending_mask(vector); } +static int msix_function_masked(PCIDevice *dev) +{ + return dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & MSIX_MASKALL_MASK; +} + static int msix_is_masked(PCIDevice *dev, int vector) { unsigned offset = vector * MSIX_ENTRY_SIZE + MSIX_VECTOR_CTRL; - return dev->msix_table_page[offset] & MSIX_VECTOR_MASK; + return msix_function_masked(dev) || + dev->msix_table_page[offset] & MSIX_VECTOR_MASK; +} + +static void msix_handle_mask_update(PCIDevice *dev, int vector) +{ + if (!msix_is_masked(dev, vector) && msix_is_pending(dev, vector)) { + msix_clr_pending(dev, vector); + msix_notify(dev, vector); + } +} + +/* Handle MSI-X capability config write. */ +void msix_write_config(PCIDevice *dev, uint32_t addr, + uint32_t val, int len) +{ + unsigned enable_pos = dev->msix_cap + MSIX_CONTROL_OFFSET; + int vector; + + if (addr + len <= enable_pos || addr > enable_pos) { + return; + } + + if (!msix_enabled(dev)) { + return; + } + + qemu_set_irq(dev->irq[0], 0); + + if (msix_function_masked(dev)) { + return; + } + + for (vector = 0; vector < dev->msix_entries_nr; ++vector) { + msix_handle_mask_update(dev, vector); + } } static void msix_mmio_writel(void *opaque, target_phys_addr_t addr, @@ -170,10 +201,7 @@ static void msix_mmio_writel(void *opaque, target_phys_addr_t addr, unsigned int offset = addr & (MSIX_PAGE_SIZE - 1) & ~0x3; int vector = offset / MSIX_ENTRY_SIZE; pci_set_long(dev->msix_table_page + offset, val); - if (!msix_is_masked(dev, vector) && msix_is_pending(dev, vector)) { - msix_clr_pending(dev, vector); - msix_notify(dev, vector); - } + msix_handle_mask_update(dev, vector); } static void msix_mmio_write_unallowed(void *opaque, target_phys_addr_t addr, |