diff options
Diffstat (limited to 'target-i386/kvm.c')
-rw-r--r-- | target-i386/kvm.c | 109 |
1 files changed, 107 insertions, 2 deletions
diff --git a/target-i386/kvm.c b/target-i386/kvm.c index df28dd254a..9697e16fdd 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -35,6 +35,8 @@ #include "hw/i386/apic.h" #include "hw/i386/apic_internal.h" #include "hw/i386/apic-msidef.h" +#include "hw/i386/intel_iommu.h" +#include "hw/i386/x86-iommu.h" #include "exec/ioport.h" #include "standard-headers/asm-x86/hyperv.h" @@ -42,6 +44,7 @@ #include "hw/pci/msi.h" #include "migration/migration.h" #include "exec/memattrs.h" +#include "trace.h" //#define DEBUG_KVM @@ -3233,8 +3236,7 @@ void kvm_arch_init_irq_routing(KVMState *s) /* If the ioapic is in QEMU and the lapics are in KVM, reserve MSI routes for signaling interrupts to the local apics. */ for (i = 0; i < IOAPIC_NUM_PINS; i++) { - struct MSIMessage msg = { 0x0, 0x0 }; - if (kvm_irqchip_add_msi_route(s, msg, NULL) < 0) { + if (kvm_irqchip_add_msi_route(s, 0, NULL) < 0) { error_report("Could not enable split IRQ mode."); exit(1); } @@ -3404,6 +3406,109 @@ int kvm_device_msix_deassign(KVMState *s, uint32_t dev_id) int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, uint64_t address, uint32_t data, PCIDevice *dev) { + X86IOMMUState *iommu = x86_iommu_get_default(); + + if (iommu) { + int ret; + MSIMessage src, dst; + X86IOMMUClass *class = X86_IOMMU_GET_CLASS(iommu); + + src.address = route->u.msi.address_hi; + src.address <<= VTD_MSI_ADDR_HI_SHIFT; + src.address |= route->u.msi.address_lo; + src.data = route->u.msi.data; + + ret = class->int_remap(iommu, &src, &dst, dev ? \ + pci_requester_id(dev) : \ + X86_IOMMU_SID_INVALID); + if (ret) { + trace_kvm_x86_fixup_msi_error(route->gsi); + return 1; + } + + route->u.msi.address_hi = dst.address >> VTD_MSI_ADDR_HI_SHIFT; + route->u.msi.address_lo = dst.address & VTD_MSI_ADDR_LO_MASK; + route->u.msi.data = dst.data; + } + + return 0; +} + +typedef struct MSIRouteEntry MSIRouteEntry; + +struct MSIRouteEntry { + PCIDevice *dev; /* Device pointer */ + int vector; /* MSI/MSIX vector index */ + int virq; /* Virtual IRQ index */ + QLIST_ENTRY(MSIRouteEntry) list; +}; + +/* List of used GSI routes */ +static QLIST_HEAD(, MSIRouteEntry) msi_route_list = \ + QLIST_HEAD_INITIALIZER(msi_route_list); + +static void kvm_update_msi_routes_all(void *private, bool global, + uint32_t index, uint32_t mask) +{ + int cnt = 0; + MSIRouteEntry *entry; + MSIMessage msg; + /* TODO: explicit route update */ + QLIST_FOREACH(entry, &msi_route_list, list) { + cnt++; + msg = pci_get_msi_message(entry->dev, entry->vector); + kvm_irqchip_update_msi_route(kvm_state, entry->virq, + msg, entry->dev); + } + kvm_irqchip_commit_routes(kvm_state); + trace_kvm_x86_update_msi_routes(cnt); +} + +int kvm_arch_add_msi_route_post(struct kvm_irq_routing_entry *route, + int vector, PCIDevice *dev) +{ + static bool notify_list_inited = false; + MSIRouteEntry *entry; + + if (!dev) { + /* These are (possibly) IOAPIC routes only used for split + * kernel irqchip mode, while what we are housekeeping are + * PCI devices only. */ + return 0; + } + + entry = g_new0(MSIRouteEntry, 1); + entry->dev = dev; + entry->vector = vector; + entry->virq = route->gsi; + QLIST_INSERT_HEAD(&msi_route_list, entry, list); + + trace_kvm_x86_add_msi_route(route->gsi); + + if (!notify_list_inited) { + /* For the first time we do add route, add ourselves into + * IOMMU's IEC notify list if needed. */ + X86IOMMUState *iommu = x86_iommu_get_default(); + if (iommu) { + x86_iommu_iec_register_notifier(iommu, + kvm_update_msi_routes_all, + NULL); + } + notify_list_inited = true; + } + return 0; +} + +int kvm_arch_release_virq_post(int virq) +{ + MSIRouteEntry *entry, *next; + QLIST_FOREACH_SAFE(entry, &msi_route_list, list, next) { + if (entry->virq == virq) { + trace_kvm_x86_remove_msi_route(virq); + QLIST_REMOVE(entry, list); + break; + } + } return 0; } |