diff options
Diffstat (limited to 'arch/xtensa/kernel/process.c')
| -rw-r--r-- | arch/xtensa/kernel/process.c | 29 |
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) | |||
| 173 | * | 173 | * |
| 174 | * Note: This is a pristine frame, so we don't need any spill region on top of | 174 | * Note: This is a pristine frame, so we don't need any spill region on top of |
| 175 | * childregs. | 175 | * childregs. |
| 176 | * | ||
| 177 | * The fun part: if we're keeping the same VM (i.e. cloning a thread, | ||
| 178 | * not an entire process), we're normally given a new usp, and we CANNOT share | ||
| 179 | * any live address register windows. If we just copy those live frames over, | ||
| 180 | * the two threads (parent and child) will overflow the same frames onto the | ||
| 181 | * parent stack at different times, likely corrupting the parent stack (esp. | ||
| 182 | * if the parent returns from functions that called clone() and calls new | ||
| 183 | * ones, before the child overflows its now old copies of its parent windows). | ||
| 184 | * One solution is to spill windows to the parent stack, but that's fairly | ||
| 185 | * involved. Much simpler to just not copy those live frames across. | ||
| 176 | */ | 186 | */ |
| 177 | 187 | ||
| 178 | int copy_thread(unsigned long clone_flags, unsigned long usp, | 188 | int copy_thread(unsigned long clone_flags, unsigned long usp, |
| @@ -180,10 +190,13 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, | |||
| 180 | struct task_struct * p, struct pt_regs * regs) | 190 | struct task_struct * p, struct pt_regs * regs) |
| 181 | { | 191 | { |
| 182 | struct pt_regs *childregs; | 192 | struct pt_regs *childregs; |
| 183 | struct thread_info *ti; | ||
| 184 | unsigned long tos; | 193 | unsigned long tos; |
| 185 | int user_mode = user_mode(regs); | 194 | int user_mode = user_mode(regs); |
| 186 | 195 | ||
| 196 | #if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS) | ||
| 197 | struct thread_info *ti; | ||
| 198 | #endif | ||
| 199 | |||
| 187 | /* Set up new TSS. */ | 200 | /* Set up new TSS. */ |
| 188 | tos = (unsigned long)task_stack_page(p) + THREAD_SIZE; | 201 | tos = (unsigned long)task_stack_page(p) + THREAD_SIZE; |
| 189 | if (user_mode) | 202 | if (user_mode) |
| @@ -191,13 +204,14 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, | |||
| 191 | else | 204 | else |
| 192 | childregs = (struct pt_regs*)tos - 1; | 205 | childregs = (struct pt_regs*)tos - 1; |
| 193 | 206 | ||
| 207 | /* This does not copy all the regs. In a bout of brilliance or madness, | ||
| 208 | ARs beyond a0-a15 exist past the end of the struct. */ | ||
| 194 | *childregs = *regs; | 209 | *childregs = *regs; |
| 195 | 210 | ||
| 196 | /* Create a call4 dummy-frame: a0 = 0, a1 = childregs. */ | 211 | /* Create a call4 dummy-frame: a0 = 0, a1 = childregs. */ |
| 197 | *((int*)childregs - 3) = (unsigned long)childregs; | 212 | *((int*)childregs - 3) = (unsigned long)childregs; |
| 198 | *((int*)childregs - 4) = 0; | 213 | *((int*)childregs - 4) = 0; |
| 199 | 214 | ||
| 200 | childregs->areg[1] = tos; | ||
| 201 | childregs->areg[2] = 0; | 215 | childregs->areg[2] = 0; |
| 202 | p->set_child_tid = p->clear_child_tid = NULL; | 216 | p->set_child_tid = p->clear_child_tid = NULL; |
| 203 | p->thread.ra = MAKE_RA_FOR_CALL((unsigned long)ret_from_fork, 0x1); | 217 | 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, | |||
| 205 | 219 | ||
| 206 | if (user_mode(regs)) { | 220 | if (user_mode(regs)) { |
| 207 | 221 | ||
| 208 | int len = childregs->wmask & ~0xf; | ||
| 209 | childregs->areg[1] = usp; | 222 | childregs->areg[1] = usp; |
| 210 | memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4], | 223 | if (clone_flags & CLONE_VM) { |
| 211 | ®s->areg[XCHAL_NUM_AREGS - len/4], len); | 224 | childregs->wmask = 1; /* can't share live windows */ |
| 225 | } else { | ||
| 226 | int len = childregs->wmask & ~0xf; | ||
| 227 | memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4], | ||
| 228 | ®s->areg[XCHAL_NUM_AREGS - len/4], len); | ||
| 229 | } | ||
| 212 | // FIXME: we need to set THREADPTR in thread_info... | 230 | // FIXME: we need to set THREADPTR in thread_info... |
| 213 | if (clone_flags & CLONE_SETTLS) | 231 | if (clone_flags & CLONE_SETTLS) |
| 214 | childregs->areg[2] = childregs->areg[6]; | 232 | childregs->areg[2] = childregs->areg[6]; |
| @@ -216,6 +234,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, | |||
| 216 | } else { | 234 | } else { |
| 217 | /* In kernel space, we start a new thread with a new stack. */ | 235 | /* In kernel space, we start a new thread with a new stack. */ |
| 218 | childregs->wmask = 1; | 236 | childregs->wmask = 1; |
| 237 | childregs->areg[1] = tos; | ||
| 219 | } | 238 | } |
| 220 | 239 | ||
| 221 | #if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS) | 240 | #if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS) |
