riscv: Introduce RISCV_ALWAYS_SWITCH_THROUGH_ECALL

Some early RISC-V SoCs have a problem when an `mret` instruction is used
outside a trap handler.

After the latest Zephyr RISC-V huge rework, the arch_switch code is
indeed calling `mret` when not in handler mode, breaking some early
RISC-V platforms.

Optionally restore the old behavior by adding a new
CONFIG_RISCV_ALWAYS_SWITCH_THROUGH_ECALL symbol.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
This commit is contained in:
Carlo Caione 2022-06-30 14:40:52 +02:00 committed by Carles Cufí
commit 7a11d883cc
4 changed files with 27 additions and 2 deletions

View file

@ -28,6 +28,16 @@ config RISCV_GP
global pointer at program start or earlier than any instruction global pointer at program start or earlier than any instruction
using GP relative addressing. using GP relative addressing.
config RISCV_ALWAYS_SWITCH_THROUGH_ECALL
bool "Do not use mret outside a trap handler context"
depends on !RISCV_PMP
help
Use mret instruction only when in a trap handler.
This is for RISC-V implementations that require every mret to be
balanced with an ecall. This is not required by the RISC-V spec
and most people should say n here to minimize context switching
overhead.
menu "RISCV Processor Options" menu "RISCV Processor Options"
config INCLUDE_RESET_VECTOR config INCLUDE_RESET_VECTOR

View file

@ -278,6 +278,15 @@ is_kernel_syscall:
beq t0, t1, do_irq_offload beq t0, t1, do_irq_offload
#endif #endif
#ifdef CONFIG_RISCV_ALWAYS_SWITCH_THROUGH_ECALL
li t1, RV_ECALL_SCHEDULE
bne t0, t1, skip_schedule
lr a0, __z_arch_esf_t_a0_OFFSET(sp)
lr a1, __z_arch_esf_t_a1_OFFSET(sp)
j reschedule
skip_schedule:
#endif
/* default fault code is K_ERR_KERNEL_OOPS */ /* default fault code is K_ERR_KERNEL_OOPS */
li a0, 3 li a0, 3
j 1f j 1f
@ -483,7 +492,7 @@ irq_done:
call z_check_stack_sentinel call z_check_stack_sentinel
#endif #endif
reschedule: check_reschedule:
/* Get pointer to current thread on this CPU */ /* Get pointer to current thread on this CPU */
lr a1, ___cpu_t_current_OFFSET(s0) lr a1, ___cpu_t_current_OFFSET(s0)
@ -501,6 +510,8 @@ reschedule:
addi sp, sp, 16 addi sp, sp, 16
beqz a0, no_reschedule beqz a0, no_reschedule
reschedule:
/* /*
* Perform context switch: * Perform context switch:
* a0 = new thread * a0 = new thread

View file

@ -44,8 +44,11 @@ arch_switch(void *switch_to, void **switched_from)
struct k_thread *new = switch_to; struct k_thread *new = switch_to;
struct k_thread *old = CONTAINER_OF(switched_from, struct k_thread, struct k_thread *old = CONTAINER_OF(switched_from, struct k_thread,
switch_handle); switch_handle);
#ifdef CONFIG_RISCV_ALWAYS_SWITCH_THROUGH_ECALL
arch_syscall_invoke2((uintptr_t)new, (uintptr_t)old, RV_ECALL_SCHEDULE);
#else
z_riscv_switch(new, old); z_riscv_switch(new, old);
#endif
} }
FUNC_NORETURN void z_riscv_fatal_error(unsigned int reason, FUNC_NORETURN void z_riscv_fatal_error(unsigned int reason,

View file

@ -21,6 +21,7 @@
*/ */
#define RV_ECALL_RUNTIME_EXCEPT 0 #define RV_ECALL_RUNTIME_EXCEPT 0
#define RV_ECALL_IRQ_OFFLOAD 1 #define RV_ECALL_IRQ_OFFLOAD 1
#define RV_ECALL_SCHEDULE 2
#ifndef _ASMLANGUAGE #ifndef _ASMLANGUAGE