xref: /OK3568_Linux_fs/kernel/arch/powerpc/kernel/ptrace/ptrace-noadv.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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