arch: riscv: handle interrupt level for CLIC

CLIC supports mintstatus.MIL (RO) and mcause.MPIL (RW) for the current
interrupt level and the previous interrut level before a trap. Each ISR
must execute MRET to set mcause.MPIL back to mintstatus.MIL.

This commit introduces CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL to handle
mcause.MPIL for interrupt preemption in nested ISR, and uses
CONFIG_RISCV_ALWAYS_SWITCH_THROUGH_ECALL to ensure ISR always switch out
with MRET.

e.g.
  With CONFIG_RISCV_ALWAYS_SWITCH_THROUGH_ECALL=n, a context-switch in
  ISR may skip MRET in this flow:
  IRQ -> _isr_wrapper -> z_riscv_switch() -> retrun to arch_switch()

Signed-off-by: Jimmy Zheng <jimmyzhe@andestech.com>
This commit is contained in:
Jimmy Zheng 2024-07-03 17:11:12 +08:00 committed by Benjamin Cabé
commit 3804387350
5 changed files with 37 additions and 0 deletions

View file

@ -220,9 +220,20 @@ config RISCV_HAS_PLIC
config RISCV_HAS_CLIC
bool
depends on RISCV_PRIVILEGED
select RISCV_ALWAYS_SWITCH_THROUGH_ECALL if MULTITHREADING
select CLIC_SUPPORT_INTERRUPT_LEVEL if !NRFX_CLIC
help
Does the SOC provide support for a Core-Local Interrupt Controller (CLIC).
config CLIC_SUPPORT_INTERRUPT_LEVEL
bool
depends on RISCV_HAS_CLIC
help
For CLIC implementations with extended interrupt level, where
higher-numbered interrupt levels can preempt lower-numbered interrupt
levels. This option handles interrupt level in ISR to ensure proper
nested ISR exits.
config RISCV_SOC_EXCEPTION_FROM_IRQ
bool
help

View file

@ -198,6 +198,12 @@ SECTION_FUNC(exception.entry, _isr_wrapper)
sr s0, __struct_arch_esf_s0_OFFSET(sp)
get_current_cpu s0
#ifdef CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL
/* Save mcause register */
csrr t0, mcause
sr t0, __struct_arch_esf_mcause_OFFSET(sp)
#endif /* CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL */
/* Save MEPC register */
csrr t0, mepc
sr t0, __struct_arch_esf_mepc_OFFSET(sp)
@ -737,6 +743,13 @@ fp_trap_exit:
/* Restore MEPC and MSTATUS registers */
lr t0, __struct_arch_esf_mepc_OFFSET(sp)
lr t2, __struct_arch_esf_mstatus_OFFSET(sp)
#ifdef CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL
/* Restore MCAUSE register for previous interrupt level. */
lr t1, __struct_arch_esf_mcause_OFFSET(sp)
csrw mcause, t1
#endif /* CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL */
csrw mepc, t0
csrw mstatus, t2

View file

@ -112,6 +112,10 @@ GEN_OFFSET_STRUCT(arch_esf, a7);
GEN_OFFSET_STRUCT(arch_esf, mepc);
GEN_OFFSET_STRUCT(arch_esf, mstatus);
#ifdef CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL
GEN_OFFSET_STRUCT(arch_esf, mcause);
#endif /* CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL */
GEN_OFFSET_STRUCT(arch_esf, s0);
#ifdef CONFIG_USERSPACE

View file

@ -110,6 +110,11 @@ void arch_new_thread(struct k_thread *thread, k_thread_stack_t *stack,
SOC_ISR_STACKING_ESR_INIT;
#endif
#ifdef CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL
/* Clear the previous interrupt level. */
stack_init->mcause = 0;
#endif
thread->callee_saved.sp = (unsigned long)stack_init;
/* where to go when returning from z_riscv_switch() */

View file

@ -78,6 +78,10 @@ struct arch_esf {
unsigned long a7; /* function argument */
#endif /* !CONFIG_RISCV_ISA_RV32E */
#ifdef CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL
unsigned long mcause; /* machine cause register */
#endif /* CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL */
unsigned long mepc; /* machine exception program counter */
unsigned long mstatus; /* machine status register */