diff options
Diffstat (limited to 'arch/x86/kernel/process.c')
| -rw-r--r-- | arch/x86/kernel/process.c | 22 | 
1 files changed, 21 insertions, 1 deletions
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index ff9b80a0e3e3..72015dba72ab 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -28,6 +28,7 @@  #include <linux/static_call.h>  #include <trace/events/power.h>  #include <linux/hw_breakpoint.h> +#include <linux/entry-common.h>  #include <asm/cpu.h>  #include <asm/apic.h>  #include <linux/uaccess.h> @@ -134,6 +135,25 @@ static int set_new_tls(struct task_struct *p, unsigned long tls)  		return do_set_thread_area_64(p, ARCH_SET_FS, tls);  } +__visible void ret_from_fork(struct task_struct *prev, struct pt_regs *regs, +				     int (*fn)(void *), void *fn_arg) +{ +	schedule_tail(prev); + +	/* Is this a kernel thread? */ +	if (unlikely(fn)) { +		fn(fn_arg); +		/* +		 * A kernel thread is allowed to return here after successfully +		 * calling kernel_execve().  Exit to userspace to complete the +		 * execve() syscall. +		 */ +		regs->ax = 0; +	} + +	syscall_exit_to_user_mode(regs); +} +  int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)  {  	unsigned long clone_flags = args->flags; @@ -149,7 +169,7 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)  	frame = &fork_frame->frame;  	frame->bp = encode_frame_pointer(childregs); -	frame->ret_addr = (unsigned long) ret_from_fork; +	frame->ret_addr = (unsigned long) ret_from_fork_asm;  	p->thread.sp = (unsigned long) fork_frame;  	p->thread.io_bitmap = NULL;  	p->thread.iopl_warn = 0;  |