Skip to content

Commit 7076aad

Browse files
author
Al Viro
committed
x86: split ret_from_fork
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
1 parent 44f4b56 commit 7076aad

File tree

7 files changed

+67
-85
lines changed

7 files changed

+67
-85
lines changed

arch/x86/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ config X86
9797
select KTIME_SCALAR if X86_32
9898
select GENERIC_STRNCPY_FROM_USER
9999
select GENERIC_STRNLEN_USER
100+
select GENERIC_KERNEL_THREAD
100101

101102
config INSTRUCTION_DECODER
102103
def_bool (KPROBES || PERF_EVENTS || UPROBES)

arch/x86/include/asm/processor.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -589,11 +589,6 @@ typedef struct {
589589
} mm_segment_t;
590590

591591

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-
597592
/* Free all resources held by a thread. */
598593
extern void release_thread(struct task_struct *);
599594

arch/x86/kernel/entry_32.S

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -994,15 +994,20 @@ END(spurious_interrupt_bug)
994994
*/
995995
.popsection
996996

997-
ENTRY(kernel_thread_helper)
998-
pushl $0 # fake return address for unwinder
997+
ENTRY(ret_from_kernel_thread)
999998
CFI_STARTPROC
1000-
movl %edi,%eax
1001-
call *%esi
999+
pushl_cfi %eax
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)
10021007
call do_exit
10031008
ud2 # padding for call trace
10041009
CFI_ENDPROC
1005-
ENDPROC(kernel_thread_helper)
1010+
ENDPROC(ret_from_kernel_thread)
10061011

10071012
#ifdef CONFIG_XEN
10081013
/* Xen doesn't set %esp to be precisely what the normal sysenter

arch/x86/kernel/entry_64.S

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -450,14 +450,24 @@ ENTRY(ret_from_fork)
450450
RESTORE_REST
451451

452452
testl $3, CS-ARGOFFSET(%rsp) # from kernel_thread?
453-
jz retint_restore_args
453+
jz 1f
454454

455455
testl $_TIF_IA32, TI_flags(%rcx) # 32-bit compat task needs IRET
456456
jnz int_ret_from_sys_call
457457

458458
RESTORE_TOP_OF_STACK %rdi, -ARGOFFSET
459459
jmp ret_from_sys_call # go to the SYSRET fastpath
460460

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+
461471
CFI_ENDPROC
462472
END(ret_from_fork)
463473

@@ -1206,21 +1216,6 @@ bad_gs:
12061216
jmp 2b
12071217
.previous
12081218

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-
12241219
/*
12251220
* execve(). This function needs to use IRET, not SYSRET, to set up all state properly.
12261221
*

arch/x86/kernel/process.c

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -298,44 +298,6 @@ sys_clone(unsigned long clone_flags, unsigned long newsp,
298298
return do_fork(clone_flags, newsp, regs, 0, parent_tid, child_tid);
299299
}
300300

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(&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-
}
337-
EXPORT_SYMBOL(kernel_thread);
338-
339301
/*
340302
* sys_execve() executes a new program.
341303
*/

arch/x86/kernel/process_32.c

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
#include <asm/switch_to.h>
5858

5959
asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
60+
asmlinkage void ret_from_kernel_thread(void) __asm__("ret_from_kernel_thread");
6061

6162
/*
6263
* Return saved PC of a blocked thread.
@@ -127,23 +128,39 @@ void release_thread(struct task_struct *dead_task)
127128
}
128129

129130
int copy_thread(unsigned long clone_flags, unsigned long sp,
130-
unsigned long unused,
131+
unsigned long arg,
131132
struct task_struct *p, struct pt_regs *regs)
132133
{
133-
struct pt_regs *childregs;
134+
struct pt_regs *childregs = task_pt_regs(p);
134135
struct task_struct *tsk;
135136
int err;
136137

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+
}
138159
*childregs = *regs;
139160
childregs->ax = 0;
140161
childregs->sp = sp;
141162

142-
p->thread.sp = (unsigned long) childregs;
143-
p->thread.sp0 = (unsigned long) (childregs+1);
144-
145163
p->thread.ip = (unsigned long) ret_from_fork;
146-
147164
task_user_gs(p) = get_user_gs(regs);
148165

149166
p->fpu_counter = 0;

arch/x86/kernel/process_64.c

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -146,29 +146,18 @@ static inline u32 read_32bit_tls(struct task_struct *t, int tls)
146146
}
147147

148148
int copy_thread(unsigned long clone_flags, unsigned long sp,
149-
unsigned long unused,
149+
unsigned long arg,
150150
struct task_struct *p, struct pt_regs *regs)
151151
{
152152
int err;
153153
struct pt_regs *childregs;
154154
struct task_struct *me = current;
155155

156-
childregs = ((struct pt_regs *)
157-
(THREAD_SIZE + task_stack_page(p))) - 1;
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-
156+
p->thread.sp0 = (unsigned long)task_stack_page(p) + THREAD_SIZE;
157+
childregs = task_pt_regs(p);
166158
p->thread.sp = (unsigned long) childregs;
167-
p->thread.sp0 = (unsigned long) (childregs+1);
168159
p->thread.usersp = me->thread.usersp;
169-
170160
set_tsk_thread_flag(p, TIF_FORK);
171-
172161
p->fpu_counter = 0;
173162
p->thread.io_bitmap_ptr = NULL;
174163

@@ -178,6 +167,24 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
178167
p->thread.fs = p->thread.fsindex ? 0 : me->thread.fs;
179168
savesegment(es, p->thread.es);
180169
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;
181188

182189
err = -ENOMEM;
183190
memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps));

0 commit comments

Comments
 (0)