This patch introduces Intel VT-d IEC (Interrupt Entry Cache) invalidation notifier list. When vIOMMU receives IEC invalidate request, all the registered units will be notified with specific invalidation requests.
Signed-off-by: Peter Xu <pet...@redhat.com> --- hw/i386/intel_iommu.c | 52 ++++++++++++++++++++++++++++++++++++------ hw/i386/intel_iommu_internal.h | 24 +++++++++++++++---- include/hw/i386/intel_iommu.h | 22 ++++++++++++++++++ 3 files changed, 87 insertions(+), 11 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index ca7e069..1082ab5 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -902,12 +902,18 @@ static void vtd_root_table_setup(IntelIOMMUState *s) static void vtd_interrupt_remap_table_setup(IntelIOMMUState *s) { + VTD_IEC_Notifier *notifier; uint64_t value = 0; value = vtd_get_quad_raw(s, DMAR_IRTA_REG); s->intr_size = 1UL << ((value & VTD_IRTA_SIZE_MASK) + 1); s->intr_root = value & VTD_IRTA_ADDR_MASK; - /* TODO: invalidate interrupt entry cache */ + QLIST_FOREACH(notifier, &s->iec_notifiers, list) { + if (notifier->iec_notify) { + /* Notify global invalidation */ + notifier->iec_notify(notifier->private, true, 0, 0); + } + } VTD_DPRINTF(CSR, "int remap table addr 0x%"PRIx64 " size %"PRIu32, s->intr_root, s->intr_size); @@ -1409,6 +1415,28 @@ static bool vtd_process_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) return true; } +static bool vtd_process_inv_iec_desc(IntelIOMMUState *s, + VTDInvDesc *inv_desc) +{ + VTD_IEC_Notifier *notifier; + + VTD_DPRINTF(INV, "inv ir glob %d index %d mask %d", + inv_desc->iec.granularity, + inv_desc->iec.index, + inv_desc->iec.index_mask); + + QLIST_FOREACH(notifier, &s->iec_notifiers, list) { + if (notifier->iec_notify) { + notifier->iec_notify(notifier->private, + inv_desc->iec.granularity, + inv_desc->iec.index, + inv_desc->iec.index_mask); + } + } + + return true; +} + static bool vtd_process_inv_desc(IntelIOMMUState *s) { VTDInvDesc inv_desc; @@ -1449,12 +1477,12 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s) break; case VTD_INV_DESC_IEC: - VTD_DPRINTF(INV, "Interrupt Entry Cache Invalidation " - "not implemented yet"); - /* - * Since currently we do not cache interrupt entries, we can - * just mark this descriptor as "good" and move on. - */ + VTD_DPRINTF(INV, "Invalidation Interrupt Entry Cache " + "Descriptor hi 0x%"PRIx64 " lo 0x%"PRIx64, + inv_desc.hi, inv_desc.lo); + if (!vtd_process_inv_iec_desc(s, &inv_desc)) { + return false; + } break; default: @@ -2202,6 +2230,15 @@ static const MemoryRegionOps vtd_mem_ir_ops = { }, }; +void vtd_iec_register_notifier(IntelIOMMUState *s, vtd_iec_notify_fn fn, + void *data) +{ + VTD_IEC_Notifier *notifier = g_new0(VTD_IEC_Notifier, 1); + notifier->iec_notify = fn; + notifier->private = data; + QLIST_INSERT_HEAD(&s->iec_notifiers, notifier, list); +} + VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) { uintptr_t key = (uintptr_t)bus; @@ -2364,6 +2401,7 @@ static void vtd_realize(DeviceState *dev, Error **errp) g_free, g_free); s->vtd_as_by_busptr = g_hash_table_new_full(vtd_uint64_hash, vtd_uint64_equal, g_free, g_free); + QLIST_INIT(&s->iec_notifiers); vtd_init(s); } diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index e1a08cb..10c20fe 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -296,12 +296,28 @@ typedef enum VTDFaultReason { #define VTD_CONTEXT_CACHE_GEN_MAX 0xffffffffUL +/* Interrupt Entry Cache Invalidation Descriptor: VT-d 6.5.2.7. */ +struct VTDInvDescIEC { + uint32_t type:4; /* Should always be 0x4 */ + uint32_t granularity:1; /* If set, it's global IR invalidation */ + uint32_t resved_1:22; + uint32_t index_mask:5; /* 2^N for continuous int invalidation */ + uint32_t index:16; /* Start index to invalidate */ + uint32_t reserved_2:16; +}; +typedef struct VTDInvDescIEC VTDInvDescIEC; + /* Queued Invalidation Descriptor */ -struct VTDInvDesc { - uint64_t lo; - uint64_t hi; +union VTDInvDesc { + struct { + uint64_t lo; + uint64_t hi; + }; + union { + VTDInvDescIEC iec; + }; }; -typedef struct VTDInvDesc VTDInvDesc; +typedef union VTDInvDesc VTDInvDesc; /* Masks for struct VTDInvDesc */ #define VTD_INV_DESC_TYPE 0xf diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 5910e6f..d7613af 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -203,6 +203,24 @@ struct VTD_MSIMessage { /* When IR is enabled, all MSI/MSI-X data bits should be zero */ #define VTD_IR_MSI_DATA (0) +/** + * iec_notifier - IEC (Interrupt Entry Cache) notifier, triggered + * when IR hhinvalidation happens. + * @private: private data + * @global: whether this is a global IEC invalidation + * @index: IRTE index to invalidate (start from) + * @mask: invalidation mask + */ +typedef void (*vtd_iec_notify_fn)(void *private, bool global, + uint32_t index, uint32_t mask); + +struct VTD_IEC_Notifier { + vtd_iec_notify_fn iec_notify; + void *private; + QLIST_ENTRY(VTD_IEC_Notifier) list; +}; +typedef struct VTD_IEC_Notifier VTD_IEC_Notifier; + /* The iommu (DMAR) device state struct */ struct IntelIOMMUState { SysBusDevice busdev; @@ -243,6 +261,7 @@ struct IntelIOMMUState { bool intr_enabled; /* Whether guest enabled IR */ dma_addr_t intr_root; /* Interrupt remapping table pointer */ uint32_t intr_size; /* Number of IR table entries */ + QLIST_HEAD(, VTD_IEC_Notifier) iec_notifiers; /* IEC notify list */ }; /* Find the VTD Address space associated with the given bus pointer, @@ -252,5 +271,8 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn); /* Get default IOMMU object */ IntelIOMMUState *vtd_iommu_get(void); int vtd_int_remap(void *iommu, MSIMessage *src, MSIMessage *dst); +/* Register IEC invalidate notifier */ +void vtd_iec_register_notifier(IntelIOMMUState *s, vtd_iec_notify_fn fn, + void *data); #endif -- 2.4.3