diff options
author | Frederic Weisbecker <fweisbec@gmail.com> | 2009-11-26 22:55:53 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-11-27 00:22:58 -0500 |
commit | 5fa10b28e57f94a90535cfeafe89dcee9f47d540 (patch) | |
tree | 951cf12a3b5b3a37546b4f6dd2994bebdac19dc0 /arch | |
parent | b2e74a265ded1a185f762ebaab967e9e0d008dd8 (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')
-rw-r--r-- | arch/x86/kernel/ptrace.c | 74 |
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 | ||
596 | static struct perf_event * | ||
597 | ptrace_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 |