Merge commit 'perf/core' into perf/hw-breakpoint
[cascardo/linux.git] / arch / x86 / kernel / ptrace.c
index 7b058a2..267cb85 100644 (file)
@@ -34,6 +34,7 @@
 #include <asm/prctl.h>
 #include <asm/proto.h>
 #include <asm/ds.h>
+#include <asm/hw_breakpoint.h>
 
 #include "tls.h"
 
@@ -137,11 +138,6 @@ static int set_segment_reg(struct task_struct *task,
        return 0;
 }
 
-static unsigned long debugreg_addr_limit(struct task_struct *task)
-{
-       return TASK_SIZE - 3;
-}
-
 #else  /* CONFIG_X86_64 */
 
 #define FLAG_MASK              (FLAG_MASK_32 | X86_EFLAGS_NT)
@@ -266,15 +262,6 @@ static int set_segment_reg(struct task_struct *task,
        return 0;
 }
 
-static unsigned long debugreg_addr_limit(struct task_struct *task)
-{
-#ifdef CONFIG_IA32_EMULATION
-       if (test_tsk_thread_flag(task, TIF_IA32))
-               return IA32_PAGE_OFFSET - 3;
-#endif
-       return TASK_SIZE_MAX - 7;
-}
-
 #endif /* CONFIG_X86_32 */
 
 static unsigned long get_flags(struct task_struct *task)
@@ -455,95 +442,159 @@ static int genregs_set(struct task_struct *target,
 }
 
 /*
- * This function is trivial and will be inlined by the compiler.
- * Having it separates the implementation details of debug
- * registers from the interface details of ptrace.
+ * Decode the length and type bits for a particular breakpoint as
+ * stored in debug register 7.  Return the "enabled" status.
  */
-static unsigned long ptrace_get_debugreg(struct task_struct *child, int n)
+static int decode_dr7(unsigned long dr7, int bpnum, unsigned *len,
+               unsigned *type)
 {
-       switch (n) {
-       case 0:         return child->thread.debugreg0;
-       case 1:         return child->thread.debugreg1;
-       case 2:         return child->thread.debugreg2;
-       case 3:         return child->thread.debugreg3;
-       case 6:         return child->thread.debugreg6;
-       case 7:         return child->thread.debugreg7;
-       }
-       return 0;
+       int bp_info = dr7 >> (DR_CONTROL_SHIFT + bpnum * DR_CONTROL_SIZE);
+
+       *len = (bp_info & 0xc) | 0x40;
+       *type = (bp_info & 0x3) | 0x80;
+       return (dr7 >> (bpnum * DR_ENABLE_SIZE)) & 0x3;
 }
 
-static int ptrace_set_debugreg(struct task_struct *child,
-                              int n, unsigned long data)
+static void ptrace_triggered(struct hw_breakpoint *bp, struct pt_regs *regs)
 {
+       struct thread_struct *thread = &(current->thread);
        int i;
 
-       if (unlikely(n == 4 || n == 5))
-               return -EIO;
+       /*
+        * Store in the virtual DR6 register the fact that the breakpoint
+        * was hit so the thread's debugger will see it.
+        */
+       for (i = 0; i < hbp_kernel_pos; i++)
+               /*
+                * We will check bp->info.address against the address stored in
+                * thread's hbp structure and not debugreg[i]. This is to ensure
+                * that the corresponding bit for 'i' in DR7 register is enabled
+                */
+               if (bp->info.address == thread->hbp[i]->info.address)
+                       break;
 
-       if (n < 4 && unlikely(data >= debugreg_addr_limit(child)))
-               return -EIO;
+       thread->debugreg6 |= (DR_TRAP0 << i);
+}
 
-       switch (n) {
-       case 0:         child->thread.debugreg0 = data; break;
-       case 1:         child->thread.debugreg1 = data; break;
-       case 2:         child->thread.debugreg2 = data; break;
-       case 3:         child->thread.debugreg3 = data; break;
+/*
+ * Handle ptrace writes to debug register 7.
+ */
+static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data)
+{
+       struct thread_struct *thread = &(tsk->thread);
+       unsigned long old_dr7 = thread->debugreg7;
+       int i, orig_ret = 0, rc = 0;
+       int enabled, second_pass = 0;
+       unsigned len, type;
+       struct hw_breakpoint *bp;
+
+       data &= ~DR_CONTROL_RESERVED;
+restore:
+       /*
+        * Loop through all the hardware breakpoints, making the
+        * appropriate changes to each.
+        */
+       for (i = 0; i < HBP_NUM; i++) {
+               enabled = decode_dr7(data, i, &len, &type);
+               bp = thread->hbp[i];
+
+               if (!enabled) {
+                       if (bp) {
+                               /* Don't unregister the breakpoints right-away,
+                                * unless all register_user_hw_breakpoint()
+                                * requests have succeeded. This prevents
+                                * any window of opportunity for debug
+                                * register grabbing by other users.
+                                */
+                               if (!second_pass)
+                                       continue;
+                               unregister_user_hw_breakpoint(tsk, bp);
+                               kfree(bp);
+                       }
+                       continue;
+               }
+               if (!bp) {
+                       rc = -ENOMEM;
+                       bp = kzalloc(sizeof(struct hw_breakpoint), GFP_KERNEL);
+                       if (bp) {
+                               bp->info.address = thread->debugreg[i];
+                               bp->triggered = ptrace_triggered;
+                               bp->info.len = len;
+                               bp->info.type = type;
+                               rc = register_user_hw_breakpoint(tsk, bp);
+                               if (rc)
+                                       kfree(bp);
+                       }
+               } else
+                       rc = modify_user_hw_breakpoint(tsk, bp);
+               if (rc)
+                       break;
+       }
+       /*
+        * Make a second pass to free the remaining unused breakpoints
+        * or to restore the original breakpoints if an error occurred.
+        */
+       if (!second_pass) {
+               second_pass = 1;
+               if (rc < 0) {
+                       orig_ret = rc;
+                       data = old_dr7;
+               }
+               goto restore;
+       }
+       return ((orig_ret < 0) ? orig_ret : rc);
+}
 
-       case 6:
-               if ((data & ~0xffffffffUL) != 0)
-                       return -EIO;
-               child->thread.debugreg6 = data;
-               break;
+/*
+ * Handle PTRACE_PEEKUSR calls for the debug register area.
+ */
+static unsigned long ptrace_get_debugreg(struct task_struct *tsk, int n)
+{
+       struct thread_struct *thread = &(tsk->thread);
+       unsigned long val = 0;
+
+       if (n < HBP_NUM)
+               val = thread->debugreg[n];
+       else if (n == 6)
+               val = thread->debugreg6;
+       else if (n == 7)
+               val = thread->debugreg7;
+       return val;
+}
 
-       case 7:
-               /*
-                * Sanity-check data. Take one half-byte at once with
-                * check = (val >> (16 + 4*i)) & 0xf. It contains the
-                * R/Wi and LENi bits; bits 0 and 1 are R/Wi, and bits
-                * 2 and 3 are LENi. Given a list of invalid values,
-                * we do mask |= 1 << invalid_value, so that
-                * (mask >> check) & 1 is a correct test for invalid
-                * values.
-                *
-                * R/Wi contains the type of the breakpoint /
-                * watchpoint, LENi contains the length of the watched
-                * data in the watchpoint case.
-                *
-                * The invalid values are:
-                * - LENi == 0x10 (undefined), so mask |= 0x0f00.       [32-bit]
-                * - R/Wi == 0x10 (break on I/O reads or writes), so
-                *   mask |= 0x4444.
-                * - R/Wi == 0x00 && LENi != 0x00, so we have mask |=
-                *   0x1110.
-                *
-                * Finally, mask = 0x0f00 | 0x4444 | 0x1110 == 0x5f54.
-                *
-                * See the Intel Manual "System Programming Guide",
-                * 15.2.4
-                *
-                * Note that LENi == 0x10 is defined on x86_64 in long
-                * mode (i.e. even for 32-bit userspace software, but
-                * 64-bit kernel), so the x86_64 mask value is 0x5454.
-                * See the AMD manual no. 24593 (AMD64 System Programming)
-                */
-#ifdef CONFIG_X86_32
-#define        DR7_MASK        0x5f54
-#else
-#define        DR7_MASK        0x5554
-#endif
-               data &= ~DR_CONTROL_RESERVED;
-               for (i = 0; i < 4; i++)
-                       if ((DR7_MASK >> ((data >> (16 + 4*i)) & 0xf)) & 1)
-                               return -EIO;
-               child->thread.debugreg7 = data;
-               if (data)
-                       set_tsk_thread_flag(child, TIF_DEBUG);
-               else
-                       clear_tsk_thread_flag(child, TIF_DEBUG);
-               break;
+/*
+ * Handle PTRACE_POKEUSR calls for the debug register area.
+ */
+int ptrace_set_debugreg(struct task_struct *tsk, int n, unsigned long val)
+{
+       struct thread_struct *thread = &(tsk->thread);
+       int rc = 0;
+
+       /* There are no DR4 or DR5 registers */
+       if (n == 4 || n == 5)
+               return -EIO;
+
+       if (n == 6) {
+               tsk->thread.debugreg6 = val;
+               goto ret_path;
        }
+       if (n < HBP_NUM) {
+               if (thread->hbp[n]) {
+                       if (arch_check_va_in_userspace(val,
+                                       thread->hbp[n]->info.len) == 0) {
+                               rc = -EIO;
+                               goto ret_path;
+                       }
+                       thread->hbp[n]->info.address = val;
+               }
+               thread->debugreg[n] = val;
+       }
+       /* All that's left is DR7 */
+       if (n == 7)
+               rc = ptrace_write_dr7(tsk, val);
 
-       return 0;
+ret_path:
+       return rc;
 }
 
 /*