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 | ||
