Currently, Xen will return IO unhandled when guests write ICPENDR*
virtual registers, which will raise a data abort inside the guest.
For Linux guest, these virtual registers will not be accessed. But
for Zephyr, these virtual registers will be accessed during the
initialization. Zephyr guest will get an IO data abort and crash.
Emulating ICPENDR is not easy with the existing vGIC, this patch
reworks the emulation to ignore write access to ICPENDR* virtual
registers and print a message about whether they are already pending
instead of returning unhandled.
More details can be found at [1].
[1] https://github.com/zephyrproject-rtos/zephyr/blob/
eaf6cf745df3807e6e
cc941c3a30de6c179ae359/drivers/interrupt_controller/intc_gicv3.c#L274
Signed-off-by: Hongda Deng <hongda.deng@arm.com>
Release-Acked-by: Ian Jackson <iwj@xenproject.org>
Reviewed-by: Julien Grall <jgrall@amazon.com>
case VRANGE32(GICD_ICPENDR, GICD_ICPENDRN):
if ( dabt.size != DABT_WORD ) goto bad_width;
- printk(XENLOG_G_ERR
- "%pv: vGICD: unhandled word write %#"PRIregister" to ICPENDR%d\n",
- v, r, gicd_reg - GICD_ICPENDR);
- return 0;
+ rank = vgic_rank_offset(v, 1, gicd_reg - GICD_ICPENDR, DABT_WORD);
+ if ( rank == NULL ) goto write_ignore;
+
+ vgic_check_inflight_irqs_pending(v->domain, v, rank->index, r);
+
+ goto write_ignore;
case VRANGE32(GICD_ISACTIVER, GICD_ISACTIVERN):
if ( dabt.size != DABT_WORD ) goto bad_width;
case VRANGE32(GICD_ICPENDR, GICD_ICPENDRN):
if ( dabt.size != DABT_WORD ) goto bad_width;
- printk(XENLOG_G_ERR
- "%pv: %s: unhandled word write %#"PRIregister" to ICPENDR%d\n",
- v, name, r, reg - GICD_ICPENDR);
- return 0;
+ rank = vgic_rank_offset(v, 1, reg - GICD_ICPENDR, DABT_WORD);
+ if ( rank == NULL ) goto write_ignore;
+
+ vgic_check_inflight_irqs_pending(v->domain, v, rank->index, r);
+
+ goto write_ignore;
case VRANGE32(GICD_ISACTIVER, GICD_ISACTIVERN):
if ( dabt.size != DABT_WORD ) goto bad_width;
info, gicr_reg, r);
case VREG32(GICR_ICPENDR0):
- if ( dabt.size != DABT_WORD ) goto bad_width;
- printk(XENLOG_G_ERR
- "%pv: vGICR: SGI: unhandled word write %#"PRIregister" to ICPENDR0\n",
- v, r);
- return 0;
+ return __vgic_v3_distr_common_mmio_write("vGICR: SGI", v,
+ info, gicr_reg, r);
case VREG32(GICR_IGRPMODR0):
/* We do not implement security extensions for guests, write ignore */
}
}
+void vgic_check_inflight_irqs_pending(struct domain *d, struct vcpu *v,
+ unsigned int rank, uint32_t r)
+{
+ const unsigned long mask = r;
+ unsigned int i;
+
+ for_each_set_bit( i, &mask, 32 )
+ {
+ struct pending_irq *p;
+ struct vcpu *v_target;
+ unsigned long flags;
+ unsigned int irq = i + 32 * rank;
+
+ v_target = vgic_get_target_vcpu(v, irq);
+
+ spin_lock_irqsave(&v_target->arch.vgic.lock, flags);
+
+ p = irq_to_pending(v_target, irq);
+
+ if ( p && !list_empty(&p->inflight) )
+ printk(XENLOG_G_WARNING
+ "%pv trying to clear pending interrupt %u.\n",
+ v, irq);
+
+ spin_unlock_irqrestore(&v_target->arch.vgic.lock, flags);
+ }
+}
+
/*
* Local variables:
* mode: C
enum gic_sgi_mode irqmode, int virq,
const struct sgi_target *target);
extern bool vgic_migrate_irq(struct vcpu *old, struct vcpu *new, unsigned int irq);
+extern void vgic_check_inflight_irqs_pending(struct domain *d, struct vcpu *v,
+ unsigned int rank, uint32_t r);
#endif /* !CONFIG_NEW_VGIC */