aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/ptrace.c
diff options
context:
space:
mode:
authorFrederic Weisbecker <fweisbec@gmail.com>2009-11-26 22:55:53 -0500
committerIngo Molnar <mingo@elte.hu>2009-11-27 00:22:58 -0500
commit5fa10b28e57f94a90535cfeafe89dcee9f47d540 (patch)
tree951cf12a3b5b3a37546b4f6dd2994bebdac19dc0 /arch/x86/kernel/ptrace.c
parentb2e74a265ded1a185f762ebaab967e9e0d008dd8 (diff)
hw-breakpoints: Use struct perf_event_attr to define user breakpoints
In-kernel user breakpoints are created using functions in which we pass breakpoint parameters as individual variables: address, length and type. Although it fits well for x86, this just does not scale across archictectures that may support this api later as these may have more or different needs. Pass in a perf_event_attr structure instead because it is meant to evolve as much as possible into a generic hardware breakpoint parameter structure. Reported-by: K.Prasad <prasad@linux.vnet.ibm.com> Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> LKML-Reference: <1259294154-5197-1-git-send-regression-fweisbec@gmail.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/ptrace.c')
-rw-r--r--arch/x86/kernel/ptrace.c74
1 files changed, 41 insertions, 33 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