x86/process: Unify copy_thread_tls()
While looking at the TSS io bitmap it turned out that any change in that area would require identical changes to copy_thread_tls(). The 32 and 64 bit variants share sufficient code to consolidate them into a common function to avoid duplication of upcoming modifications. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Acked-by: Andy Lutomirski <luto@kernel.org>
This commit is contained in:
parent
8c40397f22
commit
2fff071d28
|
@ -361,5 +361,11 @@ extern int do_get_thread_area(struct task_struct *p, int idx,
|
||||||
extern int do_set_thread_area(struct task_struct *p, int idx,
|
extern int do_set_thread_area(struct task_struct *p, int idx,
|
||||||
struct user_desc __user *info, int can_allocate);
|
struct user_desc __user *info, int can_allocate);
|
||||||
|
|
||||||
|
#ifdef CONFIG_X86_64
|
||||||
|
# define do_set_thread_area_64(p, s, t) do_arch_prctl_64(p, s, t)
|
||||||
|
#else
|
||||||
|
# define do_set_thread_area_64(p, s, t) (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* !__ASSEMBLY__ */
|
#endif /* !__ASSEMBLY__ */
|
||||||
#endif /* _ASM_X86_PTRACE_H */
|
#endif /* _ASM_X86_PTRACE_H */
|
||||||
|
|
|
@ -103,7 +103,17 @@ static inline void update_task_stack(struct task_struct *task)
|
||||||
if (static_cpu_has(X86_FEATURE_XENPV))
|
if (static_cpu_has(X86_FEATURE_XENPV))
|
||||||
load_sp0(task_top_of_stack(task));
|
load_sp0(task_top_of_stack(task));
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void kthread_frame_init(struct inactive_task_frame *frame,
|
||||||
|
unsigned long fun, unsigned long arg)
|
||||||
|
{
|
||||||
|
frame->bx = fun;
|
||||||
|
#ifdef CONFIG_X86_32
|
||||||
|
frame->di = arg;
|
||||||
|
#else
|
||||||
|
frame->r12 = arg;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _ASM_X86_SWITCH_TO_H */
|
#endif /* _ASM_X86_SWITCH_TO_H */
|
||||||
|
|
|
@ -132,6 +132,106 @@ void exit_thread(struct task_struct *tsk)
|
||||||
fpu__drop(fpu);
|
fpu__drop(fpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int set_new_tls(struct task_struct *p, unsigned long tls)
|
||||||
|
{
|
||||||
|
struct user_desc __user *utls = (struct user_desc __user *)tls;
|
||||||
|
|
||||||
|
if (in_ia32_syscall())
|
||||||
|
return do_set_thread_area(p, -1, utls, 0);
|
||||||
|
else
|
||||||
|
return do_set_thread_area_64(p, ARCH_SET_FS, tls);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int copy_io_bitmap(struct task_struct *tsk)
|
||||||
|
{
|
||||||
|
if (likely(!test_tsk_thread_flag(current, TIF_IO_BITMAP)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
tsk->thread.io_bitmap_ptr = kmemdup(current->thread.io_bitmap_ptr,
|
||||||
|
IO_BITMAP_BYTES, GFP_KERNEL);
|
||||||
|
if (!tsk->thread.io_bitmap_ptr) {
|
||||||
|
tsk->thread.io_bitmap_max = 0;
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
set_tsk_thread_flag(tsk, TIF_IO_BITMAP);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void free_io_bitmap(struct task_struct *tsk)
|
||||||
|
{
|
||||||
|
if (tsk->thread.io_bitmap_ptr) {
|
||||||
|
kfree(tsk->thread.io_bitmap_ptr);
|
||||||
|
tsk->thread.io_bitmap_ptr = NULL;
|
||||||
|
tsk->thread.io_bitmap_max = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
|
||||||
|
unsigned long arg, struct task_struct *p, unsigned long tls)
|
||||||
|
{
|
||||||
|
struct inactive_task_frame *frame;
|
||||||
|
struct fork_frame *fork_frame;
|
||||||
|
struct pt_regs *childregs;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
childregs = task_pt_regs(p);
|
||||||
|
fork_frame = container_of(childregs, struct fork_frame, regs);
|
||||||
|
frame = &fork_frame->frame;
|
||||||
|
|
||||||
|
frame->bp = 0;
|
||||||
|
frame->ret_addr = (unsigned long) ret_from_fork;
|
||||||
|
p->thread.sp = (unsigned long) fork_frame;
|
||||||
|
p->thread.io_bitmap_ptr = NULL;
|
||||||
|
memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps));
|
||||||
|
|
||||||
|
#ifdef CONFIG_X86_64
|
||||||
|
savesegment(gs, p->thread.gsindex);
|
||||||
|
p->thread.gsbase = p->thread.gsindex ? 0 : current->thread.gsbase;
|
||||||
|
savesegment(fs, p->thread.fsindex);
|
||||||
|
p->thread.fsbase = p->thread.fsindex ? 0 : current->thread.fsbase;
|
||||||
|
savesegment(es, p->thread.es);
|
||||||
|
savesegment(ds, p->thread.ds);
|
||||||
|
#else
|
||||||
|
p->thread.sp0 = (unsigned long) (childregs + 1);
|
||||||
|
/*
|
||||||
|
* Clear all status flags including IF and set fixed bit. 64bit
|
||||||
|
* does not have this initialization as the frame does not contain
|
||||||
|
* flags. The flags consistency (especially vs. AC) is there
|
||||||
|
* ensured via objtool, which lacks 32bit support.
|
||||||
|
*/
|
||||||
|
frame->flags = X86_EFLAGS_FIXED;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Kernel thread ? */
|
||||||
|
if (unlikely(p->flags & PF_KTHREAD)) {
|
||||||
|
memset(childregs, 0, sizeof(struct pt_regs));
|
||||||
|
kthread_frame_init(frame, sp, arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame->bx = 0;
|
||||||
|
*childregs = *current_pt_regs();
|
||||||
|
childregs->ax = 0;
|
||||||
|
if (sp)
|
||||||
|
childregs->sp = sp;
|
||||||
|
|
||||||
|
#ifdef CONFIG_X86_32
|
||||||
|
task_user_gs(p) = get_user_gs(current_pt_regs());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ret = copy_io_bitmap(p);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Set a new TLS for the child thread? */
|
||||||
|
if (clone_flags & CLONE_SETTLS) {
|
||||||
|
ret = set_new_tls(p, tls);
|
||||||
|
if (ret)
|
||||||
|
free_io_bitmap(p);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void flush_thread(void)
|
void flush_thread(void)
|
||||||
{
|
{
|
||||||
struct task_struct *tsk = current;
|
struct task_struct *tsk = current;
|
||||||
|
|
|
@ -112,74 +112,6 @@ void release_thread(struct task_struct *dead_task)
|
||||||
release_vm86_irqs(dead_task);
|
release_vm86_irqs(dead_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
|
|
||||||
unsigned long arg, struct task_struct *p, unsigned long tls)
|
|
||||||
{
|
|
||||||
struct pt_regs *childregs = task_pt_regs(p);
|
|
||||||
struct fork_frame *fork_frame = container_of(childregs, struct fork_frame, regs);
|
|
||||||
struct inactive_task_frame *frame = &fork_frame->frame;
|
|
||||||
struct task_struct *tsk;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For a new task use the RESET flags value since there is no before.
|
|
||||||
* All the status flags are zero; DF and all the system flags must also
|
|
||||||
* be 0, specifically IF must be 0 because we context switch to the new
|
|
||||||
* task with interrupts disabled.
|
|
||||||
*/
|
|
||||||
frame->flags = X86_EFLAGS_FIXED;
|
|
||||||
frame->bp = 0;
|
|
||||||
frame->ret_addr = (unsigned long) ret_from_fork;
|
|
||||||
p->thread.sp = (unsigned long) fork_frame;
|
|
||||||
p->thread.sp0 = (unsigned long) (childregs+1);
|
|
||||||
memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps));
|
|
||||||
|
|
||||||
if (unlikely(p->flags & PF_KTHREAD)) {
|
|
||||||
/* kernel thread */
|
|
||||||
memset(childregs, 0, sizeof(struct pt_regs));
|
|
||||||
frame->bx = sp; /* function */
|
|
||||||
frame->di = arg;
|
|
||||||
p->thread.io_bitmap_ptr = NULL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
frame->bx = 0;
|
|
||||||
*childregs = *current_pt_regs();
|
|
||||||
childregs->ax = 0;
|
|
||||||
if (sp)
|
|
||||||
childregs->sp = sp;
|
|
||||||
|
|
||||||
task_user_gs(p) = get_user_gs(current_pt_regs());
|
|
||||||
|
|
||||||
p->thread.io_bitmap_ptr = NULL;
|
|
||||||
tsk = current;
|
|
||||||
err = -ENOMEM;
|
|
||||||
|
|
||||||
if (unlikely(test_tsk_thread_flag(tsk, TIF_IO_BITMAP))) {
|
|
||||||
p->thread.io_bitmap_ptr = kmemdup(tsk->thread.io_bitmap_ptr,
|
|
||||||
IO_BITMAP_BYTES, GFP_KERNEL);
|
|
||||||
if (!p->thread.io_bitmap_ptr) {
|
|
||||||
p->thread.io_bitmap_max = 0;
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
set_tsk_thread_flag(p, TIF_IO_BITMAP);
|
|
||||||
}
|
|
||||||
|
|
||||||
err = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set a new TLS for the child thread?
|
|
||||||
*/
|
|
||||||
if (clone_flags & CLONE_SETTLS)
|
|
||||||
err = do_set_thread_area(p, -1,
|
|
||||||
(struct user_desc __user *)tls, 0);
|
|
||||||
|
|
||||||
if (err && p->thread.io_bitmap_ptr) {
|
|
||||||
kfree(p->thread.io_bitmap_ptr);
|
|
||||||
p->thread.io_bitmap_max = 0;
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
|
start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
|
||||||
{
|
{
|
||||||
|
|
|
@ -371,81 +371,6 @@ void x86_gsbase_write_task(struct task_struct *task, unsigned long gsbase)
|
||||||
task->thread.gsbase = gsbase;
|
task->thread.gsbase = gsbase;
|
||||||
}
|
}
|
||||||
|
|
||||||
int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
|
|
||||||
unsigned long arg, struct task_struct *p, unsigned long tls)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct pt_regs *childregs;
|
|
||||||
struct fork_frame *fork_frame;
|
|
||||||
struct inactive_task_frame *frame;
|
|
||||||
struct task_struct *me = current;
|
|
||||||
|
|
||||||
childregs = task_pt_regs(p);
|
|
||||||
fork_frame = container_of(childregs, struct fork_frame, regs);
|
|
||||||
frame = &fork_frame->frame;
|
|
||||||
|
|
||||||
frame->bp = 0;
|
|
||||||
frame->ret_addr = (unsigned long) ret_from_fork;
|
|
||||||
p->thread.sp = (unsigned long) fork_frame;
|
|
||||||
p->thread.io_bitmap_ptr = NULL;
|
|
||||||
|
|
||||||
savesegment(gs, p->thread.gsindex);
|
|
||||||
p->thread.gsbase = p->thread.gsindex ? 0 : me->thread.gsbase;
|
|
||||||
savesegment(fs, p->thread.fsindex);
|
|
||||||
p->thread.fsbase = p->thread.fsindex ? 0 : me->thread.fsbase;
|
|
||||||
savesegment(es, p->thread.es);
|
|
||||||
savesegment(ds, p->thread.ds);
|
|
||||||
memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps));
|
|
||||||
|
|
||||||
if (unlikely(p->flags & PF_KTHREAD)) {
|
|
||||||
/* kernel thread */
|
|
||||||
memset(childregs, 0, sizeof(struct pt_regs));
|
|
||||||
frame->bx = sp; /* function */
|
|
||||||
frame->r12 = arg;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
frame->bx = 0;
|
|
||||||
*childregs = *current_pt_regs();
|
|
||||||
|
|
||||||
childregs->ax = 0;
|
|
||||||
if (sp)
|
|
||||||
childregs->sp = sp;
|
|
||||||
|
|
||||||
err = -ENOMEM;
|
|
||||||
if (unlikely(test_tsk_thread_flag(me, TIF_IO_BITMAP))) {
|
|
||||||
p->thread.io_bitmap_ptr = kmemdup(me->thread.io_bitmap_ptr,
|
|
||||||
IO_BITMAP_BYTES, GFP_KERNEL);
|
|
||||||
if (!p->thread.io_bitmap_ptr) {
|
|
||||||
p->thread.io_bitmap_max = 0;
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
set_tsk_thread_flag(p, TIF_IO_BITMAP);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set a new TLS for the child thread?
|
|
||||||
*/
|
|
||||||
if (clone_flags & CLONE_SETTLS) {
|
|
||||||
#ifdef CONFIG_IA32_EMULATION
|
|
||||||
if (in_ia32_syscall())
|
|
||||||
err = do_set_thread_area(p, -1,
|
|
||||||
(struct user_desc __user *)tls, 0);
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
err = do_arch_prctl_64(p, ARCH_SET_FS, tls);
|
|
||||||
if (err)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
err = 0;
|
|
||||||
out:
|
|
||||||
if (err && p->thread.io_bitmap_ptr) {
|
|
||||||
kfree(p->thread.io_bitmap_ptr);
|
|
||||||
p->thread.io_bitmap_max = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
start_thread_common(struct pt_regs *regs, unsigned long new_ip,
|
start_thread_common(struct pt_regs *regs, unsigned long new_ip,
|
||||||
unsigned long new_sp,
|
unsigned long new_sp,
|
||||||
|
|
Loading…
Reference in New Issue