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