From patchwork Mon Aug 15 16:32:44 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Kiarie X-Patchwork-Id: 659341 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3sCh8X6Wv7z9t39 for ; Tue, 16 Aug 2016 02:41:32 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=YN7V2KJR; dkim-atps=neutral Received: from localhost ([::1]:38010 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bZKx8-0000Bw-OO for incoming@patchwork.ozlabs.org; Mon, 15 Aug 2016 12:41:30 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:34206) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bZKpP-0002Jq-UP for qemu-devel@nongnu.org; Mon, 15 Aug 2016 12:33:33 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bZKpJ-0007vZ-DK for qemu-devel@nongnu.org; Mon, 15 Aug 2016 12:33:30 -0400 Received: from mail-wm0-x244.google.com ([2a00:1450:400c:c09::244]:34324) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bZKpJ-0007vV-1x for qemu-devel@nongnu.org; Mon, 15 Aug 2016 12:33:25 -0400 Received: by mail-wm0-x244.google.com with SMTP id q128so11978254wma.1 for ; Mon, 15 Aug 2016 09:33:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=De4hDh+E5HDFaldF37BRhZRbdOByfe91AVOXZIXTHAo=; b=YN7V2KJROXHuKhB/vMYQeazkuzakzeaaTIzSdtxag/a6SMPmX2kZVq/qRGweXexIHH E3fAOdSm+iGYUkYpMHhSSehL+j0glfRiO6G5dLd9DgmG9eU0DcqKI1DtaPwDe5yvmLfM HeTqIVA68AlkToq5Y2DaQtlZYSi+JBy5T6JxZJA4t33paj/b2qgAL+bgi2KXnaIz4aDH toM9Rqu4lck07qnFW4eRDxL7Mm4g/GjX5NOA1nTO/c0gEaQOKWBgapffW+jKfET7Ugc6 rPG0QphtYl/xVdn9odMYxl8ufSE9qFk31xfLSO+Vl2um7OQ7r0rV4CH0qeuTKN30qCc5 N5eA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=De4hDh+E5HDFaldF37BRhZRbdOByfe91AVOXZIXTHAo=; b=lLu7RL8DgG4wyCnkYQLc8j8CYPAAS2Y2qke0XJzx06aSV46grNm9OIp1LgOpVuz2tO y+/f+lZBBCY/R+2XJrE8hPh8xFg9ACN22weeu+tkZzaBALZz88YylnYnsZhm6qWeE2Li FrvnAllP7WdBnWIgrgWmkgJ16CWnLzmaFKC2yd+6IT7Qt8LpFZ952GZrD0XPT0YIdFXq NsRXGt119jE7GtYbUsuy4yWEJwBqVigzppSx5Cv2ok5d1KDjKUPCKHUlOJ22oHKQsYd9 +cls4lRYHfhJQlov8TqJNLjGqav0AP+fg+4DNazbbStoUtuKOyWCTPruTn7B8Dsa77cD L/vQ== X-Gm-Message-State: AEkooutLjpaQ2NJ9/6oc/0fjycFdBWhKl4bCYmvt8iQjNyJTc8dqRQguqkNJOS/SS7/EYw== X-Received: by 10.28.6.202 with SMTP id 193mr16323810wmg.53.1471278804255; Mon, 15 Aug 2016 09:33:24 -0700 (PDT) Received: from debian.flybox.orange ([154.122.75.147]) by smtp.googlemail.com with ESMTPSA id bc10sm22277885wjc.32.2016.08.15.09.33.16 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 15 Aug 2016 09:33:23 -0700 (PDT) From: David Kiarie To: qemu-devel@nongnu.org Date: Mon, 15 Aug 2016 19:32:44 +0300 Message-Id: <1471278766-25277-5-git-send-email-davidkiarie4@gmail.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1471278766-25277-1-git-send-email-davidkiarie4@gmail.com> References: <1471278766-25277-1-git-send-email-davidkiarie4@gmail.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2a00:1450:400c:c09::244 Subject: [Qemu-devel] [V2 4/6] hw/iommu: AMD IOMMU interrupt remapping X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: rkrcmar@redhat.com, mst@redhat.com, peterx@redhat.com, valentine.sinitsyn@gmail.com, jan.kiszka@web.de, pbonzini@redhat.com, David Kiarie Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" Introduce AMD IOMMU interrupt remapping and hook it onto the existing interrupt remapping infrastructure Signed-off-by: David Kiarie --- hw/i386/amd_iommu.c | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++- hw/i386/amd_iommu.h | 4 +- 2 files changed, 243 insertions(+), 5 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 19da365..08d6dae 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -18,11 +18,10 @@ * with this program; if not, see . * * Cache implementation inspired by hw/i386/intel_iommu.c + * */ #include "qemu/osdep.h" -#include -#include "hw/pci/msi.h" -#include "hw/i386/pc.h" +#include "qemu/error-report.h" #include "hw/i386/amd_iommu.h" #include "hw/pci/pci_bus.h" #include "trace.h" @@ -270,6 +269,31 @@ typedef struct QEMU_PACKED { #endif /* __BIG_ENDIAN_BITFIELD */ } 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) @@ -660,6 +684,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(); } @@ -1221,6 +1250,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; @@ -1244,6 +1464,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"); } @@ -1292,6 +1518,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); @@ -1331,11 +1558,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, @@ -1347,9 +1578,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); @@ -1376,6 +1611,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 6f62e3a..b8d7bd9 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 @@ -353,6 +353,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 */