xref: /qemu/hw/intc/arm_gic.c (revision a1d7b8d896f98139dff177508895e55caf483f95)
1e69954b9Spbrook /*
29ee6e8bbSpbrook  * ARM Generic/Distributed Interrupt Controller
3e69954b9Spbrook  *
49ee6e8bbSpbrook  * Copyright (c) 2006-2007 CodeSourcery.
5e69954b9Spbrook  * Written by Paul Brook
6e69954b9Spbrook  *
78e31bf38SMatthew Fernandez  * This code is licensed under the GPL.
8e69954b9Spbrook  */
9e69954b9Spbrook 
109ee6e8bbSpbrook /* This file contains implementation code for the RealView EB interrupt
110d256bdcSPeter Maydell  * controller, MPCore distributed interrupt controller and ARMv7-M
120d256bdcSPeter Maydell  * Nested Vectored Interrupt Controller.
130d256bdcSPeter Maydell  * It is compiled in two ways:
140d256bdcSPeter Maydell  *  (1) as a standalone file to produce a sysbus device which is a GIC
150d256bdcSPeter Maydell  *  that can be used on the realview board and as one of the builtin
160d256bdcSPeter Maydell  *  private peripherals for the ARM MP CPUs (11MPCore, A9, etc)
170d256bdcSPeter Maydell  *  (2) by being directly #included into armv7m_nvic.c to produce the
180d256bdcSPeter Maydell  *  armv7m_nvic device.
190d256bdcSPeter Maydell  */
20e69954b9Spbrook 
218ef94f0bSPeter Maydell #include "qemu/osdep.h"
2283c9f4caSPaolo Bonzini #include "hw/sysbus.h"
2347b43a1fSPaolo Bonzini #include "gic_internal.h"
24da34e65cSMarkus Armbruster #include "qapi/error.h"
25dfc08079SAndreas Färber #include "qom/cpu.h"
2603dd024fSPaolo Bonzini #include "qemu/log.h"
272531088fSHollis Blanchard #include "trace.h"
285d721b78SAlexander Graf #include "sysemu/kvm.h"
29386e2955SPeter Maydell 
3068bf93ceSAlex Bennée /* #define DEBUG_GIC */
31e69954b9Spbrook 
32e69954b9Spbrook #ifdef DEBUG_GIC
3368bf93ceSAlex Bennée #define DEBUG_GIC_GATE 1
34e69954b9Spbrook #else
3568bf93ceSAlex Bennée #define DEBUG_GIC_GATE 0
36e69954b9Spbrook #endif
37e69954b9Spbrook 
3868bf93ceSAlex Bennée #define DPRINTF(fmt, ...) do {                                          \
3968bf93ceSAlex Bennée         if (DEBUG_GIC_GATE) {                                           \
4068bf93ceSAlex Bennée             fprintf(stderr, "%s: " fmt, __func__, ## __VA_ARGS__);      \
4168bf93ceSAlex Bennée         }                                                               \
4268bf93ceSAlex Bennée     } while (0)
4368bf93ceSAlex Bennée 
443355c360SAlistair Francis static const uint8_t gic_id_11mpcore[] = {
453355c360SAlistair Francis     0x00, 0x00, 0x00, 0x00, 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1
463355c360SAlistair Francis };
473355c360SAlistair Francis 
483355c360SAlistair Francis static const uint8_t gic_id_gicv1[] = {
493355c360SAlistair Francis     0x04, 0x00, 0x00, 0x00, 0x90, 0xb3, 0x1b, 0x00, 0x0d, 0xf0, 0x05, 0xb1
503355c360SAlistair Francis };
513355c360SAlistair Francis 
523355c360SAlistair Francis static const uint8_t gic_id_gicv2[] = {
533355c360SAlistair Francis     0x04, 0x00, 0x00, 0x00, 0x90, 0xb4, 0x2b, 0x00, 0x0d, 0xf0, 0x05, 0xb1
542a29ddeeSPeter Maydell };
552a29ddeeSPeter Maydell 
56fae15286SPeter Maydell static inline int gic_get_current_cpu(GICState *s)
57926c4affSPeter Maydell {
58926c4affSPeter Maydell     if (s->num_cpu > 1) {
594917cf44SAndreas Färber         return current_cpu->cpu_index;
60926c4affSPeter Maydell     }
61926c4affSPeter Maydell     return 0;
62926c4affSPeter Maydell }
63926c4affSPeter Maydell 
644a37e0e4SLuc Michel static inline int gic_get_current_vcpu(GICState *s)
654a37e0e4SLuc Michel {
664a37e0e4SLuc Michel     return gic_get_current_cpu(s) + GIC_NCPU;
674a37e0e4SLuc Michel }
684a37e0e4SLuc Michel 
69c27a5ba9SFabian Aggeler /* Return true if this GIC config has interrupt groups, which is
70c27a5ba9SFabian Aggeler  * true if we're a GICv2, or a GICv1 with the security extensions.
71c27a5ba9SFabian Aggeler  */
72c27a5ba9SFabian Aggeler static inline bool gic_has_groups(GICState *s)
73c27a5ba9SFabian Aggeler {
74c27a5ba9SFabian Aggeler     return s->revision == 2 || s->security_extn;
75c27a5ba9SFabian Aggeler }
76c27a5ba9SFabian Aggeler 
773dd0471bSLuc Michel static inline bool gic_cpu_ns_access(GICState *s, int cpu, MemTxAttrs attrs)
783dd0471bSLuc Michel {
793dd0471bSLuc Michel     return !gic_is_vcpu(cpu) && s->security_extn && !attrs.secure;
803dd0471bSLuc Michel }
813dd0471bSLuc Michel 
82e69954b9Spbrook /* TODO: Many places that call this routine could be optimized.  */
83e69954b9Spbrook /* Update interrupt status after enabled or pending bits have been changed.  */
8450491c56SLuc Michel static void gic_update(GICState *s)
85e69954b9Spbrook {
86e69954b9Spbrook     int best_irq;
87e69954b9Spbrook     int best_prio;
88e69954b9Spbrook     int irq;
89dadbb58fSPeter Maydell     int irq_level, fiq_level;
909ee6e8bbSpbrook     int cpu;
919ee6e8bbSpbrook     int cm;
92e69954b9Spbrook 
93b95690c9SWei Huang     for (cpu = 0; cpu < s->num_cpu; cpu++) {
949ee6e8bbSpbrook         cm = 1 << cpu;
959ee6e8bbSpbrook         s->current_pending[cpu] = 1023;
96679aa175SFabian Aggeler         if (!(s->ctlr & (GICD_CTLR_EN_GRP0 | GICD_CTLR_EN_GRP1))
9732951860SFabian Aggeler             || !(s->cpu_ctlr[cpu] & (GICC_CTLR_EN_GRP0 | GICC_CTLR_EN_GRP1))) {
989ee6e8bbSpbrook             qemu_irq_lower(s->parent_irq[cpu]);
99dadbb58fSPeter Maydell             qemu_irq_lower(s->parent_fiq[cpu]);
100235069a3SJohan Karlsson             continue;
101e69954b9Spbrook         }
102e69954b9Spbrook         best_prio = 0x100;
103e69954b9Spbrook         best_irq = 1023;
104a32134aaSMark Langsdorf         for (irq = 0; irq < s->num_irq; irq++) {
10567ce697aSLuc Michel             if (GIC_DIST_TEST_ENABLED(irq, cm) &&
10667ce697aSLuc Michel                 gic_test_pending(s, irq, cm) &&
10767ce697aSLuc Michel                 (!GIC_DIST_TEST_ACTIVE(irq, cm)) &&
10867ce697aSLuc Michel                 (irq < GIC_INTERNAL || GIC_DIST_TARGET(irq) & cm)) {
10967ce697aSLuc Michel                 if (GIC_DIST_GET_PRIORITY(irq, cpu) < best_prio) {
11067ce697aSLuc Michel                     best_prio = GIC_DIST_GET_PRIORITY(irq, cpu);
111e69954b9Spbrook                     best_irq = irq;
112e69954b9Spbrook                 }
113e69954b9Spbrook             }
114e69954b9Spbrook         }
115dadbb58fSPeter Maydell 
1162531088fSHollis Blanchard         if (best_irq != 1023) {
1172531088fSHollis Blanchard             trace_gic_update_bestirq(cpu, best_irq, best_prio,
1182531088fSHollis Blanchard                 s->priority_mask[cpu], s->running_priority[cpu]);
1192531088fSHollis Blanchard         }
1202531088fSHollis Blanchard 
121dadbb58fSPeter Maydell         irq_level = fiq_level = 0;
122dadbb58fSPeter Maydell 
123cad065f1SPeter Maydell         if (best_prio < s->priority_mask[cpu]) {
1249ee6e8bbSpbrook             s->current_pending[cpu] = best_irq;
1259ee6e8bbSpbrook             if (best_prio < s->running_priority[cpu]) {
12667ce697aSLuc Michel                 int group = GIC_DIST_TEST_GROUP(best_irq, cm);
127dadbb58fSPeter Maydell 
128dadbb58fSPeter Maydell                 if (extract32(s->ctlr, group, 1) &&
129dadbb58fSPeter Maydell                     extract32(s->cpu_ctlr[cpu], group, 1)) {
130dadbb58fSPeter Maydell                     if (group == 0 && s->cpu_ctlr[cpu] & GICC_CTLR_FIQ_EN) {
131dadbb58fSPeter Maydell                         DPRINTF("Raised pending FIQ %d (cpu %d)\n",
132dadbb58fSPeter Maydell                                 best_irq, cpu);
133dadbb58fSPeter Maydell                         fiq_level = 1;
1342531088fSHollis Blanchard                         trace_gic_update_set_irq(cpu, "fiq", fiq_level);
135dadbb58fSPeter Maydell                     } else {
136dadbb58fSPeter Maydell                         DPRINTF("Raised pending IRQ %d (cpu %d)\n",
137dadbb58fSPeter Maydell                                 best_irq, cpu);
138dadbb58fSPeter Maydell                         irq_level = 1;
1392531088fSHollis Blanchard                         trace_gic_update_set_irq(cpu, "irq", irq_level);
140e69954b9Spbrook                     }
141e69954b9Spbrook                 }
142dadbb58fSPeter Maydell             }
143dadbb58fSPeter Maydell         }
144dadbb58fSPeter Maydell 
145dadbb58fSPeter Maydell         qemu_set_irq(s->parent_irq[cpu], irq_level);
146dadbb58fSPeter Maydell         qemu_set_irq(s->parent_fiq[cpu], fiq_level);
1479ee6e8bbSpbrook     }
148e69954b9Spbrook }
149e69954b9Spbrook 
1508d999995SChristoffer Dall static void gic_set_irq_11mpcore(GICState *s, int irq, int level,
1518d999995SChristoffer Dall                                  int cm, int target)
1528d999995SChristoffer Dall {
1538d999995SChristoffer Dall     if (level) {
15467ce697aSLuc Michel         GIC_DIST_SET_LEVEL(irq, cm);
15567ce697aSLuc Michel         if (GIC_DIST_TEST_EDGE_TRIGGER(irq) || GIC_DIST_TEST_ENABLED(irq, cm)) {
1568d999995SChristoffer Dall             DPRINTF("Set %d pending mask %x\n", irq, target);
15767ce697aSLuc Michel             GIC_DIST_SET_PENDING(irq, target);
1588d999995SChristoffer Dall         }
1598d999995SChristoffer Dall     } else {
16067ce697aSLuc Michel         GIC_DIST_CLEAR_LEVEL(irq, cm);
1618d999995SChristoffer Dall     }
1628d999995SChristoffer Dall }
1638d999995SChristoffer Dall 
1648d999995SChristoffer Dall static void gic_set_irq_generic(GICState *s, int irq, int level,
1658d999995SChristoffer Dall                                 int cm, int target)
1668d999995SChristoffer Dall {
1678d999995SChristoffer Dall     if (level) {
16867ce697aSLuc Michel         GIC_DIST_SET_LEVEL(irq, cm);
1698d999995SChristoffer Dall         DPRINTF("Set %d pending mask %x\n", irq, target);
17067ce697aSLuc Michel         if (GIC_DIST_TEST_EDGE_TRIGGER(irq)) {
17167ce697aSLuc Michel             GIC_DIST_SET_PENDING(irq, target);
1728d999995SChristoffer Dall         }
1738d999995SChristoffer Dall     } else {
17467ce697aSLuc Michel         GIC_DIST_CLEAR_LEVEL(irq, cm);
1758d999995SChristoffer Dall     }
1768d999995SChristoffer Dall }
1778d999995SChristoffer Dall 
1789ee6e8bbSpbrook /* Process a change in an external IRQ input.  */
179e69954b9Spbrook static void gic_set_irq(void *opaque, int irq, int level)
180e69954b9Spbrook {
181544d1afaSPeter Maydell     /* Meaning of the 'irq' parameter:
182544d1afaSPeter Maydell      *  [0..N-1] : external interrupts
183544d1afaSPeter Maydell      *  [N..N+31] : PPI (internal) interrupts for CPU 0
184544d1afaSPeter Maydell      *  [N+32..N+63] : PPI (internal interrupts for CPU 1
185544d1afaSPeter Maydell      *  ...
186544d1afaSPeter Maydell      */
187fae15286SPeter Maydell     GICState *s = (GICState *)opaque;
188544d1afaSPeter Maydell     int cm, target;
189544d1afaSPeter Maydell     if (irq < (s->num_irq - GIC_INTERNAL)) {
190e69954b9Spbrook         /* The first external input line is internal interrupt 32.  */
191544d1afaSPeter Maydell         cm = ALL_CPU_MASK;
19269253800SRusty Russell         irq += GIC_INTERNAL;
19367ce697aSLuc Michel         target = GIC_DIST_TARGET(irq);
194544d1afaSPeter Maydell     } else {
195544d1afaSPeter Maydell         int cpu;
196544d1afaSPeter Maydell         irq -= (s->num_irq - GIC_INTERNAL);
197544d1afaSPeter Maydell         cpu = irq / GIC_INTERNAL;
198544d1afaSPeter Maydell         irq %= GIC_INTERNAL;
199544d1afaSPeter Maydell         cm = 1 << cpu;
200544d1afaSPeter Maydell         target = cm;
201544d1afaSPeter Maydell     }
202544d1afaSPeter Maydell 
20340d22500SChristoffer Dall     assert(irq >= GIC_NR_SGIS);
20440d22500SChristoffer Dall 
20567ce697aSLuc Michel     if (level == GIC_DIST_TEST_LEVEL(irq, cm)) {
206e69954b9Spbrook         return;
207544d1afaSPeter Maydell     }
208e69954b9Spbrook 
2093bc4b52cSMarcin Krzeminski     if (s->revision == REV_11MPCORE) {
2108d999995SChristoffer Dall         gic_set_irq_11mpcore(s, irq, level, cm, target);
211e69954b9Spbrook     } else {
2128d999995SChristoffer Dall         gic_set_irq_generic(s, irq, level, cm, target);
213e69954b9Spbrook     }
2142531088fSHollis Blanchard     trace_gic_set_irq(irq, level, cm, target);
2158d999995SChristoffer Dall 
216e69954b9Spbrook     gic_update(s);
217e69954b9Spbrook }
218e69954b9Spbrook 
2197c0fa108SFabian Aggeler static uint16_t gic_get_current_pending_irq(GICState *s, int cpu,
2207c0fa108SFabian Aggeler                                             MemTxAttrs attrs)
2217c0fa108SFabian Aggeler {
2227c0fa108SFabian Aggeler     uint16_t pending_irq = s->current_pending[cpu];
2237c0fa108SFabian Aggeler 
2247c0fa108SFabian Aggeler     if (pending_irq < GIC_MAXIRQ && gic_has_groups(s)) {
22586b350f0SLuc Michel         int group = gic_test_group(s, pending_irq, cpu);
22686b350f0SLuc Michel 
2277c0fa108SFabian Aggeler         /* On a GIC without the security extensions, reading this register
2287c0fa108SFabian Aggeler          * behaves in the same way as a secure access to a GIC with them.
2297c0fa108SFabian Aggeler          */
2303dd0471bSLuc Michel         bool secure = !gic_cpu_ns_access(s, cpu, attrs);
2317c0fa108SFabian Aggeler 
2327c0fa108SFabian Aggeler         if (group == 0 && !secure) {
2337c0fa108SFabian Aggeler             /* Group0 interrupts hidden from Non-secure access */
2347c0fa108SFabian Aggeler             return 1023;
2357c0fa108SFabian Aggeler         }
2367c0fa108SFabian Aggeler         if (group == 1 && secure && !(s->cpu_ctlr[cpu] & GICC_CTLR_ACK_CTL)) {
2377c0fa108SFabian Aggeler             /* Group1 interrupts only seen by Secure access if
2387c0fa108SFabian Aggeler              * AckCtl bit set.
2397c0fa108SFabian Aggeler              */
2407c0fa108SFabian Aggeler             return 1022;
2417c0fa108SFabian Aggeler         }
2427c0fa108SFabian Aggeler     }
2437c0fa108SFabian Aggeler     return pending_irq;
2447c0fa108SFabian Aggeler }
2457c0fa108SFabian Aggeler 
246df92cfa6SPeter Maydell static int gic_get_group_priority(GICState *s, int cpu, int irq)
247df92cfa6SPeter Maydell {
248df92cfa6SPeter Maydell     /* Return the group priority of the specified interrupt
249df92cfa6SPeter Maydell      * (which is the top bits of its priority, with the number
250df92cfa6SPeter Maydell      * of bits masked determined by the applicable binary point register).
251df92cfa6SPeter Maydell      */
252df92cfa6SPeter Maydell     int bpr;
253df92cfa6SPeter Maydell     uint32_t mask;
254df92cfa6SPeter Maydell 
255df92cfa6SPeter Maydell     if (gic_has_groups(s) &&
256df92cfa6SPeter Maydell         !(s->cpu_ctlr[cpu] & GICC_CTLR_CBPR) &&
25786b350f0SLuc Michel         gic_test_group(s, irq, cpu)) {
258fc05a6f2SLuc MICHEL         bpr = s->abpr[cpu] - 1;
259fc05a6f2SLuc MICHEL         assert(bpr >= 0);
260df92cfa6SPeter Maydell     } else {
261df92cfa6SPeter Maydell         bpr = s->bpr[cpu];
262df92cfa6SPeter Maydell     }
263df92cfa6SPeter Maydell 
264df92cfa6SPeter Maydell     /* a BPR of 0 means the group priority bits are [7:1];
265df92cfa6SPeter Maydell      * a BPR of 1 means they are [7:2], and so on down to
266df92cfa6SPeter Maydell      * a BPR of 7 meaning no group priority bits at all.
267df92cfa6SPeter Maydell      */
268df92cfa6SPeter Maydell     mask = ~0U << ((bpr & 7) + 1);
269df92cfa6SPeter Maydell 
27086b350f0SLuc Michel     return gic_get_priority(s, irq, cpu) & mask;
271df92cfa6SPeter Maydell }
272df92cfa6SPeter Maydell 
27372889c8aSPeter Maydell static void gic_activate_irq(GICState *s, int cpu, int irq)
274e69954b9Spbrook {
27572889c8aSPeter Maydell     /* Set the appropriate Active Priority Register bit for this IRQ,
27672889c8aSPeter Maydell      * and update the running priority.
27772889c8aSPeter Maydell      */
27872889c8aSPeter Maydell     int prio = gic_get_group_priority(s, cpu, irq);
279*a1d7b8d8SLuc Michel     int min_bpr = gic_is_vcpu(cpu) ? GIC_VIRT_MIN_BPR : GIC_MIN_BPR;
280*a1d7b8d8SLuc Michel     int preemption_level = prio >> (min_bpr + 1);
28172889c8aSPeter Maydell     int regno = preemption_level / 32;
28272889c8aSPeter Maydell     int bitno = preemption_level % 32;
283*a1d7b8d8SLuc Michel     uint32_t *papr = NULL;
28472889c8aSPeter Maydell 
285*a1d7b8d8SLuc Michel     if (gic_is_vcpu(cpu)) {
286*a1d7b8d8SLuc Michel         assert(regno == 0);
287*a1d7b8d8SLuc Michel         papr = &s->h_apr[gic_get_vcpu_real_id(cpu)];
288*a1d7b8d8SLuc Michel     } else if (gic_has_groups(s) && gic_test_group(s, irq, cpu)) {
289*a1d7b8d8SLuc Michel         papr = &s->nsapr[regno][cpu];
2909ee6e8bbSpbrook     } else {
291*a1d7b8d8SLuc Michel         papr = &s->apr[regno][cpu];
2929ee6e8bbSpbrook     }
29372889c8aSPeter Maydell 
294*a1d7b8d8SLuc Michel     *papr |= (1 << bitno);
295*a1d7b8d8SLuc Michel 
29672889c8aSPeter Maydell     s->running_priority[cpu] = prio;
29786b350f0SLuc Michel     gic_set_active(s, irq, cpu);
29872889c8aSPeter Maydell }
29972889c8aSPeter Maydell 
30072889c8aSPeter Maydell static int gic_get_prio_from_apr_bits(GICState *s, int cpu)
30172889c8aSPeter Maydell {
30272889c8aSPeter Maydell     /* Recalculate the current running priority for this CPU based
30372889c8aSPeter Maydell      * on the set bits in the Active Priority Registers.
30472889c8aSPeter Maydell      */
30572889c8aSPeter Maydell     int i;
306*a1d7b8d8SLuc Michel 
307*a1d7b8d8SLuc Michel     if (gic_is_vcpu(cpu)) {
308*a1d7b8d8SLuc Michel         uint32_t apr = s->h_apr[gic_get_vcpu_real_id(cpu)];
309*a1d7b8d8SLuc Michel         if (apr) {
310*a1d7b8d8SLuc Michel             return ctz32(apr) << (GIC_VIRT_MIN_BPR + 1);
311*a1d7b8d8SLuc Michel         } else {
312*a1d7b8d8SLuc Michel             return 0x100;
313*a1d7b8d8SLuc Michel         }
314*a1d7b8d8SLuc Michel     }
315*a1d7b8d8SLuc Michel 
31672889c8aSPeter Maydell     for (i = 0; i < GIC_NR_APRS; i++) {
31772889c8aSPeter Maydell         uint32_t apr = s->apr[i][cpu] | s->nsapr[i][cpu];
31872889c8aSPeter Maydell         if (!apr) {
31972889c8aSPeter Maydell             continue;
32072889c8aSPeter Maydell         }
32172889c8aSPeter Maydell         return (i * 32 + ctz32(apr)) << (GIC_MIN_BPR + 1);
32272889c8aSPeter Maydell     }
32372889c8aSPeter Maydell     return 0x100;
32472889c8aSPeter Maydell }
32572889c8aSPeter Maydell 
32672889c8aSPeter Maydell static void gic_drop_prio(GICState *s, int cpu, int group)
32772889c8aSPeter Maydell {
32872889c8aSPeter Maydell     /* Drop the priority of the currently active interrupt in the
32972889c8aSPeter Maydell      * specified group.
33072889c8aSPeter Maydell      *
33172889c8aSPeter Maydell      * Note that we can guarantee (because of the requirement to nest
33272889c8aSPeter Maydell      * GICC_IAR reads [which activate an interrupt and raise priority]
33372889c8aSPeter Maydell      * with GICC_EOIR writes [which drop the priority for the interrupt])
33472889c8aSPeter Maydell      * that the interrupt we're being called for is the highest priority
33572889c8aSPeter Maydell      * active interrupt, meaning that it has the lowest set bit in the
33672889c8aSPeter Maydell      * APR registers.
33772889c8aSPeter Maydell      *
33872889c8aSPeter Maydell      * If the guest does not honour the ordering constraints then the
33972889c8aSPeter Maydell      * behaviour of the GIC is UNPREDICTABLE, which for us means that
34072889c8aSPeter Maydell      * the values of the APR registers might become incorrect and the
34172889c8aSPeter Maydell      * running priority will be wrong, so interrupts that should preempt
34272889c8aSPeter Maydell      * might not do so, and interrupts that should not preempt might do so.
34372889c8aSPeter Maydell      */
344*a1d7b8d8SLuc Michel     if (gic_is_vcpu(cpu)) {
345*a1d7b8d8SLuc Michel         int rcpu = gic_get_vcpu_real_id(cpu);
346*a1d7b8d8SLuc Michel 
347*a1d7b8d8SLuc Michel         if (s->h_apr[rcpu]) {
348*a1d7b8d8SLuc Michel             /* Clear lowest set bit */
349*a1d7b8d8SLuc Michel             s->h_apr[rcpu] &= s->h_apr[rcpu] - 1;
350*a1d7b8d8SLuc Michel         }
351*a1d7b8d8SLuc Michel     } else {
35272889c8aSPeter Maydell         int i;
35372889c8aSPeter Maydell 
35472889c8aSPeter Maydell         for (i = 0; i < GIC_NR_APRS; i++) {
35572889c8aSPeter Maydell             uint32_t *papr = group ? &s->nsapr[i][cpu] : &s->apr[i][cpu];
35672889c8aSPeter Maydell             if (!*papr) {
35772889c8aSPeter Maydell                 continue;
35872889c8aSPeter Maydell             }
35972889c8aSPeter Maydell             /* Clear lowest set bit */
36072889c8aSPeter Maydell             *papr &= *papr - 1;
36172889c8aSPeter Maydell             break;
36272889c8aSPeter Maydell         }
363*a1d7b8d8SLuc Michel     }
36472889c8aSPeter Maydell 
36572889c8aSPeter Maydell     s->running_priority[cpu] = gic_get_prio_from_apr_bits(s, cpu);
366e69954b9Spbrook }
367e69954b9Spbrook 
368c5619bf9SFabian Aggeler uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs)
369e69954b9Spbrook {
37040d22500SChristoffer Dall     int ret, irq, src;
3719ee6e8bbSpbrook     int cm = 1 << cpu;
372c5619bf9SFabian Aggeler 
373c5619bf9SFabian Aggeler     /* gic_get_current_pending_irq() will return 1022 or 1023 appropriately
374c5619bf9SFabian Aggeler      * for the case where this GIC supports grouping and the pending interrupt
375c5619bf9SFabian Aggeler      * is in the wrong group.
376c5619bf9SFabian Aggeler      */
377a8f15a27SDaniel P. Berrange     irq = gic_get_current_pending_irq(s, cpu, attrs);
3782531088fSHollis Blanchard     trace_gic_acknowledge_irq(cpu, irq);
379c5619bf9SFabian Aggeler 
380c5619bf9SFabian Aggeler     if (irq >= GIC_MAXIRQ) {
381c5619bf9SFabian Aggeler         DPRINTF("ACK, no pending interrupt or it is hidden: %d\n", irq);
382c5619bf9SFabian Aggeler         return irq;
383c5619bf9SFabian Aggeler     }
384c5619bf9SFabian Aggeler 
38586b350f0SLuc Michel     if (gic_get_priority(s, irq, cpu) >= s->running_priority[cpu]) {
386c5619bf9SFabian Aggeler         DPRINTF("ACK, pending interrupt (%d) has insufficient priority\n", irq);
387e69954b9Spbrook         return 1023;
388e69954b9Spbrook     }
38940d22500SChristoffer Dall 
3907c14b3acSMichael Davidsaver     if (s->revision == REV_11MPCORE) {
3919ee6e8bbSpbrook         /* Clear pending flags for both level and edge triggered interrupts.
39240d22500SChristoffer Dall          * Level triggered IRQs will be reasserted once they become inactive.
39340d22500SChristoffer Dall          */
39486b350f0SLuc Michel         gic_clear_pending(s, irq, cpu);
39540d22500SChristoffer Dall         ret = irq;
39640d22500SChristoffer Dall     } else {
39740d22500SChristoffer Dall         if (irq < GIC_NR_SGIS) {
39840d22500SChristoffer Dall             /* Lookup the source CPU for the SGI and clear this in the
39940d22500SChristoffer Dall              * sgi_pending map.  Return the src and clear the overall pending
40040d22500SChristoffer Dall              * state on this CPU if the SGI is not pending from any CPUs.
40140d22500SChristoffer Dall              */
40240d22500SChristoffer Dall             assert(s->sgi_pending[irq][cpu] != 0);
40340d22500SChristoffer Dall             src = ctz32(s->sgi_pending[irq][cpu]);
40440d22500SChristoffer Dall             s->sgi_pending[irq][cpu] &= ~(1 << src);
40540d22500SChristoffer Dall             if (s->sgi_pending[irq][cpu] == 0) {
40686b350f0SLuc Michel                 gic_clear_pending(s, irq, cpu);
40740d22500SChristoffer Dall             }
40840d22500SChristoffer Dall             ret = irq | ((src & 0x7) << 10);
40940d22500SChristoffer Dall         } else {
41040d22500SChristoffer Dall             /* Clear pending state for both level and edge triggered
41140d22500SChristoffer Dall              * interrupts. (level triggered interrupts with an active line
41240d22500SChristoffer Dall              * remain pending, see gic_test_pending)
41340d22500SChristoffer Dall              */
41486b350f0SLuc Michel             gic_clear_pending(s, irq, cpu);
41540d22500SChristoffer Dall             ret = irq;
41640d22500SChristoffer Dall         }
41740d22500SChristoffer Dall     }
41840d22500SChristoffer Dall 
41972889c8aSPeter Maydell     gic_activate_irq(s, cpu, irq);
42072889c8aSPeter Maydell     gic_update(s);
42140d22500SChristoffer Dall     DPRINTF("ACK %d\n", irq);
42240d22500SChristoffer Dall     return ret;
423e69954b9Spbrook }
424e69954b9Spbrook 
42567ce697aSLuc Michel void gic_dist_set_priority(GICState *s, int cpu, int irq, uint8_t val,
42681508470SFabian Aggeler                       MemTxAttrs attrs)
4279df90ad0SChristoffer Dall {
42881508470SFabian Aggeler     if (s->security_extn && !attrs.secure) {
42967ce697aSLuc Michel         if (!GIC_DIST_TEST_GROUP(irq, (1 << cpu))) {
43081508470SFabian Aggeler             return; /* Ignore Non-secure access of Group0 IRQ */
43181508470SFabian Aggeler         }
43281508470SFabian Aggeler         val = 0x80 | (val >> 1); /* Non-secure view */
43381508470SFabian Aggeler     }
43481508470SFabian Aggeler 
4359df90ad0SChristoffer Dall     if (irq < GIC_INTERNAL) {
4369df90ad0SChristoffer Dall         s->priority1[irq][cpu] = val;
4379df90ad0SChristoffer Dall     } else {
4389df90ad0SChristoffer Dall         s->priority2[(irq) - GIC_INTERNAL] = val;
4399df90ad0SChristoffer Dall     }
4409df90ad0SChristoffer Dall }
4419df90ad0SChristoffer Dall 
44267ce697aSLuc Michel static uint32_t gic_dist_get_priority(GICState *s, int cpu, int irq,
44381508470SFabian Aggeler                                  MemTxAttrs attrs)
44481508470SFabian Aggeler {
44567ce697aSLuc Michel     uint32_t prio = GIC_DIST_GET_PRIORITY(irq, cpu);
44681508470SFabian Aggeler 
44781508470SFabian Aggeler     if (s->security_extn && !attrs.secure) {
44867ce697aSLuc Michel         if (!GIC_DIST_TEST_GROUP(irq, (1 << cpu))) {
44981508470SFabian Aggeler             return 0; /* Non-secure access cannot read priority of Group0 IRQ */
45081508470SFabian Aggeler         }
45181508470SFabian Aggeler         prio = (prio << 1) & 0xff; /* Non-secure view */
45281508470SFabian Aggeler     }
45381508470SFabian Aggeler     return prio;
45481508470SFabian Aggeler }
45581508470SFabian Aggeler 
45681508470SFabian Aggeler static void gic_set_priority_mask(GICState *s, int cpu, uint8_t pmask,
45781508470SFabian Aggeler                                   MemTxAttrs attrs)
45881508470SFabian Aggeler {
4593dd0471bSLuc Michel     if (gic_cpu_ns_access(s, cpu, attrs)) {
46081508470SFabian Aggeler         if (s->priority_mask[cpu] & 0x80) {
46181508470SFabian Aggeler             /* Priority Mask in upper half */
46281508470SFabian Aggeler             pmask = 0x80 | (pmask >> 1);
46381508470SFabian Aggeler         } else {
46481508470SFabian Aggeler             /* Non-secure write ignored if priority mask is in lower half */
46581508470SFabian Aggeler             return;
46681508470SFabian Aggeler         }
46781508470SFabian Aggeler     }
46881508470SFabian Aggeler     s->priority_mask[cpu] = pmask;
46981508470SFabian Aggeler }
47081508470SFabian Aggeler 
47181508470SFabian Aggeler static uint32_t gic_get_priority_mask(GICState *s, int cpu, MemTxAttrs attrs)
47281508470SFabian Aggeler {
47381508470SFabian Aggeler     uint32_t pmask = s->priority_mask[cpu];
47481508470SFabian Aggeler 
4753dd0471bSLuc Michel     if (gic_cpu_ns_access(s, cpu, attrs)) {
47681508470SFabian Aggeler         if (pmask & 0x80) {
47781508470SFabian Aggeler             /* Priority Mask in upper half, return Non-secure view */
47881508470SFabian Aggeler             pmask = (pmask << 1) & 0xff;
47981508470SFabian Aggeler         } else {
48081508470SFabian Aggeler             /* Priority Mask in lower half, RAZ */
48181508470SFabian Aggeler             pmask = 0;
48281508470SFabian Aggeler         }
48381508470SFabian Aggeler     }
48481508470SFabian Aggeler     return pmask;
48581508470SFabian Aggeler }
48681508470SFabian Aggeler 
48732951860SFabian Aggeler static uint32_t gic_get_cpu_control(GICState *s, int cpu, MemTxAttrs attrs)
48832951860SFabian Aggeler {
48932951860SFabian Aggeler     uint32_t ret = s->cpu_ctlr[cpu];
49032951860SFabian Aggeler 
4913dd0471bSLuc Michel     if (gic_cpu_ns_access(s, cpu, attrs)) {
49232951860SFabian Aggeler         /* Construct the NS banked view of GICC_CTLR from the correct
49332951860SFabian Aggeler          * bits of the S banked view. We don't need to move the bypass
49432951860SFabian Aggeler          * control bits because we don't implement that (IMPDEF) part
49532951860SFabian Aggeler          * of the GIC architecture.
49632951860SFabian Aggeler          */
49732951860SFabian Aggeler         ret = (ret & (GICC_CTLR_EN_GRP1 | GICC_CTLR_EOIMODE_NS)) >> 1;
49832951860SFabian Aggeler     }
49932951860SFabian Aggeler     return ret;
50032951860SFabian Aggeler }
50132951860SFabian Aggeler 
50232951860SFabian Aggeler static void gic_set_cpu_control(GICState *s, int cpu, uint32_t value,
50332951860SFabian Aggeler                                 MemTxAttrs attrs)
50432951860SFabian Aggeler {
50532951860SFabian Aggeler     uint32_t mask;
50632951860SFabian Aggeler 
5073dd0471bSLuc Michel     if (gic_cpu_ns_access(s, cpu, attrs)) {
50832951860SFabian Aggeler         /* The NS view can only write certain bits in the register;
50932951860SFabian Aggeler          * the rest are unchanged
51032951860SFabian Aggeler          */
51132951860SFabian Aggeler         mask = GICC_CTLR_EN_GRP1;
51232951860SFabian Aggeler         if (s->revision == 2) {
51332951860SFabian Aggeler             mask |= GICC_CTLR_EOIMODE_NS;
51432951860SFabian Aggeler         }
51532951860SFabian Aggeler         s->cpu_ctlr[cpu] &= ~mask;
51632951860SFabian Aggeler         s->cpu_ctlr[cpu] |= (value << 1) & mask;
51732951860SFabian Aggeler     } else {
51832951860SFabian Aggeler         if (s->revision == 2) {
51932951860SFabian Aggeler             mask = s->security_extn ? GICC_CTLR_V2_S_MASK : GICC_CTLR_V2_MASK;
52032951860SFabian Aggeler         } else {
52132951860SFabian Aggeler             mask = s->security_extn ? GICC_CTLR_V1_S_MASK : GICC_CTLR_V1_MASK;
52232951860SFabian Aggeler         }
52332951860SFabian Aggeler         s->cpu_ctlr[cpu] = value & mask;
52432951860SFabian Aggeler     }
52532951860SFabian Aggeler     DPRINTF("CPU Interface %d: Group0 Interrupts %sabled, "
52632951860SFabian Aggeler             "Group1 Interrupts %sabled\n", cpu,
52732951860SFabian Aggeler             (s->cpu_ctlr[cpu] & GICC_CTLR_EN_GRP0) ? "En" : "Dis",
52832951860SFabian Aggeler             (s->cpu_ctlr[cpu] & GICC_CTLR_EN_GRP1) ? "En" : "Dis");
52932951860SFabian Aggeler }
53032951860SFabian Aggeler 
53108efa9f2SFabian Aggeler static uint8_t gic_get_running_priority(GICState *s, int cpu, MemTxAttrs attrs)
53208efa9f2SFabian Aggeler {
53371aa735bSLuc MICHEL     if ((s->revision != REV_11MPCORE) && (s->running_priority[cpu] > 0xff)) {
53471aa735bSLuc MICHEL         /* Idle priority */
53571aa735bSLuc MICHEL         return 0xff;
53671aa735bSLuc MICHEL     }
53771aa735bSLuc MICHEL 
5383dd0471bSLuc Michel     if (gic_cpu_ns_access(s, cpu, attrs)) {
53908efa9f2SFabian Aggeler         if (s->running_priority[cpu] & 0x80) {
54008efa9f2SFabian Aggeler             /* Running priority in upper half of range: return the Non-secure
54108efa9f2SFabian Aggeler              * view of the priority.
54208efa9f2SFabian Aggeler              */
54308efa9f2SFabian Aggeler             return s->running_priority[cpu] << 1;
54408efa9f2SFabian Aggeler         } else {
54508efa9f2SFabian Aggeler             /* Running priority in lower half of range: RAZ */
54608efa9f2SFabian Aggeler             return 0;
54708efa9f2SFabian Aggeler         }
54808efa9f2SFabian Aggeler     } else {
54908efa9f2SFabian Aggeler         return s->running_priority[cpu];
55008efa9f2SFabian Aggeler     }
55108efa9f2SFabian Aggeler }
55208efa9f2SFabian Aggeler 
553a55c910eSPeter Maydell /* Return true if we should split priority drop and interrupt deactivation,
554a55c910eSPeter Maydell  * ie whether the relevant EOIMode bit is set.
555a55c910eSPeter Maydell  */
556a55c910eSPeter Maydell static bool gic_eoi_split(GICState *s, int cpu, MemTxAttrs attrs)
557a55c910eSPeter Maydell {
558a55c910eSPeter Maydell     if (s->revision != 2) {
559a55c910eSPeter Maydell         /* Before GICv2 prio-drop and deactivate are not separable */
560a55c910eSPeter Maydell         return false;
561a55c910eSPeter Maydell     }
5623dd0471bSLuc Michel     if (gic_cpu_ns_access(s, cpu, attrs)) {
563a55c910eSPeter Maydell         return s->cpu_ctlr[cpu] & GICC_CTLR_EOIMODE_NS;
564a55c910eSPeter Maydell     }
565a55c910eSPeter Maydell     return s->cpu_ctlr[cpu] & GICC_CTLR_EOIMODE;
566a55c910eSPeter Maydell }
567a55c910eSPeter Maydell 
568a55c910eSPeter Maydell static void gic_deactivate_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
569a55c910eSPeter Maydell {
570ee03cca8SPeter Maydell     int group;
571ee03cca8SPeter Maydell 
572ee03cca8SPeter Maydell     if (irq >= s->num_irq) {
573ee03cca8SPeter Maydell         /*
574ee03cca8SPeter Maydell          * This handles two cases:
575ee03cca8SPeter Maydell          * 1. If software writes the ID of a spurious interrupt [ie 1023]
576ee03cca8SPeter Maydell          * to the GICC_DIR, the GIC ignores that write.
577ee03cca8SPeter Maydell          * 2. If software writes the number of a non-existent interrupt
578ee03cca8SPeter Maydell          * this must be a subcase of "value written is not an active interrupt"
579ee03cca8SPeter Maydell          * and so this is UNPREDICTABLE. We choose to ignore it.
580ee03cca8SPeter Maydell          */
581ee03cca8SPeter Maydell         return;
582ee03cca8SPeter Maydell     }
583ee03cca8SPeter Maydell 
58486b350f0SLuc Michel     group = gic_has_groups(s) && gic_test_group(s, irq, cpu);
585a55c910eSPeter Maydell 
586a55c910eSPeter Maydell     if (!gic_eoi_split(s, cpu, attrs)) {
587a55c910eSPeter Maydell         /* This is UNPREDICTABLE; we choose to ignore it */
588a55c910eSPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
589a55c910eSPeter Maydell                       "gic_deactivate_irq: GICC_DIR write when EOIMode clear");
590a55c910eSPeter Maydell         return;
591a55c910eSPeter Maydell     }
592a55c910eSPeter Maydell 
5933dd0471bSLuc Michel     if (gic_cpu_ns_access(s, cpu, attrs) && !group) {
594a55c910eSPeter Maydell         DPRINTF("Non-secure DI for Group0 interrupt %d ignored\n", irq);
595a55c910eSPeter Maydell         return;
596a55c910eSPeter Maydell     }
597a55c910eSPeter Maydell 
59886b350f0SLuc Michel     gic_clear_active(s, irq, cpu);
599a55c910eSPeter Maydell }
600a55c910eSPeter Maydell 
60150491c56SLuc Michel static void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
602e69954b9Spbrook {
6039ee6e8bbSpbrook     int cm = 1 << cpu;
60472889c8aSPeter Maydell     int group;
60572889c8aSPeter Maydell 
606df628ff1Spbrook     DPRINTF("EOI %d\n", irq);
607a32134aaSMark Langsdorf     if (irq >= s->num_irq) {
608217bfb44SPeter Maydell         /* This handles two cases:
609217bfb44SPeter Maydell          * 1. If software writes the ID of a spurious interrupt [ie 1023]
610217bfb44SPeter Maydell          * to the GICC_EOIR, the GIC ignores that write.
611217bfb44SPeter Maydell          * 2. If software writes the number of a non-existent interrupt
612217bfb44SPeter Maydell          * this must be a subcase of "value written does not match the last
613217bfb44SPeter Maydell          * valid interrupt value read from the Interrupt Acknowledge
614217bfb44SPeter Maydell          * register" and so this is UNPREDICTABLE. We choose to ignore it.
615217bfb44SPeter Maydell          */
616217bfb44SPeter Maydell         return;
617217bfb44SPeter Maydell     }
61872889c8aSPeter Maydell     if (s->running_priority[cpu] == 0x100) {
619e69954b9Spbrook         return; /* No active IRQ.  */
62072889c8aSPeter Maydell     }
6218d999995SChristoffer Dall 
6223bc4b52cSMarcin Krzeminski     if (s->revision == REV_11MPCORE) {
623e69954b9Spbrook         /* Mark level triggered interrupts as pending if they are still
624e69954b9Spbrook            raised.  */
62567ce697aSLuc Michel         if (!GIC_DIST_TEST_EDGE_TRIGGER(irq) && GIC_DIST_TEST_ENABLED(irq, cm)
62667ce697aSLuc Michel             && GIC_DIST_TEST_LEVEL(irq, cm)
62767ce697aSLuc Michel             && (GIC_DIST_TARGET(irq) & cm) != 0) {
6289ee6e8bbSpbrook             DPRINTF("Set %d pending mask %x\n", irq, cm);
62967ce697aSLuc Michel             GIC_DIST_SET_PENDING(irq, cm);
630e69954b9Spbrook         }
6318d999995SChristoffer Dall     }
6328d999995SChristoffer Dall 
63386b350f0SLuc Michel     group = gic_has_groups(s) && gic_test_group(s, irq, cpu);
63472889c8aSPeter Maydell 
6353dd0471bSLuc Michel     if (gic_cpu_ns_access(s, cpu, attrs) && !group) {
636f9c6a7f1SFabian Aggeler         DPRINTF("Non-secure EOI for Group0 interrupt %d ignored\n", irq);
637f9c6a7f1SFabian Aggeler         return;
638f9c6a7f1SFabian Aggeler     }
639f9c6a7f1SFabian Aggeler 
640f9c6a7f1SFabian Aggeler     /* Secure EOI with GICC_CTLR.AckCtl == 0 when the IRQ is a Group 1
641f9c6a7f1SFabian Aggeler      * interrupt is UNPREDICTABLE. We choose to handle it as if AckCtl == 1,
642f9c6a7f1SFabian Aggeler      * i.e. go ahead and complete the irq anyway.
643f9c6a7f1SFabian Aggeler      */
644f9c6a7f1SFabian Aggeler 
64572889c8aSPeter Maydell     gic_drop_prio(s, cpu, group);
646a55c910eSPeter Maydell 
647a55c910eSPeter Maydell     /* In GICv2 the guest can choose to split priority-drop and deactivate */
648a55c910eSPeter Maydell     if (!gic_eoi_split(s, cpu, attrs)) {
64986b350f0SLuc Michel         gic_clear_active(s, irq, cpu);
650a55c910eSPeter Maydell     }
651e69954b9Spbrook     gic_update(s);
652e69954b9Spbrook }
653e69954b9Spbrook 
654a9d85353SPeter Maydell static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
655e69954b9Spbrook {
656fae15286SPeter Maydell     GICState *s = (GICState *)opaque;
657e69954b9Spbrook     uint32_t res;
658e69954b9Spbrook     int irq;
659e69954b9Spbrook     int i;
6609ee6e8bbSpbrook     int cpu;
6619ee6e8bbSpbrook     int cm;
6629ee6e8bbSpbrook     int mask;
663e69954b9Spbrook 
664926c4affSPeter Maydell     cpu = gic_get_current_cpu(s);
6659ee6e8bbSpbrook     cm = 1 << cpu;
666e69954b9Spbrook     if (offset < 0x100) {
667679aa175SFabian Aggeler         if (offset == 0) {      /* GICD_CTLR */
668679aa175SFabian Aggeler             if (s->security_extn && !attrs.secure) {
669679aa175SFabian Aggeler                 /* The NS bank of this register is just an alias of the
670679aa175SFabian Aggeler                  * EnableGrp1 bit in the S bank version.
671679aa175SFabian Aggeler                  */
672679aa175SFabian Aggeler                 return extract32(s->ctlr, 1, 1);
673679aa175SFabian Aggeler             } else {
674679aa175SFabian Aggeler                 return s->ctlr;
675679aa175SFabian Aggeler             }
676679aa175SFabian Aggeler         }
677e69954b9Spbrook         if (offset == 4)
6785543d1abSFabian Aggeler             /* Interrupt Controller Type Register */
6795543d1abSFabian Aggeler             return ((s->num_irq / 32) - 1)
680b95690c9SWei Huang                     | ((s->num_cpu - 1) << 5)
6815543d1abSFabian Aggeler                     | (s->security_extn << 10);
682e69954b9Spbrook         if (offset < 0x08)
683e69954b9Spbrook             return 0;
684b79f2265SRob Herring         if (offset >= 0x80) {
685c27a5ba9SFabian Aggeler             /* Interrupt Group Registers: these RAZ/WI if this is an NS
686c27a5ba9SFabian Aggeler              * access to a GIC with the security extensions, or if the GIC
687c27a5ba9SFabian Aggeler              * doesn't have groups at all.
688c27a5ba9SFabian Aggeler              */
689c27a5ba9SFabian Aggeler             res = 0;
690c27a5ba9SFabian Aggeler             if (!(s->security_extn && !attrs.secure) && gic_has_groups(s)) {
691c27a5ba9SFabian Aggeler                 /* Every byte offset holds 8 group status bits */
692c27a5ba9SFabian Aggeler                 irq = (offset - 0x080) * 8 + GIC_BASE_IRQ;
693c27a5ba9SFabian Aggeler                 if (irq >= s->num_irq) {
694c27a5ba9SFabian Aggeler                     goto bad_reg;
695c27a5ba9SFabian Aggeler                 }
696c27a5ba9SFabian Aggeler                 for (i = 0; i < 8; i++) {
69767ce697aSLuc Michel                     if (GIC_DIST_TEST_GROUP(irq + i, cm)) {
698c27a5ba9SFabian Aggeler                         res |= (1 << i);
699c27a5ba9SFabian Aggeler                     }
700c27a5ba9SFabian Aggeler                 }
701c27a5ba9SFabian Aggeler             }
702c27a5ba9SFabian Aggeler             return res;
703b79f2265SRob Herring         }
704e69954b9Spbrook         goto bad_reg;
705e69954b9Spbrook     } else if (offset < 0x200) {
706e69954b9Spbrook         /* Interrupt Set/Clear Enable.  */
707e69954b9Spbrook         if (offset < 0x180)
708e69954b9Spbrook             irq = (offset - 0x100) * 8;
709e69954b9Spbrook         else
710e69954b9Spbrook             irq = (offset - 0x180) * 8;
7119ee6e8bbSpbrook         irq += GIC_BASE_IRQ;
712a32134aaSMark Langsdorf         if (irq >= s->num_irq)
713e69954b9Spbrook             goto bad_reg;
714e69954b9Spbrook         res = 0;
715e69954b9Spbrook         for (i = 0; i < 8; i++) {
716fea8a08eSJens Wiklander             if (s->security_extn && !attrs.secure &&
71767ce697aSLuc Michel                 !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) {
718fea8a08eSJens Wiklander                 continue; /* Ignore Non-secure access of Group0 IRQ */
719fea8a08eSJens Wiklander             }
720fea8a08eSJens Wiklander 
72167ce697aSLuc Michel             if (GIC_DIST_TEST_ENABLED(irq + i, cm)) {
722e69954b9Spbrook                 res |= (1 << i);
723e69954b9Spbrook             }
724e69954b9Spbrook         }
725e69954b9Spbrook     } else if (offset < 0x300) {
726e69954b9Spbrook         /* Interrupt Set/Clear Pending.  */
727e69954b9Spbrook         if (offset < 0x280)
728e69954b9Spbrook             irq = (offset - 0x200) * 8;
729e69954b9Spbrook         else
730e69954b9Spbrook             irq = (offset - 0x280) * 8;
7319ee6e8bbSpbrook         irq += GIC_BASE_IRQ;
732a32134aaSMark Langsdorf         if (irq >= s->num_irq)
733e69954b9Spbrook             goto bad_reg;
734e69954b9Spbrook         res = 0;
73569253800SRusty Russell         mask = (irq < GIC_INTERNAL) ?  cm : ALL_CPU_MASK;
736e69954b9Spbrook         for (i = 0; i < 8; i++) {
737fea8a08eSJens Wiklander             if (s->security_extn && !attrs.secure &&
73867ce697aSLuc Michel                 !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) {
739fea8a08eSJens Wiklander                 continue; /* Ignore Non-secure access of Group0 IRQ */
740fea8a08eSJens Wiklander             }
741fea8a08eSJens Wiklander 
7428d999995SChristoffer Dall             if (gic_test_pending(s, irq + i, mask)) {
743e69954b9Spbrook                 res |= (1 << i);
744e69954b9Spbrook             }
745e69954b9Spbrook         }
746e69954b9Spbrook     } else if (offset < 0x400) {
7473bb0b038SLuc Michel         /* Interrupt Set/Clear Active.  */
7483bb0b038SLuc Michel         if (offset < 0x380) {
7493bb0b038SLuc Michel             irq = (offset - 0x300) * 8;
7503bb0b038SLuc Michel         } else if (s->revision == 2) {
7513bb0b038SLuc Michel             irq = (offset - 0x380) * 8;
7523bb0b038SLuc Michel         } else {
7533bb0b038SLuc Michel             goto bad_reg;
7543bb0b038SLuc Michel         }
7553bb0b038SLuc Michel 
7563bb0b038SLuc Michel         irq += GIC_BASE_IRQ;
757a32134aaSMark Langsdorf         if (irq >= s->num_irq)
758e69954b9Spbrook             goto bad_reg;
759e69954b9Spbrook         res = 0;
76069253800SRusty Russell         mask = (irq < GIC_INTERNAL) ?  cm : ALL_CPU_MASK;
761e69954b9Spbrook         for (i = 0; i < 8; i++) {
762fea8a08eSJens Wiklander             if (s->security_extn && !attrs.secure &&
76367ce697aSLuc Michel                 !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) {
764fea8a08eSJens Wiklander                 continue; /* Ignore Non-secure access of Group0 IRQ */
765fea8a08eSJens Wiklander             }
766fea8a08eSJens Wiklander 
76767ce697aSLuc Michel             if (GIC_DIST_TEST_ACTIVE(irq + i, mask)) {
768e69954b9Spbrook                 res |= (1 << i);
769e69954b9Spbrook             }
770e69954b9Spbrook         }
771e69954b9Spbrook     } else if (offset < 0x800) {
772e69954b9Spbrook         /* Interrupt Priority.  */
7739ee6e8bbSpbrook         irq = (offset - 0x400) + GIC_BASE_IRQ;
774a32134aaSMark Langsdorf         if (irq >= s->num_irq)
775e69954b9Spbrook             goto bad_reg;
77667ce697aSLuc Michel         res = gic_dist_get_priority(s, cpu, irq, attrs);
777e69954b9Spbrook     } else if (offset < 0xc00) {
778e69954b9Spbrook         /* Interrupt CPU Target.  */
7796b9680bbSPeter Maydell         if (s->num_cpu == 1 && s->revision != REV_11MPCORE) {
7806b9680bbSPeter Maydell             /* For uniprocessor GICs these RAZ/WI */
7816b9680bbSPeter Maydell             res = 0;
7826b9680bbSPeter Maydell         } else {
7839ee6e8bbSpbrook             irq = (offset - 0x800) + GIC_BASE_IRQ;
7846b9680bbSPeter Maydell             if (irq >= s->num_irq) {
785e69954b9Spbrook                 goto bad_reg;
7866b9680bbSPeter Maydell             }
7877995206dSPeter Maydell             if (irq < 29 && s->revision == REV_11MPCORE) {
7887995206dSPeter Maydell                 res = 0;
7897995206dSPeter Maydell             } else if (irq < GIC_INTERNAL) {
7909ee6e8bbSpbrook                 res = cm;
7919ee6e8bbSpbrook             } else {
79267ce697aSLuc Michel                 res = GIC_DIST_TARGET(irq);
7939ee6e8bbSpbrook             }
7946b9680bbSPeter Maydell         }
795e69954b9Spbrook     } else if (offset < 0xf00) {
796e69954b9Spbrook         /* Interrupt Configuration.  */
79771a62046SAdam Lackorzynski         irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ;
798a32134aaSMark Langsdorf         if (irq >= s->num_irq)
799e69954b9Spbrook             goto bad_reg;
800e69954b9Spbrook         res = 0;
801e69954b9Spbrook         for (i = 0; i < 4; i++) {
802fea8a08eSJens Wiklander             if (s->security_extn && !attrs.secure &&
80367ce697aSLuc Michel                 !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) {
804fea8a08eSJens Wiklander                 continue; /* Ignore Non-secure access of Group0 IRQ */
805fea8a08eSJens Wiklander             }
806fea8a08eSJens Wiklander 
80767ce697aSLuc Michel             if (GIC_DIST_TEST_MODEL(irq + i)) {
808e69954b9Spbrook                 res |= (1 << (i * 2));
80967ce697aSLuc Michel             }
81067ce697aSLuc Michel             if (GIC_DIST_TEST_EDGE_TRIGGER(irq + i)) {
811e69954b9Spbrook                 res |= (2 << (i * 2));
812e69954b9Spbrook             }
81367ce697aSLuc Michel         }
81440d22500SChristoffer Dall     } else if (offset < 0xf10) {
81540d22500SChristoffer Dall         goto bad_reg;
81640d22500SChristoffer Dall     } else if (offset < 0xf30) {
8177c14b3acSMichael Davidsaver         if (s->revision == REV_11MPCORE) {
81840d22500SChristoffer Dall             goto bad_reg;
81940d22500SChristoffer Dall         }
82040d22500SChristoffer Dall 
82140d22500SChristoffer Dall         if (offset < 0xf20) {
82240d22500SChristoffer Dall             /* GICD_CPENDSGIRn */
82340d22500SChristoffer Dall             irq = (offset - 0xf10);
82440d22500SChristoffer Dall         } else {
82540d22500SChristoffer Dall             irq = (offset - 0xf20);
82640d22500SChristoffer Dall             /* GICD_SPENDSGIRn */
82740d22500SChristoffer Dall         }
82840d22500SChristoffer Dall 
829fea8a08eSJens Wiklander         if (s->security_extn && !attrs.secure &&
83067ce697aSLuc Michel             !GIC_DIST_TEST_GROUP(irq, 1 << cpu)) {
831fea8a08eSJens Wiklander             res = 0; /* Ignore Non-secure access of Group0 IRQ */
832fea8a08eSJens Wiklander         } else {
83340d22500SChristoffer Dall             res = s->sgi_pending[irq][cpu];
834fea8a08eSJens Wiklander         }
8353355c360SAlistair Francis     } else if (offset < 0xfd0) {
836e69954b9Spbrook         goto bad_reg;
8373355c360SAlistair Francis     } else if (offset < 0x1000) {
838e69954b9Spbrook         if (offset & 3) {
839e69954b9Spbrook             res = 0;
840e69954b9Spbrook         } else {
8413355c360SAlistair Francis             switch (s->revision) {
8423355c360SAlistair Francis             case REV_11MPCORE:
8433355c360SAlistair Francis                 res = gic_id_11mpcore[(offset - 0xfd0) >> 2];
8443355c360SAlistair Francis                 break;
8453355c360SAlistair Francis             case 1:
8463355c360SAlistair Francis                 res = gic_id_gicv1[(offset - 0xfd0) >> 2];
8473355c360SAlistair Francis                 break;
8483355c360SAlistair Francis             case 2:
8493355c360SAlistair Francis                 res = gic_id_gicv2[(offset - 0xfd0) >> 2];
8503355c360SAlistair Francis                 break;
8513355c360SAlistair Francis             default:
8523355c360SAlistair Francis                 res = 0;
853e69954b9Spbrook             }
854e69954b9Spbrook         }
8553355c360SAlistair Francis     } else {
8563355c360SAlistair Francis         g_assert_not_reached();
8573355c360SAlistair Francis     }
858e69954b9Spbrook     return res;
859e69954b9Spbrook bad_reg:
8608c8dc39fSPeter Maydell     qemu_log_mask(LOG_GUEST_ERROR,
8618c8dc39fSPeter Maydell                   "gic_dist_readb: Bad offset %x\n", (int)offset);
862e69954b9Spbrook     return 0;
863e69954b9Spbrook }
864e69954b9Spbrook 
865a9d85353SPeter Maydell static MemTxResult gic_dist_read(void *opaque, hwaddr offset, uint64_t *data,
866a9d85353SPeter Maydell                                  unsigned size, MemTxAttrs attrs)
867e69954b9Spbrook {
868a9d85353SPeter Maydell     switch (size) {
869a9d85353SPeter Maydell     case 1:
870a9d85353SPeter Maydell         *data = gic_dist_readb(opaque, offset, attrs);
871a9d85353SPeter Maydell         return MEMTX_OK;
872a9d85353SPeter Maydell     case 2:
873a9d85353SPeter Maydell         *data = gic_dist_readb(opaque, offset, attrs);
874a9d85353SPeter Maydell         *data |= gic_dist_readb(opaque, offset + 1, attrs) << 8;
875a9d85353SPeter Maydell         return MEMTX_OK;
876a9d85353SPeter Maydell     case 4:
877a9d85353SPeter Maydell         *data = gic_dist_readb(opaque, offset, attrs);
878a9d85353SPeter Maydell         *data |= gic_dist_readb(opaque, offset + 1, attrs) << 8;
879a9d85353SPeter Maydell         *data |= gic_dist_readb(opaque, offset + 2, attrs) << 16;
880a9d85353SPeter Maydell         *data |= gic_dist_readb(opaque, offset + 3, attrs) << 24;
881a9d85353SPeter Maydell         return MEMTX_OK;
882a9d85353SPeter Maydell     default:
883a9d85353SPeter Maydell         return MEMTX_ERROR;
884e69954b9Spbrook     }
885e69954b9Spbrook }
886e69954b9Spbrook 
887a8170e5eSAvi Kivity static void gic_dist_writeb(void *opaque, hwaddr offset,
888a9d85353SPeter Maydell                             uint32_t value, MemTxAttrs attrs)
889e69954b9Spbrook {
890fae15286SPeter Maydell     GICState *s = (GICState *)opaque;
891e69954b9Spbrook     int irq;
892e69954b9Spbrook     int i;
8939ee6e8bbSpbrook     int cpu;
894e69954b9Spbrook 
895926c4affSPeter Maydell     cpu = gic_get_current_cpu(s);
896e69954b9Spbrook     if (offset < 0x100) {
897e69954b9Spbrook         if (offset == 0) {
898679aa175SFabian Aggeler             if (s->security_extn && !attrs.secure) {
899679aa175SFabian Aggeler                 /* NS version is just an alias of the S version's bit 1 */
900679aa175SFabian Aggeler                 s->ctlr = deposit32(s->ctlr, 1, 1, value);
901679aa175SFabian Aggeler             } else if (gic_has_groups(s)) {
902679aa175SFabian Aggeler                 s->ctlr = value & (GICD_CTLR_EN_GRP0 | GICD_CTLR_EN_GRP1);
903679aa175SFabian Aggeler             } else {
904679aa175SFabian Aggeler                 s->ctlr = value & GICD_CTLR_EN_GRP0;
905679aa175SFabian Aggeler             }
906679aa175SFabian Aggeler             DPRINTF("Distributor: Group0 %sabled; Group 1 %sabled\n",
907679aa175SFabian Aggeler                     s->ctlr & GICD_CTLR_EN_GRP0 ? "En" : "Dis",
908679aa175SFabian Aggeler                     s->ctlr & GICD_CTLR_EN_GRP1 ? "En" : "Dis");
909e69954b9Spbrook         } else if (offset < 4) {
910e69954b9Spbrook             /* ignored.  */
911b79f2265SRob Herring         } else if (offset >= 0x80) {
912c27a5ba9SFabian Aggeler             /* Interrupt Group Registers: RAZ/WI for NS access to secure
913c27a5ba9SFabian Aggeler              * GIC, or for GICs without groups.
914c27a5ba9SFabian Aggeler              */
915c27a5ba9SFabian Aggeler             if (!(s->security_extn && !attrs.secure) && gic_has_groups(s)) {
916c27a5ba9SFabian Aggeler                 /* Every byte offset holds 8 group status bits */
917c27a5ba9SFabian Aggeler                 irq = (offset - 0x80) * 8 + GIC_BASE_IRQ;
918c27a5ba9SFabian Aggeler                 if (irq >= s->num_irq) {
919c27a5ba9SFabian Aggeler                     goto bad_reg;
920c27a5ba9SFabian Aggeler                 }
921c27a5ba9SFabian Aggeler                 for (i = 0; i < 8; i++) {
922c27a5ba9SFabian Aggeler                     /* Group bits are banked for private interrupts */
923c27a5ba9SFabian Aggeler                     int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
924c27a5ba9SFabian Aggeler                     if (value & (1 << i)) {
925c27a5ba9SFabian Aggeler                         /* Group1 (Non-secure) */
92667ce697aSLuc Michel                         GIC_DIST_SET_GROUP(irq + i, cm);
927c27a5ba9SFabian Aggeler                     } else {
928c27a5ba9SFabian Aggeler                         /* Group0 (Secure) */
92967ce697aSLuc Michel                         GIC_DIST_CLEAR_GROUP(irq + i, cm);
930c27a5ba9SFabian Aggeler                     }
931c27a5ba9SFabian Aggeler                 }
932c27a5ba9SFabian Aggeler             }
933e69954b9Spbrook         } else {
934e69954b9Spbrook             goto bad_reg;
935e69954b9Spbrook         }
936e69954b9Spbrook     } else if (offset < 0x180) {
937e69954b9Spbrook         /* Interrupt Set Enable.  */
9389ee6e8bbSpbrook         irq = (offset - 0x100) * 8 + GIC_BASE_IRQ;
939a32134aaSMark Langsdorf         if (irq >= s->num_irq)
940e69954b9Spbrook             goto bad_reg;
94141ab7b55SChristoffer Dall         if (irq < GIC_NR_SGIS) {
9429ee6e8bbSpbrook             value = 0xff;
94341ab7b55SChristoffer Dall         }
94441ab7b55SChristoffer Dall 
945e69954b9Spbrook         for (i = 0; i < 8; i++) {
946e69954b9Spbrook             if (value & (1 << i)) {
947f47b48fbSDaniel Sangorrin                 int mask =
94867ce697aSLuc Michel                     (irq < GIC_INTERNAL) ? (1 << cpu)
94967ce697aSLuc Michel                                          : GIC_DIST_TARGET(irq + i);
95069253800SRusty Russell                 int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
95141bf234dSRabin Vincent 
952fea8a08eSJens Wiklander                 if (s->security_extn && !attrs.secure &&
95367ce697aSLuc Michel                     !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) {
954fea8a08eSJens Wiklander                     continue; /* Ignore Non-secure access of Group0 IRQ */
955fea8a08eSJens Wiklander                 }
956fea8a08eSJens Wiklander 
95767ce697aSLuc Michel                 if (!GIC_DIST_TEST_ENABLED(irq + i, cm)) {
958e69954b9Spbrook                     DPRINTF("Enabled IRQ %d\n", irq + i);
9592531088fSHollis Blanchard                     trace_gic_enable_irq(irq + i);
96041bf234dSRabin Vincent                 }
96167ce697aSLuc Michel                 GIC_DIST_SET_ENABLED(irq + i, cm);
962e69954b9Spbrook                 /* If a raised level triggered IRQ enabled then mark
963e69954b9Spbrook                    is as pending.  */
96467ce697aSLuc Michel                 if (GIC_DIST_TEST_LEVEL(irq + i, mask)
96567ce697aSLuc Michel                         && !GIC_DIST_TEST_EDGE_TRIGGER(irq + i)) {
9669ee6e8bbSpbrook                     DPRINTF("Set %d pending mask %x\n", irq + i, mask);
96767ce697aSLuc Michel                     GIC_DIST_SET_PENDING(irq + i, mask);
9689ee6e8bbSpbrook                 }
969e69954b9Spbrook             }
970e69954b9Spbrook         }
971e69954b9Spbrook     } else if (offset < 0x200) {
972e69954b9Spbrook         /* Interrupt Clear Enable.  */
9739ee6e8bbSpbrook         irq = (offset - 0x180) * 8 + GIC_BASE_IRQ;
974a32134aaSMark Langsdorf         if (irq >= s->num_irq)
975e69954b9Spbrook             goto bad_reg;
97641ab7b55SChristoffer Dall         if (irq < GIC_NR_SGIS) {
9779ee6e8bbSpbrook             value = 0;
97841ab7b55SChristoffer Dall         }
97941ab7b55SChristoffer Dall 
980e69954b9Spbrook         for (i = 0; i < 8; i++) {
981e69954b9Spbrook             if (value & (1 << i)) {
98269253800SRusty Russell                 int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
98341bf234dSRabin Vincent 
984fea8a08eSJens Wiklander                 if (s->security_extn && !attrs.secure &&
98567ce697aSLuc Michel                     !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) {
986fea8a08eSJens Wiklander                     continue; /* Ignore Non-secure access of Group0 IRQ */
987fea8a08eSJens Wiklander                 }
988fea8a08eSJens Wiklander 
98967ce697aSLuc Michel                 if (GIC_DIST_TEST_ENABLED(irq + i, cm)) {
990e69954b9Spbrook                     DPRINTF("Disabled IRQ %d\n", irq + i);
9912531088fSHollis Blanchard                     trace_gic_disable_irq(irq + i);
99241bf234dSRabin Vincent                 }
99367ce697aSLuc Michel                 GIC_DIST_CLEAR_ENABLED(irq + i, cm);
994e69954b9Spbrook             }
995e69954b9Spbrook         }
996e69954b9Spbrook     } else if (offset < 0x280) {
997e69954b9Spbrook         /* Interrupt Set Pending.  */
9989ee6e8bbSpbrook         irq = (offset - 0x200) * 8 + GIC_BASE_IRQ;
999a32134aaSMark Langsdorf         if (irq >= s->num_irq)
1000e69954b9Spbrook             goto bad_reg;
100141ab7b55SChristoffer Dall         if (irq < GIC_NR_SGIS) {
10025b0adce1SChristoffer Dall             value = 0;
100341ab7b55SChristoffer Dall         }
10049ee6e8bbSpbrook 
1005e69954b9Spbrook         for (i = 0; i < 8; i++) {
1006e69954b9Spbrook             if (value & (1 << i)) {
1007fea8a08eSJens Wiklander                 if (s->security_extn && !attrs.secure &&
100867ce697aSLuc Michel                     !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) {
1009fea8a08eSJens Wiklander                     continue; /* Ignore Non-secure access of Group0 IRQ */
1010fea8a08eSJens Wiklander                 }
1011fea8a08eSJens Wiklander 
101267ce697aSLuc Michel                 GIC_DIST_SET_PENDING(irq + i, GIC_DIST_TARGET(irq + i));
1013e69954b9Spbrook             }
1014e69954b9Spbrook         }
1015e69954b9Spbrook     } else if (offset < 0x300) {
1016e69954b9Spbrook         /* Interrupt Clear Pending.  */
10179ee6e8bbSpbrook         irq = (offset - 0x280) * 8 + GIC_BASE_IRQ;
1018a32134aaSMark Langsdorf         if (irq >= s->num_irq)
1019e69954b9Spbrook             goto bad_reg;
10205b0adce1SChristoffer Dall         if (irq < GIC_NR_SGIS) {
10215b0adce1SChristoffer Dall             value = 0;
10225b0adce1SChristoffer Dall         }
10235b0adce1SChristoffer Dall 
1024e69954b9Spbrook         for (i = 0; i < 8; i++) {
1025fea8a08eSJens Wiklander             if (s->security_extn && !attrs.secure &&
102667ce697aSLuc Michel                 !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) {
1027fea8a08eSJens Wiklander                 continue; /* Ignore Non-secure access of Group0 IRQ */
1028fea8a08eSJens Wiklander             }
1029fea8a08eSJens Wiklander 
10309ee6e8bbSpbrook             /* ??? This currently clears the pending bit for all CPUs, even
10319ee6e8bbSpbrook                for per-CPU interrupts.  It's unclear whether this is the
10329ee6e8bbSpbrook                corect behavior.  */
1033e69954b9Spbrook             if (value & (1 << i)) {
103467ce697aSLuc Michel                 GIC_DIST_CLEAR_PENDING(irq + i, ALL_CPU_MASK);
1035e69954b9Spbrook             }
1036e69954b9Spbrook         }
10373bb0b038SLuc Michel     } else if (offset < 0x380) {
10383bb0b038SLuc Michel         /* Interrupt Set Active.  */
10393bb0b038SLuc Michel         if (s->revision != 2) {
1040e69954b9Spbrook             goto bad_reg;
10413bb0b038SLuc Michel         }
10423bb0b038SLuc Michel 
10433bb0b038SLuc Michel         irq = (offset - 0x300) * 8 + GIC_BASE_IRQ;
10443bb0b038SLuc Michel         if (irq >= s->num_irq) {
10453bb0b038SLuc Michel             goto bad_reg;
10463bb0b038SLuc Michel         }
10473bb0b038SLuc Michel 
10483bb0b038SLuc Michel         /* This register is banked per-cpu for PPIs */
10493bb0b038SLuc Michel         int cm = irq < GIC_INTERNAL ? (1 << cpu) : ALL_CPU_MASK;
10503bb0b038SLuc Michel 
10513bb0b038SLuc Michel         for (i = 0; i < 8; i++) {
10523bb0b038SLuc Michel             if (s->security_extn && !attrs.secure &&
10533bb0b038SLuc Michel                 !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) {
10543bb0b038SLuc Michel                 continue; /* Ignore Non-secure access of Group0 IRQ */
10553bb0b038SLuc Michel             }
10563bb0b038SLuc Michel 
10573bb0b038SLuc Michel             if (value & (1 << i)) {
10583bb0b038SLuc Michel                 GIC_DIST_SET_ACTIVE(irq + i, cm);
10593bb0b038SLuc Michel             }
10603bb0b038SLuc Michel         }
10613bb0b038SLuc Michel     } else if (offset < 0x400) {
10623bb0b038SLuc Michel         /* Interrupt Clear Active.  */
10633bb0b038SLuc Michel         if (s->revision != 2) {
10643bb0b038SLuc Michel             goto bad_reg;
10653bb0b038SLuc Michel         }
10663bb0b038SLuc Michel 
10673bb0b038SLuc Michel         irq = (offset - 0x380) * 8 + GIC_BASE_IRQ;
10683bb0b038SLuc Michel         if (irq >= s->num_irq) {
10693bb0b038SLuc Michel             goto bad_reg;
10703bb0b038SLuc Michel         }
10713bb0b038SLuc Michel 
10723bb0b038SLuc Michel         /* This register is banked per-cpu for PPIs */
10733bb0b038SLuc Michel         int cm = irq < GIC_INTERNAL ? (1 << cpu) : ALL_CPU_MASK;
10743bb0b038SLuc Michel 
10753bb0b038SLuc Michel         for (i = 0; i < 8; i++) {
10763bb0b038SLuc Michel             if (s->security_extn && !attrs.secure &&
10773bb0b038SLuc Michel                 !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) {
10783bb0b038SLuc Michel                 continue; /* Ignore Non-secure access of Group0 IRQ */
10793bb0b038SLuc Michel             }
10803bb0b038SLuc Michel 
10813bb0b038SLuc Michel             if (value & (1 << i)) {
10823bb0b038SLuc Michel                 GIC_DIST_CLEAR_ACTIVE(irq + i, cm);
10833bb0b038SLuc Michel             }
10843bb0b038SLuc Michel         }
1085e69954b9Spbrook     } else if (offset < 0x800) {
1086e69954b9Spbrook         /* Interrupt Priority.  */
10879ee6e8bbSpbrook         irq = (offset - 0x400) + GIC_BASE_IRQ;
1088a32134aaSMark Langsdorf         if (irq >= s->num_irq)
1089e69954b9Spbrook             goto bad_reg;
109067ce697aSLuc Michel         gic_dist_set_priority(s, cpu, irq, value, attrs);
1091e69954b9Spbrook     } else if (offset < 0xc00) {
10926b9680bbSPeter Maydell         /* Interrupt CPU Target. RAZ/WI on uniprocessor GICs, with the
10936b9680bbSPeter Maydell          * annoying exception of the 11MPCore's GIC.
10946b9680bbSPeter Maydell          */
10956b9680bbSPeter Maydell         if (s->num_cpu != 1 || s->revision == REV_11MPCORE) {
10969ee6e8bbSpbrook             irq = (offset - 0x800) + GIC_BASE_IRQ;
10976b9680bbSPeter Maydell             if (irq >= s->num_irq) {
1098e69954b9Spbrook                 goto bad_reg;
10996b9680bbSPeter Maydell             }
11007995206dSPeter Maydell             if (irq < 29 && s->revision == REV_11MPCORE) {
11019ee6e8bbSpbrook                 value = 0;
11026b9680bbSPeter Maydell             } else if (irq < GIC_INTERNAL) {
11039ee6e8bbSpbrook                 value = ALL_CPU_MASK;
11046b9680bbSPeter Maydell             }
11059ee6e8bbSpbrook             s->irq_target[irq] = value & ALL_CPU_MASK;
11066b9680bbSPeter Maydell         }
1107e69954b9Spbrook     } else if (offset < 0xf00) {
1108e69954b9Spbrook         /* Interrupt Configuration.  */
11099ee6e8bbSpbrook         irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ;
1110a32134aaSMark Langsdorf         if (irq >= s->num_irq)
1111e69954b9Spbrook             goto bad_reg;
1112de7a900fSAdam Lackorzynski         if (irq < GIC_NR_SGIS)
11139ee6e8bbSpbrook             value |= 0xaa;
1114e69954b9Spbrook         for (i = 0; i < 4; i++) {
1115fea8a08eSJens Wiklander             if (s->security_extn && !attrs.secure &&
111667ce697aSLuc Michel                 !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) {
1117fea8a08eSJens Wiklander                 continue; /* Ignore Non-secure access of Group0 IRQ */
1118fea8a08eSJens Wiklander             }
1119fea8a08eSJens Wiklander 
11207c14b3acSMichael Davidsaver             if (s->revision == REV_11MPCORE) {
1121e69954b9Spbrook                 if (value & (1 << (i * 2))) {
112267ce697aSLuc Michel                     GIC_DIST_SET_MODEL(irq + i);
1123e69954b9Spbrook                 } else {
112467ce697aSLuc Michel                     GIC_DIST_CLEAR_MODEL(irq + i);
1125e69954b9Spbrook                 }
112624b790dfSAdam Lackorzynski             }
1127e69954b9Spbrook             if (value & (2 << (i * 2))) {
112867ce697aSLuc Michel                 GIC_DIST_SET_EDGE_TRIGGER(irq + i);
1129e69954b9Spbrook             } else {
113067ce697aSLuc Michel                 GIC_DIST_CLEAR_EDGE_TRIGGER(irq + i);
1131e69954b9Spbrook             }
1132e69954b9Spbrook         }
113340d22500SChristoffer Dall     } else if (offset < 0xf10) {
11349ee6e8bbSpbrook         /* 0xf00 is only handled for 32-bit writes.  */
1135e69954b9Spbrook         goto bad_reg;
113640d22500SChristoffer Dall     } else if (offset < 0xf20) {
113740d22500SChristoffer Dall         /* GICD_CPENDSGIRn */
11387c14b3acSMichael Davidsaver         if (s->revision == REV_11MPCORE) {
113940d22500SChristoffer Dall             goto bad_reg;
114040d22500SChristoffer Dall         }
114140d22500SChristoffer Dall         irq = (offset - 0xf10);
114240d22500SChristoffer Dall 
1143fea8a08eSJens Wiklander         if (!s->security_extn || attrs.secure ||
114467ce697aSLuc Michel             GIC_DIST_TEST_GROUP(irq, 1 << cpu)) {
114540d22500SChristoffer Dall             s->sgi_pending[irq][cpu] &= ~value;
114640d22500SChristoffer Dall             if (s->sgi_pending[irq][cpu] == 0) {
114767ce697aSLuc Michel                 GIC_DIST_CLEAR_PENDING(irq, 1 << cpu);
114840d22500SChristoffer Dall             }
1149fea8a08eSJens Wiklander         }
115040d22500SChristoffer Dall     } else if (offset < 0xf30) {
115140d22500SChristoffer Dall         /* GICD_SPENDSGIRn */
11527c14b3acSMichael Davidsaver         if (s->revision == REV_11MPCORE) {
115340d22500SChristoffer Dall             goto bad_reg;
115440d22500SChristoffer Dall         }
115540d22500SChristoffer Dall         irq = (offset - 0xf20);
115640d22500SChristoffer Dall 
1157fea8a08eSJens Wiklander         if (!s->security_extn || attrs.secure ||
115867ce697aSLuc Michel             GIC_DIST_TEST_GROUP(irq, 1 << cpu)) {
115967ce697aSLuc Michel             GIC_DIST_SET_PENDING(irq, 1 << cpu);
116040d22500SChristoffer Dall             s->sgi_pending[irq][cpu] |= value;
1161fea8a08eSJens Wiklander         }
116240d22500SChristoffer Dall     } else {
116340d22500SChristoffer Dall         goto bad_reg;
1164e69954b9Spbrook     }
1165e69954b9Spbrook     gic_update(s);
1166e69954b9Spbrook     return;
1167e69954b9Spbrook bad_reg:
11688c8dc39fSPeter Maydell     qemu_log_mask(LOG_GUEST_ERROR,
11698c8dc39fSPeter Maydell                   "gic_dist_writeb: Bad offset %x\n", (int)offset);
1170e69954b9Spbrook }
1171e69954b9Spbrook 
1172a8170e5eSAvi Kivity static void gic_dist_writew(void *opaque, hwaddr offset,
1173a9d85353SPeter Maydell                             uint32_t value, MemTxAttrs attrs)
1174e69954b9Spbrook {
1175a9d85353SPeter Maydell     gic_dist_writeb(opaque, offset, value & 0xff, attrs);
1176a9d85353SPeter Maydell     gic_dist_writeb(opaque, offset + 1, value >> 8, attrs);
1177e69954b9Spbrook }
1178e69954b9Spbrook 
1179a8170e5eSAvi Kivity static void gic_dist_writel(void *opaque, hwaddr offset,
1180a9d85353SPeter Maydell                             uint32_t value, MemTxAttrs attrs)
1181e69954b9Spbrook {
1182fae15286SPeter Maydell     GICState *s = (GICState *)opaque;
11838da3ff18Spbrook     if (offset == 0xf00) {
11849ee6e8bbSpbrook         int cpu;
11859ee6e8bbSpbrook         int irq;
11869ee6e8bbSpbrook         int mask;
118740d22500SChristoffer Dall         int target_cpu;
11889ee6e8bbSpbrook 
1189926c4affSPeter Maydell         cpu = gic_get_current_cpu(s);
11909ee6e8bbSpbrook         irq = value & 0x3ff;
11919ee6e8bbSpbrook         switch ((value >> 24) & 3) {
11929ee6e8bbSpbrook         case 0:
11939ee6e8bbSpbrook             mask = (value >> 16) & ALL_CPU_MASK;
11949ee6e8bbSpbrook             break;
11959ee6e8bbSpbrook         case 1:
1196fa250144SAdam Lackorzynski             mask = ALL_CPU_MASK ^ (1 << cpu);
11979ee6e8bbSpbrook             break;
11989ee6e8bbSpbrook         case 2:
1199fa250144SAdam Lackorzynski             mask = 1 << cpu;
12009ee6e8bbSpbrook             break;
12019ee6e8bbSpbrook         default:
12029ee6e8bbSpbrook             DPRINTF("Bad Soft Int target filter\n");
12039ee6e8bbSpbrook             mask = ALL_CPU_MASK;
12049ee6e8bbSpbrook             break;
12059ee6e8bbSpbrook         }
120667ce697aSLuc Michel         GIC_DIST_SET_PENDING(irq, mask);
120740d22500SChristoffer Dall         target_cpu = ctz32(mask);
120840d22500SChristoffer Dall         while (target_cpu < GIC_NCPU) {
120940d22500SChristoffer Dall             s->sgi_pending[irq][target_cpu] |= (1 << cpu);
121040d22500SChristoffer Dall             mask &= ~(1 << target_cpu);
121140d22500SChristoffer Dall             target_cpu = ctz32(mask);
121240d22500SChristoffer Dall         }
12139ee6e8bbSpbrook         gic_update(s);
12149ee6e8bbSpbrook         return;
12159ee6e8bbSpbrook     }
1216a9d85353SPeter Maydell     gic_dist_writew(opaque, offset, value & 0xffff, attrs);
1217a9d85353SPeter Maydell     gic_dist_writew(opaque, offset + 2, value >> 16, attrs);
1218a9d85353SPeter Maydell }
1219a9d85353SPeter Maydell 
1220a9d85353SPeter Maydell static MemTxResult gic_dist_write(void *opaque, hwaddr offset, uint64_t data,
1221a9d85353SPeter Maydell                                   unsigned size, MemTxAttrs attrs)
1222a9d85353SPeter Maydell {
1223a9d85353SPeter Maydell     switch (size) {
1224a9d85353SPeter Maydell     case 1:
1225a9d85353SPeter Maydell         gic_dist_writeb(opaque, offset, data, attrs);
1226a9d85353SPeter Maydell         return MEMTX_OK;
1227a9d85353SPeter Maydell     case 2:
1228a9d85353SPeter Maydell         gic_dist_writew(opaque, offset, data, attrs);
1229a9d85353SPeter Maydell         return MEMTX_OK;
1230a9d85353SPeter Maydell     case 4:
1231a9d85353SPeter Maydell         gic_dist_writel(opaque, offset, data, attrs);
1232a9d85353SPeter Maydell         return MEMTX_OK;
1233a9d85353SPeter Maydell     default:
1234a9d85353SPeter Maydell         return MEMTX_ERROR;
1235a9d85353SPeter Maydell     }
1236e69954b9Spbrook }
1237e69954b9Spbrook 
123851fd06e0SPeter Maydell static inline uint32_t gic_apr_ns_view(GICState *s, int cpu, int regno)
123951fd06e0SPeter Maydell {
124051fd06e0SPeter Maydell     /* Return the Nonsecure view of GICC_APR<regno>. This is the
124151fd06e0SPeter Maydell      * second half of GICC_NSAPR.
124251fd06e0SPeter Maydell      */
124351fd06e0SPeter Maydell     switch (GIC_MIN_BPR) {
124451fd06e0SPeter Maydell     case 0:
124551fd06e0SPeter Maydell         if (regno < 2) {
124651fd06e0SPeter Maydell             return s->nsapr[regno + 2][cpu];
124751fd06e0SPeter Maydell         }
124851fd06e0SPeter Maydell         break;
124951fd06e0SPeter Maydell     case 1:
125051fd06e0SPeter Maydell         if (regno == 0) {
125151fd06e0SPeter Maydell             return s->nsapr[regno + 1][cpu];
125251fd06e0SPeter Maydell         }
125351fd06e0SPeter Maydell         break;
125451fd06e0SPeter Maydell     case 2:
125551fd06e0SPeter Maydell         if (regno == 0) {
125651fd06e0SPeter Maydell             return extract32(s->nsapr[0][cpu], 16, 16);
125751fd06e0SPeter Maydell         }
125851fd06e0SPeter Maydell         break;
125951fd06e0SPeter Maydell     case 3:
126051fd06e0SPeter Maydell         if (regno == 0) {
126151fd06e0SPeter Maydell             return extract32(s->nsapr[0][cpu], 8, 8);
126251fd06e0SPeter Maydell         }
126351fd06e0SPeter Maydell         break;
126451fd06e0SPeter Maydell     default:
126551fd06e0SPeter Maydell         g_assert_not_reached();
126651fd06e0SPeter Maydell     }
126751fd06e0SPeter Maydell     return 0;
126851fd06e0SPeter Maydell }
126951fd06e0SPeter Maydell 
127051fd06e0SPeter Maydell static inline void gic_apr_write_ns_view(GICState *s, int cpu, int regno,
127151fd06e0SPeter Maydell                                          uint32_t value)
127251fd06e0SPeter Maydell {
127351fd06e0SPeter Maydell     /* Write the Nonsecure view of GICC_APR<regno>. */
127451fd06e0SPeter Maydell     switch (GIC_MIN_BPR) {
127551fd06e0SPeter Maydell     case 0:
127651fd06e0SPeter Maydell         if (regno < 2) {
127751fd06e0SPeter Maydell             s->nsapr[regno + 2][cpu] = value;
127851fd06e0SPeter Maydell         }
127951fd06e0SPeter Maydell         break;
128051fd06e0SPeter Maydell     case 1:
128151fd06e0SPeter Maydell         if (regno == 0) {
128251fd06e0SPeter Maydell             s->nsapr[regno + 1][cpu] = value;
128351fd06e0SPeter Maydell         }
128451fd06e0SPeter Maydell         break;
128551fd06e0SPeter Maydell     case 2:
128651fd06e0SPeter Maydell         if (regno == 0) {
128751fd06e0SPeter Maydell             s->nsapr[0][cpu] = deposit32(s->nsapr[0][cpu], 16, 16, value);
128851fd06e0SPeter Maydell         }
128951fd06e0SPeter Maydell         break;
129051fd06e0SPeter Maydell     case 3:
129151fd06e0SPeter Maydell         if (regno == 0) {
129251fd06e0SPeter Maydell             s->nsapr[0][cpu] = deposit32(s->nsapr[0][cpu], 8, 8, value);
129351fd06e0SPeter Maydell         }
129451fd06e0SPeter Maydell         break;
129551fd06e0SPeter Maydell     default:
129651fd06e0SPeter Maydell         g_assert_not_reached();
129751fd06e0SPeter Maydell     }
129851fd06e0SPeter Maydell }
129951fd06e0SPeter Maydell 
1300a9d85353SPeter Maydell static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset,
1301a9d85353SPeter Maydell                                 uint64_t *data, MemTxAttrs attrs)
1302e69954b9Spbrook {
1303e69954b9Spbrook     switch (offset) {
1304e69954b9Spbrook     case 0x00: /* Control */
130532951860SFabian Aggeler         *data = gic_get_cpu_control(s, cpu, attrs);
1306a9d85353SPeter Maydell         break;
1307e69954b9Spbrook     case 0x04: /* Priority mask */
130881508470SFabian Aggeler         *data = gic_get_priority_mask(s, cpu, attrs);
1309a9d85353SPeter Maydell         break;
1310e69954b9Spbrook     case 0x08: /* Binary Point */
13113dd0471bSLuc Michel         if (gic_cpu_ns_access(s, cpu, attrs)) {
1312421a3c22SLuc MICHEL             if (s->cpu_ctlr[cpu] & GICC_CTLR_CBPR) {
1313421a3c22SLuc MICHEL                 /* NS view of BPR when CBPR is 1 */
1314421a3c22SLuc MICHEL                 *data = MIN(s->bpr[cpu] + 1, 7);
1315421a3c22SLuc MICHEL             } else {
1316822e9cc3SFabian Aggeler                 /* BPR is banked. Non-secure copy stored in ABPR. */
1317822e9cc3SFabian Aggeler                 *data = s->abpr[cpu];
1318421a3c22SLuc MICHEL             }
1319822e9cc3SFabian Aggeler         } else {
1320a9d85353SPeter Maydell             *data = s->bpr[cpu];
1321822e9cc3SFabian Aggeler         }
1322a9d85353SPeter Maydell         break;
1323e69954b9Spbrook     case 0x0c: /* Acknowledge */
1324c5619bf9SFabian Aggeler         *data = gic_acknowledge_irq(s, cpu, attrs);
1325a9d85353SPeter Maydell         break;
132666a0a2cbSDong Xu Wang     case 0x14: /* Running Priority */
132708efa9f2SFabian Aggeler         *data = gic_get_running_priority(s, cpu, attrs);
1328a9d85353SPeter Maydell         break;
1329e69954b9Spbrook     case 0x18: /* Highest Pending Interrupt */
13307c0fa108SFabian Aggeler         *data = gic_get_current_pending_irq(s, cpu, attrs);
1331a9d85353SPeter Maydell         break;
1332aa7d461aSChristoffer Dall     case 0x1c: /* Aliased Binary Point */
1333822e9cc3SFabian Aggeler         /* GIC v2, no security: ABPR
1334822e9cc3SFabian Aggeler          * GIC v1, no security: not implemented (RAZ/WI)
1335822e9cc3SFabian Aggeler          * With security extensions, secure access: ABPR (alias of NS BPR)
1336822e9cc3SFabian Aggeler          * With security extensions, nonsecure access: RAZ/WI
1337822e9cc3SFabian Aggeler          */
13383dd0471bSLuc Michel         if (!gic_has_groups(s) || (gic_cpu_ns_access(s, cpu, attrs))) {
1339822e9cc3SFabian Aggeler             *data = 0;
1340822e9cc3SFabian Aggeler         } else {
1341a9d85353SPeter Maydell             *data = s->abpr[cpu];
1342822e9cc3SFabian Aggeler         }
1343a9d85353SPeter Maydell         break;
1344a9d477c4SChristoffer Dall     case 0xd0: case 0xd4: case 0xd8: case 0xdc:
134551fd06e0SPeter Maydell     {
134651fd06e0SPeter Maydell         int regno = (offset - 0xd0) / 4;
134751fd06e0SPeter Maydell 
134851fd06e0SPeter Maydell         if (regno >= GIC_NR_APRS || s->revision != 2) {
134951fd06e0SPeter Maydell             *data = 0;
13503dd0471bSLuc Michel         } else if (gic_cpu_ns_access(s, cpu, attrs)) {
135151fd06e0SPeter Maydell             /* NS view of GICC_APR<n> is the top half of GIC_NSAPR<n> */
135251fd06e0SPeter Maydell             *data = gic_apr_ns_view(s, regno, cpu);
135351fd06e0SPeter Maydell         } else {
135451fd06e0SPeter Maydell             *data = s->apr[regno][cpu];
135551fd06e0SPeter Maydell         }
1356a9d85353SPeter Maydell         break;
135751fd06e0SPeter Maydell     }
135851fd06e0SPeter Maydell     case 0xe0: case 0xe4: case 0xe8: case 0xec:
135951fd06e0SPeter Maydell     {
136051fd06e0SPeter Maydell         int regno = (offset - 0xe0) / 4;
136151fd06e0SPeter Maydell 
136251fd06e0SPeter Maydell         if (regno >= GIC_NR_APRS || s->revision != 2 || !gic_has_groups(s) ||
13633dd0471bSLuc Michel             gic_cpu_ns_access(s, cpu, attrs)) {
136451fd06e0SPeter Maydell             *data = 0;
136551fd06e0SPeter Maydell         } else {
136651fd06e0SPeter Maydell             *data = s->nsapr[regno][cpu];
136751fd06e0SPeter Maydell         }
136851fd06e0SPeter Maydell         break;
136951fd06e0SPeter Maydell     }
1370e69954b9Spbrook     default:
13718c8dc39fSPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
13728c8dc39fSPeter Maydell                       "gic_cpu_read: Bad offset %x\n", (int)offset);
13730cf09852SPeter Maydell         *data = 0;
13740cf09852SPeter Maydell         break;
1375e69954b9Spbrook     }
1376a9d85353SPeter Maydell     return MEMTX_OK;
1377e69954b9Spbrook }
1378e69954b9Spbrook 
1379a9d85353SPeter Maydell static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset,
1380a9d85353SPeter Maydell                                  uint32_t value, MemTxAttrs attrs)
1381e69954b9Spbrook {
1382e69954b9Spbrook     switch (offset) {
1383e69954b9Spbrook     case 0x00: /* Control */
138432951860SFabian Aggeler         gic_set_cpu_control(s, cpu, value, attrs);
1385e69954b9Spbrook         break;
1386e69954b9Spbrook     case 0x04: /* Priority mask */
138781508470SFabian Aggeler         gic_set_priority_mask(s, cpu, value, attrs);
1388e69954b9Spbrook         break;
1389e69954b9Spbrook     case 0x08: /* Binary Point */
13903dd0471bSLuc Michel         if (gic_cpu_ns_access(s, cpu, attrs)) {
1391421a3c22SLuc MICHEL             if (s->cpu_ctlr[cpu] & GICC_CTLR_CBPR) {
1392421a3c22SLuc MICHEL                 /* WI when CBPR is 1 */
1393421a3c22SLuc MICHEL                 return MEMTX_OK;
1394421a3c22SLuc MICHEL             } else {
1395822e9cc3SFabian Aggeler                 s->abpr[cpu] = MAX(value & 0x7, GIC_MIN_ABPR);
1396421a3c22SLuc MICHEL             }
1397822e9cc3SFabian Aggeler         } else {
1398822e9cc3SFabian Aggeler             s->bpr[cpu] = MAX(value & 0x7, GIC_MIN_BPR);
1399822e9cc3SFabian Aggeler         }
1400e69954b9Spbrook         break;
1401e69954b9Spbrook     case 0x10: /* End Of Interrupt */
1402f9c6a7f1SFabian Aggeler         gic_complete_irq(s, cpu, value & 0x3ff, attrs);
1403a9d85353SPeter Maydell         return MEMTX_OK;
1404aa7d461aSChristoffer Dall     case 0x1c: /* Aliased Binary Point */
14053dd0471bSLuc Michel         if (!gic_has_groups(s) || (gic_cpu_ns_access(s, cpu, attrs))) {
1406822e9cc3SFabian Aggeler             /* unimplemented, or NS access: RAZ/WI */
1407822e9cc3SFabian Aggeler             return MEMTX_OK;
1408822e9cc3SFabian Aggeler         } else {
1409822e9cc3SFabian Aggeler             s->abpr[cpu] = MAX(value & 0x7, GIC_MIN_ABPR);
1410aa7d461aSChristoffer Dall         }
1411aa7d461aSChristoffer Dall         break;
1412a9d477c4SChristoffer Dall     case 0xd0: case 0xd4: case 0xd8: case 0xdc:
141351fd06e0SPeter Maydell     {
141451fd06e0SPeter Maydell         int regno = (offset - 0xd0) / 4;
141551fd06e0SPeter Maydell 
141651fd06e0SPeter Maydell         if (regno >= GIC_NR_APRS || s->revision != 2) {
141751fd06e0SPeter Maydell             return MEMTX_OK;
141851fd06e0SPeter Maydell         }
14193dd0471bSLuc Michel         if (gic_cpu_ns_access(s, cpu, attrs)) {
142051fd06e0SPeter Maydell             /* NS view of GICC_APR<n> is the top half of GIC_NSAPR<n> */
142151fd06e0SPeter Maydell             gic_apr_write_ns_view(s, regno, cpu, value);
142251fd06e0SPeter Maydell         } else {
142351fd06e0SPeter Maydell             s->apr[regno][cpu] = value;
142451fd06e0SPeter Maydell         }
1425a9d477c4SChristoffer Dall         break;
142651fd06e0SPeter Maydell     }
142751fd06e0SPeter Maydell     case 0xe0: case 0xe4: case 0xe8: case 0xec:
142851fd06e0SPeter Maydell     {
142951fd06e0SPeter Maydell         int regno = (offset - 0xe0) / 4;
143051fd06e0SPeter Maydell 
143151fd06e0SPeter Maydell         if (regno >= GIC_NR_APRS || s->revision != 2) {
143251fd06e0SPeter Maydell             return MEMTX_OK;
143351fd06e0SPeter Maydell         }
14343dd0471bSLuc Michel         if (!gic_has_groups(s) || (gic_cpu_ns_access(s, cpu, attrs))) {
143551fd06e0SPeter Maydell             return MEMTX_OK;
143651fd06e0SPeter Maydell         }
143751fd06e0SPeter Maydell         s->nsapr[regno][cpu] = value;
143851fd06e0SPeter Maydell         break;
143951fd06e0SPeter Maydell     }
1440a55c910eSPeter Maydell     case 0x1000:
1441a55c910eSPeter Maydell         /* GICC_DIR */
1442a55c910eSPeter Maydell         gic_deactivate_irq(s, cpu, value & 0x3ff, attrs);
1443a55c910eSPeter Maydell         break;
1444e69954b9Spbrook     default:
14458c8dc39fSPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
14468c8dc39fSPeter Maydell                       "gic_cpu_write: Bad offset %x\n", (int)offset);
14470cf09852SPeter Maydell         return MEMTX_OK;
1448e69954b9Spbrook     }
1449e69954b9Spbrook     gic_update(s);
1450a9d85353SPeter Maydell     return MEMTX_OK;
1451e69954b9Spbrook }
1452e2c56465SPeter Maydell 
1453e2c56465SPeter Maydell /* Wrappers to read/write the GIC CPU interface for the current CPU */
1454a9d85353SPeter Maydell static MemTxResult gic_thiscpu_read(void *opaque, hwaddr addr, uint64_t *data,
1455a9d85353SPeter Maydell                                     unsigned size, MemTxAttrs attrs)
1456e2c56465SPeter Maydell {
1457fae15286SPeter Maydell     GICState *s = (GICState *)opaque;
1458a9d85353SPeter Maydell     return gic_cpu_read(s, gic_get_current_cpu(s), addr, data, attrs);
1459e2c56465SPeter Maydell }
1460e2c56465SPeter Maydell 
1461a9d85353SPeter Maydell static MemTxResult gic_thiscpu_write(void *opaque, hwaddr addr,
1462a9d85353SPeter Maydell                                      uint64_t value, unsigned size,
1463a9d85353SPeter Maydell                                      MemTxAttrs attrs)
1464e2c56465SPeter Maydell {
1465fae15286SPeter Maydell     GICState *s = (GICState *)opaque;
1466a9d85353SPeter Maydell     return gic_cpu_write(s, gic_get_current_cpu(s), addr, value, attrs);
1467e2c56465SPeter Maydell }
1468e2c56465SPeter Maydell 
1469e2c56465SPeter Maydell /* Wrappers to read/write the GIC CPU interface for a specific CPU.
1470fae15286SPeter Maydell  * These just decode the opaque pointer into GICState* + cpu id.
1471e2c56465SPeter Maydell  */
1472a9d85353SPeter Maydell static MemTxResult gic_do_cpu_read(void *opaque, hwaddr addr, uint64_t *data,
1473a9d85353SPeter Maydell                                    unsigned size, MemTxAttrs attrs)
1474e2c56465SPeter Maydell {
1475fae15286SPeter Maydell     GICState **backref = (GICState **)opaque;
1476fae15286SPeter Maydell     GICState *s = *backref;
1477e2c56465SPeter Maydell     int id = (backref - s->backref);
1478a9d85353SPeter Maydell     return gic_cpu_read(s, id, addr, data, attrs);
1479e2c56465SPeter Maydell }
1480e2c56465SPeter Maydell 
1481a9d85353SPeter Maydell static MemTxResult gic_do_cpu_write(void *opaque, hwaddr addr,
1482a9d85353SPeter Maydell                                     uint64_t value, unsigned size,
1483a9d85353SPeter Maydell                                     MemTxAttrs attrs)
1484e2c56465SPeter Maydell {
1485fae15286SPeter Maydell     GICState **backref = (GICState **)opaque;
1486fae15286SPeter Maydell     GICState *s = *backref;
1487e2c56465SPeter Maydell     int id = (backref - s->backref);
1488a9d85353SPeter Maydell     return gic_cpu_write(s, id, addr, value, attrs);
1489e2c56465SPeter Maydell }
1490e2c56465SPeter Maydell 
14917926c210SPavel Fedin static const MemoryRegionOps gic_ops[2] = {
14927926c210SPavel Fedin     {
14937926c210SPavel Fedin         .read_with_attrs = gic_dist_read,
14947926c210SPavel Fedin         .write_with_attrs = gic_dist_write,
14957926c210SPavel Fedin         .endianness = DEVICE_NATIVE_ENDIAN,
14967926c210SPavel Fedin     },
14977926c210SPavel Fedin     {
1498a9d85353SPeter Maydell         .read_with_attrs = gic_thiscpu_read,
1499a9d85353SPeter Maydell         .write_with_attrs = gic_thiscpu_write,
1500e2c56465SPeter Maydell         .endianness = DEVICE_NATIVE_ENDIAN,
15017926c210SPavel Fedin     }
1502e2c56465SPeter Maydell };
1503e2c56465SPeter Maydell 
1504e2c56465SPeter Maydell static const MemoryRegionOps gic_cpu_ops = {
1505a9d85353SPeter Maydell     .read_with_attrs = gic_do_cpu_read,
1506a9d85353SPeter Maydell     .write_with_attrs = gic_do_cpu_write,
1507e2c56465SPeter Maydell     .endianness = DEVICE_NATIVE_ENDIAN,
1508e2c56465SPeter Maydell };
1509e69954b9Spbrook 
151053111180SPeter Maydell static void arm_gic_realize(DeviceState *dev, Error **errp)
15112b518c56SPeter Maydell {
151253111180SPeter Maydell     /* Device instance realize function for the GIC sysbus device */
15132b518c56SPeter Maydell     int i;
151453111180SPeter Maydell     GICState *s = ARM_GIC(dev);
151553111180SPeter Maydell     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
15161e8cae4dSPeter Maydell     ARMGICClass *agc = ARM_GIC_GET_CLASS(s);
15170175ba10SMarkus Armbruster     Error *local_err = NULL;
15181e8cae4dSPeter Maydell 
15190175ba10SMarkus Armbruster     agc->parent_realize(dev, &local_err);
15200175ba10SMarkus Armbruster     if (local_err) {
15210175ba10SMarkus Armbruster         error_propagate(errp, local_err);
152253111180SPeter Maydell         return;
152353111180SPeter Maydell     }
15241e8cae4dSPeter Maydell 
15255d721b78SAlexander Graf     if (kvm_enabled() && !kvm_arm_supports_user_irq()) {
15265d721b78SAlexander Graf         error_setg(errp, "KVM with user space irqchip only works when the "
15275d721b78SAlexander Graf                          "host kernel supports KVM_CAP_ARM_USER_IRQ");
15285d721b78SAlexander Graf         return;
15295d721b78SAlexander Graf     }
15305d721b78SAlexander Graf 
15317926c210SPavel Fedin     /* This creates distributor and main CPU interface (s->cpuiomem[0]) */
15325773c049SLuc Michel     gic_init_irqs_and_mmio(s, gic_set_irq, gic_ops, NULL);
15332b518c56SPeter Maydell 
15347926c210SPavel Fedin     /* Extra core-specific regions for the CPU interfaces. This is
15357926c210SPavel Fedin      * necessary for "franken-GIC" implementations, for example on
15367926c210SPavel Fedin      * Exynos 4.
1537e2c56465SPeter Maydell      * NB that the memory region size of 0x100 applies for the 11MPCore
1538e2c56465SPeter Maydell      * and also cores following the GIC v1 spec (ie A9).
1539e2c56465SPeter Maydell      * GIC v2 defines a larger memory region (0x1000) so this will need
1540e2c56465SPeter Maydell      * to be extended when we implement A15.
1541e2c56465SPeter Maydell      */
1542b95690c9SWei Huang     for (i = 0; i < s->num_cpu; i++) {
1543e2c56465SPeter Maydell         s->backref[i] = s;
15441437c94bSPaolo Bonzini         memory_region_init_io(&s->cpuiomem[i+1], OBJECT(s), &gic_cpu_ops,
15451437c94bSPaolo Bonzini                               &s->backref[i], "gic_cpu", 0x100);
15467926c210SPavel Fedin         sysbus_init_mmio(sbd, &s->cpuiomem[i+1]);
1547496dbcd1SPeter Maydell     }
1548496dbcd1SPeter Maydell }
1549496dbcd1SPeter Maydell 
1550496dbcd1SPeter Maydell static void arm_gic_class_init(ObjectClass *klass, void *data)
1551496dbcd1SPeter Maydell {
1552496dbcd1SPeter Maydell     DeviceClass *dc = DEVICE_CLASS(klass);
15531e8cae4dSPeter Maydell     ARMGICClass *agc = ARM_GIC_CLASS(klass);
155453111180SPeter Maydell 
1555bf853881SPhilippe Mathieu-Daudé     device_class_set_parent_realize(dc, arm_gic_realize, &agc->parent_realize);
1556496dbcd1SPeter Maydell }
1557496dbcd1SPeter Maydell 
15588c43a6f0SAndreas Färber static const TypeInfo arm_gic_info = {
15591e8cae4dSPeter Maydell     .name = TYPE_ARM_GIC,
15601e8cae4dSPeter Maydell     .parent = TYPE_ARM_GIC_COMMON,
1561fae15286SPeter Maydell     .instance_size = sizeof(GICState),
1562496dbcd1SPeter Maydell     .class_init = arm_gic_class_init,
1563998a74bcSPeter Maydell     .class_size = sizeof(ARMGICClass),
1564496dbcd1SPeter Maydell };
1565496dbcd1SPeter Maydell 
1566496dbcd1SPeter Maydell static void arm_gic_register_types(void)
1567496dbcd1SPeter Maydell {
1568496dbcd1SPeter Maydell     type_register_static(&arm_gic_info);
1569496dbcd1SPeter Maydell }
1570496dbcd1SPeter Maydell 
1571496dbcd1SPeter Maydell type_init(arm_gic_register_types)
1572