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