intc/arm_gic: Implement virtualization extensions in gic_(deactivate|complete_irq)
Implement virtualization extensions in the gic_deactivate_irq() and gic_complete_irq() functions. When the guest writes an invalid vIRQ to V_EOIR or V_DIR, since the GICv2 specification is not entirely clear here, we adopt the behaviour observed on real hardware: * When V_CTRL.EOIMode is false (EOI split is disabled): - In case of an invalid vIRQ write to V_EOIR: -> If some bits are set in H_APR, an invalid vIRQ write to V_EOIR triggers a priority drop, and increments V_HCR.EOICount. -> If V_APR is already cleared, nothing happen - An invalid vIRQ write to V_DIR is ignored. * When V_CTRL.EOIMode is true: - In case of an invalid vIRQ write to V_EOIR: -> If some bits are set in H_APR, an invalid vIRQ write to V_EOIR triggers a priority drop. -> If V_APR is already cleared, nothing happen - An invalid vIRQ write to V_DIR increments V_HCR.EOICount. Signed-off-by: Luc Michel <luc.michel@greensocs.com> Message-id: 20180727095421.386-13-luc.michel@greensocs.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
439badd66d
commit
02f2e22d7c
@ -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]
|
||||
|
Loading…
Reference in New Issue
Block a user