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:
parent
d2ac7b4835
commit
7a11d883cc
4 changed files with 27 additions and 2 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue