Merge branch 'i2c/for-4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
[sfrench/cifs-2.6.git] / arch / arm64 / kernel / debug-monitors.c
index 4fbf3c54275c33c832470155afa29c52074887c2..91fff48d0f574c5060525d87b1fe85185ee178ef 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/hardirq.h>
 #include <linux/init.h>
 #include <linux/ptrace.h>
+#include <linux/kprobes.h>
 #include <linux/stat.h>
 #include <linux/uaccess.h>
 
@@ -48,6 +49,7 @@ static void mdscr_write(u32 mdscr)
        asm volatile("msr mdscr_el1, %0" :: "r" (mdscr));
        local_dbg_restore(flags);
 }
+NOKPROBE_SYMBOL(mdscr_write);
 
 static u32 mdscr_read(void)
 {
@@ -55,6 +57,7 @@ static u32 mdscr_read(void)
        asm volatile("mrs %0, mdscr_el1" : "=r" (mdscr));
        return mdscr;
 }
+NOKPROBE_SYMBOL(mdscr_read);
 
 /*
  * Allow root to disable self-hosted debug from userspace.
@@ -103,6 +106,7 @@ void enable_debug_monitors(enum dbg_active_el el)
                mdscr_write(mdscr);
        }
 }
+NOKPROBE_SYMBOL(enable_debug_monitors);
 
 void disable_debug_monitors(enum dbg_active_el el)
 {
@@ -123,6 +127,7 @@ void disable_debug_monitors(enum dbg_active_el el)
                mdscr_write(mdscr);
        }
 }
+NOKPROBE_SYMBOL(disable_debug_monitors);
 
 /*
  * OS lock clearing.
@@ -151,7 +156,6 @@ static int debug_monitors_init(void)
        /* Clear the OS lock. */
        on_each_cpu(clear_os_lock, NULL, 1);
        isb();
-       local_dbg_enable();
 
        /* Register hotplug handler. */
        __register_cpu_notifier(&os_lock_nb);
@@ -166,22 +170,15 @@ postcore_initcall(debug_monitors_init);
  */
 static void set_regs_spsr_ss(struct pt_regs *regs)
 {
-       unsigned long spsr;
-
-       spsr = regs->pstate;
-       spsr &= ~DBG_SPSR_SS;
-       spsr |= DBG_SPSR_SS;
-       regs->pstate = spsr;
+       regs->pstate |= DBG_SPSR_SS;
 }
+NOKPROBE_SYMBOL(set_regs_spsr_ss);
 
 static void clear_regs_spsr_ss(struct pt_regs *regs)
 {
-       unsigned long spsr;
-
-       spsr = regs->pstate;
-       spsr &= ~DBG_SPSR_SS;
-       regs->pstate = spsr;
+       regs->pstate &= ~DBG_SPSR_SS;
 }
+NOKPROBE_SYMBOL(clear_regs_spsr_ss);
 
 /* EL1 Single Step Handler hooks */
 static LIST_HEAD(step_hook);
@@ -225,6 +222,7 @@ static int call_step_hook(struct pt_regs *regs, unsigned int esr)
 
        return retval;
 }
+NOKPROBE_SYMBOL(call_step_hook);
 
 static void send_user_sigtrap(int si_code)
 {
@@ -266,6 +264,10 @@ static int single_step_handler(unsigned long addr, unsigned int esr,
                 */
                user_rewind_single_step(current);
        } else {
+#ifdef CONFIG_KPROBES
+               if (kprobe_single_step_handler(regs, esr) == DBG_HOOK_HANDLED)
+                       return 0;
+#endif
                if (call_step_hook(regs, esr) == DBG_HOOK_HANDLED)
                        return 0;
 
@@ -279,6 +281,7 @@ static int single_step_handler(unsigned long addr, unsigned int esr,
 
        return 0;
 }
+NOKPROBE_SYMBOL(single_step_handler);
 
 /*
  * Breakpoint handler is re-entrant as another breakpoint can
@@ -316,19 +319,28 @@ static int call_break_hook(struct pt_regs *regs, unsigned int esr)
 
        return fn ? fn(regs, esr) : DBG_HOOK_ERROR;
 }
+NOKPROBE_SYMBOL(call_break_hook);
 
 static int brk_handler(unsigned long addr, unsigned int esr,
                       struct pt_regs *regs)
 {
        if (user_mode(regs)) {
                send_user_sigtrap(TRAP_BRKPT);
-       } else if (call_break_hook(regs, esr) != DBG_HOOK_HANDLED) {
-               pr_warning("Unexpected kernel BRK exception at EL1\n");
+       }
+#ifdef CONFIG_KPROBES
+       else if ((esr & BRK64_ESR_MASK) == BRK64_ESR_KPROBES) {
+               if (kprobe_breakpoint_handler(regs, esr) != DBG_HOOK_HANDLED)
+                       return -EFAULT;
+       }
+#endif
+       else if (call_break_hook(regs, esr) != DBG_HOOK_HANDLED) {
+               pr_warn("Unexpected kernel BRK exception at EL1\n");
                return -EFAULT;
        }
 
        return 0;
 }
+NOKPROBE_SYMBOL(brk_handler);
 
 int aarch32_break_handler(struct pt_regs *regs)
 {
@@ -365,6 +377,7 @@ int aarch32_break_handler(struct pt_regs *regs)
        send_user_sigtrap(TRAP_BRKPT);
        return 0;
 }
+NOKPROBE_SYMBOL(aarch32_break_handler);
 
 static int __init debug_traps_init(void)
 {
@@ -386,6 +399,7 @@ void user_rewind_single_step(struct task_struct *task)
        if (test_ti_thread_flag(task_thread_info(task), TIF_SINGLESTEP))
                set_regs_spsr_ss(task_pt_regs(task));
 }
+NOKPROBE_SYMBOL(user_rewind_single_step);
 
 void user_fastforward_single_step(struct task_struct *task)
 {
@@ -401,6 +415,7 @@ void kernel_enable_single_step(struct pt_regs *regs)
        mdscr_write(mdscr_read() | DBG_MDSCR_SS);
        enable_debug_monitors(DBG_ACTIVE_EL1);
 }
+NOKPROBE_SYMBOL(kernel_enable_single_step);
 
 void kernel_disable_single_step(void)
 {
@@ -408,12 +423,14 @@ void kernel_disable_single_step(void)
        mdscr_write(mdscr_read() & ~DBG_MDSCR_SS);
        disable_debug_monitors(DBG_ACTIVE_EL1);
 }
+NOKPROBE_SYMBOL(kernel_disable_single_step);
 
 int kernel_active_single_step(void)
 {
        WARN_ON(!irqs_disabled());
        return mdscr_read() & DBG_MDSCR_SS;
 }
+NOKPROBE_SYMBOL(kernel_active_single_step);
 
 /* ptrace API */
 void user_enable_single_step(struct task_struct *task)
@@ -421,8 +438,10 @@ void user_enable_single_step(struct task_struct *task)
        set_ti_thread_flag(task_thread_info(task), TIF_SINGLESTEP);
        set_regs_spsr_ss(task_pt_regs(task));
 }
+NOKPROBE_SYMBOL(user_enable_single_step);
 
 void user_disable_single_step(struct task_struct *task)
 {
        clear_ti_thread_flag(task_thread_info(task), TIF_SINGLESTEP);
 }
+NOKPROBE_SYMBOL(user_disable_single_step);