Skip to content

Commit 131f7f7

Browse files
committed
[ot] hw/intc: Only clear pending IRQs at PLIC when claimed
See the comment for more details; to allow for proper NMI functionality based on regular IRQ and NMI handling routines, we must ensure that in `sifive_plic_irq_request`, we only clear an IRQs pending status when that IRQ has been claimed. Failure to do so can mean we clear an IRQ at a peripheral as part of an NMI service routine and as such never call into the IRQ handler when the NMI handler returns, as would be expected. This commit updates the SiFive PLIC so that it works as expected. Signed-off-by: Alex Jones <[email protected]>
1 parent f6c86dd commit 131f7f7

File tree

1 file changed

+21
-4
lines changed

1 file changed

+21
-4
lines changed

hw/intc/sifive_plic.c

+21-4
Original file line numberDiff line numberDiff line change
@@ -361,12 +361,29 @@ static void parse_hart_config(SiFivePLICState *plic)
361361

362362
static void sifive_plic_irq_request(void *opaque, int irq, int level)
363363
{
364-
SiFivePLICState *s = opaque;
364+
SiFivePLICState *plic = opaque;
365365

366-
assert(irq < s->num_sources);
366+
assert(irq < plic->num_sources);
367367

368-
sifive_plic_set_pending(s, irq, level > 0);
369-
sifive_plic_update(s);
368+
/*
369+
* In OpenTitan (or more generally when we have a system with support for
370+
* NMIs), we only want to clear a pending IRQ at the PLIC when it has
371+
* been claimed. Normally this is not a problem - the IRQ will cause
372+
* software to acknowledge the pending IRQ at the peripheral, which will
373+
* propagate to the PLIC, which has already been serviced. But with NMIs,
374+
* we have the case where (a) a peripheral generates an NMI and IRQ, (b)
375+
* the NMI handler disables the IRQ at the peripheral, as part of
376+
* acknowleding the NMI, (c) this would cause the PLIC to de-assert the
377+
* IRQ, but since we haven't claimed it yet we still want it to call our
378+
* ISR handler as normal after the NMI handler exits. So, we should only
379+
* de-assert a pending IRQ when it is claimed.
380+
*/
381+
bool is_claimed = (plic->claimed[irq >> 5] & (1u << (irq & 31)));
382+
bool is_pending = (plic->pending[irq >> 5] & (1u << (irq & 31)));
383+
if (level || (!is_pending || is_claimed)) {
384+
sifive_plic_set_pending(plic, irq, level > 0);
385+
}
386+
sifive_plic_update(plic);
370387
}
371388

372389
static void sifive_plic_realize(DeviceState *dev, Error **errp)

0 commit comments

Comments
 (0)