summaryrefslogtreecommitdiff
path: root/arch/x86/kernel/process.c
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2019-11-11 23:03:16 +0100
committerThomas Gleixner <tglx@linutronix.de>2019-11-16 11:23:59 +0100
commit2fff071d28b54f050f62654dad4ec111b8416d8e (patch)
tree03b6d5c3772dd8b9a9c445f03b58826d083720df /arch/x86/kernel/process.c
parent8c40397f22a4ff7996d3abdc2d9d1d90f9fc8054 (diff)
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>
Diffstat (limited to 'arch/x86/kernel/process.c')
-rw-r--r--arch/x86/kernel/process.c100
1 files changed, 100 insertions, 0 deletions
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index 5e94c4354d4e..7e07f0bb0def 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -132,6 +132,106 @@ void exit_thread(struct task_struct *tsk)
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)
{
struct task_struct *tsk = current;