Commit 7076aada authored by Al Viro's avatar Al Viro

x86: split ret_from_fork

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 44f4b56b
...@@ -97,6 +97,7 @@ config X86 ...@@ -97,6 +97,7 @@ config X86
select KTIME_SCALAR if X86_32 select KTIME_SCALAR if X86_32
select GENERIC_STRNCPY_FROM_USER select GENERIC_STRNCPY_FROM_USER
select GENERIC_STRNLEN_USER select GENERIC_STRNLEN_USER
select GENERIC_KERNEL_THREAD
config INSTRUCTION_DECODER config INSTRUCTION_DECODER
def_bool (KPROBES || PERF_EVENTS || UPROBES) def_bool (KPROBES || PERF_EVENTS || UPROBES)
......
...@@ -589,11 +589,6 @@ typedef struct { ...@@ -589,11 +589,6 @@ typedef struct {
} mm_segment_t; } mm_segment_t;
/*
* create a kernel thread without removing it from tasklists
*/
extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
/* Free all resources held by a thread. */ /* Free all resources held by a thread. */
extern void release_thread(struct task_struct *); extern void release_thread(struct task_struct *);
......
...@@ -994,15 +994,20 @@ END(spurious_interrupt_bug) ...@@ -994,15 +994,20 @@ END(spurious_interrupt_bug)
*/ */
.popsection .popsection
ENTRY(kernel_thread_helper) ENTRY(ret_from_kernel_thread)
pushl $0 # fake return address for unwinder
CFI_STARTPROC CFI_STARTPROC
movl %edi,%eax pushl_cfi %eax
call *%esi call schedule_tail
GET_THREAD_INFO(%ebp)
popl_cfi %eax
pushl_cfi $0x0202 # Reset kernel eflags
popfl_cfi
movl PT_EBP(%esp),%eax
call *PT_EBX(%esp)
call do_exit call do_exit
ud2 # padding for call trace ud2 # padding for call trace
CFI_ENDPROC CFI_ENDPROC
ENDPROC(kernel_thread_helper) ENDPROC(ret_from_kernel_thread)
#ifdef CONFIG_XEN #ifdef CONFIG_XEN
/* Xen doesn't set %esp to be precisely what the normal sysenter /* Xen doesn't set %esp to be precisely what the normal sysenter
......
...@@ -450,7 +450,7 @@ ENTRY(ret_from_fork) ...@@ -450,7 +450,7 @@ ENTRY(ret_from_fork)
RESTORE_REST RESTORE_REST
testl $3, CS-ARGOFFSET(%rsp) # from kernel_thread? testl $3, CS-ARGOFFSET(%rsp) # from kernel_thread?
jz retint_restore_args jz 1f
testl $_TIF_IA32, TI_flags(%rcx) # 32-bit compat task needs IRET testl $_TIF_IA32, TI_flags(%rcx) # 32-bit compat task needs IRET
jnz int_ret_from_sys_call jnz int_ret_from_sys_call
...@@ -458,6 +458,16 @@ ENTRY(ret_from_fork) ...@@ -458,6 +458,16 @@ ENTRY(ret_from_fork)
RESTORE_TOP_OF_STACK %rdi, -ARGOFFSET RESTORE_TOP_OF_STACK %rdi, -ARGOFFSET
jmp ret_from_sys_call # go to the SYSRET fastpath jmp ret_from_sys_call # go to the SYSRET fastpath
1:
subq $REST_SKIP, %rsp # move the stack pointer back
CFI_ADJUST_CFA_OFFSET REST_SKIP
movq %rbp, %rdi
call *%rbx
# exit
mov %eax, %edi
call do_exit
ud2 # padding for call trace
CFI_ENDPROC CFI_ENDPROC
END(ret_from_fork) END(ret_from_fork)
...@@ -1206,21 +1216,6 @@ bad_gs: ...@@ -1206,21 +1216,6 @@ bad_gs:
jmp 2b jmp 2b
.previous .previous
ENTRY(kernel_thread_helper)
pushq $0 # fake return address
CFI_STARTPROC
/*
* Here we are in the child and the registers are set as they were
* at kernel_thread() invocation in the parent.
*/
call *%rsi
# exit
mov %eax, %edi
call do_exit
ud2 # padding for call trace
CFI_ENDPROC
END(kernel_thread_helper)
/* /*
* execve(). This function needs to use IRET, not SYSRET, to set up all state properly. * execve(). This function needs to use IRET, not SYSRET, to set up all state properly.
* *
......
...@@ -298,44 +298,6 @@ sys_clone(unsigned long clone_flags, unsigned long newsp, ...@@ -298,44 +298,6 @@ sys_clone(unsigned long clone_flags, unsigned long newsp,
return do_fork(clone_flags, newsp, regs, 0, parent_tid, child_tid); return do_fork(clone_flags, newsp, regs, 0, parent_tid, child_tid);
} }
/*
* This gets run with %si containing the
* function to call, and %di containing
* the "args".
*/
extern void kernel_thread_helper(void);
/*
* Create a kernel thread
*/
int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
struct pt_regs regs;
memset(&regs, 0, sizeof(regs));
regs.si = (unsigned long) fn;
regs.di = (unsigned long) arg;
#ifdef CONFIG_X86_32
regs.ds = __USER_DS;
regs.es = __USER_DS;
regs.fs = __KERNEL_PERCPU;
regs.gs = __KERNEL_STACK_CANARY;
#else
regs.ss = __KERNEL_DS;
#endif
regs.orig_ax = -1;
regs.ip = (unsigned long) kernel_thread_helper;
regs.cs = __KERNEL_CS | get_kernel_rpl();
regs.flags = X86_EFLAGS_IF | X86_EFLAGS_BIT1;
/* Ok, create the new process.. */
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
}
EXPORT_SYMBOL(kernel_thread);
/* /*
* sys_execve() executes a new program. * sys_execve() executes a new program.
*/ */
......
...@@ -57,6 +57,7 @@ ...@@ -57,6 +57,7 @@
#include <asm/switch_to.h> #include <asm/switch_to.h>
asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
asmlinkage void ret_from_kernel_thread(void) __asm__("ret_from_kernel_thread");
/* /*
* Return saved PC of a blocked thread. * Return saved PC of a blocked thread.
...@@ -127,23 +128,39 @@ void release_thread(struct task_struct *dead_task) ...@@ -127,23 +128,39 @@ void release_thread(struct task_struct *dead_task)
} }
int copy_thread(unsigned long clone_flags, unsigned long sp, int copy_thread(unsigned long clone_flags, unsigned long sp,
unsigned long unused, unsigned long arg,
struct task_struct *p, struct pt_regs *regs) struct task_struct *p, struct pt_regs *regs)
{ {
struct pt_regs *childregs; struct pt_regs *childregs = task_pt_regs(p);
struct task_struct *tsk; struct task_struct *tsk;
int err; int err;
childregs = task_pt_regs(p); p->thread.sp = (unsigned long) childregs;
p->thread.sp0 = (unsigned long) (childregs+1);
if (unlikely(!regs)) {
/* kernel thread */
memset(childregs, 0, sizeof(struct pt_regs));
p->thread.ip = (unsigned long) ret_from_kernel_thread;
task_user_gs(p) = __KERNEL_STACK_CANARY;
childregs->ds = __USER_DS;
childregs->es = __USER_DS;
childregs->fs = __KERNEL_PERCPU;
childregs->bx = sp; /* function */
childregs->bp = arg;
childregs->orig_ax = -1;
childregs->cs = __KERNEL_CS | get_kernel_rpl();
childregs->flags = X86_EFLAGS_IF | X86_EFLAGS_BIT1;
p->fpu_counter = 0;
p->thread.io_bitmap_ptr = NULL;
memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps));
return 0;
}
*childregs = *regs; *childregs = *regs;
childregs->ax = 0; childregs->ax = 0;
childregs->sp = sp; childregs->sp = sp;
p->thread.sp = (unsigned long) childregs;
p->thread.sp0 = (unsigned long) (childregs+1);
p->thread.ip = (unsigned long) ret_from_fork; p->thread.ip = (unsigned long) ret_from_fork;
task_user_gs(p) = get_user_gs(regs); task_user_gs(p) = get_user_gs(regs);
p->fpu_counter = 0; p->fpu_counter = 0;
......
...@@ -146,29 +146,18 @@ static inline u32 read_32bit_tls(struct task_struct *t, int tls) ...@@ -146,29 +146,18 @@ static inline u32 read_32bit_tls(struct task_struct *t, int tls)
} }
int copy_thread(unsigned long clone_flags, unsigned long sp, int copy_thread(unsigned long clone_flags, unsigned long sp,
unsigned long unused, unsigned long arg,
struct task_struct *p, struct pt_regs *regs) struct task_struct *p, struct pt_regs *regs)
{ {
int err; int err;
struct pt_regs *childregs; struct pt_regs *childregs;
struct task_struct *me = current; struct task_struct *me = current;
childregs = ((struct pt_regs *) p->thread.sp0 = (unsigned long)task_stack_page(p) + THREAD_SIZE;
(THREAD_SIZE + task_stack_page(p))) - 1; childregs = task_pt_regs(p);
*childregs = *regs;
childregs->ax = 0;
if (user_mode(regs))
childregs->sp = sp;
else
childregs->sp = (unsigned long)childregs;
p->thread.sp = (unsigned long) childregs; p->thread.sp = (unsigned long) childregs;
p->thread.sp0 = (unsigned long) (childregs+1);
p->thread.usersp = me->thread.usersp; p->thread.usersp = me->thread.usersp;
set_tsk_thread_flag(p, TIF_FORK); set_tsk_thread_flag(p, TIF_FORK);
p->fpu_counter = 0; p->fpu_counter = 0;
p->thread.io_bitmap_ptr = NULL; p->thread.io_bitmap_ptr = NULL;
...@@ -178,6 +167,24 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, ...@@ -178,6 +167,24 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
p->thread.fs = p->thread.fsindex ? 0 : me->thread.fs; p->thread.fs = p->thread.fsindex ? 0 : me->thread.fs;
savesegment(es, p->thread.es); savesegment(es, p->thread.es);
savesegment(ds, p->thread.ds); savesegment(ds, p->thread.ds);
memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps));
if (unlikely(!regs)) {
/* kernel thread */
memset(childregs, 0, sizeof(struct pt_regs));
childregs->sp = (unsigned long)childregs;
childregs->ss = __KERNEL_DS;
childregs->bx = sp; /* function */
childregs->bp = arg;
childregs->orig_ax = -1;
childregs->cs = __KERNEL_CS | get_kernel_rpl();
childregs->flags = X86_EFLAGS_IF | X86_EFLAGS_BIT1;
return 0;
}
*childregs = *regs;
childregs->ax = 0;
childregs->sp = sp;
err = -ENOMEM; err = -ENOMEM;
memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps)); memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment