From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:51069) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bjOBr-0002l7-6I for qemu-devel@nongnu.org; Mon, 12 Sep 2016 06:10:20 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bjOBl-0004la-1L for qemu-devel@nongnu.org; Mon, 12 Sep 2016 06:10:14 -0400 Received: from mail-wm0-f43.google.com ([74.125.82.43]:36794) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bjOBk-0004Zg-Nq for qemu-devel@nongnu.org; Mon, 12 Sep 2016 06:10:08 -0400 Received: by mail-wm0-f43.google.com with SMTP id b187so127365101wme.1 for ; Mon, 12 Sep 2016 03:09:47 -0700 (PDT) From: David Kiarie Date: Mon, 12 Sep 2016 13:08:07 +0300 Message-Id: <1473674889-2727-5-git-send-email-davidkiarie4@gmail.com> In-Reply-To: <1473674889-2727-1-git-send-email-davidkiarie4@gmail.com> References: <1473674889-2727-1-git-send-email-davidkiarie4@gmail.com> Subject: [Qemu-devel] [v4 4/6] hw/iommu: AMD IOMMU interrupt remapping List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: jan.kiszka@web.de, mst@redhat.com, rkrcmar@redhat.com, peterx@redhat.com, ehabkost@redhat.com, pbonzini@redhat.com, alex.williamson@redhat.com, David Kiarie Introduce AMD IOMMU interrupt remapping and hook it onto the existing interrupt remapping infrastructure Signed-off-by: David Kiarie --- hw/i386/amd_iommu.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++++++- hw/i386/amd_iommu.h | 4 +- hw/intc/ioapic.c | 1 - 3 files changed, 242 insertions(+), 3 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 226fea5..76d3816 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -20,6 +20,7 @@ * Cache implementation inspired by hw/i386/intel_iommu.c */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "hw/i386/amd_iommu.h" #include "trace.h" @@ -255,6 +256,31 @@ typedef struct QEMU_PACKED { uint32_t reserved_5:16; } CMDCompletePPR; +typedef union IRTE { + struct { +#ifdef HOST_WORDS_BIGENDIAN + uint32_t destination:8; + uint32_t rsvd_1:1; + uint32_t dm:1; + uint32_t rq_eoi:1; + uint32_t int_type:3; + uint32_t no_fault:1; + uint32_t valid:1; +#else + uint32_t valid:1; + uint32_t no_fault:1; + uint32_t int_type:3; + uint32_t rq_eoi:1; + uint32_t dm:1; + uint32_t rsvd_1:1; + uint32_t destination:8; +#endif + uint32_t vector:8; + uint32_t rsvd_2:8; + } bits; + uint32_t data; +} IRTE; + /* configure MMIO registers at startup/reset */ static void amdvi_set_quad(AMDVIState *s, hwaddr addr, uint64_t val, uint64_t romask, uint64_t w1cmask) @@ -641,6 +667,11 @@ static void amdvi_inval_inttable(AMDVIState *s, CMDInvalIntrTable *inval) amdvi_log_illegalcom_error(s, inval->type, s->cmdbuf + s->cmdbuf_head); return; } + + if (s->ir_cache) { + x86_iommu_iec_notify_all(X86_IOMMU_DEVICE(s), true, 0, 0); + } + trace_amdvi_intr_inval(); } @@ -1203,6 +1234,197 @@ static IOMMUTLBEntry amdvi_translate(MemoryRegion *iommu, hwaddr addr, return ret; } +static inline int amdvi_ir_handle_non_vectored(MSIMessage *src, + MSIMessage *dst, uint8_t bitpos, + uint64_t dte) +{ + if ((dte & (1UL << bitpos))) { + /* passing interrupt enabled */ + memcpy(dst, src, sizeof(*dst)); + } else { + /* should be target aborted */ + return -AMDVI_TARGET_ABORT; + } + return 0; +} + +static int amdvi_remap_ir_intctl(uint64_t dte, IRTE irte, + MSIMessage *src, MSIMessage *dst) +{ + int ret = 0; + + switch ((dte >> AMDVI_DTE_INTCTL_RSHIFT) & 3UL) { + case AMDVI_INTCTL_PASS: + /* pass */ + memcpy(dst, src, sizeof(*dst)); + break; + case AMDVI_INTCTL_REMAP: + /* remap */ + if (irte.bits.valid) { + /* LOCAL APIC address */ + dst->address = AMDVI_LOCAL_APIC_ADDR; + /* destination mode */ + dst->address |= ((uint64_t)irte.bits.dm) << + AMDVI_MSI_ADDR_DM_RSHIFT; + /* RH */ + dst->address |= ((uint64_t)irte.bits.rq_eoi) << + AMDVI_MSI_ADDR_RH_RSHIFT; + /* Destination ID */ + dst->address |= ((uint64_t)irte.bits.destination) << + AMDVI_MSI_ADDR_DEST_RSHIFT; + /* construct data - vector */ + dst->data |= irte.bits.vector; + /* Interrupt type */ + dst->data |= ((uint64_t)irte.bits.int_type) << + AMDVI_MSI_DATA_DM_RSHIFT; + } else { + ret = -AMDVI_TARGET_ABORT; + } + break; + case AMDVI_INTCTL_ABORT: + case AMDVI_INTCTL_RSVD: + ret = -AMDVI_TARGET_ABORT; + } + return ret; +} + +static int amdvi_irte_get(AMDVIState *s, MSIMessage *src, IRTE *irte, + uint64_t *dte, uint16_t devid) +{ + uint64_t irte_root, offset = devid * AMDVI_DEVTAB_ENTRY_SIZE, + ir_table_size; + + irte_root = dte[2] & AMDVI_IRTEROOT_MASK; + offset = (src->data & AMDVI_IRTE_INDEX_MASK) << 2; + ir_table_size = 1UL << (dte[2] & AMDVI_IR_TABLE_SIZE_MASK); + /* enforce IR table size */ + if (offset > (ir_table_size * AMDVI_DEFAULT_IRTE_SIZE)) { + trace_amdvi_invalid_irte_entry(offset, ir_table_size); + return -AMDVI_TARGET_ABORT; + } + /* read IRTE */ + if (dma_memory_read(&address_space_memory, irte_root + offset, + irte, sizeof(*irte))) { + trace_amdvi_irte_get_fail(irte_root, offset); + return -AMDVI_DEV_TAB_HW; + } + return 0; +} + +static int amdvi_int_remap(X86IOMMUState *iommu, MSIMessage *src, + MSIMessage *dst, uint16_t sid) +{ + trace_amdvi_ir_request(src->data, src->address, sid); + + AMDVIState *s = AMD_IOMMU_DEVICE(iommu); + int ret = 0; + uint64_t dte[4]; + uint32_t bitpos; + IRTE irte; + + amdvi_get_dte(s, sid, dte); + + /* interrupt remapping disabled */ + if (!(dte[2] & AMDVI_IR_VALID)) { + memcpy(dst, src, sizeof(*src)); + return ret; + } + + ret = amdvi_irte_get(s, src, &irte, dte, sid); + if (ret < 0) { + goto no_remap; + } + switch (src->data & AMDVI_IR_TYPE_MASK) { + case AMDVI_MT_FIXED: + case AMDVI_MT_ARBIT: + ret = amdvi_remap_ir_intctl(dte[2], irte, src, dst); + if (ret < 0) { + goto no_remap; + } else { + s->ir_cache = true; + trace_amdvi_ir_remap(dst->data, dst->address, sid); + return ret; + } + /* not handling SMI currently */ + case AMDVI_MT_SMI: + error_report("SMI interrupts not currently handled"); + goto no_remap; + case AMDVI_MT_NMI: + bitpos = AMDVI_DTE_NMIPASS_LSHIFT; + break; + case AMDVI_MT_INIT: + bitpos = AMDVI_DTE_INTPASS_LSHIFT; + break; + case AMDVI_MT_EXTINT: + bitpos = AMDVI_DTE_EINTPASS_LSHIFT; + break; + case AMDVI_MT_LINT1: + bitpos = AMDVI_DTE_LINT1PASS_LSHIFT; + break; + case AMDVI_MT_LINT0: + bitpos = AMDVI_DTE_LINT0PASS_LSHIFT; + default: + goto no_remap; + } + + ret = amdvi_ir_handle_non_vectored(src, dst, bitpos, dte[2]); + if (ret < 0){ + goto no_remap; + } + s->ir_cache = true; + trace_amdvi_ir_remap(dst->data, dst->address, sid); + return ret; +no_remap: + memcpy(dst, src, sizeof(*src)); + trace_amdvi_ir_target_abort(dst->data, dst->address, sid); + return ret; +} + +static MemTxResult amdvi_ir_read(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) +{ + return MEMTX_OK; +} + +static MemTxResult amdvi_ir_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size, MemTxAttrs attrs) +{ + AMDVIAddressSpace *as = opaque; + MSIMessage from = { addr + AMDVI_INT_ADDR_FIRST, val }, to = { 0, 0}; + int ret = 0; + + ret = amdvi_int_remap(X86_IOMMU_DEVICE(as->iommu_state), &from, &to, + attrs.requester_id); + + if (ret < 0) { + trace_amdvi_ir_target_abort(from.data, from.address, + attrs.requester_id); + return MEMTX_ERROR; + } + + if(dma_memory_write(&address_space_memory, to.address, &to.data, size)) { + trace_amdvi_ir_write_fail(to.address, to.data); + return MEMTX_ERROR; + } + + return MEMTX_OK; +} + +static const MemoryRegionOps amdvi_ir_ops = { + .read_with_attrs = amdvi_ir_read, + .write_with_attrs = amdvi_ir_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) { AMDVIState *s = opaque; @@ -1226,6 +1448,12 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) memory_region_init_iommu(&iommu_as[devfn]->iommu, OBJECT(s), &s->iommu_ops, "amd-iommu", UINT64_MAX); + memory_region_init_io(&iommu_as[devfn]->iommu_ir, OBJECT(s), + &amdvi_ir_ops, iommu_as[devfn], "amd-iommu-ir", + AMDVI_INT_ADDR_SIZE); + memory_region_add_subregion(&iommu_as[devfn]->iommu, + AMDVI_INT_ADDR_FIRST, + &iommu_as[devfn]->iommu_ir); address_space_init(&iommu_as[devfn]->as, &iommu_as[devfn]->iommu, "amd-iommu"); } @@ -1274,6 +1502,7 @@ static void amdvi_init(AMDVIState *s) s->enabled = false; s->ats_enabled = false; s->cmdbuf_enabled = false; + s->ir_cache = false; /* reset MMIO */ memset(s->mmior, 0, AMDVI_MMIO_SIZE); @@ -1313,11 +1542,15 @@ static void amdvi_realize(DeviceState *dev, Error **err) AMDVIState *s = AMD_IOMMU_DEVICE(dev); X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(dev); PCIBus *bus = PC_MACHINE(qdev_get_machine())->bus; + PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); s->iotlb = g_hash_table_new_full(amdvi_uint64_hash, amdvi_uint64_equal, g_free, g_free); - /* This device should take care of IOMMU PCI properties */ + /* AMD IOMMU has Interrupt Remapping on by default */ + x86_iommu->intr_supported = true; x86_iommu->type = TYPE_AMD; + + /* This device should take care of IOMMU PCI properties */ qdev_set_parent_bus(DEVICE(&s->pci), &bus->qbus); object_property_set_bool(OBJECT(&s->pci), true, "realized", err); s->capab_offset = pci_add_capability(&s->pci.dev, AMDVI_CAPAB_ID_SEC, 0, @@ -1329,9 +1562,13 @@ static void amdvi_realize(DeviceState *dev, Error **err) memory_region_init_io(&s->mmio, OBJECT(s), &mmio_mem_ops, s, "amdvi-mmio", AMDVI_MMIO_SIZE); + x86_iommu->ioapic_bdf = PCI_BUILD_BDF(AMDVI_BUS_NUM, + AMDVI_SB_IOAPIC_ID); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio); sysbus_mmio_map(SYS_BUS_DEVICE(s), 0, AMDVI_BASE_ADDR); pci_setup_iommu(bus, amdvi_host_dma_iommu, s); + pcms->ioapic_as = amdvi_host_dma_iommu(bus, s, AMDVI_SB_IOAPIC_ID); s->devid = object_property_get_int(OBJECT(&s->pci), "addr", err); msi_init(&s->pci.dev, 0, 1, true, false, err); amdvi_init(s); @@ -1358,6 +1595,7 @@ static void amdvi_class_init(ObjectClass *klass, void* data) dc->vmsd = &vmstate_amdvi; dc->hotpluggable = false; dc_class->realize = amdvi_realize; + dc_class->int_remap = amdvi_int_remap; } static const TypeInfo amdvi = { diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 5c4a13b..28b9603 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -235,7 +235,7 @@ #define AMDVI_BUS_NUM 0x0 /* AMD-Vi specific IOAPIC Device function */ -#define AMDVI_DEVFN_IOAPIC 0xa0 +#define AMDVI_SB_IOAPIC_ID 0xa0 #define AMDVI_LOCAL_APIC_ADDR 0xfee00000 @@ -338,6 +338,8 @@ typedef struct AMDVIState { uint32_t evtlog_len; /* event log length */ uint32_t evtlog_head; /* current IOMMU write position */ uint32_t evtlog_tail; /* current Software read position */ + /* whether we have remapped any interrupts and hence IR cache */ + bool ir_cache; /* unused for now */ hwaddr excl_base; /* base DVA - IOMMU exclusion range */ diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index f2d4c15..3fefe4a 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -412,7 +412,6 @@ static void ioapic_machine_done_notify(Notifier *notifier, void *data) } kvm_irqchip_commit_routes(kvm_state); } - } #endif } -- 2.1.4