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:
vgic_remove_lr() — clears the LR, saves state = ACT.
update_field() — sets enabled = false.
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.)
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
Activeat 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 viaIARbut not yetEOIR. This reconfiguration traps to EL2 and lands invgic_int_set_field()(vgic.c:685), which does:vgic_remove_lr()— clears the LR, savesstate = ACT.update_field()— setsenabled = false.vgic_route()— tries to re-place the interrupt.But
vgic_route()(vgic.c:147) returns immediately: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 incrementsEOICountand 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.)