From patchwork Tue Oct 20 17:22:05 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shlomo Pongratz X-Patchwork-Id: 533098 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 01097140307 for ; Wed, 21 Oct 2015 05:20:59 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=VdnfeKUp; dkim-atps=neutral Received: from localhost ([::1]:47216 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Zoacw-0001oI-Nw for incoming@patchwork.ozlabs.org; Tue, 20 Oct 2015 13:23:10 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:51041) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Zoabr-0000bF-Dg for qemu-devel@nongnu.org; Tue, 20 Oct 2015 13:22:05 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Zoabk-0003Qi-L7 for qemu-devel@nongnu.org; Tue, 20 Oct 2015 13:22:03 -0400 Received: from mail-wi0-x230.google.com ([2a00:1450:400c:c05::230]:33110) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Zoabk-0003QM-BK for qemu-devel@nongnu.org; Tue, 20 Oct 2015 13:21:56 -0400 Received: by wijp11 with SMTP id p11so58008762wij.0 for ; Tue, 20 Oct 2015 10:21:55 -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=7ylgsCUwnEXlmdfKl91tpZlGd5ck9JCwcCu9/IQT5FA=; b=VdnfeKUpGD0j/aXfnVEkmdKBjGkuyQtSUi83f5UoDe6Fxnlyz4L8TawmclFCjYghc6 RMgfSlQMit/lVGqQhu44V8mwU/TORwrChdwGO/Ayx8MYcQTqaecZVwTmzgHSmgEZtpyn RkKL531Cdkw26Clcz/1sHQuOU36gtvq5KCZ+bHGfwgbS9mUuF0eNafGNzbFerdL3Z8N0 TMgIlBCDBrp6IiZBc870N9rDMmZE1WUtBUBPI4tA+oU5t9wIe5l6uWUeqOB/yumWvYfs 4pMqdGirGoVJhdIGpSBZzrNdmOp4UB3DaTKv3TOsIJp1J6UKm+tvCXGg1og3EheKuL0x lP2Q== X-Received: by 10.180.12.206 with SMTP id a14mr6145698wic.25.1445361715725; Tue, 20 Oct 2015 10:21:55 -0700 (PDT) Received: from localhost.localdomain (bzq-79-181-26-227.red.bezeqint.net. [79.181.26.227]) by smtp.gmail.com with ESMTPSA id jd9sm3771828wic.0.2015.10.20.10.21.54 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 20 Oct 2015 10:21:55 -0700 (PDT) From: Shlomo Pongratz To: qemu-devel@nongnu.org Date: Tue, 20 Oct 2015 20:22:05 +0300 Message-Id: <1445361732-16257-3-git-send-email-shlomopongratz@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1445361732-16257-1-git-send-email-shlomopongratz@gmail.com> References: <1445361732-16257-1-git-send-email-shlomopongratz@gmail.com> X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2a00:1450:400c:c05::230 Cc: peter.maydell@linaro.org, eric.auger@linaro.org, Shlomo Pongratz , p.fedin@samsung.com, shannon.zhao@linaro.org, ashoks@broadcom.com, imammedo@redhat.com Subject: [Qemu-devel] [PATCH RFC V5 2/9] hw/intc: arm_gicv3_interrupts X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Shlomo Pongratz This patch includes the part of the GIC's code that handles the interrupts. Signed-off-by: Shlomo Pongratz --- hw/intc/Makefile.objs | 1 + hw/intc/arm_gicv3_interrupts.c | 295 +++++++++++++++++++++++++++++++++++++++++ hw/intc/arm_gicv3_interrupts.h | 11 ++ hw/intc/gicv3_internal.h | 19 +++ 4 files changed, 326 insertions(+) create mode 100644 hw/intc/arm_gicv3_interrupts.c create mode 100644 hw/intc/arm_gicv3_interrupts.h diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 004b0c2..e8cdd27 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -13,6 +13,7 @@ common-obj-$(CONFIG_ARM_GIC) += arm_gic_common.o common-obj-$(CONFIG_ARM_GIC) += arm_gic.o common-obj-$(CONFIG_ARM_GIC) += arm_gicv2m.o common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_common.o +common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_interrupts.o common-obj-$(CONFIG_OPENPIC) += openpic.o obj-$(CONFIG_APIC) += apic.o apic_common.o diff --git a/hw/intc/arm_gicv3_interrupts.c b/hw/intc/arm_gicv3_interrupts.c new file mode 100644 index 0000000..da2293e --- /dev/null +++ b/hw/intc/arm_gicv3_interrupts.c @@ -0,0 +1,295 @@ +#include "gicv3_internal.h" +#include "qom/cpu.h" +#include "arm_gicv3_interrupts.h" + +/* TODO: Many places that call this routine could be optimized. */ +/* Update interrupt status after enabled or pending bits have been changed. */ +void gicv3_update(GICv3State *s) +{ + int best_irq; + int best_prio; + int irq; + int irq_level, fiq_level; + int cpu; + + for (cpu = 0; cpu < NUM_CPU(s); cpu++) { + s->current_pending[cpu] = 1023; + if (!(s->ctlr & (GICD_CTLR_EN_GRP0 | GICD_CTLR_EN_GRP1_ALL)) + || !test_bit(cpu, s->cpu_enabled) + || !(s->cpu_ctlr[cpu] & (GICC_CTLR_EN_GRP0 | GICC_CTLR_EN_GRP1))) { + qemu_irq_lower(s->parent_irq[cpu]); + qemu_irq_lower(s->parent_fiq[cpu]); + /* In original GICv2 there is a return here. But if status is + * disabled then all parent IRQs need to be lowered + * And assume CPU i is disabled then with the original GICv2 + * implementation CPU - 1 will be considered but not CPU + 1 + */ + continue; + } + best_prio = 0x100; + best_irq = 1023; + for (irq = 0; irq < s->num_irq; irq++) { + if (GIC_TEST_ENABLED(irq, cpu) && gic_test_pending(s, irq, cpu) && + (irq < GICV3_INTERNAL || GIC_TEST_TARGET(irq, cpu))) { + if (GIC_GET_PRIORITY(irq, cpu) < best_prio) { + best_prio = GIC_GET_PRIORITY(irq, cpu); + best_irq = irq; + } + } + } + + irq_level = fiq_level = 0; + + if (best_prio < s->priority_mask[cpu]) { + s->current_pending[cpu] = best_irq; + if (best_prio < s->running_priority[cpu]) { + int group = GIC_TEST_GROUP(best_irq, cpu); + if (extract32(s->ctlr, group, 1) && + extract32(s->cpu_ctlr[cpu], group, 1)) { + if (group == 0 && s->cpu_ctlr[cpu] & GICC_CTLR_FIQ_EN) { + DPRINTF("Raised pending FIQ %d (cpu %d)\n", + best_irq, cpu); + fiq_level = 1; + } else { + DPRINTF("Raised pending IRQ %d (cpu %d)\n", + best_irq, cpu); + irq_level = 1; + } + } + } + } + + qemu_set_irq(s->parent_irq[cpu], irq_level); + qemu_set_irq(s->parent_fiq[cpu], fiq_level); + } +} + +static void gicv3_set_irq_generic(GICv3State *s, int irq, int level, + int cm, unsigned long *target) +{ + if (level) { + /* if cm is -1 then the macro will set them all */ + GIC_SET_LEVEL(irq, cm); + DPRINTF("Set %d pending cpu %d\n", irq, cm); + if (GIC_TEST_EDGE_TRIGGER(irq)) { + if (cm < 0) + GIC_SET_PENDING_MASK(irq, target); + else + GIC_SET_PENDING(irq, cm); + } + } else { + GIC_CLEAR_LEVEL(irq, cm); + } +} + +/* Process a change in an external IRQ input. */ +void gicv3_set_irq(void *opaque, int irq, int level) +{ + /* Meaning of the 'irq' parameter: + * [0..N-1] : external interrupts + * [N..N+31] : PPI (internal) interrupts for CPU 0 + * [N+32..N+63] : PPI (internal interrupts for CPU 1 + * ... + */ + GICv3State *s = (GICv3State *)opaque; + int cm; + unsigned long *target; + + if (irq < (s->num_irq - GICV3_INTERNAL)) { + /* The first external input line is internal interrupt 32. */ + cm = ALL_CPU_MASK; + irq += GICV3_INTERNAL; + target = GIC_TARGET(irq); + } else { + int cpu; + irq -= (s->num_irq - GICV3_INTERNAL); + cpu = irq / GICV3_INTERNAL; + irq %= GICV3_INTERNAL; + cm = cpu; + target = NULL; + } + + assert(irq >= GICV3_NR_SGIS); + + if (level == GIC_TEST_LEVEL(irq, cm)) { + return; + } + + gicv3_set_irq_generic(s, irq, level, cm, target); + + gicv3_update(s); +} + +static uint16_t gic_get_current_pending_irq(GICv3State *s, int cpu, + MemTxAttrs attrs) +{ + uint16_t pending_irq = s->current_pending[cpu]; + + if (pending_irq < GICV3_MAXIRQ && gic_has_groups(s)) { + /* GICv3 section 4.1.4 + * In systems that support a single security state, + * there is no security distinction between Secure and Non -Secure + * interrupt groups. That is, "Secure" and "Non-Secure" interrupts are + * always accessible. + */ + if (s->security_levels > 1) { + int group = GIC_TEST_GROUP(pending_irq, cpu); + bool secure = attrs.secure; + if (group == 0) { + if (!secure) { + fprintf(stderr, "%s::%d\n", __func__, __LINE__); + /* Group0 interrupts hidden from Non-secure access */ + return 1023; + } + } else { + /* Note GICv3 5.6.18: AckCtl was removed in GICv3 + * Also look at GICv3 5.3.20 for group 1 secure and non secure + */ + if (secure) { + if (s->ctlr & GICD_CTLR_EN_GRP1NS) { + /* Secure access to non secure group 1 interrupts */ + fprintf(stderr, "%s::%d\n", __func__, __LINE__); + return 1022; + } + } else { + if (s->ctlr & GICD_CTLR_EN_GRP1S) { + /* Non secure access to secure group 1 interrupts */ + fprintf(stderr, "%s::%d\n", __func__, __LINE__); + return 1022; + } + } + } + } + } + return pending_irq; +} + +static void gic_set_running_irq(GICv3State *s, int cpu, int irq) +{ + s->running_irq[cpu] = irq; + if (irq == 1023) { + s->running_priority[cpu] = 0x100; + } else { + s->running_priority[cpu] = GIC_GET_PRIORITY(irq, cpu); + } + gicv3_update(s); +} + +uint32_t gicv3_acknowledge_irq(GICv3State *s, int cpu, MemTxAttrs attrs) +{ + int ret, irq, src; + + /* gic_get_current_pending_irq() will return 1022 or 1023 appropriately + * for the case where this GIC supports grouping and the pending interrupt + * is in the wrong group. + */ + irq = gic_get_current_pending_irq(s, cpu, attrs); + + if (irq >= GICV3_MAXIRQ) { + //DPRINTF("ACK, no pending interrupt or it is hidden: %d\n", irq); + return irq; + } + + if (GIC_GET_PRIORITY(irq, cpu) >= s->running_priority[cpu]) { + //DPRINTF("ACK, pending interrupt (%d) has insufficient priority\n", irq); + return 1023; + } + + s->irq_state[irq].last_active[cpu] = s->running_irq[cpu]; + + if (irq < GICV3_NR_SGIS) { + /* Lookup the source CPU for the SGI and clear this in the + * sgi_pending map. Return the src and clear the overall pending + * state on this CPU if the SGI is not pending from any CPUs. + */ + assert(s->sgi[irq].state[cpu].pending != 0); + src = find_first_bit(s->sgi[irq].state[cpu].pending, s->num_cpu); + if (src < s->num_cpu) + clear_bit(src, s->sgi[irq].state[cpu].pending); + if (bitmap_empty(s->sgi[irq].state[cpu].pending, s->num_cpu)) { + GIC_CLEAR_PENDING(irq, cpu); + } + /* GICv3 kernel driver dosen't mask src bits like GICv2 driver + * so don't add src i.e. ret = irq | ((src & 0x7) << 10); + * Section 4.2.10 in GICv3 specification + */ + ret = irq; + } else { + //DPRINTF("ACK irq(%d) cpu(%d) \n", irq, cpu); + /* Clear pending state for both level and edge triggered + * interrupts. (level triggered interrupts with an active line + * remain pending, see gic_test_pending) + */ + GIC_CLEAR_PENDING(irq, cpu); + ret = irq; + } + + gic_set_running_irq(s, cpu, irq); + DPRINTF("out ACK irq-ret(%d) cpu(%d) \n", ret, cpu); + return ret; +} + +void gicv3_set_priority(GICv3State *s, int cpu, int irq, uint8_t val, + MemTxAttrs attrs) +{ + DPRINTF("%s cpu(%d) secure(%d)\n", __func__, cpu, attrs.secure); + if (s->security_levels == 1 && !attrs.secure) { + if (!GIC_TEST_GROUP(irq, cpu)) { + return; /* Ignore Non-secure access of Group0 IRQ */ + } + val = 0x80 | (val >> 1); /* Non-secure view */ + } + + if (irq < GICV3_INTERNAL) { + s->priority1[irq].p[cpu] = val; + } else { + s->priority2[irq - GICV3_INTERNAL] = val; + } +} + +void gicv3_complete_irq(GICv3State *s, int cpu, int irq, MemTxAttrs attrs) +{ + DPRINTF("EOI irq(%d) cpu (%d)\n", irq, cpu); + if (irq >= s->num_irq) { + /* This handles two cases: + * 1. If software writes the ID of a spurious interrupt [ie 1023] + * to the GICC_EOIR, the GIC ignores that write. + * 2. If software writes the number of a non-existent interrupt + * this must be a subcase of "value written does not match the last + * valid interrupt value read from the Interrupt Acknowledge + * register" and so this is UNPREDICTABLE. We choose to ignore it. + */ + return; + } + + if (s->running_irq[cpu] == 1023) { + DPRINTF("No active IRQ ignored cpu(%d) irq(%d)\n", irq, cpu); + return; /* No active IRQ. */ + } + + if (s->security_levels == 1 && !attrs.secure && !GIC_TEST_GROUP(irq, cpu)) { + DPRINTF("Non-secure EOI for Group0 interrupt %d ignored\n", irq); + fprintf(stderr, "Non-secure EOI for Group0 interrupt %d ignored cpu(%d)\n", irq, cpu); + return; + } + + /* Secure EOI with GICC_CTLR.AckCtl == 0 when the IRQ is a Group 1 + * interrupt is UNPREDICTABLE. We choose to handle it as if AckCtl == 1, + * i.e. go ahead and complete the irq anyway. + */ + + if (irq != s->running_irq[cpu]) { + /* Complete an IRQ that is not currently running. */ + int tmp = s->running_irq[cpu]; + while (s->irq_state[tmp].last_active[cpu] != 1023) { + if (s->irq_state[tmp].last_active[cpu] == irq) { + s->irq_state[tmp].last_active[cpu] = s->irq_state[irq].last_active[cpu]; + break; + } + tmp = s->irq_state[tmp].last_active[cpu]; + } + } else { + /* Complete the current running IRQ. */ + gic_set_running_irq(s, cpu, s->irq_state[s->running_irq[cpu]].last_active[cpu]); + } +} diff --git a/hw/intc/arm_gicv3_interrupts.h b/hw/intc/arm_gicv3_interrupts.h new file mode 100644 index 0000000..2f04c88 --- /dev/null +++ b/hw/intc/arm_gicv3_interrupts.h @@ -0,0 +1,11 @@ +#ifndef QEMU_ARM_GICV3_INTERRUPTS_H +#define QEMU_ARM_GICV3_INTERRUPTS_H + +uint32_t gicv3_acknowledge_irq(GICv3State *s, int cpu, MemTxAttrs attrs); +void gicv3_complete_irq(GICv3State *s, int cpu, int irq, MemTxAttrs attrs); +void gicv3_update(GICv3State *s); +void gicv3_set_priority(GICv3State *s, int cpu, int irq, uint8_t val, + MemTxAttrs attrs); +void gicv3_set_irq(void *opaque, int irq, int level); + +#endif /* !QEMU_ARM_GIC_INTERRUPTS_H */ diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index e0b4a08..362455c 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -207,4 +207,23 @@ static inline bool gic_test_pending(GICv3State *s, int irq, int cm) #define GICC_CTLR_EOIMODE (1U << 9) #define GICC_CTLR_EOIMODE_NS (1U << 10) +#define NUM_CPU(s) ((s)->num_cpu) + +/* Return true if this GIC config has interrupt groups, which is + * true if we're a GICv3. Keep just + */ +static inline bool gic_has_groups(GICv3State *s) +{ + return 1; +} + +#undef DEBUG_GICV3 + +#ifdef DEBUG_GICV3 +#define DPRINTF(fmt, ...) \ +do { fprintf(stderr, "arm_gicv3::%s: " fmt , __func__, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while(0) +#endif + #endif /* !QEMU_ARM_GIC_INTERNAL_H */