summaryrefslogtreecommitdiffstats
path: root/arch/xtensa/kernel/process.c
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2012-10-25 15:47:19 +0200
committerArnd Bergmann <arnd@arndb.de>2012-10-25 15:47:35 +0200
commit2adca5672ffe3121bbb3bd061af9f047e0f8023f (patch)
tree01825a2f14c8d640509c39967ff9d461eaacea2f /arch/xtensa/kernel/process.c
parentARM: dts: mxs: add the "clock-names" for gpmi-nand (diff)
parentARM: at91: drop duplicated config SOC_AT91SAM9 entry (diff)
downloadlinux-2adca5672ffe3121bbb3bd061af9f047e0f8023f.tar.xz
linux-2adca5672ffe3121bbb3bd061af9f047e0f8023f.zip
Merge tag 'at91-fixes' of git://github.com/at91linux/linux-at91 into fixes
From Nicolas Ferre <nicolas.ferre@atmel.com>: A mix of typos and critical fixes. The most important ones are a duplicated definition of a Kconfig variable and the handling of external interrupts for non-DT case. The new at91sam9g10 was suffering a recognition issue due to an ID mis-interpreted: this was leading to a kernel panic. * tag 'at91-fixes' of git://github.com/at91linux/linux-at91: (257 commits) ARM: at91: drop duplicated config SOC_AT91SAM9 entry ARM: at91/i2c: change id to let i2c-at91 work ARM: at91/i2c: change id to let i2c-gpio work ARM: at91/dts: at91sam9g20ek_common: Fix typos in buttons labels. ARM: at91: fix external interrupt specification in board code ARM: at91: fix external interrupts in non-DT case ARM: at91: at91sam9g10: fix SOC type detection ARM: at91/tc: fix typo in the DT document Signed-off-by: Arnd Bergmann <arnd@arndb.de>
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 bc020825cce5..1908f6642d31 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)