summaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/intc/arm_gic.c51
1 files changed, 47 insertions, 4 deletions
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index d80acde989..3cddf65826 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -583,20 +583,19 @@ static void gic_deactivate_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
{
int group;
- if (irq >= s->num_irq) {
+ if (irq >= GIC_MAXIRQ || (!gic_is_vcpu(cpu) && irq >= s->num_irq)) {
/*
* This handles two cases:
* 1. If software writes the ID of a spurious interrupt [ie 1023]
* to the GICC_DIR, the GIC ignores that write.
* 2. If software writes the number of a non-existent interrupt
* this must be a subcase of "value written is not an active interrupt"
- * and so this is UNPREDICTABLE. We choose to ignore it.
+ * and so this is UNPREDICTABLE. We choose to ignore it. For vCPUs,
+ * all IRQs potentially exist, so this limit does not apply.
*/
return;
}
- group = gic_has_groups(s) && gic_test_group(s, irq, cpu);
-
if (!gic_eoi_split(s, cpu, attrs)) {
/* This is UNPREDICTABLE; we choose to ignore it */
qemu_log_mask(LOG_GUEST_ERROR,
@@ -604,6 +603,17 @@ static void gic_deactivate_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
return;
}
+ if (gic_is_vcpu(cpu) && !gic_virq_is_valid(s, irq, cpu)) {
+ /* This vIRQ does not have an LR entry which is either active or
+ * pending and active. Increment EOICount and ignore the write.
+ */
+ int rcpu = gic_get_vcpu_real_id(cpu);
+ s->h_hcr[rcpu] += 1 << R_GICH_HCR_EOICount_SHIFT;
+ return;
+ }
+
+ group = gic_has_groups(s) && gic_test_group(s, irq, cpu);
+
if (gic_cpu_ns_access(s, cpu, attrs) && !group) {
DPRINTF("Non-secure DI for Group0 interrupt %d ignored\n", irq);
return;
@@ -618,6 +628,39 @@ static void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
int group;
DPRINTF("EOI %d\n", irq);
+ if (gic_is_vcpu(cpu)) {
+ /* The call to gic_prio_drop() will clear a bit in GICH_APR iff the
+ * running prio is < 0x100.
+ */
+ bool prio_drop = s->running_priority[cpu] < 0x100;
+
+ if (irq >= GIC_MAXIRQ) {
+ /* Ignore spurious interrupt */
+ return;
+ }
+
+ gic_drop_prio(s, cpu, 0);
+
+ if (!gic_eoi_split(s, cpu, attrs)) {
+ bool valid = gic_virq_is_valid(s, irq, cpu);
+ if (prio_drop && !valid) {
+ /* We are in a situation where:
+ * - V_CTRL.EOIMode is false (no EOI split),
+ * - The call to gic_drop_prio() cleared a bit in GICH_APR,
+ * - This vIRQ does not have an LR entry which is either
+ * active or pending and active.
+ * In that case, we must increment EOICount.
+ */
+ int rcpu = gic_get_vcpu_real_id(cpu);
+ s->h_hcr[rcpu] += 1 << R_GICH_HCR_EOICount_SHIFT;
+ } else if (valid) {
+ gic_clear_active(s, irq, cpu);
+ }
+ }
+
+ return;
+ }
+
if (irq >= s->num_irq) {
/* This handles two cases:
* 1. If software writes the ID of a spurious interrupt [ie 1023]