From 19f12d2dca02da3072c0c31e2da281f6a447699e Mon Sep 17 00:00:00 2001 From: Matteo Concas Date: Tue, 24 Jun 2025 15:30:16 +0200 Subject: [PATCH] cpu/riscv: Add Smdbltrp extension compatibility If the Double Trap Extension is implemented, the MDT bit of the mstatus (or mstatush in RV32) register will be set when a trap is to be taken. The MIE (Machine Interrupt Enable) bit can only be set to 1 if the MDT bit is zero. Thus, we need to clear MDT first if we want to enable interrupts when dispatching a thread. MDT is also cleared in register a1 before restoring the interrupt frame as writing 1 to MDT will cause MIE to be set to 0. In RV64 this happens regardless of the value written to MIE in the same write. In RV32, MDT is in the mstatush so we do not need to clear during restore as this register is not restored. With this change all 60 SMP tests pass (compared to 20/60 before the fix). The tests have been run on hardware using two RV64 CPUs that implement the double trap extension. Update #5274 --- .../score/cpu/riscv/include/rtems/score/cpu.h | 2 ++ .../score/cpu/riscv/riscv-exception-handler.S | 30 ++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/cpukit/score/cpu/riscv/include/rtems/score/cpu.h b/cpukit/score/cpu/riscv/include/rtems/score/cpu.h index eed59f93d5..8bdf240f8c 100644 --- a/cpukit/score/cpu/riscv/include/rtems/score/cpu.h +++ b/cpukit/score/cpu/riscv/include/rtems/score/cpu.h @@ -45,6 +45,8 @@ extern "C" { #include #define RISCV_MSTATUS_MIE 0x8 +#define RISCV_MSTATUS_MDT 0x40000000000 +#define RISCV_MSTATUSH_MDT 0x400 #define CPU_ISR_PASSES_FRAME_POINTER FALSE diff --git a/cpukit/score/cpu/riscv/riscv-exception-handler.S b/cpukit/score/cpu/riscv/riscv-exception-handler.S index 34e7cbb0b3..eada726917 100644 --- a/cpukit/score/cpu/riscv/riscv-exception-handler.S +++ b/cpukit/score/cpu/riscv/riscv-exception-handler.S @@ -106,6 +106,20 @@ SYM(_RISCV_Exception_handler): /* Check if this is a synchronous or interrupt exception */ bgez a0, .Lsynchronous_exception + /* + * Interrupt exception, clear MDT bit. + * This is only necessary if the Smdbltrp extension is implemented. + * In that case not clearing the MDT bit would prevent us from setting + * the MIE bit later. + */ +#if __riscv_xlen == 64 + li t0, RISCV_MSTATUS_MDT + csrrc zero, mstatus, t0 +#elif __riscv_xlen == 32 + li t0, RISCV_MSTATUSH_MDT + csrrc zero, mstatush, t0 +#endif + /* Increment interrupt nest and thread dispatch disable level */ lw t0, PER_CPU_ISR_NEST_LEVEL(s0) lw t1, PER_CPU_THREAD_DISPATCH_DISABLE_LEVEL(s0) @@ -185,7 +199,6 @@ SYM(_RISCV_Exception_handler): /* Restore */ LREG a0, RISCV_INTERRUPT_FRAME_MSTATUS(sp) LREG a1, RISCV_INTERRUPT_FRAME_MEPC(sp) - LREG a2, RISCV_INTERRUPT_FRAME_A2(sp) LREG s0, RISCV_INTERRUPT_FRAME_S0(sp) LREG s1, RISCV_INTERRUPT_FRAME_S1(sp) LREG ra, RISCV_INTERRUPT_FRAME_RA(sp) @@ -201,6 +214,20 @@ SYM(_RISCV_Exception_handler): LREG t4, RISCV_INTERRUPT_FRAME_T4(sp) LREG t5, RISCV_INTERRUPT_FRAME_T5(sp) LREG t6, RISCV_INTERRUPT_FRAME_T6(sp) + + /* + * Clear MDT bit before restoring mstatus register. + * This is only necessary if the Smdbltrp extension is implemented. + * In that case, writing to mstatus with MDT set would clear the MIE + * bit, regardless of the MIE value written. + * On RV32, the MDT bit is in the mstatush CSR which is not restored. + */ +#if __riscv_xlen == 64 + li a2, RISCV_MSTATUS_MDT + not a2, a2 + and a0, a0, a2 +#endif + csrw mstatus, a0 csrw mepc, a1 #if __riscv_flen > 0 @@ -229,6 +256,7 @@ SYM(_RISCV_Exception_handler): #endif LREG a0, RISCV_INTERRUPT_FRAME_A0(sp) LREG a1, RISCV_INTERRUPT_FRAME_A1(sp) + LREG a2, RISCV_INTERRUPT_FRAME_A2(sp) addi sp, sp, CPU_INTERRUPT_FRAME_SIZE