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>
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 72f094b..b8030cb 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -28,6 +28,16 @@
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
diff --git a/arch/riscv/core/isr.S b/arch/riscv/core/isr.S
index 7a9968c..de9ade6 100644
--- a/arch/riscv/core/isr.S
+++ b/arch/riscv/core/isr.S
@@ -278,6 +278,15 @@
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 @@
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 @@
addi sp, sp, 16
beqz a0, no_reschedule
+reschedule:
+
/*
* Perform context switch:
* a0 = new thread
diff --git a/arch/riscv/include/kernel_arch_func.h b/arch/riscv/include/kernel_arch_func.h
index 7de898f..1eee9e9 100644
--- a/arch/riscv/include/kernel_arch_func.h
+++ b/arch/riscv/include/kernel_arch_func.h
@@ -44,8 +44,11 @@
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,
diff --git a/include/zephyr/arch/riscv/syscall.h b/include/zephyr/arch/riscv/syscall.h
index be58172..e9d9211 100644
--- a/include/zephyr/arch/riscv/syscall.h
+++ b/include/zephyr/arch/riscv/syscall.h
@@ -21,6 +21,7 @@
*/
#define RV_ECALL_RUNTIME_EXCEPT 0
#define RV_ECALL_IRQ_OFFLOAD 1
+#define RV_ECALL_SCHEDULE 2
#ifndef _ASMLANGUAGE