Merge branch 'x86-pti-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / arch / x86 / kernel / process_64.c
index 0fa7aa19f09e00696f38d19e156107760c2bb2cb..31b4755369f084575f6b3a0ec30b340392106f70 100644 (file)
 #include <asm/vdso.h>
 #include <asm/intel_rdt_sched.h>
 #include <asm/unistd.h>
+#include <asm/fsgsbase.h>
 #ifdef CONFIG_IA32_EMULATION
 /* Not included via unistd.h */
 #include <asm/unistd_32_ia32.h>
 #endif
 
 /* Prints also some state that isn't saved in the pt_regs */
-void __show_regs(struct pt_regs *regs, int all)
+void __show_regs(struct pt_regs *regs, enum show_regs_mode mode)
 {
        unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L, fs, gs, shadowgs;
        unsigned long d0, d1, d2, d3, d6, d7;
@@ -85,9 +86,17 @@ void __show_regs(struct pt_regs *regs, int all)
        printk(KERN_DEFAULT "R13: %016lx R14: %016lx R15: %016lx\n",
               regs->r13, regs->r14, regs->r15);
 
-       if (!all)
+       if (mode == SHOW_REGS_SHORT)
                return;
 
+       if (mode == SHOW_REGS_USER) {
+               rdmsrl(MSR_FS_BASE, fs);
+               rdmsrl(MSR_KERNEL_GS_BASE, shadowgs);
+               printk(KERN_DEFAULT "FS:  %016lx GS:  %016lx\n",
+                      fs, shadowgs);
+               return;
+       }
+
        asm("movl %%ds,%0" : "=r" (ds));
        asm("movl %%cs,%0" : "=r" (cs));
        asm("movl %%es,%0" : "=r" (es));
@@ -276,6 +285,138 @@ static __always_inline void load_seg_legacy(unsigned short prev_index,
        }
 }
 
+static __always_inline void x86_fsgsbase_load(struct thread_struct *prev,
+                                             struct thread_struct *next)
+{
+       load_seg_legacy(prev->fsindex, prev->fsbase,
+                       next->fsindex, next->fsbase, FS);
+       load_seg_legacy(prev->gsindex, prev->gsbase,
+                       next->gsindex, next->gsbase, GS);
+}
+
+static unsigned long x86_fsgsbase_read_task(struct task_struct *task,
+                                           unsigned short selector)
+{
+       unsigned short idx = selector >> 3;
+       unsigned long base;
+
+       if (likely((selector & SEGMENT_TI_MASK) == 0)) {
+               if (unlikely(idx >= GDT_ENTRIES))
+                       return 0;
+
+               /*
+                * There are no user segments in the GDT with nonzero bases
+                * other than the TLS segments.
+                */
+               if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
+                       return 0;
+
+               idx -= GDT_ENTRY_TLS_MIN;
+               base = get_desc_base(&task->thread.tls_array[idx]);
+       } else {
+#ifdef CONFIG_MODIFY_LDT_SYSCALL
+               struct ldt_struct *ldt;
+
+               /*
+                * If performance here mattered, we could protect the LDT
+                * with RCU.  This is a slow path, though, so we can just
+                * take the mutex.
+                */
+               mutex_lock(&task->mm->context.lock);
+               ldt = task->mm->context.ldt;
+               if (unlikely(idx >= ldt->nr_entries))
+                       base = 0;
+               else
+                       base = get_desc_base(ldt->entries + idx);
+               mutex_unlock(&task->mm->context.lock);
+#else
+               base = 0;
+#endif
+       }
+
+       return base;
+}
+
+void x86_fsbase_write_cpu(unsigned long fsbase)
+{
+       /*
+        * Set the selector to 0 as a notion, that the segment base is
+        * overwritten, which will be checked for skipping the segment load
+        * during context switch.
+        */
+       loadseg(FS, 0);
+       wrmsrl(MSR_FS_BASE, fsbase);
+}
+
+void x86_gsbase_write_cpu_inactive(unsigned long gsbase)
+{
+       /* Set the selector to 0 for the same reason as %fs above. */
+       loadseg(GS, 0);
+       wrmsrl(MSR_KERNEL_GS_BASE, gsbase);
+}
+
+unsigned long x86_fsbase_read_task(struct task_struct *task)
+{
+       unsigned long fsbase;
+
+       if (task == current)
+               fsbase = x86_fsbase_read_cpu();
+       else if (task->thread.fsindex == 0)
+               fsbase = task->thread.fsbase;
+       else
+               fsbase = x86_fsgsbase_read_task(task, task->thread.fsindex);
+
+       return fsbase;
+}
+
+unsigned long x86_gsbase_read_task(struct task_struct *task)
+{
+       unsigned long gsbase;
+
+       if (task == current)
+               gsbase = x86_gsbase_read_cpu_inactive();
+       else if (task->thread.gsindex == 0)
+               gsbase = task->thread.gsbase;
+       else
+               gsbase = x86_fsgsbase_read_task(task, task->thread.gsindex);
+
+       return gsbase;
+}
+
+int x86_fsbase_write_task(struct task_struct *task, unsigned long fsbase)
+{
+       /*
+        * Not strictly needed for %fs, but do it for symmetry
+        * with %gs
+        */
+       if (unlikely(fsbase >= TASK_SIZE_MAX))
+               return -EPERM;
+
+       preempt_disable();
+       task->thread.fsbase = fsbase;
+       if (task == current)
+               x86_fsbase_write_cpu(fsbase);
+       task->thread.fsindex = 0;
+       preempt_enable();
+
+       return 0;
+}
+
+int x86_gsbase_write_task(struct task_struct *task, unsigned long gsbase)
+{
+       if (unlikely(gsbase >= TASK_SIZE_MAX))
+               return -EPERM;
+
+       preempt_disable();
+       task->thread.gsbase = gsbase;
+       if (task == current)
+               x86_gsbase_write_cpu_inactive(gsbase);
+       task->thread.gsindex = 0;
+       preempt_enable();
+
+       return 0;
+}
+
 int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
                unsigned long arg, struct task_struct *p, unsigned long tls)
 {
@@ -463,10 +604,7 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
        if (unlikely(next->ds | prev->ds))
                loadsegment(ds, next->ds);
 
-       load_seg_legacy(prev->fsindex, prev->fsbase,
-                       next->fsindex, next->fsbase, FS);
-       load_seg_legacy(prev->gsindex, prev->gsbase,
-                       next->gsindex, next->gsbase, GS);
+       x86_fsgsbase_load(prev, next);
 
        switch_fpu_finish(next_fpu, cpu);
 
@@ -617,54 +755,25 @@ static long prctl_map_vdso(const struct vdso_image *image, unsigned long addr)
 long do_arch_prctl_64(struct task_struct *task, int option, unsigned long arg2)
 {
        int ret = 0;
-       int doit = task == current;
-       int cpu;
 
        switch (option) {
-       case ARCH_SET_GS:
-               if (arg2 >= TASK_SIZE_MAX)
-                       return -EPERM;
-               cpu = get_cpu();
-               task->thread.gsindex = 0;
-               task->thread.gsbase = arg2;
-               if (doit) {
-                       load_gs_index(0);
-                       ret = wrmsrl_safe(MSR_KERNEL_GS_BASE, arg2);
-               }
-               put_cpu();
+       case ARCH_SET_GS: {
+               ret = x86_gsbase_write_task(task, arg2);
                break;
-       case ARCH_SET_FS:
-               /* Not strictly needed for fs, but do it for symmetry
-                  with gs */
-               if (arg2 >= TASK_SIZE_MAX)
-                       return -EPERM;
-               cpu = get_cpu();
-               task->thread.fsindex = 0;
-               task->thread.fsbase = arg2;
-               if (doit) {
-                       /* set the selector to 0 to not confuse __switch_to */
-                       loadsegment(fs, 0);
-                       ret = wrmsrl_safe(MSR_FS_BASE, arg2);
-               }
-               put_cpu();
+       }
+       case ARCH_SET_FS: {
+               ret = x86_fsbase_write_task(task, arg2);
                break;
+       }
        case ARCH_GET_FS: {
-               unsigned long base;
+               unsigned long base = x86_fsbase_read_task(task);
 
-               if (doit)
-                       rdmsrl(MSR_FS_BASE, base);
-               else
-                       base = task->thread.fsbase;
                ret = put_user(base, (unsigned long __user *)arg2);
                break;
        }
        case ARCH_GET_GS: {
-               unsigned long base;
+               unsigned long base = x86_gsbase_read_task(task);
 
-               if (doit)
-                       rdmsrl(MSR_KERNEL_GS_BASE, base);
-               else
-                       base = task->thread.gsbase;
                ret = put_user(base, (unsigned long __user *)arg2);
                break;
        }