Skip to content

vGIC: disabling an active HW interrupt orphans it, leaking the physical interrupt #371

Description

@irenebus

Hi, I was running Linux with Bao on my board (an ARMv8-A platform) and using the UART as the serial console. When init started (Run /sbin/init as init process) and the serial driver switched from polling to interrupt mode, the console output stalled — no more UART interrupts were delivered after the first one. After some debugging I
found that the UART interrupt was permanently stuck in Active at the GIC level.

With some debug prints I was able to trace what happens:

The interrupt is disabled while it is Active — that is, already acknowledged via IAR but not yet EOIR. This reconfiguration traps to EL2 and lands in vgic_int_set_field() (vgic.c:685), which does:

  1. vgic_remove_lr() — clears the LR, saves state = ACT.
  2. update_field() — sets enabled = false.
  3. vgic_route() — tries to re-place the interrupt.

But vgic_route() (vgic.c:147) returns immediately:

if ((interrupt->state == INV) || !interrupt->enabled) {
    return;
}

The interrupt is now orphaned: state = ACT, in_lr = false, and not in the spilled queue either.

Later the guest writes EOIR, finding no correspond entry in LRs, so it increments EOICount and fires the LRENP maintenance interrupt. vgic_eoir_highest_spilled_active() scans the spilled queues and still finds nothing — we never put it there. The physical deactivation never happens, and the UART interrupt line is dead.

(Changing the affinity of an active interrupt seems to have a similar issue.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions