[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH RFC V5 2/9] hw/intc: arm_gicv3_interrupts
From: |
Shlomo Pongratz |
Subject: |
[Qemu-devel] [PATCH RFC V5 2/9] hw/intc: arm_gicv3_interrupts |
Date: |
Tue, 20 Oct 2015 20:22:05 +0300 |
From: Shlomo Pongratz <address@hidden>
This patch includes the part of the GIC's code that handles
the interrupts.
Signed-off-by: Shlomo Pongratz <address@hidden>
---
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 */
--
1.9.1
- [Qemu-devel] [PATCH RFC V5 0/9] Implement GIC-500 from GICv3 family for arm64, Shlomo Pongratz, 2015/10/20
- [Qemu-devel] [PATCH RFC V5 2/9] hw/intc: arm_gicv3_interrupts,
Shlomo Pongratz <=
- [Qemu-devel] [PATCH RFC V5 7/9] hw/intc: arm_gicv3, Shlomo Pongratz, 2015/10/20
- [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, Shlomo Pongratz, 2015/10/20
- Re: [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, Pavel Fedin, 2015/10/21
- Re: [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, Shlomo Pongratz, 2015/10/21
- Re: [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, Pavel Fedin, 2015/10/22
- Re: [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, Shlomo Pongratz, 2015/10/21
- Re: [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, Pavel Fedin, 2015/10/21
- Re: [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, Shlomo Pongratz, 2015/10/21
- Re: [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, Pavel Fedin, 2015/10/21
- Re: [Qemu-devel] [PATCH RFC V5 6/9] hw/intc: arm_gicv3_spi_its, Shlomo Pongratz, 2015/10/21