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) |