linux/arch/powerpc/kernel/ptrace/ptrace-noadv.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2
   3#include <linux/regset.h>
   4#include <linux/hw_breakpoint.h>
   5
   6#include <asm/debug.h>
   7
   8#include "ptrace-decl.h"
   9
  10void user_enable_single_step(struct task_struct *task)
  11{
  12        struct pt_regs *regs = task->thread.regs;
  13
  14        if (regs != NULL)
  15                regs_set_return_msr(regs, (regs->msr & ~MSR_BE) | MSR_SE);
  16        set_tsk_thread_flag(task, TIF_SINGLESTEP);
  17}
  18
  19void user_enable_block_step(struct task_struct *task)
  20{
  21        struct pt_regs *regs = task->thread.regs;
  22
  23        if (regs != NULL)
  24                regs_set_return_msr(regs, (regs->msr & ~MSR_SE) | MSR_BE);
  25        set_tsk_thread_flag(task, TIF_SINGLESTEP);
  26}
  27
  28void user_disable_single_step(struct task_struct *task)
  29{
  30        struct pt_regs *regs = task->thread.regs;
  31
  32        if (regs != NULL)
  33                regs_set_return_msr(regs, regs->msr & ~(MSR_SE | MSR_BE));
  34
  35        clear_tsk_thread_flag(task, TIF_SINGLESTEP);
  36}
  37
  38void ppc_gethwdinfo(struct ppc_debug_info *dbginfo)
  39{
  40        dbginfo->version = 1;
  41        dbginfo->num_instruction_bps = 0;
  42        if (ppc_breakpoint_available())
  43                dbginfo->num_data_bps = nr_wp_slots();
  44        else
  45                dbginfo->num_data_bps = 0;
  46        dbginfo->num_condition_regs = 0;
  47        dbginfo->data_bp_alignment = sizeof(long);
  48        dbginfo->sizeof_condition = 0;
  49        if (IS_ENABLED(CONFIG_HAVE_HW_BREAKPOINT)) {
  50                dbginfo->features = PPC_DEBUG_FEATURE_DATA_BP_RANGE;
  51                if (dawr_enabled())
  52                        dbginfo->features |= PPC_DEBUG_FEATURE_DATA_BP_DAWR;
  53        } else {
  54                dbginfo->features = 0;
  55        }
  56        if (cpu_has_feature(CPU_FTR_ARCH_31))
  57                dbginfo->features |= PPC_DEBUG_FEATURE_DATA_BP_ARCH_31;
  58}
  59
  60int ptrace_get_debugreg(struct task_struct *child, unsigned long addr,
  61                        unsigned long __user *datalp)
  62{
  63        unsigned long dabr_fake;
  64
  65        /* We only support one DABR and no IABRS at the moment */
  66        if (addr > 0)
  67                return -EINVAL;
  68        dabr_fake = ((child->thread.hw_brk[0].address & (~HW_BRK_TYPE_DABR)) |
  69                     (child->thread.hw_brk[0].type & HW_BRK_TYPE_DABR));
  70        return put_user(dabr_fake, datalp);
  71}
  72
  73/*
  74 * ptrace_set_debugreg() fakes DABR and DABR is only one. So even if
  75 * internal hw supports more than one watchpoint, we support only one
  76 * watchpoint with this interface.
  77 */
  78int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, unsigned long data)
  79{
  80#ifdef CONFIG_HAVE_HW_BREAKPOINT
  81        int ret;
  82        struct thread_struct *thread = &task->thread;
  83        struct perf_event *bp;
  84        struct perf_event_attr attr;
  85#endif /* CONFIG_HAVE_HW_BREAKPOINT */
  86        bool set_bp = true;
  87        struct arch_hw_breakpoint hw_brk;
  88
  89        /* For ppc64 we support one DABR and no IABR's at the moment (ppc64).
  90         *  For embedded processors we support one DAC and no IAC's at the
  91         *  moment.
  92         */
  93        if (addr > 0)
  94                return -EINVAL;
  95
  96        /* The bottom 3 bits in dabr are flags */
  97        if ((data & ~0x7UL) >= TASK_SIZE)
  98                return -EIO;
  99
 100        /* For processors using DABR (i.e. 970), the bottom 3 bits are flags.
 101         *  It was assumed, on previous implementations, that 3 bits were
 102         *  passed together with the data address, fitting the design of the
 103         *  DABR register, as follows:
 104         *
 105         *  bit 0: Read flag
 106         *  bit 1: Write flag
 107         *  bit 2: Breakpoint translation
 108         *
 109         *  Thus, we use them here as so.
 110         */
 111
 112        /* Ensure breakpoint translation bit is set */
 113        if (data && !(data & HW_BRK_TYPE_TRANSLATE))
 114                return -EIO;
 115        hw_brk.address = data & (~HW_BRK_TYPE_DABR);
 116        hw_brk.type = (data & HW_BRK_TYPE_DABR) | HW_BRK_TYPE_PRIV_ALL;
 117        hw_brk.len = DABR_MAX_LEN;
 118        hw_brk.hw_len = DABR_MAX_LEN;
 119        set_bp = (data) && (hw_brk.type & HW_BRK_TYPE_RDWR);
 120#ifdef CONFIG_HAVE_HW_BREAKPOINT
 121        bp = thread->ptrace_bps[0];
 122        if (!set_bp) {
 123                if (bp) {
 124                        unregister_hw_breakpoint(bp);
 125                        thread->ptrace_bps[0] = NULL;
 126                }
 127                return 0;
 128        }
 129        if (bp) {
 130                attr = bp->attr;
 131                attr.bp_addr = hw_brk.address;
 132                attr.bp_len = DABR_MAX_LEN;
 133                arch_bp_generic_fields(hw_brk.type, &attr.bp_type);
 134
 135                /* Enable breakpoint */
 136                attr.disabled = false;
 137
 138                ret =  modify_user_hw_breakpoint(bp, &attr);
 139                if (ret)
 140                        return ret;
 141
 142                thread->ptrace_bps[0] = bp;
 143                thread->hw_brk[0] = hw_brk;
 144                return 0;
 145        }
 146
 147        /* Create a new breakpoint request if one doesn't exist already */
 148        hw_breakpoint_init(&attr);
 149        attr.bp_addr = hw_brk.address;
 150        attr.bp_len = DABR_MAX_LEN;
 151        arch_bp_generic_fields(hw_brk.type,
 152                               &attr.bp_type);
 153
 154        thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr,
 155                                               ptrace_triggered, NULL, task);
 156        if (IS_ERR(bp)) {
 157                thread->ptrace_bps[0] = NULL;
 158                return PTR_ERR(bp);
 159        }
 160
 161#else /* !CONFIG_HAVE_HW_BREAKPOINT */
 162        if (set_bp && (!ppc_breakpoint_available()))
 163                return -ENODEV;
 164#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 165        task->thread.hw_brk[0] = hw_brk;
 166        return 0;
 167}
 168
 169#ifdef CONFIG_HAVE_HW_BREAKPOINT
 170static int find_empty_ptrace_bp(struct thread_struct *thread)
 171{
 172        int i;
 173
 174        for (i = 0; i < nr_wp_slots(); i++) {
 175                if (!thread->ptrace_bps[i])
 176                        return i;
 177        }
 178        return -1;
 179}
 180#endif
 181
 182static int find_empty_hw_brk(struct thread_struct *thread)
 183{
 184        int i;
 185
 186        for (i = 0; i < nr_wp_slots(); i++) {
 187                if (!thread->hw_brk[i].address)
 188                        return i;
 189        }
 190        return -1;
 191}
 192
 193long ppc_set_hwdebug(struct task_struct *child, struct ppc_hw_breakpoint *bp_info)
 194{
 195        int i;
 196#ifdef CONFIG_HAVE_HW_BREAKPOINT
 197        int len = 0;
 198        struct thread_struct *thread = &child->thread;
 199        struct perf_event *bp;
 200        struct perf_event_attr attr;
 201#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 202        struct arch_hw_breakpoint brk;
 203
 204        if (bp_info->version != 1)
 205                return -ENOTSUPP;
 206        /*
 207         * We only support one data breakpoint
 208         */
 209        if ((bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_RW) == 0 ||
 210            (bp_info->trigger_type & ~PPC_BREAKPOINT_TRIGGER_RW) != 0 ||
 211            bp_info->condition_mode != PPC_BREAKPOINT_CONDITION_NONE)
 212                return -EINVAL;
 213
 214        if ((unsigned long)bp_info->addr >= TASK_SIZE)
 215                return -EIO;
 216
 217        brk.address = ALIGN_DOWN(bp_info->addr, HW_BREAKPOINT_SIZE);
 218        brk.type = HW_BRK_TYPE_TRANSLATE | HW_BRK_TYPE_PRIV_ALL;
 219        brk.len = DABR_MAX_LEN;
 220        brk.hw_len = DABR_MAX_LEN;
 221        if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_READ)
 222                brk.type |= HW_BRK_TYPE_READ;
 223        if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE)
 224                brk.type |= HW_BRK_TYPE_WRITE;
 225#ifdef CONFIG_HAVE_HW_BREAKPOINT
 226        if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE)
 227                len = bp_info->addr2 - bp_info->addr;
 228        else if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_EXACT)
 229                len = 1;
 230        else
 231                return -EINVAL;
 232
 233        i = find_empty_ptrace_bp(thread);
 234        if (i < 0)
 235                return -ENOSPC;
 236
 237        /* Create a new breakpoint request if one doesn't exist already */
 238        hw_breakpoint_init(&attr);
 239        attr.bp_addr = (unsigned long)bp_info->addr;
 240        attr.bp_len = len;
 241        arch_bp_generic_fields(brk.type, &attr.bp_type);
 242
 243        bp = register_user_hw_breakpoint(&attr, ptrace_triggered, NULL, child);
 244        thread->ptrace_bps[i] = bp;
 245        if (IS_ERR(bp)) {
 246                thread->ptrace_bps[i] = NULL;
 247                return PTR_ERR(bp);
 248        }
 249
 250        return i + 1;
 251#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 252
 253        if (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT)
 254                return -EINVAL;
 255
 256        i = find_empty_hw_brk(&child->thread);
 257        if (i < 0)
 258                return -ENOSPC;
 259
 260        if (!ppc_breakpoint_available())
 261                return -ENODEV;
 262
 263        child->thread.hw_brk[i] = brk;
 264
 265        return i + 1;
 266}
 267
 268long ppc_del_hwdebug(struct task_struct *child, long data)
 269{
 270#ifdef CONFIG_HAVE_HW_BREAKPOINT
 271        int ret = 0;
 272        struct thread_struct *thread = &child->thread;
 273        struct perf_event *bp;
 274#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 275        if (data < 1 || data > nr_wp_slots())
 276                return -EINVAL;
 277
 278#ifdef CONFIG_HAVE_HW_BREAKPOINT
 279        bp = thread->ptrace_bps[data - 1];
 280        if (bp) {
 281                unregister_hw_breakpoint(bp);
 282                thread->ptrace_bps[data - 1] = NULL;
 283        } else {
 284                ret = -ENOENT;
 285        }
 286        return ret;
 287#else /* CONFIG_HAVE_HW_BREAKPOINT */
 288        if (!(child->thread.hw_brk[data - 1].flags & HW_BRK_FLAG_DISABLED) &&
 289            child->thread.hw_brk[data - 1].address == 0)
 290                return -ENOENT;
 291
 292        child->thread.hw_brk[data - 1].address = 0;
 293        child->thread.hw_brk[data - 1].type = 0;
 294        child->thread.hw_brk[data - 1].flags = 0;
 295#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 296
 297        return 0;
 298}
 299