ciad cleared the last interrupt the L2VIC delivered regardless of its mask. So a ciad for any other interrupt would get applied incorrectly.
Only clear the active interrupt when the ciad acknowledges the CPU interrupt that its VID group is delivered on, and only if that interrupt was taken or set with siad. Reviewed-by: Pierrick Bouvier <[email protected]> Signed-off-by: Brian Cain <[email protected]> --- include/hw/intc/hex-l2vic.h | 14 ++++++++++---- hw/intc/hex-l2vic.c | 10 +++++++--- target/hexagon/op_helper.c | 14 +++++++++----- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/include/hw/intc/hex-l2vic.h b/include/hw/intc/hex-l2vic.h index edc278266cb..ae067fe95eb 100644 --- a/include/hw/intc/hex-l2vic.h +++ b/include/hw/intc/hex-l2vic.h @@ -31,8 +31,13 @@ typedef struct HexL2VicInterfaceClass { void (*update_vid)(HexL2VicInterface *l2vic, uint32_t group, uint32_t value); - /* Clear interrupt using CIAD instruction */ - void (*clear_interrupt)(HexL2VicInterface *l2vic); + /* + * Clear interrupts using the CIAD instruction. + * @mask the CPU interrupts that the CIAD acknowledged. The interrupt is + * only cleared if @mask has the CPU interrupt that its VID group is + * delivered on. + */ + void (*clear_interrupt)(HexL2VicInterface *l2vic, uint32_t mask); } HexL2VicInterfaceClass; DECLARE_OBJ_CHECKERS(HexL2VicInterface, HexL2VicInterfaceClass, @@ -52,10 +57,11 @@ static inline void l2vic_update_vid(HexL2VicInterface *l2vic, uint32_t group, k->update_vid(l2vic, group, value); } -static inline void l2vic_clear_interrupt(HexL2VicInterface *l2vic) +static inline void l2vic_clear_interrupt(HexL2VicInterface *l2vic, + uint32_t mask) { HexL2VicInterfaceClass *k = HEX_L2VIC_INTERFACE_GET_CLASS(l2vic); - k->clear_interrupt(l2vic); + k->clear_interrupt(l2vic, mask); } #endif /* HW_INTC_HEX_L2VIC_H */ diff --git a/hw/intc/hex-l2vic.c b/hw/intc/hex-l2vic.c index 736f1be3b1d..b73ecb821e4 100644 --- a/hw/intc/hex-l2vic.c +++ b/hw/intc/hex-l2vic.c @@ -448,12 +448,16 @@ static void l2vic_interface_update_vid_impl(HexL2VicInterface *iface, l2vic_update_all(s); } -static void l2vic_interface_clear_interrupt_impl(HexL2VicInterface *iface) +static void l2vic_interface_clear_interrupt_impl(HexL2VicInterface *iface, + uint32_t mask) { HexL2VICState *s = HEX_L2VIC(iface); + const uint32_t size = L2VIC_INTERRUPT_MAX; + const uint32_t active_irq = find_first_bit32(s->int_status, size); - if (s->vid < L2VIC_INTERRUPT_MAX) { - clear_bit32(s->vid, s->int_status); + /* VID group n is delivered on CPU interrupt n + 2 */ + if (active_irq != size && (mask & BIT(get_vid(s, active_irq) + 2))) { + clear_bit32(active_irq, s->int_status); } l2vic_update_all(s); } diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 7cc327bb36f..40891e5ee1a 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -1686,26 +1686,30 @@ void HELPER(raise_stack_overflow)(CPUHexagonState *env, uint32_t slot, void HELPER(ciad)(CPUHexagonState *env, uint32_t mask) { uint32_t iad; + uint32_t was_set; HexagonCPU *cpu; BQL_LOCK_GUARD(); cpu = env_archcpu(env); if (cpu->cfg.hex_def->hex_version >= HEX_VER_V81) { iad = hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IAD, - env->threadId) & ~mask; - hexagon_globalreg_write(cpu->globalregs, HEX_SREG_IAD, iad, + env->threadId); + was_set = iad & mask; + hexagon_globalreg_write(cpu->globalregs, HEX_SREG_IAD, iad & ~mask, env->threadId); } else { uint32_t ipendad = hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, env->threadId); - fSET_FIELD(ipendad, IPENDAD_IAD, - fGET_FIELD(ipendad, IPENDAD_IAD) & ~mask); + iad = fGET_FIELD(ipendad, IPENDAD_IAD); + was_set = iad & mask; + fSET_FIELD(ipendad, IPENDAD_IAD, iad & ~mask); hexagon_globalreg_write(cpu->globalregs, HEX_SREG_IPENDAD, ipendad, env->threadId); } - l2vic_clear_interrupt(cpu->l2vic); + /* Only an interrupt that was taken, or set with siad, can be cleared */ + l2vic_clear_interrupt(cpu->l2vic, was_set); hex_interrupt_update(env); } -- 2.34.1
