]> git.ipfire.org Git - thirdparty/qemu.git/commitdiff
hw/intc: riscv_aplic: Fix level trigger IRQ in direct delivery mode
authorJim Shu <jim.shu@sifive.com>
Tue, 28 Apr 2026 16:01:00 +0000 (00:01 +0800)
committerAlistair Francis <alistair.francis@wdc.com>
Thu, 21 May 2026 23:45:47 +0000 (09:45 +1000)
According to the AIA spec ch4.7 ("Precise effects on interrupt-pending
bits"), pending bit of APLIC should be set/cleared whenever the
rectified input value is high/low in the both level-trigger mode
and direct delivery mode.

Currently, QEMU APLIC only clears the pending bit when interrupt is
claimed in APLIC, but not clears it when the rectified input value is
low. (e.g. IRQ source signal is low in the LEVEL_HIGH/Level1 mode).
The software may receive an additional IRQ if the peripheral
triggers one after the software clears the APLIC IRQ but before it
clears the peripheral's IRQ.

Thus, we also clear the pending bit via the rectified input value in the
level-trigger mode.

This change doesn't affect MSI delivery mode. Calling
riscv_aplic_msi_irq_update() when IRQ pending is low will do nothing.

Signed-off-by: Jim Shu <jim.shu@sifive.com>
Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com>
Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
Message-ID: <20260428160103.3551125-2-jim.shu@sifive.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
hw/intc/riscv_aplic.c

index 8f70043111494f9bd5bb8c294e6ba17125e8b2f1..791e0b01b96fa993add784e3895d42ebe70d55f2 100644 (file)
@@ -591,14 +591,14 @@ static void riscv_aplic_request(void *opaque, int irq, int level)
         }
         break;
     case APLIC_SOURCECFG_SM_LEVEL_HIGH:
-        if ((level > 0) && !(state & APLIC_ISTATE_PENDING)) {
-            riscv_aplic_set_pending_raw(aplic, irq, true);
+        if ((level > 0) != !!(state & APLIC_ISTATE_PENDING)) {
+            riscv_aplic_set_pending_raw(aplic, irq, level > 0);
             update = true;
         }
         break;
     case APLIC_SOURCECFG_SM_LEVEL_LOW:
-        if ((level <= 0) && !(state & APLIC_ISTATE_PENDING)) {
-            riscv_aplic_set_pending_raw(aplic, irq, true);
+        if ((level <= 0) != !!(state & APLIC_ISTATE_PENDING)) {
+            riscv_aplic_set_pending_raw(aplic, irq, level <= 0);
             update = true;
         }
         break;