riscv/swint: Give the full tcb to the context switch routine

Why? The tcb can contain info that is needed by the context switch
routine. One example is lazy-FPU handling; the integer registers can
be stored into the stack, because they are always stored & restored.

Lazy-FPU however needs a non-volatile location to store the FPU registers
as the save feature will skip saving a clean FPU, but the restore must
always restore the FPU registers if the thread uses FPU.
This commit is contained in:
Ville Juven 2023-05-16 18:03:53 +03:00 committed by Xiang Xiao
parent 4494e75e87
commit 040eb3c990
5 changed files with 39 additions and 19 deletions

View File

@ -139,7 +139,7 @@ void up_exit(int status)
/* Then switch contexts */
riscv_fullcontextrestore(tcb->xcp.regs);
riscv_fullcontextrestore(tcb);
/* riscv_fullcontextrestore() should not return but could if the software
* interrupts are disabled.

View File

@ -29,6 +29,7 @@
#ifndef __ASSEMBLY__
# include <nuttx/compiler.h>
# include <nuttx/sched.h>
# include <sys/types.h>
# include <stdint.h>
# include <syscall.h>
@ -207,6 +208,18 @@ void riscv_fpuconfig(void);
# define riscv_fpuconfig()
#endif
/* Save / restore context of task */
static inline void riscv_savecontext(struct tcb_s *tcb)
{
tcb->xcp.regs = (uintptr_t *)CURRENT_REGS;
}
static inline void riscv_restorecontext(struct tcb_s *tcb)
{
CURRENT_REGS = (uintptr_t *)tcb->xcp.regs;
}
/* RISC-V PMP Config ********************************************************/
int riscv_config_pmp_region(uintptr_t region, uintptr_t attr,
@ -304,19 +317,19 @@ void *riscv_perform_syscall(uintptr_t *regs);
/* SYS call 1:
*
* void riscv_fullcontextrestore(uintptr_t *restoreregs) noreturn_function;
* void riscv_fullcontextrestore(struct tcb_s *next) noreturn_function;
*/
#define riscv_fullcontextrestore(restoreregs) \
sys_call1(SYS_restore_context, (uintptr_t)restoreregs)
#define riscv_fullcontextrestore(next) \
sys_call1(SYS_restore_context, (uintptr_t)next)
/* SYS call 2:
*
* void riscv_switchcontext(uintptr_t *saveregs, uintptr_t *restoreregs);
* riscv_switchcontext(struct tcb_s *prev, struct tcb_s *next);
*/
#define riscv_switchcontext(saveregs, restoreregs) \
sys_call2(SYS_switch_context, (uintptr_t)saveregs, (uintptr_t)restoreregs)
#define riscv_switchcontext(prev, next) \
sys_call2(SYS_switch_context, (uintptr_t)prev, (uintptr_t)next)
#ifdef CONFIG_BUILD_KERNEL
/* SYS call 3:

View File

@ -155,5 +155,7 @@ retry:
#ifdef CONFIG_SMP
rtcb->irqcount--;
#endif
riscv_fullcontextrestore(regs);
rtcb->xcp.regs = regs;
riscv_fullcontextrestore(rtcb);
}

View File

@ -135,12 +135,12 @@ int riscv_swint(int irq, void *context, void *arg)
/* A0=SYS_restore_context: This a restore context command:
*
* void
* riscv_fullcontextrestore(uintptr_t *restoreregs) noreturn_function;
* void riscv_fullcontextrestore(struct tcb_s *prev) noreturn_function;
*
* At this point, the following values are saved in context:
*
* A0 = SYS_restore_context
* A1 = restoreregs
* A1 = next
*
* In this case, we simply need to set CURRENT_REGS to restore register
* area referenced in the saved A1. context == CURRENT_REGS is the
@ -150,21 +150,23 @@ int riscv_swint(int irq, void *context, void *arg)
case SYS_restore_context:
{
struct tcb_s *next = (struct tcb_s *)regs[REG_A1];
DEBUGASSERT(regs[REG_A1] != 0);
CURRENT_REGS = (uintptr_t *)regs[REG_A1];
CURRENT_REGS = (uintptr_t *)next->xcp.regs;
}
break;
/* A0=SYS_switch_context: This a switch context command:
*
* void
* riscv_switchcontext(uintptr_t *saveregs, uintptr_t *restoreregs);
* riscv_switchcontext(struct tcb_s *prev, struct tcb_s *next);
*
* At this point, the following values are saved in context:
*
* A0 = SYS_switch_context
* A1 = saveregs
* A2 = restoreregs
* A1 = prev
* A2 = next
*
* In this case, we save the context registers to the save register
* area referenced by the saved contents of R5 and then set
@ -174,9 +176,12 @@ int riscv_swint(int irq, void *context, void *arg)
case SYS_switch_context:
{
struct tcb_s *prev = (struct tcb_s *)regs[REG_A1];
struct tcb_s *next = (struct tcb_s *)regs[REG_A2];
DEBUGASSERT(regs[REG_A1] != 0 && regs[REG_A2] != 0);
*(uintptr_t **)regs[REG_A1] = (uintptr_t *)regs;
CURRENT_REGS = (uintptr_t *)regs[REG_A2];
prev->xcp.regs = (uintptr_t *)CURRENT_REGS;
CURRENT_REGS = (uintptr_t *)next->xcp.regs;
}
break;

View File

@ -69,7 +69,7 @@ void up_switch_context(struct tcb_s *tcb, struct tcb_s *rtcb)
* Just copy the CURRENT_REGS into the OLD rtcb.
*/
riscv_savestate(rtcb->xcp.regs);
riscv_savecontext(rtcb);
/* Update scheduler parameters */
@ -79,7 +79,7 @@ void up_switch_context(struct tcb_s *tcb, struct tcb_s *rtcb)
* changes will be made when the interrupt returns.
*/
riscv_restorestate(tcb->xcp.regs);
riscv_restorecontext(tcb);
}
/* No, then we will need to perform the user context switch */
@ -92,7 +92,7 @@ void up_switch_context(struct tcb_s *tcb, struct tcb_s *rtcb)
/* Then switch contexts */
riscv_switchcontext(&rtcb->xcp.regs, tcb->xcp.regs);
riscv_switchcontext(rtcb, tcb);
/* riscv_switchcontext forces a context switch to the task at the
* head of the ready-to-run list. It does not 'return' in the