Merge tag 'sched-urgent-2020-12-27' of git://git.kernel.org/pub/scm/linux/kernel...
[linux/fpc-iii.git] / arch / powerpc / kernel / ptrace / ptrace-noadv.c
blobaa36fcad36cd609d8efe04c47db3ec603d98f645
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 #include <linux/regset.h>
4 #include <linux/hw_breakpoint.h>
6 #include <asm/debug.h>
8 #include "ptrace-decl.h"
10 void user_enable_single_step(struct task_struct *task)
12 struct pt_regs *regs = task->thread.regs;
14 if (regs != NULL) {
15 regs->msr &= ~MSR_BE;
16 regs->msr |= MSR_SE;
18 set_tsk_thread_flag(task, TIF_SINGLESTEP);
21 void user_enable_block_step(struct task_struct *task)
23 struct pt_regs *regs = task->thread.regs;
25 if (regs != NULL) {
26 regs->msr &= ~MSR_SE;
27 regs->msr |= MSR_BE;
29 set_tsk_thread_flag(task, TIF_SINGLESTEP);
32 void user_disable_single_step(struct task_struct *task)
34 struct pt_regs *regs = task->thread.regs;
36 if (regs != NULL)
37 regs->msr &= ~(MSR_SE | MSR_BE);
39 clear_tsk_thread_flag(task, TIF_SINGLESTEP);
42 void ppc_gethwdinfo(struct ppc_debug_info *dbginfo)
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;
60 if (cpu_has_feature(CPU_FTR_ARCH_31))
61 dbginfo->features |= PPC_DEBUG_FEATURE_DATA_BP_ARCH_31;
64 int ptrace_get_debugreg(struct task_struct *child, unsigned long addr,
65 unsigned long __user *datalp)
67 unsigned long dabr_fake;
69 /* We only support one DABR and no IABRS at the moment */
70 if (addr > 0)
71 return -EINVAL;
72 dabr_fake = ((child->thread.hw_brk[0].address & (~HW_BRK_TYPE_DABR)) |
73 (child->thread.hw_brk[0].type & HW_BRK_TYPE_DABR));
74 return put_user(dabr_fake, datalp);
78 * ptrace_set_debugreg() fakes DABR and DABR is only one. So even if
79 * internal hw supports more than one watchpoint, we support only one
80 * watchpoint with this interface.
82 int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, unsigned long data)
84 #ifdef CONFIG_HAVE_HW_BREAKPOINT
85 int ret;
86 struct thread_struct *thread = &task->thread;
87 struct perf_event *bp;
88 struct perf_event_attr attr;
89 #endif /* CONFIG_HAVE_HW_BREAKPOINT */
90 bool set_bp = true;
91 struct arch_hw_breakpoint hw_brk;
93 /* For ppc64 we support one DABR and no IABR's at the moment (ppc64).
94 * For embedded processors we support one DAC and no IAC's at the
95 * moment.
97 if (addr > 0)
98 return -EINVAL;
100 /* The bottom 3 bits in dabr are flags */
101 if ((data & ~0x7UL) >= TASK_SIZE)
102 return -EIO;
104 /* For processors using DABR (i.e. 970), the bottom 3 bits are flags.
105 * It was assumed, on previous implementations, that 3 bits were
106 * passed together with the data address, fitting the design of the
107 * DABR register, as follows:
109 * bit 0: Read flag
110 * bit 1: Write flag
111 * bit 2: Breakpoint translation
113 * Thus, we use them here as so.
116 /* Ensure breakpoint translation bit is set */
117 if (data && !(data & HW_BRK_TYPE_TRANSLATE))
118 return -EIO;
119 hw_brk.address = data & (~HW_BRK_TYPE_DABR);
120 hw_brk.type = (data & HW_BRK_TYPE_DABR) | HW_BRK_TYPE_PRIV_ALL;
121 hw_brk.len = DABR_MAX_LEN;
122 hw_brk.hw_len = DABR_MAX_LEN;
123 set_bp = (data) && (hw_brk.type & HW_BRK_TYPE_RDWR);
124 #ifdef CONFIG_HAVE_HW_BREAKPOINT
125 bp = thread->ptrace_bps[0];
126 if (!set_bp) {
127 if (bp) {
128 unregister_hw_breakpoint(bp);
129 thread->ptrace_bps[0] = NULL;
131 return 0;
133 if (bp) {
134 attr = bp->attr;
135 attr.bp_addr = hw_brk.address;
136 attr.bp_len = DABR_MAX_LEN;
137 arch_bp_generic_fields(hw_brk.type, &attr.bp_type);
139 /* Enable breakpoint */
140 attr.disabled = false;
142 ret = modify_user_hw_breakpoint(bp, &attr);
143 if (ret)
144 return ret;
146 thread->ptrace_bps[0] = bp;
147 thread->hw_brk[0] = hw_brk;
148 return 0;
151 /* Create a new breakpoint request if one doesn't exist already */
152 hw_breakpoint_init(&attr);
153 attr.bp_addr = hw_brk.address;
154 attr.bp_len = DABR_MAX_LEN;
155 arch_bp_generic_fields(hw_brk.type,
156 &attr.bp_type);
158 thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr,
159 ptrace_triggered, NULL, task);
160 if (IS_ERR(bp)) {
161 thread->ptrace_bps[0] = NULL;
162 return PTR_ERR(bp);
165 #else /* !CONFIG_HAVE_HW_BREAKPOINT */
166 if (set_bp && (!ppc_breakpoint_available()))
167 return -ENODEV;
168 #endif /* CONFIG_HAVE_HW_BREAKPOINT */
169 task->thread.hw_brk[0] = hw_brk;
170 return 0;
173 #ifdef CONFIG_HAVE_HW_BREAKPOINT
174 static int find_empty_ptrace_bp(struct thread_struct *thread)
176 int i;
178 for (i = 0; i < nr_wp_slots(); i++) {
179 if (!thread->ptrace_bps[i])
180 return i;
182 return -1;
184 #endif
186 static int find_empty_hw_brk(struct thread_struct *thread)
188 int i;
190 for (i = 0; i < nr_wp_slots(); i++) {
191 if (!thread->hw_brk[i].address)
192 return i;
194 return -1;
197 long ppc_set_hwdebug(struct task_struct *child, struct ppc_hw_breakpoint *bp_info)
199 int i;
200 #ifdef CONFIG_HAVE_HW_BREAKPOINT
201 int len = 0;
202 struct thread_struct *thread = &child->thread;
203 struct perf_event *bp;
204 struct perf_event_attr attr;
205 #endif /* CONFIG_HAVE_HW_BREAKPOINT */
206 struct arch_hw_breakpoint brk;
208 if (bp_info->version != 1)
209 return -ENOTSUPP;
211 * We only support one data breakpoint
213 if ((bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_RW) == 0 ||
214 (bp_info->trigger_type & ~PPC_BREAKPOINT_TRIGGER_RW) != 0 ||
215 bp_info->condition_mode != PPC_BREAKPOINT_CONDITION_NONE)
216 return -EINVAL;
218 if ((unsigned long)bp_info->addr >= TASK_SIZE)
219 return -EIO;
221 brk.address = ALIGN_DOWN(bp_info->addr, HW_BREAKPOINT_SIZE);
222 brk.type = HW_BRK_TYPE_TRANSLATE | HW_BRK_TYPE_PRIV_ALL;
223 brk.len = DABR_MAX_LEN;
224 brk.hw_len = DABR_MAX_LEN;
225 if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_READ)
226 brk.type |= HW_BRK_TYPE_READ;
227 if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE)
228 brk.type |= HW_BRK_TYPE_WRITE;
229 #ifdef CONFIG_HAVE_HW_BREAKPOINT
230 if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE)
231 len = bp_info->addr2 - bp_info->addr;
232 else if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_EXACT)
233 len = 1;
234 else
235 return -EINVAL;
237 i = find_empty_ptrace_bp(thread);
238 if (i < 0)
239 return -ENOSPC;
241 /* Create a new breakpoint request if one doesn't exist already */
242 hw_breakpoint_init(&attr);
243 attr.bp_addr = (unsigned long)bp_info->addr;
244 attr.bp_len = len;
245 arch_bp_generic_fields(brk.type, &attr.bp_type);
247 bp = register_user_hw_breakpoint(&attr, ptrace_triggered, NULL, child);
248 thread->ptrace_bps[i] = bp;
249 if (IS_ERR(bp)) {
250 thread->ptrace_bps[i] = NULL;
251 return PTR_ERR(bp);
254 return i + 1;
255 #endif /* CONFIG_HAVE_HW_BREAKPOINT */
257 if (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT)
258 return -EINVAL;
260 i = find_empty_hw_brk(&child->thread);
261 if (i < 0)
262 return -ENOSPC;
264 if (!ppc_breakpoint_available())
265 return -ENODEV;
267 child->thread.hw_brk[i] = brk;
269 return i + 1;
272 long ppc_del_hwdebug(struct task_struct *child, long data)
274 #ifdef CONFIG_HAVE_HW_BREAKPOINT
275 int ret = 0;
276 struct thread_struct *thread = &child->thread;
277 struct perf_event *bp;
278 #endif /* CONFIG_HAVE_HW_BREAKPOINT */
279 if (data < 1 || data > nr_wp_slots())
280 return -EINVAL;
282 #ifdef CONFIG_HAVE_HW_BREAKPOINT
283 bp = thread->ptrace_bps[data - 1];
284 if (bp) {
285 unregister_hw_breakpoint(bp);
286 thread->ptrace_bps[data - 1] = NULL;
287 } else {
288 ret = -ENOENT;
290 return ret;
291 #else /* CONFIG_HAVE_HW_BREAKPOINT */
292 if (!(child->thread.hw_brk[data - 1].flags & HW_BRK_FLAG_DISABLED) &&
293 child->thread.hw_brk[data - 1].address == 0)
294 return -ENOENT;
296 child->thread.hw_brk[data - 1].address = 0;
297 child->thread.hw_brk[data - 1].type = 0;
298 child->thread.hw_brk[data - 1].flags = 0;
299 #endif /* CONFIG_HAVE_HW_BREAKPOINT */
301 return 0;