Hi all,
I'd like to ask whether the following behavior of SDEI_EVENT_0 is intended, and whether it's a known limitation of the SDEI + GIC SGI semantics. I've read the code carefully but want to confirm the intent before relying on or working around it.
## Setup
- TF-A SDEI, event 0 is a "signalable" private event whose backing interrupt is a Secure SGI (on our platform, SGI 9). - include/services/sdei.h, SDEI_DEFINE_EVENT_0(_intr) -> SDEI_PRIVATE_EVENT(SDEI_EVENT_0, _intr, SDEI_MAPF_SIGNALABLE) - services/std_svc/sdei/sdei_main.c: asserts event 0 must be a Secure SGI and may only carry SIGNALABLE|PRIVATE.
## What I observe in code
When the PE is SDEI-masked (client called SDEI_PE_MASK) and the SGI backing event 0 fires, the handler does *not* preserve the trigger:
1. The EL3 FIQ is taken and acked in bl31/ehf.c ehf_el3_interrupt_handler() via plat_ic_acknowledge_interrupt() (reads ICC_IAR — interrupt now Active, its Pending bit consumed).
2. It reaches sdei_intr_handler() (services/std_svc/sdei/sdei_intr_mgmt.c). At L463, state->pe_masked is true, so it calls handle_masked_trigger() and returns 0.
3. handle_masked_trigger() : /* Nothing to do for event 0 */ if (map->ev_num == SDEI_EVENT_0) return; i.e. for event 0 it does none of the "freeze for replay" steps that a normal private event takes : plat_ic_disable_interrupt(intr); plat_ic_set_interrupt_pending(intr); plat_ic_end_of_interrupt(intr_raw); state->pending_enables = true;
So while the PE is masked, an event-0 trigger is acknowledged and then silently dropped, with nothing stored for replay on the next SDEI_PE_UNMASK.
## Why I think this happens (and want to confirm)
The "freeze for replay" mechanism for normal private events relies on re-asserting Pending via plat_ic_set_interrupt_pending(), because the ack already consumed the Pending bit. But for an SGI this is not permitted — plat/common/plat_gicv3.c: asserts it out:
void plat_ic_set_interrupt_pending(unsigned int id) { /* Disallow setting SGIs pending */ assert(id >= MIN_PPI_ID); gicv3_set_interrupt_pending(id, plat_my_core_pos()); }
So event 0 (backed by an SGI) cannot have its Pending bit re-asserted in software, which is consistent with the "Nothing to do for event 0" early return. Combined with the level/merge semantics of GIC SGIs (a sender firing the same SGI N times is represented as a single Pending, not a count), this means a masked-PE client can at most learn "an event happened while masked", never how many or which — by design.
## My questions
1. Is dropping an event-0 trigger while the PE is masked the intended behaviour (a consequence of "event 0 is a signalable SGI, counting is not guaranteed"), rather than a bug? The "Nothing to do for event 0" comment reads as deliberate, but I'd like explicit confirmation.
2. Is the recommended mitigation simply to avoid firing event 0 while the PE is masked (buffer on the sender side, deliver after unmask), or to use a normal explicit event for anything requiring precise accounting? Or is there a platform hook intended for this case that I'm missing (e.g. plat_sdei_handle_masked_trigger — though that path is only taken for shared ANY-PE events , not event 0)?
3. Separately (not the focus of this question): on this masked path neither handle_masked_trigger() nor sdei_intr_handler() calls plat_ic_end_of_interrupt() for event 0, and neither the EHF top-level handler nor the runtime_exceptions.S dispatch macro performs a fallback EOI. So the acked SGI appears to be left Active with no EOI on this path. Is there a later EOI for this SGI that I've missed, or is that also expected here? I don't want to conflate this with the "drop" question above; I'm just flagging it.
For reference, this is on an in-tree SDEI path; the platform only defines event 0 via SDEI_DEFINE_EVENT_0(PLAT_SDEI_SGI_PRIVATE) and does not modify handle_masked_trigger().
Thanks, Tianliang
tf-a@lists.trustedfirmware.org