powerpc: Update ptrace to use ppc_breakpoint_available()
authorMichael Neuling <mikey@neuling.org>
Tue, 27 Mar 2018 04:37:18 +0000 (15:37 +1100)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 27 Mar 2018 12:52:44 +0000 (23:52 +1100)
This updates the ptrace code to use ppc_breakpoint_available().

We now advertise via PPC_PTRACE_GETHWDBGINFO zero breakpoints when the
DAWR is missing (ie. POWER9). This results in GDB falling back to
software emulation of the breakpoint (which is slow).

For the features advertised by PPC_PTRACE_GETHWDBGINFO, we keep
advertising DAWR as if we don't GDB assumes 1 breakpoint irrespective
of the number of breakpoints advertised. GDB then fails later when
trying to set this one breakpoint.

Signed-off-by: Michael Neuling <mikey@neuling.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/kernel/hw_breakpoint.c
arch/powerpc/kernel/ptrace.c

index 53b9c1dfd7d978dddf909d3699e06713d83c025a..4c1012b80d3bb5d5ec96e5de6408cd9cc887dd4b 100644 (file)
@@ -33,6 +33,7 @@
 #include <asm/hw_breakpoint.h>
 #include <asm/processor.h>
 #include <asm/sstep.h>
+#include <asm/debug.h>
 #include <linux/uaccess.h>
 
 /*
@@ -171,6 +172,8 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
         * HW_BREAKPOINT_ALIGN by rounding off to the lower address, the
         * 'symbolsize' should satisfy the check below.
         */
+       if (!ppc_breakpoint_available())
+               return -ENODEV;
        length_max = 8; /* DABR */
        if (cpu_has_feature(CPU_FTR_DAWR)) {
                length_max = 512 ; /* 64 doublewords */
index ca72d7391d404f9acb4dee60b800a2b1edd2f03c..d23cf632edf065b7c23b175a00afa1ace9f53b44 100644 (file)
@@ -41,6 +41,7 @@
 #include <asm/switch_to.h>
 #include <asm/tm.h>
 #include <asm/asm-prototypes.h>
+#include <asm/debug.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/syscalls.h>
@@ -2378,6 +2379,7 @@ static int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
        struct perf_event_attr attr;
 #endif /* CONFIG_HAVE_HW_BREAKPOINT */
 #ifndef CONFIG_PPC_ADV_DEBUG_REGS
+       bool set_bp = true;
        struct arch_hw_breakpoint hw_brk;
 #endif
 
@@ -2411,9 +2413,10 @@ static int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
        hw_brk.address = data & (~HW_BRK_TYPE_DABR);
        hw_brk.type = (data & HW_BRK_TYPE_DABR) | HW_BRK_TYPE_PRIV_ALL;
        hw_brk.len = 8;
+       set_bp = (data) && (hw_brk.type & HW_BRK_TYPE_RDWR);
 #ifdef CONFIG_HAVE_HW_BREAKPOINT
        bp = thread->ptrace_bps[0];
-       if ((!data) || !(hw_brk.type & HW_BRK_TYPE_RDWR)) {
+       if (!set_bp) {
                if (bp) {
                        unregister_hw_breakpoint(bp);
                        thread->ptrace_bps[0] = NULL;
@@ -2450,6 +2453,9 @@ static int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
                return PTR_ERR(bp);
        }
 
+#else /* !CONFIG_HAVE_HW_BREAKPOINT */
+       if (set_bp && (!ppc_breakpoint_available()))
+               return -ENODEV;
 #endif /* CONFIG_HAVE_HW_BREAKPOINT */
        task->thread.hw_brk = hw_brk;
 #else /* CONFIG_PPC_ADV_DEBUG_REGS */
@@ -2904,6 +2910,9 @@ static long ppc_set_hwdebug(struct task_struct *child,
        if (child->thread.hw_brk.address)
                return -ENOSPC;
 
+       if (!ppc_breakpoint_available())
+               return -ENODEV;
+
        child->thread.hw_brk = brk;
 
        return 1;
@@ -3052,7 +3061,10 @@ long arch_ptrace(struct task_struct *child, long request,
 #endif
 #else /* !CONFIG_PPC_ADV_DEBUG_REGS */
                dbginfo.num_instruction_bps = 0;
-               dbginfo.num_data_bps = 1;
+               if (ppc_breakpoint_available())
+                       dbginfo.num_data_bps = 1;
+               else
+                       dbginfo.num_data_bps = 0;
                dbginfo.num_condition_regs = 0;
 #ifdef CONFIG_PPC64
                dbginfo.data_bp_alignment = 8;