summaryrefslogtreecommitdiffstats
path: root/arch/xtensa/kernel/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/xtensa/kernel/process.c')
-rw-r--r--arch/xtensa/kernel/process.c29
1 files changed, 24 insertions, 5 deletions
diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c
index bc020825cce..1908f6642d3 100644
--- a/arch/xtensa/kernel/process.c
+++ b/arch/xtensa/kernel/process.c
@@ -173,6 +173,16 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
*
* Note: This is a pristine frame, so we don't need any spill region on top of
* childregs.
+ *
+ * The fun part: if we're keeping the same VM (i.e. cloning a thread,
+ * not an entire process), we're normally given a new usp, and we CANNOT share
+ * any live address register windows. If we just copy those live frames over,
+ * the two threads (parent and child) will overflow the same frames onto the
+ * parent stack at different times, likely corrupting the parent stack (esp.
+ * if the parent returns from functions that called clone() and calls new
+ * ones, before the child overflows its now old copies of its parent windows).
+ * One solution is to spill windows to the parent stack, but that's fairly
+ * involved. Much simpler to just not copy those live frames across.
*/
int copy_thread(unsigned long clone_flags, unsigned long usp,
@@ -180,10 +190,13 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
struct task_struct * p, struct pt_regs * regs)
{
struct pt_regs *childregs;
- struct thread_info *ti;
unsigned long tos;
int user_mode = user_mode(regs);
+#if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS)
+ struct thread_info *ti;
+#endif
+
/* Set up new TSS. */
tos = (unsigned long)task_stack_page(p) + THREAD_SIZE;
if (user_mode)
@@ -191,13 +204,14 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
else
childregs = (struct pt_regs*)tos - 1;
+ /* This does not copy all the regs. In a bout of brilliance or madness,
+ ARs beyond a0-a15 exist past the end of the struct. */
*childregs = *regs;
/* Create a call4 dummy-frame: a0 = 0, a1 = childregs. */
*((int*)childregs - 3) = (unsigned long)childregs;
*((int*)childregs - 4) = 0;
- childregs->areg[1] = tos;
childregs->areg[2] = 0;
p->set_child_tid = p->clear_child_tid = NULL;
p->thread.ra = MAKE_RA_FOR_CALL((unsigned long)ret_from_fork, 0x1);
@@ -205,10 +219,14 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
if (user_mode(regs)) {
- int len = childregs->wmask & ~0xf;
childregs->areg[1] = usp;
- memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4],
- &regs->areg[XCHAL_NUM_AREGS - len/4], len);
+ if (clone_flags & CLONE_VM) {
+ childregs->wmask = 1; /* can't share live windows */
+ } else {
+ int len = childregs->wmask & ~0xf;
+ memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4],
+ &regs->areg[XCHAL_NUM_AREGS - len/4], len);
+ }
// FIXME: we need to set THREADPTR in thread_info...
if (clone_flags & CLONE_SETTLS)
childregs->areg[2] = childregs->areg[6];
@@ -216,6 +234,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
} else {
/* In kernel space, we start a new thread with a new stack. */
childregs->wmask = 1;
+ childregs->areg[1] = tos;
}
#if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS)