From patchwork Tue Jan 28 20:32:40 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christoffer Dall X-Patchwork-Id: 23816 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-ie0-f199.google.com (mail-ie0-f199.google.com [209.85.223.199]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 61F2A202B2 for ; Tue, 28 Jan 2014 20:32:59 +0000 (UTC) Received: by mail-ie0-f199.google.com with SMTP id x13sf2801062ief.10 for ; Tue, 28 Jan 2014 12:32:58 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :list-post:list-help:list-archive:list-unsubscribe; bh=Tmdg5FAUzCk3PFQZhCw9wndVq+dpJCcztohvGGVjRkw=; b=kCxVCEhJQnRias2kESbx2lzdmTUvvxOq0WytxzcflzktseHiDqjPHbnDSruE2ZLoV/ H4Zsb3tTt+x8NWGMBkbTu/B5Gn1A+L3oP/TG0I1xHmJ1ktdcPVGr0vtAHFvqxTRF9wxF m6G00Sq4uvm1D+AyIui3RZUH+654l4r/D44+iKpjhzDMgJWKWAkK025iRpFtES2K5+KJ +w8WhDusw49qZoUJo51C5Tzyhw6FkffTxyAHhK8rpENXlD4+KK4WcaAjuvf/XktWXF+K mknrJtqmWE2dyOPVXWDnBYcYwBCsmRmJNwJxk6ViuIQe9k+qZyjgJbhYy5W7rnpLF3/9 qLUA== X-Gm-Message-State: ALoCoQkp3CbFi2pA3wLoO3oS2SyLga6IYBMmIBf2EyX+bvHi5nyJnxdjhL50e/cTK/ae1+bzovzm X-Received: by 10.182.81.7 with SMTP id v7mr1322141obx.28.1390941178669; Tue, 28 Jan 2014 12:32:58 -0800 (PST) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.49.231 with SMTP id q94ls2279213qga.45.gmail; Tue, 28 Jan 2014 12:32:58 -0800 (PST) X-Received: by 10.58.181.71 with SMTP id du7mr2419082vec.25.1390941178597; Tue, 28 Jan 2014 12:32:58 -0800 (PST) Received: from mail-vc0-f176.google.com (mail-vc0-f176.google.com [209.85.220.176]) by mx.google.com with ESMTPS id fv9si6772034vcb.20.2014.01.28.12.32.58 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 28 Jan 2014 12:32:58 -0800 (PST) Received-SPF: neutral (google.com: 209.85.220.176 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) client-ip=209.85.220.176; Received: by mail-vc0-f176.google.com with SMTP id la4so580265vcb.7 for ; Tue, 28 Jan 2014 12:32:58 -0800 (PST) X-Received: by 10.221.39.138 with SMTP id tm10mr2724463vcb.7.1390941178481; Tue, 28 Jan 2014 12:32:58 -0800 (PST) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patches@linaro.org Received: by 10.220.174.196 with SMTP id u4csp59534vcz; Tue, 28 Jan 2014 12:32:57 -0800 (PST) X-Received: by 10.68.162.131 with SMTP id ya3mr3698872pbb.102.1390941177364; Tue, 28 Jan 2014 12:32:57 -0800 (PST) Received: from mail-pd0-f174.google.com (mail-pd0-f174.google.com [209.85.192.174]) by mx.google.com with ESMTPS id eb3si16701411pbc.176.2014.01.28.12.32.57 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 28 Jan 2014 12:32:57 -0800 (PST) Received-SPF: neutral (google.com: 209.85.192.174 is neither permitted nor denied by best guess record for domain of christoffer.dall@linaro.org) client-ip=209.85.192.174; Received: by mail-pd0-f174.google.com with SMTP id z10so801174pdj.19 for ; Tue, 28 Jan 2014 12:32:57 -0800 (PST) X-Received: by 10.68.231.169 with SMTP id th9mr3605758pbc.113.1390941176949; Tue, 28 Jan 2014 12:32:56 -0800 (PST) Received: from localhost.localdomain (c-67-169-181-221.hsd1.ca.comcast.net. [67.169.181.221]) by mx.google.com with ESMTPSA id sq7sm45438283pbc.19.2014.01.28.12.32.55 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 28 Jan 2014 12:32:56 -0800 (PST) From: Christoffer Dall To: qemu-devel@nongnu.org Cc: kvmarm@lists.cs.columbia.edu, patches@linaro.org, Christoffer Dall Subject: [PATCH v5 3/8] arm_gic: Fix GIC pending behavior Date: Tue, 28 Jan 2014 12:32:40 -0800 Message-Id: <1390941165-2079-4-git-send-email-christoffer.dall@linaro.org> X-Mailer: git-send-email 1.8.5.2 In-Reply-To: <1390941165-2079-1-git-send-email-christoffer.dall@linaro.org> References: <1390941165-2079-1-git-send-email-christoffer.dall@linaro.org> X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: christoffer.dall@linaro.org X-Original-Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.220.176 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Precedence: list Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org List-ID: X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , The existing implementation of the pending behavior in gic_set_irq, gic_acknowledge_irq, gic_complete_irq, and the distributor pending set/clear registers does not follow the semantics of the GICv2.0 specs, but may implement the 11MPCore support. Therefore, maintain the existing semantics for 11MPCore and v7M NVIC and change the behavior to be in accordance with the GICv2.0 specs for "generic implementations" (s->revision == 1 || s->revision == 2). Generic implementations distinguish between setting a level-triggered interrupt pending through writes to the GICD_ISPENDR and when hardware raises the interrupt line. Writing to the GICD_ICPENDR will not cause the interrupt to become non-pending if the line is still active, and conversely, if the line is deactivated but the interrupt is marked as pending through a write to GICD_ISPENDR, the interrupt remains pending. Handle this situation in the GIC_TEST_PENDING (which now becomes a static inline named gic_test_pending) and let the 'pending' field correspond only to the latched state of the D-flip flop in the GICv2.0 specs Figure 4-10. The following changes are added: gic_test_pending: Make this a static inline and split out the 11MPCore from the generic behavior. For the generic behavior, consider interrupts pending if: ((s->irq_state[irq].pending & (cm) != 0) || (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_LEVEL(irq, cm)) gic_set_irq: Split out the 11MPCore from the generic behavior. For the generic behavior, always GIC_SET_LEVEL(irq, 1) on positive level, but only GIC_SET_PENDING for edge-triggered interrupts and vice versa on a negative level. gic_complete_irq: Only resample the line for line-triggered interrupts on an 11MPCore. Generic implementations will sample the line directly in gic_test_pending(). Signed-off-by: Christoffer Dall --- Changes [v4 -> v5]: - Factor out GIC_NR_SGIS and gic_dist_writeb bugfixes - Change meaning of pending field for level-triggered interrupts for GIC v1/v2, to only capture manually written state to pending registers. Add or-clause to gic_test_pending to sample the line state, as per Peter's suggestions: https://lists.cs.columbia.edu/pipermail/kvmarm/2014-January/008798.html Changes [v3 -> v4]: - Maintain 11MPCore semantics - Combine all pending interrupts fixing patches into this patch. See the detailed description above. Changes [v1 -> v2]: - Fix bisection issue, by not using gic_clear_pending yet. hw/intc/arm_gic.c | 74 ++++++++++++++++++++++++++++++++++++-------------- hw/intc/gic_internal.h | 16 ++++++++++- 2 files changed, 69 insertions(+), 21 deletions(-) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 1c4a114..5e2cf14 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -66,7 +66,7 @@ void gic_update(GICState *s) best_prio = 0x100; best_irq = 1023; for (irq = 0; irq < s->num_irq; irq++) { - if (GIC_TEST_ENABLED(irq, cm) && GIC_TEST_PENDING(irq, cm)) { + if (GIC_TEST_ENABLED(irq, cm) && gic_test_pending(s, irq, cm)) { if (GIC_GET_PRIORITY(irq, cpu) < best_prio) { best_prio = GIC_GET_PRIORITY(irq, cpu); best_irq = irq; @@ -89,14 +89,46 @@ void gic_set_pending_private(GICState *s, int cpu, int irq) { int cm = 1 << cpu; - if (GIC_TEST_PENDING(irq, cm)) + if (gic_test_pending(s, irq, cm)) { return; + } DPRINTF("Set %d pending cpu %d\n", irq, cpu); GIC_SET_PENDING(irq, cm); gic_update(s); } +static void gic_set_irq_11mpcore(GICState *s, int irq, int level, + int cm, int target) +{ + if (level) { + GIC_SET_LEVEL(irq, cm); + if (GIC_TEST_EDGE_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) { + DPRINTF("Set %d pending mask %x\n", irq, target); + GIC_SET_PENDING(irq, target); + } + } else { + GIC_CLEAR_LEVEL(irq, cm); + } +} + +static void gic_set_irq_generic(GICState *s, int irq, int level, + int cm, int target) +{ + if (level) { + GIC_SET_LEVEL(irq, cm); + DPRINTF("Set %d pending mask %x\n", irq, target); + if (GIC_TEST_EDGE_TRIGGER(irq)) { + GIC_SET_PENDING(irq, target); + } + } else { + if (GIC_TEST_EDGE_TRIGGER(irq)) { + GIC_CLEAR_PENDING(irq, target); + } + GIC_CLEAR_LEVEL(irq, cm); + } +} + /* Process a change in an external IRQ input. */ static void gic_set_irq(void *opaque, int irq, int level) { @@ -126,15 +158,12 @@ static void gic_set_irq(void *opaque, int irq, int level) return; } - if (level) { - GIC_SET_LEVEL(irq, cm); - if (GIC_TEST_EDGE_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) { - DPRINTF("Set %d pending mask %x\n", irq, target); - GIC_SET_PENDING(irq, target); - } + if (s->revision == REV_11MPCORE) { + gic_set_irq_11mpcore(s, irq, level, cm, target); } else { - GIC_CLEAR_LEVEL(irq, cm); + gic_set_irq_generic(s, irq, level, cm, target); } + gic_update(s); } @@ -160,9 +189,10 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu) return 1023; } s->last_active[new_irq][cpu] = s->running_irq[cpu]; - /* Clear pending flags for both level and edge triggered interrupts. - Level triggered IRQs will be reasserted once they become inactive. */ - GIC_CLEAR_PENDING(new_irq, GIC_TEST_MODEL(new_irq) ? ALL_CPU_MASK : cm); + + cm = GIC_TEST_MODEL(new_irq) ? ALL_CPU_MASK : cm; + GIC_CLEAR_PENDING(new_irq, cm); + gic_set_running_irq(s, cpu, new_irq); DPRINTF("ACK %d\n", new_irq); return new_irq; @@ -195,14 +225,18 @@ void gic_complete_irq(GICState *s, int cpu, int irq) } if (s->running_irq[cpu] == 1023) return; /* No active IRQ. */ - /* Mark level triggered interrupts as pending if they are still - raised. */ - if (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm) - && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) { - DPRINTF("Set %d pending mask %x\n", irq, cm); - GIC_SET_PENDING(irq, cm); - update = 1; + + if (s->revision == REV_11MPCORE) { + /* Mark level triggered interrupts as pending if they are still + raised. */ + if (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm) + && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) { + DPRINTF("Set %d pending mask %x\n", irq, cm); + GIC_SET_PENDING(irq, cm); + update = 1; + } } + if (irq != s->running_irq[cpu]) { /* Complete an IRQ that is not currently running. */ int tmp = s->running_irq[cpu]; @@ -273,7 +307,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset) res = 0; mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK; for (i = 0; i < 8; i++) { - if (GIC_TEST_PENDING(irq + i, mask)) { + if (gic_test_pending(s, irq + i, mask)) { res |= (1 << i); } } diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index 8c02d58..92a6f7a 100644 --- a/hw/intc/gic_internal.h +++ b/hw/intc/gic_internal.h @@ -34,7 +34,6 @@ #define GIC_TEST_ENABLED(irq, cm) ((s->irq_state[irq].enabled & (cm)) != 0) #define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm) #define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm) -#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0) #define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm) #define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm) #define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0) @@ -63,4 +62,19 @@ void gic_update(GICState *s); void gic_init_irqs_and_distributor(GICState *s, int num_irq); void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val); +static inline bool gic_test_pending(GICState *s, int irq, int cm) +{ + if (s->revision == REV_NVIC || s->revision == REV_11MPCORE) { + return s->irq_state[irq].pending & cm; + } else { + /* Edge-triggered interrupts are marked pending on a rising edge, but + * level-triggered interrupts are either considered pending when the + * level is active or if software has explicitly written to + * GICD_ISPENDR to set the state pending. + */ + return (s->irq_state[irq].pending & cm) || + (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_LEVEL(irq, cm)); + } +} + #endif /* !QEMU_ARM_GIC_INTERNAL_H */