Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix vfork #293

Merged
merged 3 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions arch/risc-v/include/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,12 @@ struct xcptcontext

uintreg_t *regs;

#ifdef CONFIG_LIB_SYSCALL
/* User integer registers upon system call entry */

uintreg_t *sregs;
#endif

/* FPU register save area */

#if defined(CONFIG_ARCH_FPU) && defined(CONFIG_ARCH_LAZYFPU)
Expand Down
20 changes: 7 additions & 13 deletions arch/risc-v/src/common/riscv_doirq.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@

uintreg_t *riscv_doirq(int irq, uintreg_t *regs)
{
struct tcb_s **running_task = &g_running_tasks[this_cpu()];
struct tcb_s *tcb = this_task();

board_autoled_on(LED_INIRQ);
Expand All @@ -71,9 +72,10 @@ uintreg_t *riscv_doirq(int irq, uintreg_t *regs)
{
regs[REG_EPC] += 4;
}
else

if (*running_task != NULL)
{
tcb->xcp.regs = regs;
(*running_task)->xcp.regs = regs;
}

/* Current regs non-zero indicates that we are processing an interrupt;
Expand All @@ -97,7 +99,7 @@ uintreg_t *riscv_doirq(int irq, uintreg_t *regs)
* returning from the interrupt.
*/

if (regs != tcb->xcp.regs)
if ((*running_task) != tcb)
{
#ifdef CONFIG_ARCH_ADDRENV
/* Make sure that the address environment for the previously
Expand All @@ -114,15 +116,7 @@ uintreg_t *riscv_doirq(int irq, uintreg_t *regs)
* crashes.
*/

g_running_tasks[this_cpu()] = tcb;

/* If a context switch occurred while processing the interrupt then
* current_regs may have change value. If we return any value
* different from the input regs, then the lower level will know
* that a context switch occurred during interrupt processing.
*/

regs = tcb->xcp.regs;
*running_task = tcb;
}

/* Set current_regs to NULL to indicate that we are no longer in an
Expand All @@ -133,5 +127,5 @@ uintreg_t *riscv_doirq(int irq, uintreg_t *regs)

#endif
board_autoled_off(LED_INIRQ);
return regs;
return tcb->xcp.regs;
}
4 changes: 4 additions & 0 deletions arch/risc-v/src/common/riscv_exit.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ void up_exit(int status)

nxsched_resume_scheduler(tcb);

/* g_running_tasks is not valid now */

g_running_tasks[this_cpu()] = NULL;

/* Then switch contexts */

riscv_fullcontextrestore(tcb);
Expand Down
47 changes: 27 additions & 20 deletions arch/risc-v/src/common/riscv_fork.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,19 +108,14 @@ pid_t riscv_fork(const struct fork_s *context)
uintptr_t newtop;
uintptr_t stacktop;
uintptr_t stackutil;
irqstate_t flags;
#ifdef CONFIG_SCHED_THREAD_LOCAL
uintptr_t tp;
#endif
UNUSED(context);

/* parent regs may change in irq, we should disable irq here */

flags = up_irq_save();

/* Allocate and initialize a TCB for the child task. */

child = nxtask_setup_fork((start_t)parent->xcp.regs[REG_RA]);
child = nxtask_setup_fork((start_t)parent->xcp.sregs[REG_RA]);
if (!child)
{
sinfo("nxtask_setup_fork failed\n");
Expand All @@ -130,36 +125,50 @@ pid_t riscv_fork(const struct fork_s *context)
/* Copy parent user stack to child */

stacktop = (uintptr_t)parent->stack_base_ptr + parent->adj_stack_size;
DEBUGASSERT(stacktop > parent->xcp.regs[REG_SP]);
stackutil = stacktop - parent->xcp.regs[REG_SP];
DEBUGASSERT(stacktop > parent->xcp.sregs[REG_SP]);
stackutil = stacktop - parent->xcp.sregs[REG_SP];

/* Copy the parent stack contents (overwrites child's SP and TP) */
/* Copy goes to child's user stack top */

newtop = (uintptr_t)child->cmn.stack_base_ptr + child->cmn.adj_stack_size;
newsp = newtop - stackutil;

memcpy((void *)newsp, (const void *)parent->xcp.sregs[REG_SP], stackutil);

#ifdef CONFIG_SCHED_THREAD_LOCAL
/* Save child's thread pointer */

tp = child->cmn.xcp.regs[REG_TP];
#endif

/* Set up frame for context and copy the parent's user context there */
/* Determine the integer context save area */

memcpy((void *)(newsp - XCPTCONTEXT_SIZE),
parent->xcp.regs, XCPTCONTEXT_SIZE);
#ifdef CONFIG_ARCH_KERNEL_STACK
if (child->cmn.xcp.kstack)
{
/* Set context to kernel stack */

/* Save FPU */
stacktop = (uintptr_t)child->cmn.xcp.ktopstk;
}
else
#endif
{
/* Set context to user stack */

riscv_savefpu(child->cmn.xcp.regs, riscv_fpuregs(&child->cmn));
stacktop = newsp;
}

/* Copy the parent stack contents */
/* Set the new register restore area to the new stack top */

memcpy((void *)newsp, (const void *)parent->xcp.regs[REG_SP], stackutil);
child->cmn.xcp.regs = (void *)(stacktop - XCPTCONTEXT_SIZE);

/* Set the new register restore area to the new stack top */
/* Copy the parent integer context (overwrites child's SP and TP) */

child->cmn.xcp.regs = (void *)(newsp - XCPTCONTEXT_SIZE);
memcpy(child->cmn.xcp.regs, parent->xcp.sregs, XCPTCONTEXT_SIZE);

/* Save FPU */

riscv_savefpu(child->cmn.xcp.regs, riscv_fpuregs(&child->cmn));

/* Return 0 to child */

Expand All @@ -169,8 +178,6 @@ pid_t riscv_fork(const struct fork_s *context)
child->cmn.xcp.regs[REG_TP] = tp;
#endif

up_irq_restore(flags);

/* And, finally, start the child task. On a failure, nxtask_start_fork()
* will discard the TCB by calling nxtask_abort_fork().
*/
Expand Down
4 changes: 0 additions & 4 deletions arch/risc-v/src/common/riscv_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,6 @@
#define PMP_ACCESS_DENIED (-1) /* Access set and denied */
#define PMP_ACCESS_FULL (1) /* Access set and allowed */

/* Return values from riscv_swint */

#define SWINT_CONTEXT_SWITCH (1) /* Indicate we need context switch */

#ifndef __ASSEMBLY__

/* Use ASM as rv64ilp32 compiler generated address is limited */
Expand Down
4 changes: 4 additions & 0 deletions arch/risc-v/src/common/riscv_sigdeliver.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ void riscv_sigdeliver(void)
rtcb->irqcount--;
#endif

/* g_running_tasks is not valid now */

g_running_tasks[this_cpu()] = NULL;

rtcb->xcp.regs = regs;
riscv_fullcontextrestore(rtcb);
}
4 changes: 1 addition & 3 deletions arch/risc-v/src/common/riscv_swint.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ uintptr_t dispatch_syscall(unsigned int nbr, uintptr_t parm1,

/* Set the user register context to TCB */

rtcb->xcp.regs = context;
rtcb->xcp.sregs = context;

/* Indicate that we are in a syscall handler */

Expand Down Expand Up @@ -257,7 +257,6 @@ int riscv_swint(int irq, void *context, void *arg)
struct tcb_s *next = (struct tcb_s *)(uintptr_t)regs[REG_A2];

DEBUGASSERT(regs[REG_A1] != 0 && regs[REG_A2] != 0);
prev->xcp.regs = regs;
riscv_savecontext(prev);
new_regs = next->xcp.regs;
riscv_restorecontext(next);
Expand Down Expand Up @@ -496,7 +495,6 @@ int riscv_swint(int irq, void *context, void *arg)
if (regs != new_regs)
{
restore_critical_section(this_task(), this_cpu());
return SWINT_CONTEXT_SWITCH;
}

return OK;
Expand Down
27 changes: 11 additions & 16 deletions arch/risc-v/src/common/supervisor/riscv_perform_syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,24 @@

void *riscv_perform_syscall(uintreg_t *regs)
{
struct tcb_s **running_task = &g_running_tasks[this_cpu()];
struct tcb_s *tcb;
int cpu;
int ret;

if (*running_task != NULL)
{
(*running_task)->xcp.regs = regs;
}

/* Set up the interrupt register set needed by swint() */

up_set_current_regs(regs);

/* Run the system call handler (swint) */

ret = riscv_swint(0, regs, NULL);
riscv_swint(0, regs, NULL);
tcb = this_task();

if (ret == SWINT_CONTEXT_SWITCH)
if ((*running_task) != tcb)
{
#ifdef CONFIG_ARCH_ADDRENV
/* Make sure that the address environment for the previously
Expand All @@ -65,20 +70,10 @@ void *riscv_perform_syscall(uintreg_t *regs)
* assertion logic for reporting crashes.
*/

cpu = this_cpu();
tcb = current_task(cpu);
g_running_tasks[cpu] = tcb;

/* If a context switch occurred while processing the interrupt then
* current_regs may have change value. If we return any value
* different from the input regs, then the lower level will know
* that a context switch occurred during interrupt processing.
*/

regs = tcb->xcp.regs;
*running_task = tcb;
}

up_set_current_regs(NULL);

return regs;
return tcb->xcp.regs;
}
Loading