aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2012-09-10 16:44:54 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2012-09-30 22:53:31 -0400
commit7076aada1040de4ed79a5977dbabdb5e5ea5e249 (patch)
treedfcf480d21cd714a1cc42f38417c427d06526f5a /arch/x86
parent44f4b56b54d2ab5d06c1726f2cde8ca15c8fac47 (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/Kconfig1
-rw-r--r--arch/x86/include/asm/processor.h5
-rw-r--r--arch/x86/kernel/entry_32.S15
-rw-r--r--arch/x86/kernel/entry_64.S27
-rw-r--r--arch/x86/kernel/process.c38
-rw-r--r--arch/x86/kernel/process_32.c31
-rw-r--r--arch/x86/kernel/process_64.c35
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
101config INSTRUCTION_DECODER 102config 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 */
595extern 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. */
598extern void release_thread(struct task_struct *); 593extern 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
997ENTRY(kernel_thread_helper) 997ENTRY(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
1005ENDPROC(kernel_thread_helper) 1010ENDPROC(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
4611:
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
462END(ret_from_fork) 472END(ret_from_fork)
463 473
@@ -1206,21 +1216,6 @@ bad_gs:
1206 jmp 2b 1216 jmp 2b
1207 .previous 1217 .previous
1208 1218
1209ENTRY(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
1222END(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 */
306extern void kernel_thread_helper(void);
307
308/*
309 * Create a kernel thread
310 */
311int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
312{
313 struct pt_regs regs;
314
315 memset(&regs, 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, &regs, 0, NULL, NULL);
336}
337EXPORT_SYMBOL(kernel_thread);
338
339/*
340 * sys_execve() executes a new program. 302 * sys_execve() executes a new program.
341 */ 303 */
342long sys_execve(const char __user *name, 304long 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
59asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); 59asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
60asmlinkage 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
129int copy_thread(unsigned long clone_flags, unsigned long sp, 130int 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
148int copy_thread(unsigned long clone_flags, unsigned long sp, 148int 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));