Merge tag 'iwlwifi-next-for-kalle-2014-12-30' of https://git.kernel.org/pub/scm/linux...
[cascardo/linux.git] / arch / x86 / kernel / traps.c
index de801f2..88900e2 100644 (file)
@@ -60,6 +60,7 @@
 #include <asm/fixmap.h>
 #include <asm/mach_traps.h>
 #include <asm/alternative.h>
+#include <asm/mpx.h>
 
 #ifdef CONFIG_X86_64
 #include <asm/x86_init.h>
@@ -228,7 +229,6 @@ dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \
 
 DO_ERROR(X86_TRAP_DE,     SIGFPE,  "divide error",             divide_error)
 DO_ERROR(X86_TRAP_OF,     SIGSEGV, "overflow",                 overflow)
-DO_ERROR(X86_TRAP_BR,     SIGSEGV, "bounds",                   bounds)
 DO_ERROR(X86_TRAP_UD,     SIGILL,  "invalid opcode",           invalid_op)
 DO_ERROR(X86_TRAP_OLD_MF, SIGFPE,  "coprocessor segment overrun",coprocessor_segment_overrun)
 DO_ERROR(X86_TRAP_TS,     SIGSEGV, "invalid TSS",              invalid_TSS)
@@ -286,6 +286,89 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
 }
 #endif
 
+dotraplinkage void do_bounds(struct pt_regs *regs, long error_code)
+{
+       struct task_struct *tsk = current;
+       struct xsave_struct *xsave_buf;
+       enum ctx_state prev_state;
+       struct bndcsr *bndcsr;
+       siginfo_t *info;
+
+       prev_state = exception_enter();
+       if (notify_die(DIE_TRAP, "bounds", regs, error_code,
+                       X86_TRAP_BR, SIGSEGV) == NOTIFY_STOP)
+               goto exit;
+       conditional_sti(regs);
+
+       if (!user_mode(regs))
+               die("bounds", regs, error_code);
+
+       if (!cpu_feature_enabled(X86_FEATURE_MPX)) {
+               /* The exception is not from Intel MPX */
+               goto exit_trap;
+       }
+
+       /*
+        * We need to look at BNDSTATUS to resolve this exception.
+        * It is not directly accessible, though, so we need to
+        * do an xsave and then pull it out of the xsave buffer.
+        */
+       fpu_save_init(&tsk->thread.fpu);
+       xsave_buf = &(tsk->thread.fpu.state->xsave);
+       bndcsr = get_xsave_addr(xsave_buf, XSTATE_BNDCSR);
+       if (!bndcsr)
+               goto exit_trap;
+
+       /*
+        * The error code field of the BNDSTATUS register communicates status
+        * information of a bound range exception #BR or operation involving
+        * bound directory.
+        */
+       switch (bndcsr->bndstatus & MPX_BNDSTA_ERROR_CODE) {
+       case 2: /* Bound directory has invalid entry. */
+               if (mpx_handle_bd_fault(xsave_buf))
+                       goto exit_trap;
+               break; /* Success, it was handled */
+       case 1: /* Bound violation. */
+               info = mpx_generate_siginfo(regs, xsave_buf);
+               if (IS_ERR(info)) {
+                       /*
+                        * We failed to decode the MPX instruction.  Act as if
+                        * the exception was not caused by MPX.
+                        */
+                       goto exit_trap;
+               }
+               /*
+                * Success, we decoded the instruction and retrieved
+                * an 'info' containing the address being accessed
+                * which caused the exception.  This information
+                * allows and application to possibly handle the
+                * #BR exception itself.
+                */
+               do_trap(X86_TRAP_BR, SIGSEGV, "bounds", regs, error_code, info);
+               kfree(info);
+               break;
+       case 0: /* No exception caused by Intel MPX operations. */
+               goto exit_trap;
+       default:
+               die("bounds", regs, error_code);
+       }
+
+exit:
+       exception_exit(prev_state);
+       return;
+exit_trap:
+       /*
+        * This path out is for all the cases where we could not
+        * handle the exception in some way (like allocating a
+        * table or telling userspace about it.  We will also end
+        * up here if the kernel has MPX turned off at compile
+        * time..
+        */
+       do_trap(X86_TRAP_BR, SIGSEGV, "bounds", regs, error_code, NULL);
+       exception_exit(prev_state);
+}
+
 dotraplinkage void
 do_general_protection(struct pt_regs *regs, long error_code)
 {
@@ -387,7 +470,7 @@ NOKPROBE_SYMBOL(do_int3);
  * for scheduling or signal handling. The actual stack switch is done in
  * entry.S
  */
-asmlinkage __visible struct pt_regs *sync_regs(struct pt_regs *eregs)
+asmlinkage __visible notrace struct pt_regs *sync_regs(struct pt_regs *eregs)
 {
        struct pt_regs *regs = eregs;
        /* Did already sync */
@@ -413,7 +496,7 @@ struct bad_iret_stack {
        struct pt_regs regs;
 };
 
-asmlinkage __visible
+asmlinkage __visible notrace
 struct bad_iret_stack *fixup_bad_iret(struct bad_iret_stack *s)
 {
        /*
@@ -436,6 +519,7 @@ struct bad_iret_stack *fixup_bad_iret(struct bad_iret_stack *s)
        BUG_ON(!user_mode_vm(&new_stack->regs));
        return new_stack;
 }
+NOKPROBE_SYMBOL(fixup_bad_iret);
 #endif
 
 /*