diff options
Diffstat (limited to 'arch/alpha/kernel/process.c')
-rw-r--r-- | arch/alpha/kernel/process.c | 79 |
1 files changed, 20 insertions, 59 deletions
diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index 83638aa096d5..4054e0ffe2b2 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c | |||
@@ -263,33 +263,35 @@ alpha_vfork(struct pt_regs *regs) | |||
263 | 263 | ||
264 | /* | 264 | /* |
265 | * Copy an alpha thread.. | 265 | * Copy an alpha thread.. |
266 | * | ||
267 | * Note the "stack_offset" stuff: when returning to kernel mode, we need | ||
268 | * to have some extra stack-space for the kernel stack that still exists | ||
269 | * after the "ret_from_fork". When returning to user mode, we only want | ||
270 | * the space needed by the syscall stack frame (ie "struct pt_regs"). | ||
271 | * Use the passed "regs" pointer to determine how much space we need | ||
272 | * for a kernel fork(). | ||
273 | */ | 266 | */ |
274 | 267 | ||
275 | int | 268 | int |
276 | copy_thread(unsigned long clone_flags, unsigned long usp, | 269 | copy_thread(unsigned long clone_flags, unsigned long usp, |
277 | unsigned long unused, | 270 | unsigned long arg, |
278 | struct task_struct * p, struct pt_regs * regs) | 271 | struct task_struct * p, struct pt_regs * regs) |
279 | { | 272 | { |
280 | extern void ret_from_fork(void); | 273 | extern void ret_from_fork(void); |
274 | extern void ret_from_kernel_thread(void); | ||
281 | 275 | ||
282 | struct thread_info *childti = task_thread_info(p); | 276 | struct thread_info *childti = task_thread_info(p); |
283 | struct pt_regs * childregs; | 277 | struct pt_regs *childregs = task_pt_regs(p); |
284 | struct switch_stack * childstack, *stack; | 278 | struct switch_stack *childstack, *stack; |
285 | unsigned long stack_offset, settls; | 279 | unsigned long settls; |
286 | 280 | ||
287 | stack_offset = PAGE_SIZE - sizeof(struct pt_regs); | 281 | childstack = ((struct switch_stack *) childregs) - 1; |
288 | if (!(regs->ps & 8)) | 282 | if (unlikely(!regs)) { |
289 | stack_offset = (PAGE_SIZE-1) & (unsigned long) regs; | 283 | /* kernel thread */ |
290 | childregs = (struct pt_regs *) | 284 | memset(childstack, 0, |
291 | (stack_offset + PAGE_SIZE + task_stack_page(p)); | 285 | sizeof(struct switch_stack) + sizeof(struct pt_regs)); |
292 | 286 | childstack->r26 = (unsigned long) ret_from_kernel_thread; | |
287 | childstack->r9 = usp; /* function */ | ||
288 | childstack->r10 = arg; | ||
289 | childregs->hae = alpha_mv.hae_cache, | ||
290 | childti->pcb.usp = 0; | ||
291 | childti->pcb.ksp = (unsigned long) childstack; | ||
292 | childti->pcb.flags = 1; /* set FEN, clear everything else */ | ||
293 | return 0; | ||
294 | } | ||
293 | *childregs = *regs; | 295 | *childregs = *regs; |
294 | settls = regs->r20; | 296 | settls = regs->r20; |
295 | childregs->r0 = 0; | 297 | childregs->r0 = 0; |
@@ -297,7 +299,6 @@ copy_thread(unsigned long clone_flags, unsigned long usp, | |||
297 | childregs->r20 = 1; /* OSF/1 has some strange fork() semantics. */ | 299 | childregs->r20 = 1; /* OSF/1 has some strange fork() semantics. */ |
298 | regs->r20 = 0; | 300 | regs->r20 = 0; |
299 | stack = ((struct switch_stack *) regs) - 1; | 301 | stack = ((struct switch_stack *) regs) - 1; |
300 | childstack = ((struct switch_stack *) childregs) - 1; | ||
301 | *childstack = *stack; | 302 | *childstack = *stack; |
302 | childstack->r26 = (unsigned long) ret_from_fork; | 303 | childstack->r26 = (unsigned long) ret_from_fork; |
303 | childti->pcb.usp = usp; | 304 | childti->pcb.usp = usp; |
@@ -386,27 +387,6 @@ dump_elf_task_fp(elf_fpreg_t *dest, struct task_struct *task) | |||
386 | EXPORT_SYMBOL(dump_elf_task_fp); | 387 | EXPORT_SYMBOL(dump_elf_task_fp); |
387 | 388 | ||
388 | /* | 389 | /* |
389 | * sys_execve() executes a new program. | ||
390 | */ | ||
391 | asmlinkage int | ||
392 | do_sys_execve(const char __user *ufilename, | ||
393 | const char __user *const __user *argv, | ||
394 | const char __user *const __user *envp, struct pt_regs *regs) | ||
395 | { | ||
396 | int error; | ||
397 | char *filename; | ||
398 | |||
399 | filename = getname(ufilename); | ||
400 | error = PTR_ERR(filename); | ||
401 | if (IS_ERR(filename)) | ||
402 | goto out; | ||
403 | error = do_execve(filename, argv, envp, regs); | ||
404 | putname(filename); | ||
405 | out: | ||
406 | return error; | ||
407 | } | ||
408 | |||
409 | /* | ||
410 | * Return saved PC of a blocked thread. This assumes the frame | 390 | * Return saved PC of a blocked thread. This assumes the frame |
411 | * pointer is the 6th saved long on the kernel stack and that the | 391 | * pointer is the 6th saved long on the kernel stack and that the |
412 | * saved return address is the first long in the frame. This all | 392 | * saved return address is the first long in the frame. This all |
@@ -459,22 +439,3 @@ get_wchan(struct task_struct *p) | |||
459 | } | 439 | } |
460 | return pc; | 440 | return pc; |
461 | } | 441 | } |
462 | |||
463 | int kernel_execve(const char *path, const char *const argv[], const char *const envp[]) | ||
464 | { | ||
465 | /* Avoid the HAE being gratuitously wrong, which would cause us | ||
466 | to do the whole turn off interrupts thing and restore it. */ | ||
467 | struct pt_regs regs = {.hae = alpha_mv.hae_cache}; | ||
468 | int err = do_execve(path, argv, envp, ®s); | ||
469 | if (!err) { | ||
470 | struct pt_regs *p = current_pt_regs(); | ||
471 | /* copy regs to normal position and off to userland we go... */ | ||
472 | *p = regs; | ||
473 | __asm__ __volatile__ ( | ||
474 | "mov %0, $sp;" | ||
475 | "br $31, ret_from_sys_call" | ||
476 | : : "r"(p)); | ||
477 | } | ||
478 | return err; | ||
479 | } | ||
480 | EXPORT_SYMBOL(kernel_execve); | ||