So far we deliver MSI messages by writing them into the target MMIO area. This reflects what happens on hardware, but imposes some limitations on the emulation when introducing KVM in-kernel irqchip models. For those we will need to track the message origin. Moreover, different architecture or accelerators may want to overload the delivery handler. Therefore, this commit introduces a delivery hook that is called by the MSI/MSI-X layer when devices send normal messages, but also on spurious deliveries that ended up on the APIC MMIO handler. Our default delivery handler for APIC-based PCs then dispatches between real MSIs and other DMA requests that happened to take the MSI patch. Signed-off-by: Jan Kiszka <jan.kiszka@xxxxxxxxxxx> --- hw/apic.c | 19 ++++++++++++------- hw/apic.h | 1 + hw/msi.c | 10 +++++++++- hw/msi.h | 2 ++ hw/msix.c | 2 +- hw/pc.c | 11 +++++++++++ 6 files changed, 36 insertions(+), 9 deletions(-) diff --git a/hw/apic.c b/hw/apic.c index e43219f..c1d557d 100644 --- a/hw/apic.c +++ b/hw/apic.c @@ -19,6 +19,7 @@ #include "hw.h" #include "apic.h" #include "ioapic.h" +#include "msi.h" #include "qemu-timer.h" #include "host-utils.h" #include "sysbus.h" @@ -803,13 +804,15 @@ static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr) return val; } -static void apic_send_msi(target_phys_addr_t addr, uint32_t data) +void apic_deliver_msi(MSIMessage *msg) { - uint8_t dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT; - uint8_t vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT; - uint8_t dest_mode = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1; - uint8_t trigger_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1; - uint8_t delivery = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7; + uint8_t dest = + (msg->address & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT; + uint8_t vector = + (msg->data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT; + uint8_t dest_mode = (msg->address >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1; + uint8_t trigger_mode = (msg->data >> MSI_DATA_TRIGGER_SHIFT) & 0x1; + uint8_t delivery = (msg->data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7; /* XXX: Ignore redirection hint. */ apic_deliver_irq(dest, dest_mode, delivery, vector, trigger_mode); } @@ -825,7 +828,9 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) * APIC is connected directly to the CPU. * Mapping them on the global bus happens to work because * MSI registers are reserved in APIC MMIO and vice versa. */ - apic_send_msi(addr, val); + MSIMessage msg = { .address = addr, .data = val }; + + msi_deliver(&msg); return; } diff --git a/hw/apic.h b/hw/apic.h index c398c83..fa848fd 100644 --- a/hw/apic.h +++ b/hw/apic.h @@ -18,6 +18,7 @@ void cpu_set_apic_tpr(DeviceState *s, uint8_t val); uint8_t cpu_get_apic_tpr(DeviceState *s); void apic_init_reset(DeviceState *s); void apic_sipi(DeviceState *s); +void apic_deliver_msi(MSIMessage *msg); /* pc.c */ int cpu_is_bsp(CPUState *env); diff --git a/hw/msi.c b/hw/msi.c index 3c7ebc3..9055155 100644 --- a/hw/msi.c +++ b/hw/msi.c @@ -40,6 +40,14 @@ /* Flag for interrupt controller to declare MSI/MSI-X support */ bool msi_supported; +static void msi_unsupported(MSIMessage *msg) +{ + /* If we get here, the board failed to register a delivery handler. */ + abort(); +} + +void (*msi_deliver)(MSIMessage *msg) = msi_unsupported; + /* If we get rid of cap allocator, we won't need this. */ static inline uint8_t msi_cap_sizeof(uint16_t flags) { @@ -381,7 +389,7 @@ void msi_notify(PCIDevice *dev, unsigned int vector) "notify vector 0x%x" " address: 0x%"PRIx64" data: 0x%"PRIx32"\n", vector, msg.address, msg.data); - stl_le_phys(msg.address, msg.data); + msi_deliver(&msg); } /* Normally called by pci_default_write_config(). */ diff --git a/hw/msi.h b/hw/msi.h index 22e3932..f3152f3 100644 --- a/hw/msi.h +++ b/hw/msi.h @@ -46,4 +46,6 @@ static inline bool msi_present(const PCIDevice *dev) return dev->cap_present & QEMU_PCI_CAP_MSI; } +extern void (*msi_deliver)(MSIMessage *msg); + #endif /* QEMU_MSI_H */ diff --git a/hw/msix.c b/hw/msix.c index 50fa504..08cc526 100644 --- a/hw/msix.c +++ b/hw/msix.c @@ -478,7 +478,7 @@ void msix_notify(PCIDevice *dev, unsigned vector) msix_message_from_vector(dev, vector, &msg); - stl_le_phys(msg.address, msg.data); + msi_deliver(&msg); } void msix_reset(PCIDevice *dev) diff --git a/hw/pc.c b/hw/pc.c index 768a20c..7d29a4a 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -24,6 +24,7 @@ #include "hw.h" #include "pc.h" #include "apic.h" +#include "msi.h" #include "fdc.h" #include "ide.h" #include "pci.h" @@ -102,6 +103,15 @@ void isa_irq_handler(void *opaque, int n, int level) qemu_set_irq(isa->ioapic[n], level); }; +static void pc_msi_deliver(MSIMessage *msg) +{ + if ((msg->address & 0xfff00000) == MSI_ADDR_BASE) { + apic_deliver_msi(msg); + } else { + stl_phys(msg->address, msg->data); + } +} + static void ioport80_write(void *opaque, uint32_t addr, uint32_t data) { } @@ -889,6 +899,7 @@ static DeviceState *apic_init(void *env, uint8_t apic_id) on the global memory bus. */ /* XXX: what if the base changes? */ sysbus_mmio_map(d, 0, MSI_ADDR_BASE); + msi_deliver = pc_msi_deliver; apic_mapped = 1; } -- 1.7.3.4 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html