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(tsk: 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(tsk: 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(tsk: 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: &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: &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: &attr,
155 triggered: ptrace_triggered, NULL, tsk: task);
156 if (IS_ERR(ptr: bp)) {
157 thread->ptrace_bps[0] = NULL;
158 return PTR_ERR(ptr: 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: &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(ptr: bp)) {
246 thread->ptrace_bps[i] = NULL;
247 return PTR_ERR(ptr: 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(thread: &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

source code of linux/arch/powerpc/kernel/ptrace/ptrace-noadv.c