riscv_fork.c: Fix vfork() for kernel mode + SMP
There was an error in the fork() routine when system calls are in use:
the child context is saved on the child's user stack, which is incorrect,
the context must be saved on the kernel stack instead.
The result is a full system crash if (when) the child executes on a
different CPU which does not have the same MMU mappings active.
diff --git a/arch/risc-v/src/common/riscv_fork.c b/arch/risc-v/src/common/riscv_fork.c
index 896d8d4..6f17f8d 100644
--- a/arch/risc-v/src/common/riscv_fork.c
+++ b/arch/risc-v/src/common/riscv_fork.c
@@ -133,34 +133,48 @@
DEBUGASSERT(stacktop > parent->xcp.regs[REG_SP]);
stackutil = stacktop - parent->xcp.regs[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.regs[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 */
+
+ stacktop = (uintptr_t)child->cmn.xcp.ktopstk;
+ }
+ else
+#endif
+ {
+ /* Set context to user stack */
+
+ stacktop = newsp;
+ }
+
+ /* Set the new register restore area to the new stack top */
+
+ child->cmn.xcp.regs = (void *)(stacktop - XCPTCONTEXT_SIZE);
+
+ /* Copy the parent integer context (overwrites child's SP and TP) */
+
+ memcpy(child->cmn.xcp.regs, parent->xcp.regs, XCPTCONTEXT_SIZE);
/* Save FPU */
riscv_savefpu(child->cmn.xcp.regs, riscv_fpuregs(&child->cmn));
- /* Copy the parent stack contents */
-
- memcpy((void *)newsp, (const void *)parent->xcp.regs[REG_SP], stackutil);
-
- /* Set the new register restore area to the new stack top */
-
- child->cmn.xcp.regs = (void *)(newsp - XCPTCONTEXT_SIZE);
-
/* Return 0 to child */
child->cmn.xcp.regs[REG_A0] = 0;