diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2012-09-10 16:44:54 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2012-09-30 22:53:31 -0400 |
commit | 7076aada1040de4ed79a5977dbabdb5e5ea5e249 (patch) | |
tree | dfcf480d21cd714a1cc42f38417c427d06526f5a /arch/x86 | |
parent | 44f4b56b54d2ab5d06c1726f2cde8ca15c8fac47 (diff) |
x86: split ret_from_fork
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/Kconfig | 1 | ||||
-rw-r--r-- | arch/x86/include/asm/processor.h | 5 | ||||
-rw-r--r-- | arch/x86/kernel/entry_32.S | 15 | ||||
-rw-r--r-- | arch/x86/kernel/entry_64.S | 27 | ||||
-rw-r--r-- | arch/x86/kernel/process.c | 38 | ||||
-rw-r--r-- | arch/x86/kernel/process_32.c | 31 | ||||
-rw-r--r-- | arch/x86/kernel/process_64.c | 35 |
7 files changed, 67 insertions, 85 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 8ec3a1aa4abd..d93eb9d1bb97 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -97,6 +97,7 @@ config X86 | |||
97 | select KTIME_SCALAR if X86_32 | 97 | select KTIME_SCALAR if X86_32 |
98 | select GENERIC_STRNCPY_FROM_USER | 98 | select GENERIC_STRNCPY_FROM_USER |
99 | select GENERIC_STRNLEN_USER | 99 | select GENERIC_STRNLEN_USER |
100 | select GENERIC_KERNEL_THREAD | ||
100 | 101 | ||
101 | config INSTRUCTION_DECODER | 102 | config INSTRUCTION_DECODER |
102 | def_bool (KPROBES || PERF_EVENTS || UPROBES) | 103 | def_bool (KPROBES || PERF_EVENTS || UPROBES) |
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index d048cad9bcad..078f3fdedf95 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h | |||
@@ -589,11 +589,6 @@ typedef struct { | |||
589 | } mm_segment_t; | 589 | } mm_segment_t; |
590 | 590 | ||
591 | 591 | ||
592 | /* | ||
593 | * create a kernel thread without removing it from tasklists | ||
594 | */ | ||
595 | extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags); | ||
596 | |||
597 | /* Free all resources held by a thread. */ | 592 | /* Free all resources held by a thread. */ |
598 | extern void release_thread(struct task_struct *); | 593 | extern void release_thread(struct task_struct *); |
599 | 594 | ||
diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index 623f28837476..ac1107346fc9 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S | |||
@@ -994,15 +994,20 @@ END(spurious_interrupt_bug) | |||
994 | */ | 994 | */ |
995 | .popsection | 995 | .popsection |
996 | 996 | ||
997 | ENTRY(kernel_thread_helper) | 997 | ENTRY(ret_from_kernel_thread) |
998 | pushl $0 # fake return address for unwinder | ||
999 | CFI_STARTPROC | 998 | CFI_STARTPROC |
1000 | movl %edi,%eax | 999 | pushl_cfi %eax |
1001 | call *%esi | 1000 | call schedule_tail |
1001 | GET_THREAD_INFO(%ebp) | ||
1002 | popl_cfi %eax | ||
1003 | pushl_cfi $0x0202 # Reset kernel eflags | ||
1004 | popfl_cfi | ||
1005 | movl PT_EBP(%esp),%eax | ||
1006 | call *PT_EBX(%esp) | ||
1002 | call do_exit | 1007 | call do_exit |
1003 | ud2 # padding for call trace | 1008 | ud2 # padding for call trace |
1004 | CFI_ENDPROC | 1009 | CFI_ENDPROC |
1005 | ENDPROC(kernel_thread_helper) | 1010 | ENDPROC(ret_from_kernel_thread) |
1006 | 1011 | ||
1007 | #ifdef CONFIG_XEN | 1012 | #ifdef CONFIG_XEN |
1008 | /* Xen doesn't set %esp to be precisely what the normal sysenter | 1013 | /* Xen doesn't set %esp to be precisely what the normal sysenter |
diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index 69babd8c834f..5526d17db676 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S | |||
@@ -450,7 +450,7 @@ ENTRY(ret_from_fork) | |||
450 | RESTORE_REST | 450 | RESTORE_REST |
451 | 451 | ||
452 | testl $3, CS-ARGOFFSET(%rsp) # from kernel_thread? | 452 | testl $3, CS-ARGOFFSET(%rsp) # from kernel_thread? |
453 | jz retint_restore_args | 453 | jz 1f |
454 | 454 | ||
455 | testl $_TIF_IA32, TI_flags(%rcx) # 32-bit compat task needs IRET | 455 | testl $_TIF_IA32, TI_flags(%rcx) # 32-bit compat task needs IRET |
456 | jnz int_ret_from_sys_call | 456 | jnz int_ret_from_sys_call |
@@ -458,6 +458,16 @@ ENTRY(ret_from_fork) | |||
458 | RESTORE_TOP_OF_STACK %rdi, -ARGOFFSET | 458 | RESTORE_TOP_OF_STACK %rdi, -ARGOFFSET |
459 | jmp ret_from_sys_call # go to the SYSRET fastpath | 459 | jmp ret_from_sys_call # go to the SYSRET fastpath |
460 | 460 | ||
461 | 1: | ||
462 | subq $REST_SKIP, %rsp # move the stack pointer back | ||
463 | CFI_ADJUST_CFA_OFFSET REST_SKIP | ||
464 | movq %rbp, %rdi | ||
465 | call *%rbx | ||
466 | # exit | ||
467 | mov %eax, %edi | ||
468 | call do_exit | ||
469 | ud2 # padding for call trace | ||
470 | |||
461 | CFI_ENDPROC | 471 | CFI_ENDPROC |
462 | END(ret_from_fork) | 472 | END(ret_from_fork) |
463 | 473 | ||
@@ -1206,21 +1216,6 @@ bad_gs: | |||
1206 | jmp 2b | 1216 | jmp 2b |
1207 | .previous | 1217 | .previous |
1208 | 1218 | ||
1209 | ENTRY(kernel_thread_helper) | ||
1210 | pushq $0 # fake return address | ||
1211 | CFI_STARTPROC | ||
1212 | /* | ||
1213 | * Here we are in the child and the registers are set as they were | ||
1214 | * at kernel_thread() invocation in the parent. | ||
1215 | */ | ||
1216 | call *%rsi | ||
1217 | # exit | ||
1218 | mov %eax, %edi | ||
1219 | call do_exit | ||
1220 | ud2 # padding for call trace | ||
1221 | CFI_ENDPROC | ||
1222 | END(kernel_thread_helper) | ||
1223 | |||
1224 | /* | 1219 | /* |
1225 | * execve(). This function needs to use IRET, not SYSRET, to set up all state properly. | 1220 | * execve(). This function needs to use IRET, not SYSRET, to set up all state properly. |
1226 | * | 1221 | * |
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 7162e9c1f598..6947ec968bf8 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c | |||
@@ -299,44 +299,6 @@ sys_clone(unsigned long clone_flags, unsigned long newsp, | |||
299 | } | 299 | } |
300 | 300 | ||
301 | /* | 301 | /* |
302 | * This gets run with %si containing the | ||
303 | * function to call, and %di containing | ||
304 | * the "args". | ||
305 | */ | ||
306 | extern void kernel_thread_helper(void); | ||
307 | |||
308 | /* | ||
309 | * Create a kernel thread | ||
310 | */ | ||
311 | int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) | ||
312 | { | ||
313 | struct pt_regs regs; | ||
314 | |||
315 | memset(®s, 0, sizeof(regs)); | ||
316 | |||
317 | regs.si = (unsigned long) fn; | ||
318 | regs.di = (unsigned long) arg; | ||
319 | |||
320 | #ifdef CONFIG_X86_32 | ||
321 | regs.ds = __USER_DS; | ||
322 | regs.es = __USER_DS; | ||
323 | regs.fs = __KERNEL_PERCPU; | ||
324 | regs.gs = __KERNEL_STACK_CANARY; | ||
325 | #else | ||
326 | regs.ss = __KERNEL_DS; | ||
327 | #endif | ||
328 | |||
329 | regs.orig_ax = -1; | ||
330 | regs.ip = (unsigned long) kernel_thread_helper; | ||
331 | regs.cs = __KERNEL_CS | get_kernel_rpl(); | ||
332 | regs.flags = X86_EFLAGS_IF | X86_EFLAGS_BIT1; | ||
333 | |||
334 | /* Ok, create the new process.. */ | ||
335 | return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); | ||
336 | } | ||
337 | EXPORT_SYMBOL(kernel_thread); | ||
338 | |||
339 | /* | ||
340 | * sys_execve() executes a new program. | 302 | * sys_execve() executes a new program. |
341 | */ | 303 | */ |
342 | long sys_execve(const char __user *name, | 304 | long sys_execve(const char __user *name, |
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index 75fcad146def..c9939875d267 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c | |||
@@ -57,6 +57,7 @@ | |||
57 | #include <asm/switch_to.h> | 57 | #include <asm/switch_to.h> |
58 | 58 | ||
59 | asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); | 59 | asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); |
60 | asmlinkage void ret_from_kernel_thread(void) __asm__("ret_from_kernel_thread"); | ||
60 | 61 | ||
61 | /* | 62 | /* |
62 | * Return saved PC of a blocked thread. | 63 | * Return saved PC of a blocked thread. |
@@ -127,23 +128,39 @@ void release_thread(struct task_struct *dead_task) | |||
127 | } | 128 | } |
128 | 129 | ||
129 | int copy_thread(unsigned long clone_flags, unsigned long sp, | 130 | int copy_thread(unsigned long clone_flags, unsigned long sp, |
130 | unsigned long unused, | 131 | unsigned long arg, |
131 | struct task_struct *p, struct pt_regs *regs) | 132 | struct task_struct *p, struct pt_regs *regs) |
132 | { | 133 | { |
133 | struct pt_regs *childregs; | 134 | struct pt_regs *childregs = task_pt_regs(p); |
134 | struct task_struct *tsk; | 135 | struct task_struct *tsk; |
135 | int err; | 136 | int err; |
136 | 137 | ||
137 | childregs = task_pt_regs(p); | 138 | p->thread.sp = (unsigned long) childregs; |
139 | p->thread.sp0 = (unsigned long) (childregs+1); | ||
140 | |||
141 | if (unlikely(!regs)) { | ||
142 | /* kernel thread */ | ||
143 | memset(childregs, 0, sizeof(struct pt_regs)); | ||
144 | p->thread.ip = (unsigned long) ret_from_kernel_thread; | ||
145 | task_user_gs(p) = __KERNEL_STACK_CANARY; | ||
146 | childregs->ds = __USER_DS; | ||
147 | childregs->es = __USER_DS; | ||
148 | childregs->fs = __KERNEL_PERCPU; | ||
149 | childregs->bx = sp; /* function */ | ||
150 | childregs->bp = arg; | ||
151 | childregs->orig_ax = -1; | ||
152 | childregs->cs = __KERNEL_CS | get_kernel_rpl(); | ||
153 | childregs->flags = X86_EFLAGS_IF | X86_EFLAGS_BIT1; | ||
154 | p->fpu_counter = 0; | ||
155 | p->thread.io_bitmap_ptr = NULL; | ||
156 | memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps)); | ||
157 | return 0; | ||
158 | } | ||
138 | *childregs = *regs; | 159 | *childregs = *regs; |
139 | childregs->ax = 0; | 160 | childregs->ax = 0; |
140 | childregs->sp = sp; | 161 | childregs->sp = sp; |
141 | 162 | ||
142 | p->thread.sp = (unsigned long) childregs; | ||
143 | p->thread.sp0 = (unsigned long) (childregs+1); | ||
144 | |||
145 | p->thread.ip = (unsigned long) ret_from_fork; | 163 | p->thread.ip = (unsigned long) ret_from_fork; |
146 | |||
147 | task_user_gs(p) = get_user_gs(regs); | 164 | task_user_gs(p) = get_user_gs(regs); |
148 | 165 | ||
149 | p->fpu_counter = 0; | 166 | p->fpu_counter = 0; |
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 0a980c9d7cb8..937f2af6f2d4 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c | |||
@@ -146,29 +146,18 @@ static inline u32 read_32bit_tls(struct task_struct *t, int tls) | |||
146 | } | 146 | } |
147 | 147 | ||
148 | int copy_thread(unsigned long clone_flags, unsigned long sp, | 148 | int copy_thread(unsigned long clone_flags, unsigned long sp, |
149 | unsigned long unused, | 149 | unsigned long arg, |
150 | struct task_struct *p, struct pt_regs *regs) | 150 | struct task_struct *p, struct pt_regs *regs) |
151 | { | 151 | { |
152 | int err; | 152 | int err; |
153 | struct pt_regs *childregs; | 153 | struct pt_regs *childregs; |
154 | struct task_struct *me = current; | 154 | struct task_struct *me = current; |
155 | 155 | ||
156 | childregs = ((struct pt_regs *) | 156 | p->thread.sp0 = (unsigned long)task_stack_page(p) + THREAD_SIZE; |
157 | (THREAD_SIZE + task_stack_page(p))) - 1; | 157 | childregs = task_pt_regs(p); |
158 | *childregs = *regs; | ||
159 | |||
160 | childregs->ax = 0; | ||
161 | if (user_mode(regs)) | ||
162 | childregs->sp = sp; | ||
163 | else | ||
164 | childregs->sp = (unsigned long)childregs; | ||
165 | |||
166 | p->thread.sp = (unsigned long) childregs; | 158 | p->thread.sp = (unsigned long) childregs; |
167 | p->thread.sp0 = (unsigned long) (childregs+1); | ||
168 | p->thread.usersp = me->thread.usersp; | 159 | p->thread.usersp = me->thread.usersp; |
169 | |||
170 | set_tsk_thread_flag(p, TIF_FORK); | 160 | set_tsk_thread_flag(p, TIF_FORK); |
171 | |||
172 | p->fpu_counter = 0; | 161 | p->fpu_counter = 0; |
173 | p->thread.io_bitmap_ptr = NULL; | 162 | p->thread.io_bitmap_ptr = NULL; |
174 | 163 | ||
@@ -178,6 +167,24 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, | |||
178 | p->thread.fs = p->thread.fsindex ? 0 : me->thread.fs; | 167 | p->thread.fs = p->thread.fsindex ? 0 : me->thread.fs; |
179 | savesegment(es, p->thread.es); | 168 | savesegment(es, p->thread.es); |
180 | savesegment(ds, p->thread.ds); | 169 | savesegment(ds, p->thread.ds); |
170 | memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps)); | ||
171 | |||
172 | if (unlikely(!regs)) { | ||
173 | /* kernel thread */ | ||
174 | memset(childregs, 0, sizeof(struct pt_regs)); | ||
175 | childregs->sp = (unsigned long)childregs; | ||
176 | childregs->ss = __KERNEL_DS; | ||
177 | childregs->bx = sp; /* function */ | ||
178 | childregs->bp = arg; | ||
179 | childregs->orig_ax = -1; | ||
180 | childregs->cs = __KERNEL_CS | get_kernel_rpl(); | ||
181 | childregs->flags = X86_EFLAGS_IF | X86_EFLAGS_BIT1; | ||
182 | return 0; | ||
183 | } | ||
184 | *childregs = *regs; | ||
185 | |||
186 | childregs->ax = 0; | ||
187 | childregs->sp = sp; | ||
181 | 188 | ||
182 | err = -ENOMEM; | 189 | err = -ENOMEM; |
183 | memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps)); | 190 | memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps)); |