diff options
| -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)); |
