File Index
Files and directories
Sign in
FILE

ptrace-noadv.c

File preview with syntax highlighting.

linux-6.19.14/arch/powerpc/kernel/ptrace/ptrace-noadv.c C
  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}
~/ Read-only file preview.