aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/kernel/ptrace.c74
-rw-r--r--include/linux/hw_breakpoint.h36
-rw-r--r--kernel/hw_breakpoint.c87
3 files changed, 75 insertions, 122 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 75e0cd847bd6..2941b32ea666 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -593,6 +593,34 @@ static unsigned long ptrace_get_dr7(struct perf_event *bp[])
593 return dr7; 593 return dr7;
594} 594}
595 595
596static struct perf_event *
597ptrace_modify_breakpoint(struct perf_event *bp, int len, int type,
598 struct task_struct *tsk)
599{
600 int err;
601 int gen_len, gen_type;
602 DEFINE_BREAKPOINT_ATTR(attr);
603
604 /*
605 * We shoud have at least an inactive breakpoint at this
606 * slot. It means the user is writing dr7 without having
607 * written the address register first
608 */
609 if (!bp)
610 return ERR_PTR(-EINVAL);
611
612 err = arch_bp_generic_fields(len, type, &gen_len, &gen_type);
613 if (err)
614 return ERR_PTR(err);
615
616 attr = bp->attr;
617 attr.bp_len = gen_len;
618 attr.bp_type = gen_type;
619 attr.disabled = 0;
620
621 return modify_user_hw_breakpoint(bp, &attr, bp->callback, tsk);
622}
623
596/* 624/*
597 * Handle ptrace writes to debug register 7. 625 * Handle ptrace writes to debug register 7.
598 */ 626 */
@@ -603,7 +631,6 @@ static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data)
603 int i, orig_ret = 0, rc = 0; 631 int i, orig_ret = 0, rc = 0;
604 int enabled, second_pass = 0; 632 int enabled, second_pass = 0;
605 unsigned len, type; 633 unsigned len, type;
606 int gen_len, gen_type;
607 struct perf_event *bp; 634 struct perf_event *bp;
608 635
609 data &= ~DR_CONTROL_RESERVED; 636 data &= ~DR_CONTROL_RESERVED;
@@ -634,33 +661,12 @@ restore:
634 continue; 661 continue;
635 } 662 }
636 663
637 /* 664 bp = ptrace_modify_breakpoint(bp, len, type, tsk);
638 * We shoud have at least an inactive breakpoint at this
639 * slot. It means the user is writing dr7 without having
640 * written the address register first
641 */
642 if (!bp) {
643 rc = -EINVAL;
644 break;
645 }
646
647 rc = arch_bp_generic_fields(len, type, &gen_len, &gen_type);
648 if (rc)
649 break;
650
651 /*
652 * This is a temporary thing as bp is unregistered/registered
653 * to simulate modification
654 */
655 bp = modify_user_hw_breakpoint(bp, bp->attr.bp_addr, gen_len,
656 gen_type, bp->callback,
657 tsk, true);
658 thread->ptrace_bps[i] = NULL;
659 665
660 /* Incorrect bp, or we have a bug in bp API */ 666 /* Incorrect bp, or we have a bug in bp API */
661 if (IS_ERR(bp)) { 667 if (IS_ERR(bp)) {
662 rc = PTR_ERR(bp); 668 rc = PTR_ERR(bp);
663 bp = NULL; 669 thread->ptrace_bps[i] = NULL;
664 break; 670 break;
665 } 671 }
666 thread->ptrace_bps[i] = bp; 672 thread->ptrace_bps[i] = bp;
@@ -707,24 +713,26 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
707{ 713{
708 struct perf_event *bp; 714 struct perf_event *bp;
709 struct thread_struct *t = &tsk->thread; 715 struct thread_struct *t = &tsk->thread;
716 DEFINE_BREAKPOINT_ATTR(attr);
710 717
711 if (!t->ptrace_bps[nr]) { 718 if (!t->ptrace_bps[nr]) {
712 /* 719 /*
713 * Put stub len and type to register (reserve) an inactive but 720 * Put stub len and type to register (reserve) an inactive but
714 * correct bp 721 * correct bp
715 */ 722 */
716 bp = register_user_hw_breakpoint(addr, HW_BREAKPOINT_LEN_1, 723 attr.bp_addr = addr;
717 HW_BREAKPOINT_W, 724 attr.bp_len = HW_BREAKPOINT_LEN_1;
718 ptrace_triggered, tsk, 725 attr.bp_type = HW_BREAKPOINT_W;
719 false); 726 attr.disabled = 1;
727
728 bp = register_user_hw_breakpoint(&attr, ptrace_triggered, tsk);
720 } else { 729 } else {
721 bp = t->ptrace_bps[nr]; 730 bp = t->ptrace_bps[nr];
722 t->ptrace_bps[nr] = NULL; 731 t->ptrace_bps[nr] = NULL;
723 bp = modify_user_hw_breakpoint(bp, addr, bp->attr.bp_len, 732
724 bp->attr.bp_type, 733 attr = bp->attr;
725 bp->callback, 734 attr.bp_addr = addr;
726 tsk, 735 bp = modify_user_hw_breakpoint(bp, &attr, bp->callback, tsk);
727 bp->attr.disabled);
728 } 736 }
729 /* 737 /*
730 * CHECKME: the previous code returned -EIO if the addr wasn't a 738 * CHECKME: the previous code returned -EIO if the addr wasn't a
diff --git a/include/linux/hw_breakpoint.h b/include/linux/hw_breakpoint.h
index c9f7f7c7b0e0..5da472e434b7 100644
--- a/include/linux/hw_breakpoint.h
+++ b/include/linux/hw_breakpoint.h
@@ -20,6 +20,14 @@ enum {
20 20
21#ifdef CONFIG_HAVE_HW_BREAKPOINT 21#ifdef CONFIG_HAVE_HW_BREAKPOINT
22 22
23/* As it's for in-kernel or ptrace use, we want it to be pinned */
24#define DEFINE_BREAKPOINT_ATTR(name) \
25struct perf_event_attr name = { \
26 .type = PERF_TYPE_BREAKPOINT, \
27 .size = sizeof(name), \
28 .pinned = 1, \
29};
30
23static inline unsigned long hw_breakpoint_addr(struct perf_event *bp) 31static inline unsigned long hw_breakpoint_addr(struct perf_event *bp)
24{ 32{
25 return bp->attr.bp_addr; 33 return bp->attr.bp_addr;
@@ -36,22 +44,16 @@ static inline int hw_breakpoint_len(struct perf_event *bp)
36} 44}
37 45
38extern struct perf_event * 46extern struct perf_event *
39register_user_hw_breakpoint(unsigned long addr, 47register_user_hw_breakpoint(struct perf_event_attr *attr,
40 int len,
41 int type,
42 perf_callback_t triggered, 48 perf_callback_t triggered,
43 struct task_struct *tsk, 49 struct task_struct *tsk);
44 bool active);
45 50
46/* FIXME: only change from the attr, and don't unregister */ 51/* FIXME: only change from the attr, and don't unregister */
47extern struct perf_event * 52extern struct perf_event *
48modify_user_hw_breakpoint(struct perf_event *bp, 53modify_user_hw_breakpoint(struct perf_event *bp,
49 unsigned long addr, 54 struct perf_event_attr *attr,
50 int len,
51 int type,
52 perf_callback_t triggered, 55 perf_callback_t triggered,
53 struct task_struct *tsk, 56 struct task_struct *tsk);
54 bool active);
55 57
56/* 58/*
57 * Kernel breakpoints are not associated with any particular thread. 59 * Kernel breakpoints are not associated with any particular thread.
@@ -89,20 +91,14 @@ static inline struct arch_hw_breakpoint *counter_arch_bp(struct perf_event *bp)
89#else /* !CONFIG_HAVE_HW_BREAKPOINT */ 91#else /* !CONFIG_HAVE_HW_BREAKPOINT */
90 92
91static inline struct perf_event * 93static inline struct perf_event *
92register_user_hw_breakpoint(unsigned long addr, 94register_user_hw_breakpoint(struct perf_event_attr *attr,
93 int len,
94 int type,
95 perf_callback_t triggered, 95 perf_callback_t triggered,
96 struct task_struct *tsk, 96 struct task_struct *tsk) { return NULL; }
97 bool active) { return NULL; }
98static inline struct perf_event * 97static inline struct perf_event *
99modify_user_hw_breakpoint(struct perf_event *bp, 98modify_user_hw_breakpoint(struct perf_event *bp,
100 unsigned long addr, 99 struct perf_event_attr *attr,
101 int len,
102 int type,
103 perf_callback_t triggered, 100 perf_callback_t triggered,
104 struct task_struct *tsk, 101 struct task_struct *tsk) { return NULL; }
105 bool active) { return NULL; }
106static inline struct perf_event * 102static inline struct perf_event *
107register_wide_hw_breakpoint_cpu(unsigned long addr, 103register_wide_hw_breakpoint_cpu(unsigned long addr,
108 int len, 104 int len,
diff --git a/kernel/hw_breakpoint.c b/kernel/hw_breakpoint.c
index 32e1018191be..2a47514f12fd 100644
--- a/kernel/hw_breakpoint.c
+++ b/kernel/hw_breakpoint.c
@@ -289,90 +289,32 @@ int register_perf_hw_breakpoint(struct perf_event *bp)
289 return __register_perf_hw_breakpoint(bp); 289 return __register_perf_hw_breakpoint(bp);
290} 290}
291 291
292/*
293 * Register a breakpoint bound to a task and a given cpu.
294 * If cpu is -1, the breakpoint is active for the task in every cpu
295 * If the task is -1, the breakpoint is active for every tasks in the given
296 * cpu.
297 */
298static struct perf_event *
299register_user_hw_breakpoint_cpu(unsigned long addr,
300 int len,
301 int type,
302 perf_callback_t triggered,
303 pid_t pid,
304 int cpu,
305 bool active)
306{
307 struct perf_event_attr *attr;
308 struct perf_event *bp;
309
310 attr = kzalloc(sizeof(*attr), GFP_KERNEL);
311 if (!attr)
312 return ERR_PTR(-ENOMEM);
313
314 attr->type = PERF_TYPE_BREAKPOINT;
315 attr->size = sizeof(*attr);
316 attr->bp_addr = addr;
317 attr->bp_len = len;
318 attr->bp_type = type;
319 /*
320 * Such breakpoints are used by debuggers to trigger signals when
321 * we hit the excepted memory op. We can't miss such events, they
322 * must be pinned.
323 */
324 attr->pinned = 1;
325
326 if (!active)
327 attr->disabled = 1;
328
329 bp = perf_event_create_kernel_counter(attr, cpu, pid, triggered);
330 kfree(attr);
331
332 return bp;
333}
334
335/** 292/**
336 * register_user_hw_breakpoint - register a hardware breakpoint for user space 293 * register_user_hw_breakpoint - register a hardware breakpoint for user space
337 * @addr: is the memory address that triggers the breakpoint 294 * @attr: breakpoint attributes
338 * @len: the length of the access to the memory (1 byte, 2 bytes etc...)
339 * @type: the type of the access to the memory (read/write/exec)
340 * @triggered: callback to trigger when we hit the breakpoint 295 * @triggered: callback to trigger when we hit the breakpoint
341 * @tsk: pointer to 'task_struct' of the process to which the address belongs 296 * @tsk: pointer to 'task_struct' of the process to which the address belongs
342 * @active: should we activate it while registering it
343 *
344 */ 297 */
345struct perf_event * 298struct perf_event *
346register_user_hw_breakpoint(unsigned long addr, 299register_user_hw_breakpoint(struct perf_event_attr *attr,
347 int len,
348 int type,
349 perf_callback_t triggered, 300 perf_callback_t triggered,
350 struct task_struct *tsk, 301 struct task_struct *tsk)
351 bool active)
352{ 302{
353 return register_user_hw_breakpoint_cpu(addr, len, type, triggered, 303 return perf_event_create_kernel_counter(attr, -1, tsk->pid, triggered);
354 tsk->pid, -1, active);
355} 304}
356EXPORT_SYMBOL_GPL(register_user_hw_breakpoint); 305EXPORT_SYMBOL_GPL(register_user_hw_breakpoint);
357 306
358/** 307/**
359 * modify_user_hw_breakpoint - modify a user-space hardware breakpoint 308 * modify_user_hw_breakpoint - modify a user-space hardware breakpoint
360 * @bp: the breakpoint structure to modify 309 * @bp: the breakpoint structure to modify
361 * @addr: is the memory address that triggers the breakpoint 310 * @attr: new breakpoint attributes
362 * @len: the length of the access to the memory (1 byte, 2 bytes etc...)
363 * @type: the type of the access to the memory (read/write/exec)
364 * @triggered: callback to trigger when we hit the breakpoint 311 * @triggered: callback to trigger when we hit the breakpoint
365 * @tsk: pointer to 'task_struct' of the process to which the address belongs 312 * @tsk: pointer to 'task_struct' of the process to which the address belongs
366 * @active: should we activate it while registering it
367 */ 313 */
368struct perf_event * 314struct perf_event *
369modify_user_hw_breakpoint(struct perf_event *bp, 315modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr,
370 unsigned long addr,
371 int len,
372 int type,
373 perf_callback_t triggered, 316 perf_callback_t triggered,
374 struct task_struct *tsk, 317 struct task_struct *tsk)
375 bool active)
376{ 318{
377 /* 319 /*
378 * FIXME: do it without unregistering 320 * FIXME: do it without unregistering
@@ -381,8 +323,7 @@ modify_user_hw_breakpoint(struct perf_event *bp,
381 */ 323 */
382 unregister_hw_breakpoint(bp); 324 unregister_hw_breakpoint(bp);
383 325
384 return register_user_hw_breakpoint(addr, len, type, triggered, 326 return perf_event_create_kernel_counter(attr, -1, tsk->pid, triggered);
385 tsk, active);
386} 327}
387EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint); 328EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint);
388 329
@@ -406,8 +347,16 @@ register_kernel_hw_breakpoint_cpu(unsigned long addr,
406 int cpu, 347 int cpu,
407 bool active) 348 bool active)
408{ 349{
409 return register_user_hw_breakpoint_cpu(addr, len, type, triggered, 350 DEFINE_BREAKPOINT_ATTR(attr);
410 -1, cpu, active); 351
352 attr.bp_addr = addr;
353 attr.bp_len = len;
354 attr.bp_type = type;
355
356 if (!active)
357 attr.disabled = 1;
358
359 return perf_event_create_kernel_counter(&attr, cpu, -1, triggered);
411} 360}
412 361
413/** 362/**