gicv3: Split EOI mode

Separate priority drop from deactivation so that when the kernel
delegates interrupts to userlevel, they can be left in the active state
until user level performs the ack invocation which deactivates them.
This is more efficient than doing a disable and enable operation
for each handled interrupt as it is core local and doesn't require
sending operations to the GIC distributor..

Signed-off-by: Kent McLeod <kent@kry10.com>
This commit is contained in:
Kent McLeod 2022-09-29 22:17:23 +10:00 committed by Gerwin Klein
parent 97559a40b2
commit a4b0c8d36a
4 changed files with 53 additions and 7 deletions

View file

@ -306,14 +306,35 @@ static inline void maskInterrupt(bool_t disable, irq_t irq)
}
}
static inline void deactivateInterrupt(irq_t irq)
{
word_t hw_irq = IRQT_TO_IRQ(irq);
/* Perform deactivation of hw_irq */
SYSTEM_WRITE_WORD(ICC_DIR_EL1, hw_irq);
}
static inline void ackInterrupt(irq_t irq)
{
assert(IS_IRQ_VALID(active_irq[CURRENT_CPU_INDEX()])
&& (active_irq[CURRENT_CPU_INDEX()] & IRQ_MASK) == IRQT_TO_IRQ(irq));
active_irq[CURRENT_CPU_INDEX()] = IRQ_NONE;
word_t hw_irq = IRQT_TO_IRQ(irq);
/* Set End of Interrupt for active IRQ: ICC_EOIR1_EL1 */
SYSTEM_WRITE_WORD(ICC_EOIR1_EL1, active_irq[CURRENT_CPU_INDEX()]);
active_irq[CURRENT_CPU_INDEX()] = IRQ_NONE;
/* Perform priority drop for current IRQ */
SYSTEM_WRITE_WORD(ICC_EOIR1_EL1, hw_irq);
// If the IRQ is not going to user level then we need to deactivate it too.
if (unlikely(hw_irq > maxIRQ) ||
intStateIRQTable[IRQT_TO_IDX(irq)] != IRQSignal) {
/* There needs to be an isb() to ensure completion of the system
* register write in ackInterrupt
*/
isb();
deactivateInterrupt(irq);
}
}

View file

@ -97,6 +97,20 @@ static inline void maskInterrupt(bool_t disable, irq_t irq);
*/
static inline void ackInterrupt(irq_t irq);
/**
* Deactivates the interrupt
*
* When the interrupt controller supports delegating the interrupt to a lower
* privilege level, this function can be called to signal the completion of
* interrupt processing so that the interrupt state machine can be moved out of
* the active state.
*
* Currently only supported by gicv3 driver.
*
* @param[in] irq The interrupt request
*/
static inline void deactivateInterrupt(irq_t irq);
/**
* Called when getActiveIRQ returns irqInvalid while the kernel is handling an
* interrupt entry. An implementation is not required to do anything here, but
@ -116,3 +130,7 @@ static inline void handleSpuriousIRQ(void);
*/
static inline void handleReservedIRQ(irq_t irq);
#ifndef CONFIG_ARM_GIC_V3_SUPPORT
static inline void deactivateInterrupt(irq_t irq) {}
#endif

View file

@ -276,9 +276,9 @@ BOOT_CODE static void cpu_iface_init(void)
/* Set priority mask register: ICC_PMR_EL1 */
SYSTEM_WRITE_WORD(ICC_PMR_EL1, DEFAULT_PMR_VALUE);
/* EOI drops priority and deactivates the interrupt: ICC_CTLR_EL1 */
/* EOI drops priority of the interrupt, deactivation happens separately: ICC_CTLR_EL1 */
SYSTEM_READ_WORD(ICC_CTLR_EL1, icc_ctlr);
icc_ctlr &= ~GICC_CTLR_EL1_EOImode_drop;
icc_ctlr |= GICC_CTLR_EL1_EOImode_drop;
SYSTEM_WRITE_WORD(ICC_CTLR_EL1, icc_ctlr);
/* Enable Group1 interrupts: ICC_IGRPEN1_EL1 */

View file

@ -149,8 +149,12 @@ void invokeIRQHandler_AckIRQ(irq_t irq)
return;
}
#endif
maskInterrupt(false, irq);
#endif
if (config_set(CONFIG_ARM_GIC_V3_SUPPORT)) {
deactivateInterrupt(irq);
} else {
maskInterrupt(false, irq);
}
#endif /* CONFIG_ARCH_RISCV */
}
void invokeIRQHandler_SetIRQHandler(irq_t irq, cap_t cap, cte_t *slot)
@ -218,8 +222,11 @@ void handleInterrupt(irq_t irq)
#endif
}
#ifndef CONFIG_ARCH_RISCV
maskInterrupt(true, irq);
if (!config_set(CONFIG_ARM_GIC_V3_SUPPORT)) {
maskInterrupt(true, irq);
}
#endif
break;
}