diff options
author | Oleg Nesterov <oleg@redhat.com> | 2013-07-08 19:00:59 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-07-09 13:33:26 -0400 |
commit | 9afe33ada275f2413dfeae27cc58fbb27474ac72 (patch) | |
tree | b866d91c90e43c28db6444853cb381e514d65fc4 | |
parent | 29a55513414187b50d3cebb99884955a78d97283 (diff) |
ptrace/x86: introduce ptrace_register_breakpoint()
No functional changes, preparation.
Extract the "register breakpoint" code from ptrace_get_debugreg() into
the new/generic helper, ptrace_register_breakpoint(). It will have more
users.
The patch also adds another simple helper, ptrace_fill_bp_fields(), to
factor out the arch_bp_generic_fields() logic in register/modify.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jan Kratochvil <jan.kratochvil@redhat.com>
Cc: Michael Neuling <mikey@neuling.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Prasad <prasad@linux.vnet.ibm.com>
Cc: Russell King <linux@arm.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | arch/x86/kernel/ptrace.c | 86 |
1 files changed, 50 insertions, 36 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 98b0a2ccc33c..052636801b41 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -601,22 +601,48 @@ static unsigned long ptrace_get_dr7(struct perf_event *bp[]) | |||
601 | return dr7; | 601 | return dr7; |
602 | } | 602 | } |
603 | 603 | ||
604 | static int | 604 | static int ptrace_fill_bp_fields(struct perf_event_attr *attr, |
605 | ptrace_modify_breakpoint(struct perf_event *bp, int len, int type, | 605 | int len, int type, bool disabled) |
606 | struct task_struct *tsk, int disabled) | 606 | { |
607 | int err, bp_len, bp_type; | ||
608 | |||
609 | err = arch_bp_generic_fields(len, type, &bp_len, &bp_type); | ||
610 | if (!err) { | ||
611 | attr->bp_len = bp_len; | ||
612 | attr->bp_type = bp_type; | ||
613 | attr->disabled = disabled; | ||
614 | } | ||
615 | |||
616 | return err; | ||
617 | } | ||
618 | |||
619 | static struct perf_event * | ||
620 | ptrace_register_breakpoint(struct task_struct *tsk, int len, int type, | ||
621 | unsigned long addr, bool disabled) | ||
607 | { | 622 | { |
608 | int err; | ||
609 | int gen_len, gen_type; | ||
610 | struct perf_event_attr attr; | 623 | struct perf_event_attr attr; |
624 | int err; | ||
625 | |||
626 | ptrace_breakpoint_init(&attr); | ||
627 | attr.bp_addr = addr; | ||
611 | 628 | ||
612 | err = arch_bp_generic_fields(len, type, &gen_len, &gen_type); | 629 | err = ptrace_fill_bp_fields(&attr, len, type, disabled); |
613 | if (err) | 630 | if (err) |
614 | return err; | 631 | return ERR_PTR(err); |
632 | |||
633 | return register_user_hw_breakpoint(&attr, ptrace_triggered, | ||
634 | NULL, tsk); | ||
635 | } | ||
615 | 636 | ||
616 | attr = bp->attr; | 637 | static int ptrace_modify_breakpoint(struct perf_event *bp, int len, int type, |
617 | attr.bp_len = gen_len; | 638 | int disabled) |
618 | attr.bp_type = gen_type; | 639 | { |
619 | attr.disabled = disabled; | 640 | struct perf_event_attr attr = bp->attr; |
641 | int err; | ||
642 | |||
643 | err = ptrace_fill_bp_fields(&attr, len, type, disabled); | ||
644 | if (err) | ||
645 | return err; | ||
620 | 646 | ||
621 | return modify_user_hw_breakpoint(bp, &attr); | 647 | return modify_user_hw_breakpoint(bp, &attr); |
622 | } | 648 | } |
@@ -653,7 +679,7 @@ restore: | |||
653 | break; | 679 | break; |
654 | } | 680 | } |
655 | 681 | ||
656 | rc = ptrace_modify_breakpoint(bp, len, type, tsk, disabled); | 682 | rc = ptrace_modify_breakpoint(bp, len, type, disabled); |
657 | if (rc) | 683 | if (rc) |
658 | break; | 684 | break; |
659 | } | 685 | } |
@@ -693,26 +719,14 @@ static unsigned long ptrace_get_debugreg(struct task_struct *tsk, int n) | |||
693 | static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr, | 719 | static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr, |
694 | unsigned long addr) | 720 | unsigned long addr) |
695 | { | 721 | { |
696 | struct perf_event *bp; | ||
697 | struct thread_struct *t = &tsk->thread; | 722 | struct thread_struct *t = &tsk->thread; |
698 | struct perf_event_attr attr; | 723 | struct perf_event *bp = t->ptrace_bps[nr]; |
699 | int err = 0; | 724 | int err = 0; |
700 | 725 | ||
701 | if (!t->ptrace_bps[nr]) { | 726 | if (!bp) { |
702 | ptrace_breakpoint_init(&attr); | ||
703 | /* | ||
704 | * Put stub len and type to register (reserve) an inactive but | ||
705 | * correct bp | ||
706 | */ | ||
707 | attr.bp_addr = addr; | ||
708 | attr.bp_len = HW_BREAKPOINT_LEN_1; | ||
709 | attr.bp_type = HW_BREAKPOINT_W; | ||
710 | attr.disabled = 1; | ||
711 | |||
712 | bp = register_user_hw_breakpoint(&attr, ptrace_triggered, | ||
713 | NULL, tsk); | ||
714 | |||
715 | /* | 727 | /* |
728 | * Put stub len and type to create an inactive but correct bp. | ||
729 | * | ||
716 | * CHECKME: the previous code returned -EIO if the addr wasn't | 730 | * CHECKME: the previous code returned -EIO if the addr wasn't |
717 | * a valid task virtual addr. The new one will return -EINVAL in | 731 | * a valid task virtual addr. The new one will return -EINVAL in |
718 | * this case. | 732 | * this case. |
@@ -721,20 +735,20 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr, | |||
721 | * writing for the user. And anyway this is the previous | 735 | * writing for the user. And anyway this is the previous |
722 | * behaviour. | 736 | * behaviour. |
723 | */ | 737 | */ |
724 | if (IS_ERR(bp)) { | 738 | bp = ptrace_register_breakpoint(tsk, |
739 | X86_BREAKPOINT_LEN_1, X86_BREAKPOINT_WRITE, | ||
740 | addr, true); | ||
741 | if (IS_ERR(bp)) | ||
725 | err = PTR_ERR(bp); | 742 | err = PTR_ERR(bp); |
726 | goto out; | 743 | else |
727 | } | 744 | t->ptrace_bps[nr] = bp; |
728 | |||
729 | t->ptrace_bps[nr] = bp; | ||
730 | } else { | 745 | } else { |
731 | bp = t->ptrace_bps[nr]; | 746 | struct perf_event_attr attr = bp->attr; |
732 | 747 | ||
733 | attr = bp->attr; | ||
734 | attr.bp_addr = addr; | 748 | attr.bp_addr = addr; |
735 | err = modify_user_hw_breakpoint(bp, &attr); | 749 | err = modify_user_hw_breakpoint(bp, &attr); |
736 | } | 750 | } |
737 | out: | 751 | |
738 | return err; | 752 | return err; |
739 | } | 753 | } |
740 | 754 | ||