riscv: Add support for hardware stacking / unstacking
Some RISC-V SoCs implement a mechanism for hardware supported stacking / unstacking of registers during ISR / exceptions. What happens is that on ISR / exception entry part of the context is automatically saved by the hardware on the stack without software intervention, and the same part of the context is restored by the hardware usually on mret. This is currently not yet supported by Zephyr, where the full context must be saved by software in the full fledged ESF. This patcheset is trying to address exactly this case. At least three things are needed to support in a general fashion this problem: (1) a way to store in software only the part of the ESF not already stacked by hardware, (2) a way to restore in software only the part of the context that is not going to be restored by hardware and (3) a way to define a custom ESF. Point (3) is important because the full ESF frame is now composed by a custom part depending on the hardware (that can choose which register to stack / unstack and the order they are saved onto the stack) and a part defined in software for the remaining part of the context. In this patch a new CONFIG_RISCV_SOC_HAS_ISR_STACKING is introduced that enables the code path supporting the three points by the mean of three macros that must be implemented by the user in a soc_stacking.h file: SOC_ISR_SW_STACKING, SOC_ISR_SW_UNSTACKING and SOC_ISR_STACKING_ESF (refer to the symbol help for more details). This is an example of soc_isr_stacking.h for an hardware that doesn't do any hardware stacking / unstacking but everything is managed in software: #ifndef __SOC_ISR_STACKING #define __SOC_ISR_STACKING #if !defined(_ASMLANGUAGE) #define SOC_ISR_STACKING_ESF_DECLARE \ struct __esf { \ unsigned long ra; \ unsigned long t0; \ unsigned long t1; \ unsigned long t2; \ unsigned long t3; \ unsigned long t4; \ unsigned long t5; \ unsigned long t6; \ unsigned long a0; \ unsigned long a1; \ unsigned long a2; \ unsigned long a3; \ unsigned long a4; \ unsigned long a5; \ unsigned long a6; \ unsigned long a7; \ unsigned long mepc; \ unsigned long mstatus; \ unsigned long s0; \ } __aligned(16) #else #define SOC_ISR_SW_STACKING \ addi sp, sp, -__z_arch_esf_t_SIZEOF; \ DO_CALLER_SAVED(sr); #define SOC_ISR_SW_UNSTACKING \ DO_CALLER_SAVED(lr); #endif /* _ASMLANGUAGE */ #endif /* __SOC_ISR_STACKING */ Signed-off-by: Carlo Caione <ccaione@baylibre.com>
This commit is contained in:
parent
b0d70ba176
commit
c13d23a43e
3 changed files with 51 additions and 0 deletions
|
@ -46,6 +46,37 @@ config INCLUDE_RESET_VECTOR
|
||||||
Include the reset vector stub, which initializes the stack and
|
Include the reset vector stub, which initializes the stack and
|
||||||
prepares for running C code.
|
prepares for running C code.
|
||||||
|
|
||||||
|
config RISCV_SOC_HAS_ISR_STACKING
|
||||||
|
bool
|
||||||
|
depends on !USERSPACE
|
||||||
|
help
|
||||||
|
Enable low-level SOC-specific hardware stacking / unstacking
|
||||||
|
operations during ISR. This hidden option needs to be selected by SoC
|
||||||
|
if this feature is supported.
|
||||||
|
|
||||||
|
Some SOCs implement a mechanism for which, on interrupt handling,
|
||||||
|
part of the context is automatically saved by the hardware on the
|
||||||
|
stack according to a custom ESF format. The same part of the context
|
||||||
|
is automatically restored by hardware on mret.
|
||||||
|
|
||||||
|
Enabling this option requires that the SoC provides a
|
||||||
|
soc_isr_stacking.h header which defines the following:
|
||||||
|
|
||||||
|
- SOC_ISR_SW_STACKING: macro guarded by _ASMLANGUAGE called by the
|
||||||
|
IRQ wrapper assembly code on ISR entry to save in the ESF the
|
||||||
|
remaining part of the context not pushed already on the stack by
|
||||||
|
the hardware.
|
||||||
|
|
||||||
|
- SOC_ISR_SW_UNSTACKING: macro guarded by _ASMLANGUAGE called by the
|
||||||
|
IRQ wrapper assembly code on ISR exit to restore the part of the
|
||||||
|
context from the ESF that won't be restored by hardware on mret.
|
||||||
|
|
||||||
|
- SOC_ISR_STACKING_ESF_DECLARE: structure declaration for the ESF
|
||||||
|
guarded by !_ASMLANGUAGE. The ESF should be defined to account for
|
||||||
|
the hardware stacked registers in the proper order as they are
|
||||||
|
saved on the stack by the hardware, and the registers saved by the
|
||||||
|
software macros. The structure must be called '__esf'.
|
||||||
|
|
||||||
config RISCV_SOC_CONTEXT_SAVE
|
config RISCV_SOC_CONTEXT_SAVE
|
||||||
bool "SOC-based context saving in IRQ handlers"
|
bool "SOC-based context saving in IRQ handlers"
|
||||||
select RISCV_SOC_OFFSETS
|
select RISCV_SOC_OFFSETS
|
||||||
|
|
|
@ -17,6 +17,10 @@
|
||||||
#include <zephyr/arch/riscv/syscall.h>
|
#include <zephyr/arch/riscv/syscall.h>
|
||||||
#include "asm_macros.inc"
|
#include "asm_macros.inc"
|
||||||
|
|
||||||
|
#ifdef CONFIG_RISCV_SOC_HAS_ISR_STACKING
|
||||||
|
#include <soc_isr_stacking.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Convenience macros for loading/storing register states. */
|
/* Convenience macros for loading/storing register states. */
|
||||||
|
|
||||||
#define DO_FP_CALLER_SAVED(op, reg) \
|
#define DO_FP_CALLER_SAVED(op, reg) \
|
||||||
|
@ -138,9 +142,13 @@ SECTION_FUNC(exception.entry, _isr_wrapper)
|
||||||
1:
|
1:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_RISCV_SOC_HAS_ISR_STACKING
|
||||||
|
SOC_ISR_SW_STACKING
|
||||||
|
#else
|
||||||
/* Save caller-saved registers on current thread stack. */
|
/* Save caller-saved registers on current thread stack. */
|
||||||
addi sp, sp, -__z_arch_esf_t_SIZEOF
|
addi sp, sp, -__z_arch_esf_t_SIZEOF
|
||||||
DO_CALLER_SAVED(sr) ;
|
DO_CALLER_SAVED(sr) ;
|
||||||
|
#endif /* CONFIG_RISCV_SOC_HAS_ISR_STACKING */
|
||||||
|
|
||||||
/* Save s0 in the esf and load it with &_current_cpu. */
|
/* Save s0 in the esf and load it with &_current_cpu. */
|
||||||
sr s0, __z_arch_esf_t_s0_OFFSET(sp)
|
sr s0, __z_arch_esf_t_s0_OFFSET(sp)
|
||||||
|
@ -592,8 +600,12 @@ no_fp: /* make sure this is reflected in the restored mstatus */
|
||||||
/* Restore s0 (it is no longer ours) */
|
/* Restore s0 (it is no longer ours) */
|
||||||
lr s0, __z_arch_esf_t_s0_OFFSET(sp)
|
lr s0, __z_arch_esf_t_s0_OFFSET(sp)
|
||||||
|
|
||||||
|
#ifdef CONFIG_RISCV_SOC_HAS_ISR_STACKING
|
||||||
|
SOC_ISR_SW_UNSTACKING
|
||||||
|
#else
|
||||||
/* Restore caller-saved registers from thread stack */
|
/* Restore caller-saved registers from thread stack */
|
||||||
DO_CALLER_SAVED(lr)
|
DO_CALLER_SAVED(lr)
|
||||||
|
#endif /* CONFIG_RISCV_SOC_HAS_ISR_STACKING */
|
||||||
|
|
||||||
#ifdef CONFIG_USERSPACE
|
#ifdef CONFIG_USERSPACE
|
||||||
/* retrieve saved stack pointer */
|
/* retrieve saved stack pointer */
|
||||||
|
|
|
@ -23,6 +23,10 @@
|
||||||
#include <soc_context.h>
|
#include <soc_context.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_RISCV_SOC_HAS_ISR_STACKING
|
||||||
|
#include <soc_isr_stacking.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -49,6 +53,9 @@ struct soc_esf {
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(CONFIG_RISCV_SOC_HAS_ISR_STACKING)
|
||||||
|
SOC_ISR_STACKING_ESF_DECLARE;
|
||||||
|
#else
|
||||||
struct __esf {
|
struct __esf {
|
||||||
unsigned long ra; /* return address */
|
unsigned long ra; /* return address */
|
||||||
|
|
||||||
|
@ -109,6 +116,7 @@ struct __esf {
|
||||||
struct soc_esf soc_context;
|
struct soc_esf soc_context;
|
||||||
#endif
|
#endif
|
||||||
} __aligned(16);
|
} __aligned(16);
|
||||||
|
#endif /* CONFIG_RISCV_SOC_HAS_ISR_STACKING */
|
||||||
|
|
||||||
typedef struct __esf z_arch_esf_t;
|
typedef struct __esf z_arch_esf_t;
|
||||||
#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE
|
#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue