diff options
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r-- | arch/arm/kernel/calls.S | 2 | ||||
-rw-r--r-- | arch/arm/kernel/entry-common.S | 29 | ||||
-rw-r--r-- | arch/arm/kernel/process.c | 75 | ||||
-rw-r--r-- | arch/arm/kernel/sys_arm.c | 63 |
4 files changed, 38 insertions, 131 deletions
diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S index e337879595e5..831cd38c8d99 100644 --- a/arch/arm/kernel/calls.S +++ b/arch/arm/kernel/calls.S | |||
@@ -20,7 +20,7 @@ | |||
20 | CALL(sys_creat) | 20 | CALL(sys_creat) |
21 | CALL(sys_link) | 21 | CALL(sys_link) |
22 | /* 10 */ CALL(sys_unlink) | 22 | /* 10 */ CALL(sys_unlink) |
23 | CALL(sys_execve_wrapper) | 23 | CALL(sys_execve) |
24 | CALL(sys_chdir) | 24 | CALL(sys_chdir) |
25 | CALL(OBSOLETE(sys_time)) /* used by libc4 */ | 25 | CALL(OBSOLETE(sys_time)) /* used by libc4 */ |
26 | CALL(sys_mknod) | 26 | CALL(sys_mknod) |
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index f45987037bf1..e340fa1db203 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S | |||
@@ -91,6 +91,30 @@ ENTRY(ret_from_fork) | |||
91 | b ret_slow_syscall | 91 | b ret_slow_syscall |
92 | ENDPROC(ret_from_fork) | 92 | ENDPROC(ret_from_fork) |
93 | 93 | ||
94 | ENTRY(ret_from_kernel_thread) | ||
95 | UNWIND(.fnstart) | ||
96 | UNWIND(.cantunwind) | ||
97 | bl schedule_tail | ||
98 | mov r0, r4 | ||
99 | adr lr, BSYM(1f) @ kernel threads should not exit | ||
100 | mov pc, r5 | ||
101 | 1: bl do_exit | ||
102 | nop | ||
103 | UNWIND(.fnend) | ||
104 | ENDPROC(ret_from_kernel_thread) | ||
105 | |||
106 | /* | ||
107 | * turn a kernel thread into userland process | ||
108 | * use: ret_from_kernel_execve(struct pt_regs *normal) | ||
109 | */ | ||
110 | ENTRY(ret_from_kernel_execve) | ||
111 | mov why, #0 @ not a syscall | ||
112 | str why, [r0, #S_R0] @ ... and we want 0 in ->ARM_r0 as well | ||
113 | get_thread_info tsk @ thread structure | ||
114 | mov sp, r0 @ stack pointer just under pt_regs | ||
115 | b ret_slow_syscall | ||
116 | ENDPROC(ret_from_kernel_execve) | ||
117 | |||
94 | .equ NR_syscalls,0 | 118 | .equ NR_syscalls,0 |
95 | #define CALL(x) .equ NR_syscalls,NR_syscalls+1 | 119 | #define CALL(x) .equ NR_syscalls,NR_syscalls+1 |
96 | #include "calls.S" | 120 | #include "calls.S" |
@@ -517,11 +541,6 @@ sys_vfork_wrapper: | |||
517 | b sys_vfork | 541 | b sys_vfork |
518 | ENDPROC(sys_vfork_wrapper) | 542 | ENDPROC(sys_vfork_wrapper) |
519 | 543 | ||
520 | sys_execve_wrapper: | ||
521 | add r3, sp, #S_OFF | ||
522 | b sys_execve | ||
523 | ENDPROC(sys_execve_wrapper) | ||
524 | |||
525 | sys_clone_wrapper: | 544 | sys_clone_wrapper: |
526 | add ip, sp, #S_OFF | 545 | add ip, sp, #S_OFF |
527 | str ip, [sp, #4] | 546 | str ip, [sp, #4] |
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 04eea22d7958..f98c17ff1957 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c | |||
@@ -373,6 +373,7 @@ void release_thread(struct task_struct *dead_task) | |||
373 | } | 373 | } |
374 | 374 | ||
375 | asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); | 375 | asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); |
376 | asmlinkage void ret_from_kernel_thread(void) __asm__("ret_from_kernel_thread"); | ||
376 | 377 | ||
377 | int | 378 | int |
378 | copy_thread(unsigned long clone_flags, unsigned long stack_start, | 379 | copy_thread(unsigned long clone_flags, unsigned long stack_start, |
@@ -381,13 +382,20 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start, | |||
381 | struct thread_info *thread = task_thread_info(p); | 382 | struct thread_info *thread = task_thread_info(p); |
382 | struct pt_regs *childregs = task_pt_regs(p); | 383 | struct pt_regs *childregs = task_pt_regs(p); |
383 | 384 | ||
384 | *childregs = *regs; | ||
385 | childregs->ARM_r0 = 0; | ||
386 | childregs->ARM_sp = stack_start; | ||
387 | |||
388 | memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save)); | 385 | memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save)); |
386 | |||
387 | if (likely(regs)) { | ||
388 | *childregs = *regs; | ||
389 | childregs->ARM_r0 = 0; | ||
390 | childregs->ARM_sp = stack_start; | ||
391 | thread->cpu_context.pc = (unsigned long)ret_from_fork; | ||
392 | } else { | ||
393 | thread->cpu_context.r4 = stk_sz; | ||
394 | thread->cpu_context.r5 = stack_start; | ||
395 | thread->cpu_context.pc = (unsigned long)ret_from_kernel_thread; | ||
396 | childregs->ARM_cpsr = SVC_MODE; | ||
397 | } | ||
389 | thread->cpu_context.sp = (unsigned long)childregs; | 398 | thread->cpu_context.sp = (unsigned long)childregs; |
390 | thread->cpu_context.pc = (unsigned long)ret_from_fork; | ||
391 | 399 | ||
392 | clear_ptrace_hw_breakpoint(p); | 400 | clear_ptrace_hw_breakpoint(p); |
393 | 401 | ||
@@ -423,63 +431,6 @@ int dump_fpu (struct pt_regs *regs, struct user_fp *fp) | |||
423 | } | 431 | } |
424 | EXPORT_SYMBOL(dump_fpu); | 432 | EXPORT_SYMBOL(dump_fpu); |
425 | 433 | ||
426 | /* | ||
427 | * Shuffle the argument into the correct register before calling the | ||
428 | * thread function. r4 is the thread argument, r5 is the pointer to | ||
429 | * the thread function, and r6 points to the exit function. | ||
430 | */ | ||
431 | extern void kernel_thread_helper(void); | ||
432 | asm( ".pushsection .text\n" | ||
433 | " .align\n" | ||
434 | " .type kernel_thread_helper, #function\n" | ||
435 | "kernel_thread_helper:\n" | ||
436 | #ifdef CONFIG_TRACE_IRQFLAGS | ||
437 | " bl trace_hardirqs_on\n" | ||
438 | #endif | ||
439 | " msr cpsr_c, r7\n" | ||
440 | " mov r0, r4\n" | ||
441 | " mov lr, r6\n" | ||
442 | " mov pc, r5\n" | ||
443 | " .size kernel_thread_helper, . - kernel_thread_helper\n" | ||
444 | " .popsection"); | ||
445 | |||
446 | #ifdef CONFIG_ARM_UNWIND | ||
447 | extern void kernel_thread_exit(long code); | ||
448 | asm( ".pushsection .text\n" | ||
449 | " .align\n" | ||
450 | " .type kernel_thread_exit, #function\n" | ||
451 | "kernel_thread_exit:\n" | ||
452 | " .fnstart\n" | ||
453 | " .cantunwind\n" | ||
454 | " bl do_exit\n" | ||
455 | " nop\n" | ||
456 | " .fnend\n" | ||
457 | " .size kernel_thread_exit, . - kernel_thread_exit\n" | ||
458 | " .popsection"); | ||
459 | #else | ||
460 | #define kernel_thread_exit do_exit | ||
461 | #endif | ||
462 | |||
463 | /* | ||
464 | * Create a kernel thread. | ||
465 | */ | ||
466 | pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) | ||
467 | { | ||
468 | struct pt_regs regs; | ||
469 | |||
470 | memset(®s, 0, sizeof(regs)); | ||
471 | |||
472 | regs.ARM_r4 = (unsigned long)arg; | ||
473 | regs.ARM_r5 = (unsigned long)fn; | ||
474 | regs.ARM_r6 = (unsigned long)kernel_thread_exit; | ||
475 | regs.ARM_r7 = SVC_MODE | PSR_ENDSTATE | PSR_ISETSTATE; | ||
476 | regs.ARM_pc = (unsigned long)kernel_thread_helper; | ||
477 | regs.ARM_cpsr = regs.ARM_r7 | PSR_I_BIT; | ||
478 | |||
479 | return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); | ||
480 | } | ||
481 | EXPORT_SYMBOL(kernel_thread); | ||
482 | |||
483 | unsigned long get_wchan(struct task_struct *p) | 434 | unsigned long get_wchan(struct task_struct *p) |
484 | { | 435 | { |
485 | struct stackframe frame; | 436 | struct stackframe frame; |
diff --git a/arch/arm/kernel/sys_arm.c b/arch/arm/kernel/sys_arm.c index 76cbb055dd05..c2a898aa57aa 100644 --- a/arch/arm/kernel/sys_arm.c +++ b/arch/arm/kernel/sys_arm.c | |||
@@ -59,69 +59,6 @@ asmlinkage int sys_vfork(struct pt_regs *regs) | |||
59 | return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL); | 59 | return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL); |
60 | } | 60 | } |
61 | 61 | ||
62 | /* sys_execve() executes a new program. | ||
63 | * This is called indirectly via a small wrapper | ||
64 | */ | ||
65 | asmlinkage int sys_execve(const char __user *filenamei, | ||
66 | const char __user *const __user *argv, | ||
67 | const char __user *const __user *envp, struct pt_regs *regs) | ||
68 | { | ||
69 | int error; | ||
70 | char * filename; | ||
71 | |||
72 | filename = getname(filenamei); | ||
73 | error = PTR_ERR(filename); | ||
74 | if (IS_ERR(filename)) | ||
75 | goto out; | ||
76 | error = do_execve(filename, argv, envp, regs); | ||
77 | putname(filename); | ||
78 | out: | ||
79 | return error; | ||
80 | } | ||
81 | |||
82 | int kernel_execve(const char *filename, | ||
83 | const char *const argv[], | ||
84 | const char *const envp[]) | ||
85 | { | ||
86 | struct pt_regs regs; | ||
87 | int ret; | ||
88 | |||
89 | memset(®s, 0, sizeof(struct pt_regs)); | ||
90 | ret = do_execve(filename, | ||
91 | (const char __user *const __user *)argv, | ||
92 | (const char __user *const __user *)envp, ®s); | ||
93 | if (ret < 0) | ||
94 | goto out; | ||
95 | |||
96 | /* | ||
97 | * Save argc to the register structure for userspace. | ||
98 | */ | ||
99 | regs.ARM_r0 = ret; | ||
100 | |||
101 | /* | ||
102 | * We were successful. We won't be returning to our caller, but | ||
103 | * instead to user space by manipulating the kernel stack. | ||
104 | */ | ||
105 | asm( "add r0, %0, %1\n\t" | ||
106 | "mov r1, %2\n\t" | ||
107 | "mov r2, %3\n\t" | ||
108 | "bl memmove\n\t" /* copy regs to top of stack */ | ||
109 | "mov r8, #0\n\t" /* not a syscall */ | ||
110 | "mov r9, %0\n\t" /* thread structure */ | ||
111 | "mov sp, r0\n\t" /* reposition stack pointer */ | ||
112 | "b ret_to_user" | ||
113 | : | ||
114 | : "r" (current_thread_info()), | ||
115 | "Ir" (THREAD_START_SP - sizeof(regs)), | ||
116 | "r" (®s), | ||
117 | "Ir" (sizeof(regs)) | ||
118 | : "r0", "r1", "r2", "r3", "r8", "r9", "ip", "lr", "memory"); | ||
119 | |||
120 | out: | ||
121 | return ret; | ||
122 | } | ||
123 | EXPORT_SYMBOL(kernel_execve); | ||
124 | |||
125 | /* | 62 | /* |
126 | * Since loff_t is a 64 bit type we avoid a lot of ABI hassle | 63 | * Since loff_t is a 64 bit type we avoid a lot of ABI hassle |
127 | * with a different argument ordering. | 64 | * with a different argument ordering. |