aboutsummaryrefslogtreecommitdiffstats
path: root/arch/xtensa/kernel/ptrace.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/xtensa/kernel/ptrace.c')
-rw-r--r--arch/xtensa/kernel/ptrace.c164
1 files changed, 157 insertions, 7 deletions
diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c
index 4d54b481123b..a651f3a628ee 100644
--- a/arch/xtensa/kernel/ptrace.c
+++ b/arch/xtensa/kernel/ptrace.c
@@ -13,21 +13,23 @@
13 * Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca> 13 * Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca>
14 */ 14 */
15 15
16#include <linux/errno.h>
17#include <linux/hw_breakpoint.h>
16#include <linux/kernel.h> 18#include <linux/kernel.h>
17#include <linux/sched.h>
18#include <linux/mm.h> 19#include <linux/mm.h>
19#include <linux/errno.h> 20#include <linux/perf_event.h>
20#include <linux/ptrace.h> 21#include <linux/ptrace.h>
21#include <linux/smp.h> 22#include <linux/sched.h>
22#include <linux/security.h> 23#include <linux/security.h>
23#include <linux/signal.h> 24#include <linux/signal.h>
25#include <linux/smp.h>
24 26
25#include <asm/pgtable.h> 27#include <asm/coprocessor.h>
28#include <asm/elf.h>
26#include <asm/page.h> 29#include <asm/page.h>
27#include <asm/uaccess.h> 30#include <asm/pgtable.h>
28#include <asm/ptrace.h> 31#include <asm/ptrace.h>
29#include <asm/elf.h> 32#include <asm/uaccess.h>
30#include <asm/coprocessor.h>
31 33
32 34
33void user_enable_single_step(struct task_struct *child) 35void user_enable_single_step(struct task_struct *child)
@@ -267,6 +269,146 @@ int ptrace_pokeusr(struct task_struct *child, long regno, long val)
267 return 0; 269 return 0;
268} 270}
269 271
272#ifdef CONFIG_HAVE_HW_BREAKPOINT
273static void ptrace_hbptriggered(struct perf_event *bp,
274 struct perf_sample_data *data,
275 struct pt_regs *regs)
276{
277 int i;
278 siginfo_t info;
279 struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp);
280
281 if (bp->attr.bp_type & HW_BREAKPOINT_X) {
282 for (i = 0; i < XCHAL_NUM_IBREAK; ++i)
283 if (current->thread.ptrace_bp[i] == bp)
284 break;
285 i <<= 1;
286 } else {
287 for (i = 0; i < XCHAL_NUM_DBREAK; ++i)
288 if (current->thread.ptrace_wp[i] == bp)
289 break;
290 i = (i << 1) | 1;
291 }
292
293 info.si_signo = SIGTRAP;
294 info.si_errno = i;
295 info.si_code = TRAP_HWBKPT;
296 info.si_addr = (void __user *)bkpt->address;
297
298 force_sig_info(SIGTRAP, &info, current);
299}
300
301static struct perf_event *ptrace_hbp_create(struct task_struct *tsk, int type)
302{
303 struct perf_event_attr attr;
304
305 ptrace_breakpoint_init(&attr);
306
307 /* Initialise fields to sane defaults. */
308 attr.bp_addr = 0;
309 attr.bp_len = 1;
310 attr.bp_type = type;
311 attr.disabled = 1;
312
313 return register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL,
314 tsk);
315}
316
317/*
318 * Address bit 0 choose instruction (0) or data (1) break register, bits
319 * 31..1 are the register number.
320 * Both PTRACE_GETHBPREGS and PTRACE_SETHBPREGS transfer two 32-bit words:
321 * address (0) and control (1).
322 * Instruction breakpoint contorl word is 0 to clear breakpoint, 1 to set.
323 * Data breakpoint control word bit 31 is 'trigger on store', bit 30 is
324 * 'trigger on load, bits 29..0 are length. Length 0 is used to clear a
325 * breakpoint. To set a breakpoint length must be a power of 2 in the range
326 * 1..64 and the address must be length-aligned.
327 */
328
329static long ptrace_gethbpregs(struct task_struct *child, long addr,
330 long __user *datap)
331{
332 struct perf_event *bp;
333 u32 user_data[2] = {0};
334 bool dbreak = addr & 1;
335 unsigned idx = addr >> 1;
336
337 if ((!dbreak && idx >= XCHAL_NUM_IBREAK) ||
338 (dbreak && idx >= XCHAL_NUM_DBREAK))
339 return -EINVAL;
340
341 if (dbreak)
342 bp = child->thread.ptrace_wp[idx];
343 else
344 bp = child->thread.ptrace_bp[idx];
345
346 if (bp) {
347 user_data[0] = bp->attr.bp_addr;
348 user_data[1] = bp->attr.disabled ? 0 : bp->attr.bp_len;
349 if (dbreak) {
350 if (bp->attr.bp_type & HW_BREAKPOINT_R)
351 user_data[1] |= DBREAKC_LOAD_MASK;
352 if (bp->attr.bp_type & HW_BREAKPOINT_W)
353 user_data[1] |= DBREAKC_STOR_MASK;
354 }
355 }
356
357 if (copy_to_user(datap, user_data, sizeof(user_data)))
358 return -EFAULT;
359
360 return 0;
361}
362
363static long ptrace_sethbpregs(struct task_struct *child, long addr,
364 long __user *datap)
365{
366 struct perf_event *bp;
367 struct perf_event_attr attr;
368 u32 user_data[2];
369 bool dbreak = addr & 1;
370 unsigned idx = addr >> 1;
371 int bp_type = 0;
372
373 if ((!dbreak && idx >= XCHAL_NUM_IBREAK) ||
374 (dbreak && idx >= XCHAL_NUM_DBREAK))
375 return -EINVAL;
376
377 if (copy_from_user(user_data, datap, sizeof(user_data)))
378 return -EFAULT;
379
380 if (dbreak) {
381 bp = child->thread.ptrace_wp[idx];
382 if (user_data[1] & DBREAKC_LOAD_MASK)
383 bp_type |= HW_BREAKPOINT_R;
384 if (user_data[1] & DBREAKC_STOR_MASK)
385 bp_type |= HW_BREAKPOINT_W;
386 } else {
387 bp = child->thread.ptrace_bp[idx];
388 bp_type = HW_BREAKPOINT_X;
389 }
390
391 if (!bp) {
392 bp = ptrace_hbp_create(child,
393 bp_type ? bp_type : HW_BREAKPOINT_RW);
394 if (IS_ERR(bp))
395 return PTR_ERR(bp);
396 if (dbreak)
397 child->thread.ptrace_wp[idx] = bp;
398 else
399 child->thread.ptrace_bp[idx] = bp;
400 }
401
402 attr = bp->attr;
403 attr.bp_addr = user_data[0];
404 attr.bp_len = user_data[1] & ~(DBREAKC_LOAD_MASK | DBREAKC_STOR_MASK);
405 attr.bp_type = bp_type;
406 attr.disabled = !attr.bp_len;
407
408 return modify_user_hw_breakpoint(bp, &attr);
409}
410#endif
411
270long arch_ptrace(struct task_struct *child, long request, 412long arch_ptrace(struct task_struct *child, long request,
271 unsigned long addr, unsigned long data) 413 unsigned long addr, unsigned long data)
272{ 414{
@@ -307,7 +449,15 @@ long arch_ptrace(struct task_struct *child, long request,
307 case PTRACE_SETXTREGS: 449 case PTRACE_SETXTREGS:
308 ret = ptrace_setxregs(child, datap); 450 ret = ptrace_setxregs(child, datap);
309 break; 451 break;
452#ifdef CONFIG_HAVE_HW_BREAKPOINT
453 case PTRACE_GETHBPREGS:
454 ret = ptrace_gethbpregs(child, addr, datap);
455 break;
310 456
457 case PTRACE_SETHBPREGS:
458 ret = ptrace_sethbpregs(child, addr, datap);
459 break;
460#endif
311 default: 461 default:
312 ret = ptrace_request(child, request, addr, data); 462 ret = ptrace_request(child, request, addr, data);
313 break; 463 break;