xref: /qemu/hw/intc/arm_gic_kvm.c (revision 84f298ea)
17702e47cSPaolo Bonzini /*
27702e47cSPaolo Bonzini  * ARM Generic Interrupt Controller using KVM in-kernel support
37702e47cSPaolo Bonzini  *
47702e47cSPaolo Bonzini  * Copyright (c) 2012 Linaro Limited
57702e47cSPaolo Bonzini  * Written by Peter Maydell
6855011beSChristoffer Dall  * Save/Restore logic added by Christoffer Dall.
77702e47cSPaolo Bonzini  *
87702e47cSPaolo Bonzini  * This program is free software; you can redistribute it and/or modify
97702e47cSPaolo Bonzini  * it under the terms of the GNU General Public License as published by
107702e47cSPaolo Bonzini  * the Free Software Foundation, either version 2 of the License, or
117702e47cSPaolo Bonzini  * (at your option) any later version.
127702e47cSPaolo Bonzini  *
137702e47cSPaolo Bonzini  * This program is distributed in the hope that it will be useful,
147702e47cSPaolo Bonzini  * but WITHOUT ANY WARRANTY; without even the implied warranty of
157702e47cSPaolo Bonzini  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
167702e47cSPaolo Bonzini  * GNU General Public License for more details.
177702e47cSPaolo Bonzini  *
187702e47cSPaolo Bonzini  * You should have received a copy of the GNU General Public License along
197702e47cSPaolo Bonzini  * with this program; if not, see <http://www.gnu.org/licenses/>.
207702e47cSPaolo Bonzini  */
217702e47cSPaolo Bonzini 
228ef94f0bSPeter Maydell #include "qemu/osdep.h"
23da34e65cSMarkus Armbruster #include "qapi/error.h"
240b8fa32fSMarkus Armbruster #include "qemu/module.h"
25795c40b8SJuan Quintela #include "migration/blocker.h"
267702e47cSPaolo Bonzini #include "sysemu/kvm.h"
277702e47cSPaolo Bonzini #include "kvm_arm.h"
2847b43a1fSPaolo Bonzini #include "gic_internal.h"
294b3cfe72SPavel Fedin #include "vgic_common.h"
30db1015e9SEduardo Habkost #include "qom/object.h"
317702e47cSPaolo Bonzini 
327702e47cSPaolo Bonzini #define TYPE_KVM_ARM_GIC "kvm-arm-gic"
33db1015e9SEduardo Habkost typedef struct KVMARMGICClass KVMARMGICClass;
34fa34a3c5SEduardo Habkost /* This is reusing the GICState typedef from ARM_GIC_COMMON */
35fa34a3c5SEduardo Habkost DECLARE_OBJ_CHECKERS(GICState, KVMARMGICClass,
36fa34a3c5SEduardo Habkost                      KVM_ARM_GIC, TYPE_KVM_ARM_GIC)
377702e47cSPaolo Bonzini 
38db1015e9SEduardo Habkost struct KVMARMGICClass {
397702e47cSPaolo Bonzini     ARMGICCommonClass parent_class;
407702e47cSPaolo Bonzini     DeviceRealize parent_realize;
41d39270b5SPeter Maydell     ResettablePhases parent_phases;
42db1015e9SEduardo Habkost };
437702e47cSPaolo Bonzini 
kvm_arm_gic_set_irq(uint32_t num_irq,int irq,int level)444b3cfe72SPavel Fedin void kvm_arm_gic_set_irq(uint32_t num_irq, int irq, int level)
457702e47cSPaolo Bonzini {
467702e47cSPaolo Bonzini     /* Meaning of the 'irq' parameter:
477702e47cSPaolo Bonzini      *  [0..N-1] : external interrupts
487702e47cSPaolo Bonzini      *  [N..N+31] : PPI (internal) interrupts for CPU 0
497702e47cSPaolo Bonzini      *  [N+32..N+63] : PPI (internal interrupts for CPU 1
507702e47cSPaolo Bonzini      *  ...
517702e47cSPaolo Bonzini      * Convert this to the kernel's desired encoding, which
527702e47cSPaolo Bonzini      * has separate fields in the irq number for type,
537702e47cSPaolo Bonzini      * CPU number and interrupt number.
547702e47cSPaolo Bonzini      */
55f6530926SEric Auger     int irqtype, cpu;
567702e47cSPaolo Bonzini 
574b3cfe72SPavel Fedin     if (irq < (num_irq - GIC_INTERNAL)) {
587702e47cSPaolo Bonzini         /* External interrupt. The kernel numbers these like the GIC
597702e47cSPaolo Bonzini          * hardware, with external interrupt IDs starting after the
607702e47cSPaolo Bonzini          * internal ones.
617702e47cSPaolo Bonzini          */
627702e47cSPaolo Bonzini         irqtype = KVM_ARM_IRQ_TYPE_SPI;
637702e47cSPaolo Bonzini         cpu = 0;
647702e47cSPaolo Bonzini         irq += GIC_INTERNAL;
657702e47cSPaolo Bonzini     } else {
667702e47cSPaolo Bonzini         /* Internal interrupt: decode into (cpu, interrupt id) */
677702e47cSPaolo Bonzini         irqtype = KVM_ARM_IRQ_TYPE_PPI;
684b3cfe72SPavel Fedin         irq -= (num_irq - GIC_INTERNAL);
697702e47cSPaolo Bonzini         cpu = irq / GIC_INTERNAL;
707702e47cSPaolo Bonzini         irq %= GIC_INTERNAL;
717702e47cSPaolo Bonzini     }
72f6530926SEric Auger     kvm_arm_set_irq(cpu, irqtype, irq, !!level);
737702e47cSPaolo Bonzini }
747702e47cSPaolo Bonzini 
kvm_arm_gicv2_set_irq(void * opaque,int irq,int level)754b3cfe72SPavel Fedin static void kvm_arm_gicv2_set_irq(void *opaque, int irq, int level)
764b3cfe72SPavel Fedin {
774b3cfe72SPavel Fedin     GICState *s = (GICState *)opaque;
784b3cfe72SPavel Fedin 
794b3cfe72SPavel Fedin     kvm_arm_gic_set_irq(s->num_irq, irq, level);
804b3cfe72SPavel Fedin }
814b3cfe72SPavel Fedin 
kvm_arm_gic_can_save_restore(GICState * s)82855011beSChristoffer Dall static bool kvm_arm_gic_can_save_restore(GICState *s)
83855011beSChristoffer Dall {
84855011beSChristoffer Dall     return s->dev_fd >= 0;
85855011beSChristoffer Dall }
86855011beSChristoffer Dall 
874b3cfe72SPavel Fedin #define KVM_VGIC_ATTR(offset, cpu) \
884b3cfe72SPavel Fedin     ((((uint64_t)(cpu) << KVM_DEV_ARM_VGIC_CPUID_SHIFT) & \
894b3cfe72SPavel Fedin       KVM_DEV_ARM_VGIC_CPUID_MASK) | \
904b3cfe72SPavel Fedin      (((uint64_t)(offset) << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) & \
914b3cfe72SPavel Fedin       KVM_DEV_ARM_VGIC_OFFSET_MASK))
92855011beSChristoffer Dall 
kvm_gicd_access(GICState * s,int offset,int cpu,uint32_t * val,bool write)93855011beSChristoffer Dall static void kvm_gicd_access(GICState *s, int offset, int cpu,
94855011beSChristoffer Dall                             uint32_t *val, bool write)
95855011beSChristoffer Dall {
964b3cfe72SPavel Fedin     kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
97556969e9SEric Auger                       KVM_VGIC_ATTR(offset, cpu), val, write, &error_abort);
98855011beSChristoffer Dall }
99855011beSChristoffer Dall 
kvm_gicc_access(GICState * s,int offset,int cpu,uint32_t * val,bool write)100855011beSChristoffer Dall static void kvm_gicc_access(GICState *s, int offset, int cpu,
101855011beSChristoffer Dall                             uint32_t *val, bool write)
102855011beSChristoffer Dall {
1034b3cfe72SPavel Fedin     kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_REGS,
104556969e9SEric Auger                       KVM_VGIC_ATTR(offset, cpu), val, write, &error_abort);
105855011beSChristoffer Dall }
106855011beSChristoffer Dall 
107855011beSChristoffer Dall #define for_each_irq_reg(_ctr, _max_irq, _field_width) \
108855011beSChristoffer Dall     for (_ctr = 0; _ctr < ((_max_irq) / (32 / (_field_width))); _ctr++)
109855011beSChristoffer Dall 
110855011beSChristoffer Dall /*
111855011beSChristoffer Dall  * Translate from the in-kernel field for an IRQ value to/from the qemu
112855011beSChristoffer Dall  * representation.
113855011beSChristoffer Dall  */
114855011beSChristoffer Dall typedef void (*vgic_translate_fn)(GICState *s, int irq, int cpu,
115855011beSChristoffer Dall                                   uint32_t *field, bool to_kernel);
116855011beSChristoffer Dall 
117855011beSChristoffer Dall /* synthetic translate function used for clear/set registers to completely
1183b163b01SStefan Weil  * clear a setting using a clear-register before setting the remaining bits
119855011beSChristoffer Dall  * using a set-register */
translate_clear(GICState * s,int irq,int cpu,uint32_t * field,bool to_kernel)120855011beSChristoffer Dall static void translate_clear(GICState *s, int irq, int cpu,
121855011beSChristoffer Dall                             uint32_t *field, bool to_kernel)
122855011beSChristoffer Dall {
123855011beSChristoffer Dall     if (to_kernel) {
124855011beSChristoffer Dall         *field = ~0;
125855011beSChristoffer Dall     } else {
126855011beSChristoffer Dall         /* does not make sense: qemu model doesn't use set/clear regs */
127855011beSChristoffer Dall         abort();
128855011beSChristoffer Dall     }
129855011beSChristoffer Dall }
130855011beSChristoffer Dall 
translate_group(GICState * s,int irq,int cpu,uint32_t * field,bool to_kernel)131eb8b9530SPeter Maydell static void translate_group(GICState *s, int irq, int cpu,
132eb8b9530SPeter Maydell                             uint32_t *field, bool to_kernel)
133eb8b9530SPeter Maydell {
134eb8b9530SPeter Maydell     int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
135eb8b9530SPeter Maydell 
136eb8b9530SPeter Maydell     if (to_kernel) {
13767ce697aSLuc Michel         *field = GIC_DIST_TEST_GROUP(irq, cm);
138eb8b9530SPeter Maydell     } else {
139eb8b9530SPeter Maydell         if (*field & 1) {
14067ce697aSLuc Michel             GIC_DIST_SET_GROUP(irq, cm);
141eb8b9530SPeter Maydell         }
142eb8b9530SPeter Maydell     }
143eb8b9530SPeter Maydell }
144eb8b9530SPeter Maydell 
translate_enabled(GICState * s,int irq,int cpu,uint32_t * field,bool to_kernel)145855011beSChristoffer Dall static void translate_enabled(GICState *s, int irq, int cpu,
146855011beSChristoffer Dall                               uint32_t *field, bool to_kernel)
147855011beSChristoffer Dall {
148855011beSChristoffer Dall     int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
149855011beSChristoffer Dall 
150855011beSChristoffer Dall     if (to_kernel) {
15167ce697aSLuc Michel         *field = GIC_DIST_TEST_ENABLED(irq, cm);
152855011beSChristoffer Dall     } else {
153855011beSChristoffer Dall         if (*field & 1) {
15467ce697aSLuc Michel             GIC_DIST_SET_ENABLED(irq, cm);
155855011beSChristoffer Dall         }
156855011beSChristoffer Dall     }
157855011beSChristoffer Dall }
158855011beSChristoffer Dall 
translate_pending(GICState * s,int irq,int cpu,uint32_t * field,bool to_kernel)159855011beSChristoffer Dall static void translate_pending(GICState *s, int irq, int cpu,
160855011beSChristoffer Dall                               uint32_t *field, bool to_kernel)
161855011beSChristoffer Dall {
162855011beSChristoffer Dall     int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
163855011beSChristoffer Dall 
164855011beSChristoffer Dall     if (to_kernel) {
165855011beSChristoffer Dall         *field = gic_test_pending(s, irq, cm);
166855011beSChristoffer Dall     } else {
167855011beSChristoffer Dall         if (*field & 1) {
16867ce697aSLuc Michel             GIC_DIST_SET_PENDING(irq, cm);
169855011beSChristoffer Dall             /* TODO: Capture is level-line is held high in the kernel */
170855011beSChristoffer Dall         }
171855011beSChristoffer Dall     }
172855011beSChristoffer Dall }
173855011beSChristoffer Dall 
translate_active(GICState * s,int irq,int cpu,uint32_t * field,bool to_kernel)174855011beSChristoffer Dall static void translate_active(GICState *s, int irq, int cpu,
175855011beSChristoffer Dall                              uint32_t *field, bool to_kernel)
176855011beSChristoffer Dall {
177855011beSChristoffer Dall     int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
178855011beSChristoffer Dall 
179855011beSChristoffer Dall     if (to_kernel) {
18067ce697aSLuc Michel         *field = GIC_DIST_TEST_ACTIVE(irq, cm);
181855011beSChristoffer Dall     } else {
182855011beSChristoffer Dall         if (*field & 1) {
18367ce697aSLuc Michel             GIC_DIST_SET_ACTIVE(irq, cm);
184855011beSChristoffer Dall         }
185855011beSChristoffer Dall     }
186855011beSChristoffer Dall }
187855011beSChristoffer Dall 
translate_trigger(GICState * s,int irq,int cpu,uint32_t * field,bool to_kernel)188855011beSChristoffer Dall static void translate_trigger(GICState *s, int irq, int cpu,
189855011beSChristoffer Dall                               uint32_t *field, bool to_kernel)
190855011beSChristoffer Dall {
191855011beSChristoffer Dall     if (to_kernel) {
19267ce697aSLuc Michel         *field = (GIC_DIST_TEST_EDGE_TRIGGER(irq)) ? 0x2 : 0x0;
193855011beSChristoffer Dall     } else {
194855011beSChristoffer Dall         if (*field & 0x2) {
19567ce697aSLuc Michel             GIC_DIST_SET_EDGE_TRIGGER(irq);
196855011beSChristoffer Dall         }
197855011beSChristoffer Dall     }
198855011beSChristoffer Dall }
199855011beSChristoffer Dall 
translate_priority(GICState * s,int irq,int cpu,uint32_t * field,bool to_kernel)200855011beSChristoffer Dall static void translate_priority(GICState *s, int irq, int cpu,
201855011beSChristoffer Dall                                uint32_t *field, bool to_kernel)
202855011beSChristoffer Dall {
203855011beSChristoffer Dall     if (to_kernel) {
20467ce697aSLuc Michel         *field = GIC_DIST_GET_PRIORITY(irq, cpu) & 0xff;
205855011beSChristoffer Dall     } else {
20667ce697aSLuc Michel         gic_dist_set_priority(s, cpu, irq,
20767ce697aSLuc Michel                               *field & 0xff, MEMTXATTRS_UNSPECIFIED);
208855011beSChristoffer Dall     }
209855011beSChristoffer Dall }
210855011beSChristoffer Dall 
translate_targets(GICState * s,int irq,int cpu,uint32_t * field,bool to_kernel)211855011beSChristoffer Dall static void translate_targets(GICState *s, int irq, int cpu,
212855011beSChristoffer Dall                               uint32_t *field, bool to_kernel)
213855011beSChristoffer Dall {
214855011beSChristoffer Dall     if (to_kernel) {
215855011beSChristoffer Dall         *field = s->irq_target[irq] & 0xff;
216855011beSChristoffer Dall     } else {
217855011beSChristoffer Dall         s->irq_target[irq] = *field & 0xff;
218855011beSChristoffer Dall     }
219855011beSChristoffer Dall }
220855011beSChristoffer Dall 
translate_sgisource(GICState * s,int irq,int cpu,uint32_t * field,bool to_kernel)221855011beSChristoffer Dall static void translate_sgisource(GICState *s, int irq, int cpu,
222855011beSChristoffer Dall                                 uint32_t *field, bool to_kernel)
223855011beSChristoffer Dall {
224855011beSChristoffer Dall     if (to_kernel) {
225855011beSChristoffer Dall         *field = s->sgi_pending[irq][cpu] & 0xff;
226855011beSChristoffer Dall     } else {
227855011beSChristoffer Dall         s->sgi_pending[irq][cpu] = *field & 0xff;
228855011beSChristoffer Dall     }
229855011beSChristoffer Dall }
230855011beSChristoffer Dall 
231855011beSChristoffer Dall /* Read a register group from the kernel VGIC */
kvm_dist_get(GICState * s,uint32_t offset,int width,int maxirq,vgic_translate_fn translate_fn)232855011beSChristoffer Dall static void kvm_dist_get(GICState *s, uint32_t offset, int width,
233855011beSChristoffer Dall                          int maxirq, vgic_translate_fn translate_fn)
234855011beSChristoffer Dall {
235855011beSChristoffer Dall     uint32_t reg;
236855011beSChristoffer Dall     int i;
237855011beSChristoffer Dall     int j;
238855011beSChristoffer Dall     int irq;
239855011beSChristoffer Dall     int cpu;
240855011beSChristoffer Dall     int regsz = 32 / width; /* irqs per kernel register */
241855011beSChristoffer Dall     uint32_t field;
242855011beSChristoffer Dall 
243855011beSChristoffer Dall     for_each_irq_reg(i, maxirq, width) {
244855011beSChristoffer Dall         irq = i * regsz;
245855011beSChristoffer Dall         cpu = 0;
246855011beSChristoffer Dall         while ((cpu < s->num_cpu && irq < GIC_INTERNAL) || cpu == 0) {
247855011beSChristoffer Dall             kvm_gicd_access(s, offset, cpu, &reg, false);
248855011beSChristoffer Dall             for (j = 0; j < regsz; j++) {
249855011beSChristoffer Dall                 field = extract32(reg, j * width, width);
250855011beSChristoffer Dall                 translate_fn(s, irq + j, cpu, &field, false);
251855011beSChristoffer Dall             }
252855011beSChristoffer Dall 
253855011beSChristoffer Dall             cpu++;
254855011beSChristoffer Dall         }
255855011beSChristoffer Dall         offset += 4;
256855011beSChristoffer Dall     }
257855011beSChristoffer Dall }
258855011beSChristoffer Dall 
259855011beSChristoffer Dall /* Write a register group to the kernel VGIC */
kvm_dist_put(GICState * s,uint32_t offset,int width,int maxirq,vgic_translate_fn translate_fn)260855011beSChristoffer Dall static void kvm_dist_put(GICState *s, uint32_t offset, int width,
261855011beSChristoffer Dall                          int maxirq, vgic_translate_fn translate_fn)
262855011beSChristoffer Dall {
263855011beSChristoffer Dall     uint32_t reg;
264855011beSChristoffer Dall     int i;
265855011beSChristoffer Dall     int j;
266855011beSChristoffer Dall     int irq;
267855011beSChristoffer Dall     int cpu;
268855011beSChristoffer Dall     int regsz = 32 / width; /* irqs per kernel register */
269855011beSChristoffer Dall     uint32_t field;
270855011beSChristoffer Dall 
271855011beSChristoffer Dall     for_each_irq_reg(i, maxirq, width) {
272855011beSChristoffer Dall         irq = i * regsz;
273855011beSChristoffer Dall         cpu = 0;
274855011beSChristoffer Dall         while ((cpu < s->num_cpu && irq < GIC_INTERNAL) || cpu == 0) {
275855011beSChristoffer Dall             reg = 0;
276855011beSChristoffer Dall             for (j = 0; j < regsz; j++) {
277855011beSChristoffer Dall                 translate_fn(s, irq + j, cpu, &field, true);
278855011beSChristoffer Dall                 reg = deposit32(reg, j * width, width, field);
279855011beSChristoffer Dall             }
280855011beSChristoffer Dall             kvm_gicd_access(s, offset, cpu, &reg, true);
281855011beSChristoffer Dall 
282855011beSChristoffer Dall             cpu++;
283855011beSChristoffer Dall         }
284855011beSChristoffer Dall         offset += 4;
285855011beSChristoffer Dall     }
286855011beSChristoffer Dall }
287855011beSChristoffer Dall 
kvm_arm_gic_put(GICState * s)2887702e47cSPaolo Bonzini static void kvm_arm_gic_put(GICState *s)
2897702e47cSPaolo Bonzini {
290855011beSChristoffer Dall     uint32_t reg;
291855011beSChristoffer Dall     int i;
292855011beSChristoffer Dall     int cpu;
293855011beSChristoffer Dall     int num_cpu;
294855011beSChristoffer Dall     int num_irq;
295855011beSChristoffer Dall 
296855011beSChristoffer Dall     /* Note: We do the restore in a slightly different order than the save
297855011beSChristoffer Dall      * (where the order doesn't matter and is simply ordered according to the
298855011beSChristoffer Dall      * register offset values */
299855011beSChristoffer Dall 
300855011beSChristoffer Dall     /*****************************************************************
301855011beSChristoffer Dall      * Distributor State
302855011beSChristoffer Dall      */
303855011beSChristoffer Dall 
304679aa175SFabian Aggeler     /* s->ctlr -> GICD_CTLR */
305679aa175SFabian Aggeler     reg = s->ctlr;
306855011beSChristoffer Dall     kvm_gicd_access(s, 0x0, 0, &reg, true);
307855011beSChristoffer Dall 
308855011beSChristoffer Dall     /* Sanity checking on GICD_TYPER and s->num_irq, s->num_cpu */
309855011beSChristoffer Dall     kvm_gicd_access(s, 0x4, 0, &reg, false);
310855011beSChristoffer Dall     num_irq = ((reg & 0x1f) + 1) * 32;
311855011beSChristoffer Dall     num_cpu = ((reg & 0xe0) >> 5) + 1;
312855011beSChristoffer Dall 
313855011beSChristoffer Dall     if (num_irq < s->num_irq) {
314855011beSChristoffer Dall             fprintf(stderr, "Restoring %u IRQs, but kernel supports max %d\n",
315855011beSChristoffer Dall                     s->num_irq, num_irq);
316855011beSChristoffer Dall             abort();
317855011beSChristoffer Dall     } else if (num_cpu != s->num_cpu) {
318855011beSChristoffer Dall             fprintf(stderr, "Restoring %u CPU interfaces, kernel only has %d\n",
319855011beSChristoffer Dall                     s->num_cpu, num_cpu);
320855011beSChristoffer Dall             /* Did we not create the VCPUs in the kernel yet? */
321855011beSChristoffer Dall             abort();
322855011beSChristoffer Dall     }
323855011beSChristoffer Dall 
324855011beSChristoffer Dall     /* TODO: Consider checking compatibility with the IIDR ? */
325855011beSChristoffer Dall 
326855011beSChristoffer Dall     /* irq_state[n].enabled -> GICD_ISENABLERn */
327855011beSChristoffer Dall     kvm_dist_put(s, 0x180, 1, s->num_irq, translate_clear);
328855011beSChristoffer Dall     kvm_dist_put(s, 0x100, 1, s->num_irq, translate_enabled);
329855011beSChristoffer Dall 
330eb8b9530SPeter Maydell     /* irq_state[n].group -> GICD_IGROUPRn */
331eb8b9530SPeter Maydell     kvm_dist_put(s, 0x80, 1, s->num_irq, translate_group);
332eb8b9530SPeter Maydell 
333855011beSChristoffer Dall     /* s->irq_target[irq] -> GICD_ITARGETSRn
334855011beSChristoffer Dall      * (restore targets before pending to ensure the pending state is set on
335855011beSChristoffer Dall      * the appropriate CPU interfaces in the kernel) */
336855011beSChristoffer Dall     kvm_dist_put(s, 0x800, 8, s->num_irq, translate_targets);
337855011beSChristoffer Dall 
33874fdb781SAlex Bennée     /* irq_state[n].trigger -> GICD_ICFGRn
33974fdb781SAlex Bennée      * (restore configuration registers before pending IRQs so we treat
34074fdb781SAlex Bennée      * level/edge correctly) */
34174fdb781SAlex Bennée     kvm_dist_put(s, 0xc00, 2, s->num_irq, translate_trigger);
34274fdb781SAlex Bennée 
343855011beSChristoffer Dall     /* irq_state[n].pending + irq_state[n].level -> GICD_ISPENDRn */
344855011beSChristoffer Dall     kvm_dist_put(s, 0x280, 1, s->num_irq, translate_clear);
345855011beSChristoffer Dall     kvm_dist_put(s, 0x200, 1, s->num_irq, translate_pending);
346855011beSChristoffer Dall 
347855011beSChristoffer Dall     /* irq_state[n].active -> GICD_ISACTIVERn */
348855011beSChristoffer Dall     kvm_dist_put(s, 0x380, 1, s->num_irq, translate_clear);
349855011beSChristoffer Dall     kvm_dist_put(s, 0x300, 1, s->num_irq, translate_active);
350855011beSChristoffer Dall 
351855011beSChristoffer Dall 
352855011beSChristoffer Dall     /* s->priorityX[irq] -> ICD_IPRIORITYRn */
353855011beSChristoffer Dall     kvm_dist_put(s, 0x400, 8, s->num_irq, translate_priority);
354855011beSChristoffer Dall 
355855011beSChristoffer Dall     /* s->sgi_pending -> ICD_CPENDSGIRn */
356855011beSChristoffer Dall     kvm_dist_put(s, 0xf10, 8, GIC_NR_SGIS, translate_clear);
357855011beSChristoffer Dall     kvm_dist_put(s, 0xf20, 8, GIC_NR_SGIS, translate_sgisource);
358855011beSChristoffer Dall 
359855011beSChristoffer Dall 
360855011beSChristoffer Dall     /*****************************************************************
361855011beSChristoffer Dall      * CPU Interface(s) State
362855011beSChristoffer Dall      */
363855011beSChristoffer Dall 
364855011beSChristoffer Dall     for (cpu = 0; cpu < s->num_cpu; cpu++) {
36532951860SFabian Aggeler         /* s->cpu_ctlr[cpu] -> GICC_CTLR */
36632951860SFabian Aggeler         reg = s->cpu_ctlr[cpu];
367855011beSChristoffer Dall         kvm_gicc_access(s, 0x00, cpu, &reg, true);
368855011beSChristoffer Dall 
369855011beSChristoffer Dall         /* s->priority_mask[cpu] -> GICC_PMR */
370855011beSChristoffer Dall         reg = (s->priority_mask[cpu] & 0xff);
371855011beSChristoffer Dall         kvm_gicc_access(s, 0x04, cpu, &reg, true);
372855011beSChristoffer Dall 
373855011beSChristoffer Dall         /* s->bpr[cpu] -> GICC_BPR */
374855011beSChristoffer Dall         reg = (s->bpr[cpu] & 0x7);
375855011beSChristoffer Dall         kvm_gicc_access(s, 0x08, cpu, &reg, true);
376855011beSChristoffer Dall 
377855011beSChristoffer Dall         /* s->abpr[cpu] -> GICC_ABPR */
378855011beSChristoffer Dall         reg = (s->abpr[cpu] & 0x7);
379855011beSChristoffer Dall         kvm_gicc_access(s, 0x1c, cpu, &reg, true);
380855011beSChristoffer Dall 
381855011beSChristoffer Dall         /* s->apr[n][cpu] -> GICC_APRn */
382855011beSChristoffer Dall         for (i = 0; i < 4; i++) {
383855011beSChristoffer Dall             reg = s->apr[i][cpu];
384855011beSChristoffer Dall             kvm_gicc_access(s, 0xd0 + i * 4, cpu, &reg, true);
385855011beSChristoffer Dall         }
386855011beSChristoffer Dall     }
3877702e47cSPaolo Bonzini }
3887702e47cSPaolo Bonzini 
kvm_arm_gic_get(GICState * s)3897702e47cSPaolo Bonzini static void kvm_arm_gic_get(GICState *s)
3907702e47cSPaolo Bonzini {
391855011beSChristoffer Dall     uint32_t reg;
392855011beSChristoffer Dall     int i;
393855011beSChristoffer Dall     int cpu;
394855011beSChristoffer Dall 
395855011beSChristoffer Dall     /*****************************************************************
396855011beSChristoffer Dall      * Distributor State
397855011beSChristoffer Dall      */
398855011beSChristoffer Dall 
399679aa175SFabian Aggeler     /* GICD_CTLR -> s->ctlr */
400855011beSChristoffer Dall     kvm_gicd_access(s, 0x0, 0, &reg, false);
401679aa175SFabian Aggeler     s->ctlr = reg;
402855011beSChristoffer Dall 
403855011beSChristoffer Dall     /* Sanity checking on GICD_TYPER -> s->num_irq, s->num_cpu */
404855011beSChristoffer Dall     kvm_gicd_access(s, 0x4, 0, &reg, false);
405855011beSChristoffer Dall     s->num_irq = ((reg & 0x1f) + 1) * 32;
406855011beSChristoffer Dall     s->num_cpu = ((reg & 0xe0) >> 5) + 1;
407855011beSChristoffer Dall 
408855011beSChristoffer Dall     if (s->num_irq > GIC_MAXIRQ) {
409855011beSChristoffer Dall             fprintf(stderr, "Too many IRQs reported from the kernel: %d\n",
410855011beSChristoffer Dall                     s->num_irq);
411855011beSChristoffer Dall             abort();
412855011beSChristoffer Dall     }
413855011beSChristoffer Dall 
414855011beSChristoffer Dall     /* GICD_IIDR -> ? */
415855011beSChristoffer Dall     kvm_gicd_access(s, 0x8, 0, &reg, false);
416855011beSChristoffer Dall 
417855011beSChristoffer Dall     /* Clear all the IRQ settings */
418855011beSChristoffer Dall     for (i = 0; i < s->num_irq; i++) {
419855011beSChristoffer Dall         memset(&s->irq_state[i], 0, sizeof(s->irq_state[0]));
420855011beSChristoffer Dall     }
421855011beSChristoffer Dall 
422eb8b9530SPeter Maydell     /* GICD_IGROUPRn -> irq_state[n].group */
423eb8b9530SPeter Maydell     kvm_dist_get(s, 0x80, 1, s->num_irq, translate_group);
424eb8b9530SPeter Maydell 
425855011beSChristoffer Dall     /* GICD_ISENABLERn -> irq_state[n].enabled */
426855011beSChristoffer Dall     kvm_dist_get(s, 0x100, 1, s->num_irq, translate_enabled);
427855011beSChristoffer Dall 
428855011beSChristoffer Dall     /* GICD_ISPENDRn -> irq_state[n].pending + irq_state[n].level */
429855011beSChristoffer Dall     kvm_dist_get(s, 0x200, 1, s->num_irq, translate_pending);
430855011beSChristoffer Dall 
431855011beSChristoffer Dall     /* GICD_ISACTIVERn -> irq_state[n].active */
432855011beSChristoffer Dall     kvm_dist_get(s, 0x300, 1, s->num_irq, translate_active);
433855011beSChristoffer Dall 
434855011beSChristoffer Dall     /* GICD_ICFRn -> irq_state[n].trigger */
435855011beSChristoffer Dall     kvm_dist_get(s, 0xc00, 2, s->num_irq, translate_trigger);
436855011beSChristoffer Dall 
437855011beSChristoffer Dall     /* GICD_IPRIORITYRn -> s->priorityX[irq] */
438855011beSChristoffer Dall     kvm_dist_get(s, 0x400, 8, s->num_irq, translate_priority);
439855011beSChristoffer Dall 
440855011beSChristoffer Dall     /* GICD_ITARGETSRn -> s->irq_target[irq] */
441855011beSChristoffer Dall     kvm_dist_get(s, 0x800, 8, s->num_irq, translate_targets);
442855011beSChristoffer Dall 
443855011beSChristoffer Dall     /* GICD_CPENDSGIRn -> s->sgi_pending */
444855011beSChristoffer Dall     kvm_dist_get(s, 0xf10, 8, GIC_NR_SGIS, translate_sgisource);
445855011beSChristoffer Dall 
446855011beSChristoffer Dall 
447855011beSChristoffer Dall     /*****************************************************************
448855011beSChristoffer Dall      * CPU Interface(s) State
449855011beSChristoffer Dall      */
450855011beSChristoffer Dall 
451855011beSChristoffer Dall     for (cpu = 0; cpu < s->num_cpu; cpu++) {
45232951860SFabian Aggeler         /* GICC_CTLR -> s->cpu_ctlr[cpu] */
453855011beSChristoffer Dall         kvm_gicc_access(s, 0x00, cpu, &reg, false);
45432951860SFabian Aggeler         s->cpu_ctlr[cpu] = reg;
455855011beSChristoffer Dall 
456855011beSChristoffer Dall         /* GICC_PMR -> s->priority_mask[cpu] */
457855011beSChristoffer Dall         kvm_gicc_access(s, 0x04, cpu, &reg, false);
458855011beSChristoffer Dall         s->priority_mask[cpu] = (reg & 0xff);
459855011beSChristoffer Dall 
460855011beSChristoffer Dall         /* GICC_BPR -> s->bpr[cpu] */
461855011beSChristoffer Dall         kvm_gicc_access(s, 0x08, cpu, &reg, false);
462855011beSChristoffer Dall         s->bpr[cpu] = (reg & 0x7);
463855011beSChristoffer Dall 
464855011beSChristoffer Dall         /* GICC_ABPR -> s->abpr[cpu] */
465855011beSChristoffer Dall         kvm_gicc_access(s, 0x1c, cpu, &reg, false);
466855011beSChristoffer Dall         s->abpr[cpu] = (reg & 0x7);
467855011beSChristoffer Dall 
468855011beSChristoffer Dall         /* GICC_APRn -> s->apr[n][cpu] */
469855011beSChristoffer Dall         for (i = 0; i < 4; i++) {
470855011beSChristoffer Dall             kvm_gicc_access(s, 0xd0 + i * 4, cpu, &reg, false);
471855011beSChristoffer Dall             s->apr[i][cpu] = reg;
472855011beSChristoffer Dall         }
473855011beSChristoffer Dall     }
4747702e47cSPaolo Bonzini }
4757702e47cSPaolo Bonzini 
kvm_arm_gic_reset_hold(Object * obj,ResetType type)476ad80e367SPeter Maydell static void kvm_arm_gic_reset_hold(Object *obj, ResetType type)
4777702e47cSPaolo Bonzini {
478d39270b5SPeter Maydell     GICState *s = ARM_GIC_COMMON(obj);
4797702e47cSPaolo Bonzini     KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s);
4807702e47cSPaolo Bonzini 
481d39270b5SPeter Maydell     if (kgc->parent_phases.hold) {
482ad80e367SPeter Maydell         kgc->parent_phases.hold(obj, type);
483d39270b5SPeter Maydell     }
48424182fbcSPavel Fedin 
48524182fbcSPavel Fedin     if (kvm_arm_gic_can_save_restore(s)) {
4867702e47cSPaolo Bonzini         kvm_arm_gic_put(s);
4877702e47cSPaolo Bonzini     }
48824182fbcSPavel Fedin }
4897702e47cSPaolo Bonzini 
kvm_arm_gic_realize(DeviceState * dev,Error ** errp)4907702e47cSPaolo Bonzini static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
4917702e47cSPaolo Bonzini {
4927702e47cSPaolo Bonzini     int i;
4937702e47cSPaolo Bonzini     GICState *s = KVM_ARM_GIC(dev);
4947702e47cSPaolo Bonzini     KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s);
4950175ba10SMarkus Armbruster     Error *local_err = NULL;
4961da41cc1SChristoffer Dall     int ret;
4977702e47cSPaolo Bonzini 
4980175ba10SMarkus Armbruster     kgc->parent_realize(dev, &local_err);
4990175ba10SMarkus Armbruster     if (local_err) {
5000175ba10SMarkus Armbruster         error_propagate(errp, local_err);
5017702e47cSPaolo Bonzini         return;
5027702e47cSPaolo Bonzini     }
5037702e47cSPaolo Bonzini 
5045543d1abSFabian Aggeler     if (s->security_extn) {
5055543d1abSFabian Aggeler         error_setg(errp, "the in-kernel VGIC does not implement the "
5065543d1abSFabian Aggeler                    "security extensions");
5075543d1abSFabian Aggeler         return;
5085543d1abSFabian Aggeler     }
5095543d1abSFabian Aggeler 
5105773c049SLuc Michel     if (s->virt_extn) {
5115773c049SLuc Michel         error_setg(errp, "the in-kernel VGIC does not implement the "
5125773c049SLuc Michel                    "virtualization extensions");
5135773c049SLuc Michel         return;
5145773c049SLuc Michel     }
5155773c049SLuc Michel 
516fe44dc91SAshijeet Acharya     if (!kvm_arm_gic_can_save_restore(s)) {
517fe44dc91SAshijeet Acharya         error_setg(&s->migration_blocker, "This operating system kernel does "
518fe44dc91SAshijeet Acharya                                           "not support vGICv2 migration");
519c8a7fc51SSteve Sistare         if (migrate_add_blocker(&s->migration_blocker, errp) < 0) {
520fe44dc91SAshijeet Acharya             return;
521fe44dc91SAshijeet Acharya         }
522fe44dc91SAshijeet Acharya     }
523fe44dc91SAshijeet Acharya 
5245773c049SLuc Michel     gic_init_irqs_and_mmio(s, kvm_arm_gicv2_set_irq, NULL, NULL);
5256a1a9cfaSEric Auger 
5266a1a9cfaSEric Auger     for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) {
5276a1a9cfaSEric Auger         qemu_irq irq = qdev_get_gpio_in(dev, i);
5286a1a9cfaSEric Auger         kvm_irqchip_set_qemuirq_gsi(kvm_state, irq, i);
5296a1a9cfaSEric Auger     }
5306a1a9cfaSEric Auger 
5311da41cc1SChristoffer Dall     /* Try to create the device via the device control API */
5321da41cc1SChristoffer Dall     s->dev_fd = -1;
5331da41cc1SChristoffer Dall     ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_V2, false);
5341da41cc1SChristoffer Dall     if (ret >= 0) {
5351da41cc1SChristoffer Dall         s->dev_fd = ret;
5364b3cfe72SPavel Fedin 
5374b3cfe72SPavel Fedin         /* Newstyle API is used, we may have attributes */
5384b3cfe72SPavel Fedin         if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)) {
5394b3cfe72SPavel Fedin             uint32_t numirqs = s->num_irq;
5404b3cfe72SPavel Fedin             kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0,
541556969e9SEric Auger                               &numirqs, true, &error_abort);
5424b3cfe72SPavel Fedin         }
5434b3cfe72SPavel Fedin         /* Tell the kernel to complete VGIC initialization now */
5444b3cfe72SPavel Fedin         if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
5454b3cfe72SPavel Fedin                                   KVM_DEV_ARM_VGIC_CTRL_INIT)) {
5464b3cfe72SPavel Fedin             kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
547556969e9SEric Auger                               KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true,
548556969e9SEric Auger                               &error_abort);
5494b3cfe72SPavel Fedin         }
55084f298eaSPaolo Bonzini     } else {
5511904f9b5SPeter Maydell         error_setg_errno(errp, -ret, "error creating in-kernel VGIC");
5521904f9b5SPeter Maydell         error_append_hint(errp,
5531904f9b5SPeter Maydell                           "Perhaps the host CPU does not support GICv2?\n");
5541da41cc1SChristoffer Dall         return;
5551da41cc1SChristoffer Dall     }
5561da41cc1SChristoffer Dall 
5577702e47cSPaolo Bonzini     /* Distributor */
5587702e47cSPaolo Bonzini     kvm_arm_register_device(&s->iomem,
5597702e47cSPaolo Bonzini                             (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
5601da41cc1SChristoffer Dall                             | KVM_VGIC_V2_ADDR_TYPE_DIST,
5611da41cc1SChristoffer Dall                             KVM_DEV_ARM_VGIC_GRP_ADDR,
5621da41cc1SChristoffer Dall                             KVM_VGIC_V2_ADDR_TYPE_DIST,
56319d1bd0bSEric Auger                             s->dev_fd, 0);
5647702e47cSPaolo Bonzini     /* CPU interface for current core. Unlike arm_gic, we don't
5657702e47cSPaolo Bonzini      * provide the "interface for core #N" memory regions, because
5667702e47cSPaolo Bonzini      * cores with a VGIC don't have those.
5677702e47cSPaolo Bonzini      */
5687702e47cSPaolo Bonzini     kvm_arm_register_device(&s->cpuiomem[0],
5697702e47cSPaolo Bonzini                             (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
5701da41cc1SChristoffer Dall                             | KVM_VGIC_V2_ADDR_TYPE_CPU,
5711da41cc1SChristoffer Dall                             KVM_DEV_ARM_VGIC_GRP_ADDR,
5721da41cc1SChristoffer Dall                             KVM_VGIC_V2_ADDR_TYPE_CPU,
57319d1bd0bSEric Auger                             s->dev_fd, 0);
57424182fbcSPavel Fedin 
575d19a4d4eSEric Auger     if (kvm_has_gsi_routing()) {
576d19a4d4eSEric Auger         /* set up irq routing */
577d19a4d4eSEric Auger         for (i = 0; i < s->num_irq - GIC_INTERNAL; ++i) {
578d19a4d4eSEric Auger             kvm_irqchip_add_irq_route(kvm_state, i, 0, i);
579d19a4d4eSEric Auger         }
580d19a4d4eSEric Auger 
581d19a4d4eSEric Auger         kvm_gsi_routing_allowed = true;
582d19a4d4eSEric Auger 
583d19a4d4eSEric Auger         kvm_irqchip_commit_routes(kvm_state);
584d19a4d4eSEric Auger     }
5857702e47cSPaolo Bonzini }
5867702e47cSPaolo Bonzini 
kvm_arm_gic_class_init(ObjectClass * klass,void * data)5877702e47cSPaolo Bonzini static void kvm_arm_gic_class_init(ObjectClass *klass, void *data)
5887702e47cSPaolo Bonzini {
5897702e47cSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
590d39270b5SPeter Maydell     ResettableClass *rc = RESETTABLE_CLASS(klass);
5917702e47cSPaolo Bonzini     ARMGICCommonClass *agcc = ARM_GIC_COMMON_CLASS(klass);
5927702e47cSPaolo Bonzini     KVMARMGICClass *kgc = KVM_ARM_GIC_CLASS(klass);
5937702e47cSPaolo Bonzini 
5947702e47cSPaolo Bonzini     agcc->pre_save = kvm_arm_gic_get;
5957702e47cSPaolo Bonzini     agcc->post_load = kvm_arm_gic_put;
596bf853881SPhilippe Mathieu-Daudé     device_class_set_parent_realize(dc, kvm_arm_gic_realize,
597bf853881SPhilippe Mathieu-Daudé                                     &kgc->parent_realize);
598d39270b5SPeter Maydell     resettable_class_set_parent_phases(rc, NULL, kvm_arm_gic_reset_hold, NULL,
599d39270b5SPeter Maydell                                        &kgc->parent_phases);
6007702e47cSPaolo Bonzini }
6017702e47cSPaolo Bonzini 
6027702e47cSPaolo Bonzini static const TypeInfo kvm_arm_gic_info = {
6037702e47cSPaolo Bonzini     .name = TYPE_KVM_ARM_GIC,
6047702e47cSPaolo Bonzini     .parent = TYPE_ARM_GIC_COMMON,
6057702e47cSPaolo Bonzini     .instance_size = sizeof(GICState),
6067702e47cSPaolo Bonzini     .class_init = kvm_arm_gic_class_init,
6077702e47cSPaolo Bonzini     .class_size = sizeof(KVMARMGICClass),
6087702e47cSPaolo Bonzini };
6097702e47cSPaolo Bonzini 
kvm_arm_gic_register_types(void)6107702e47cSPaolo Bonzini static void kvm_arm_gic_register_types(void)
6117702e47cSPaolo Bonzini {
6127702e47cSPaolo Bonzini     type_register_static(&kvm_arm_gic_info);
6137702e47cSPaolo Bonzini }
6147702e47cSPaolo Bonzini 
6157702e47cSPaolo Bonzini type_init(kvm_arm_gic_register_types)
616