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
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"
config INCLUDE_RESET_VECTOR

View file

@ -278,6 +278,15 @@ is_kernel_syscall:
beq t0, t1, do_irq_offload
#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 */
li a0, 3
j 1f
@ -483,7 +492,7 @@ irq_done:
call z_check_stack_sentinel
#endif
reschedule:
check_reschedule:
/* Get pointer to current thread on this CPU */
lr a1, ___cpu_t_current_OFFSET(s0)
@ -501,6 +510,8 @@ reschedule:
addi sp, sp, 16
beqz a0, no_reschedule
reschedule:
/*
* Perform context switch:
* 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 *old = CONTAINER_OF(switched_from, struct k_thread,
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);
#endif
}
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_IRQ_OFFLOAD 1
#define RV_ECALL_SCHEDULE 2
#ifndef _ASMLANGUAGE