diff options
author | Markus Metzger <markus.t.metzger@intel.com> | 2008-04-08 05:01:58 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-05-12 15:27:53 -0400 |
commit | 93fa7636dfdc059b25df148f230c0991096afdef (patch) | |
tree | cf277bd09091ac69abb5f7fdc21c705b8f186f88 /arch/x86/kernel/ptrace.c | |
parent | 492c2e476eac010962850006c49df326919b284c (diff) |
x86, ptrace: PEBS support
Polish the ds.h interface and add support for PEBS.
Ds.c is meant to be the resource allocator for per-thread and per-cpu
BTS and PEBS recording.
It is used by ptrace/utrace to provide execution tracing of debugged tasks.
It will be used by profilers (e.g. perfmon2).
It may be used by kernel debuggers to provide a kernel execution trace.
Changes in detail:
- guard DS and ptrace by CONFIG macros
- separate DS and BTS more clearly
- simplify field accesses
- add functions to manage PEBS buffers
- add simple protection/allocation mechanism
- added support for Atom
Opens:
- buffer overflow handling
Currently, only circular buffers are supported. This is all we need
for debugging. Profilers would want an overflow notification.
This is planned to be added when perfmon2 is made to use the ds.h
interface.
- utrace intermediate layer
Signed-off-by: Markus Metzger <markus.t.metzger@intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch/x86/kernel/ptrace.c')
-rw-r--r-- | arch/x86/kernel/ptrace.c | 444 |
1 files changed, 269 insertions, 175 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index fb03ef380f0e..b7ff783dc5fe 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -554,45 +554,115 @@ static int ptrace_set_debugreg(struct task_struct *child, | |||
554 | return 0; | 554 | return 0; |
555 | } | 555 | } |
556 | 556 | ||
557 | #ifdef X86_BTS | 557 | #ifdef CONFIG_X86_PTRACE_BTS |
558 | /* | ||
559 | * The configuration for a particular BTS hardware implementation. | ||
560 | */ | ||
561 | struct bts_configuration { | ||
562 | /* the size of a BTS record in bytes; at most BTS_MAX_RECORD_SIZE */ | ||
563 | unsigned char sizeof_bts; | ||
564 | /* the size of a field in the BTS record in bytes */ | ||
565 | unsigned char sizeof_field; | ||
566 | /* a bitmask to enable/disable BTS in DEBUGCTL MSR */ | ||
567 | unsigned long debugctl_mask; | ||
568 | }; | ||
569 | static struct bts_configuration bts_cfg; | ||
570 | |||
571 | #define BTS_MAX_RECORD_SIZE (8 * 3) | ||
572 | |||
573 | |||
574 | /* | ||
575 | * Branch Trace Store (BTS) uses the following format. Different | ||
576 | * architectures vary in the size of those fields. | ||
577 | * - source linear address | ||
578 | * - destination linear address | ||
579 | * - flags | ||
580 | * | ||
581 | * Later architectures use 64bit pointers throughout, whereas earlier | ||
582 | * architectures use 32bit pointers in 32bit mode. | ||
583 | * | ||
584 | * We compute the base address for the first 8 fields based on: | ||
585 | * - the field size stored in the DS configuration | ||
586 | * - the relative field position | ||
587 | * | ||
588 | * In order to store additional information in the BTS buffer, we use | ||
589 | * a special source address to indicate that the record requires | ||
590 | * special interpretation. | ||
591 | * | ||
592 | * Netburst indicated via a bit in the flags field whether the branch | ||
593 | * was predicted; this is ignored. | ||
594 | */ | ||
558 | 595 | ||
559 | static int ptrace_bts_get_size(struct task_struct *child) | 596 | enum bts_field { |
597 | bts_from = 0, | ||
598 | bts_to, | ||
599 | bts_flags, | ||
600 | |||
601 | bts_escape = (unsigned long)-1, | ||
602 | bts_qual = bts_to, | ||
603 | bts_jiffies = bts_flags | ||
604 | }; | ||
605 | |||
606 | static inline unsigned long bts_get(const char *base, enum bts_field field) | ||
560 | { | 607 | { |
561 | if (!child->thread.ds_area_msr) | 608 | base += (bts_cfg.sizeof_field * field); |
562 | return -ENXIO; | 609 | return *(unsigned long *)base; |
610 | } | ||
563 | 611 | ||
564 | return ds_get_bts_index((void *)child->thread.ds_area_msr); | 612 | static inline void bts_set(char *base, enum bts_field field, unsigned long val) |
613 | { | ||
614 | base += (bts_cfg.sizeof_field * field);; | ||
615 | (*(unsigned long *)base) = val; | ||
565 | } | 616 | } |
566 | 617 | ||
567 | static int ptrace_bts_read_record(struct task_struct *child, | 618 | /* |
568 | long index, | 619 | * Translate a BTS record from the raw format into the bts_struct format |
620 | * | ||
621 | * out (out): bts_struct interpretation | ||
622 | * raw: raw BTS record | ||
623 | */ | ||
624 | static void ptrace_bts_translate_record(struct bts_struct *out, const void *raw) | ||
625 | { | ||
626 | memset(out, 0, sizeof(*out)); | ||
627 | if (bts_get(raw, bts_from) == bts_escape) { | ||
628 | out->qualifier = bts_get(raw, bts_qual); | ||
629 | out->variant.jiffies = bts_get(raw, bts_jiffies); | ||
630 | } else { | ||
631 | out->qualifier = BTS_BRANCH; | ||
632 | out->variant.lbr.from_ip = bts_get(raw, bts_from); | ||
633 | out->variant.lbr.to_ip = bts_get(raw, bts_to); | ||
634 | } | ||
635 | } | ||
636 | |||
637 | static int ptrace_bts_read_record(struct task_struct *child, size_t index, | ||
569 | struct bts_struct __user *out) | 638 | struct bts_struct __user *out) |
570 | { | 639 | { |
571 | struct bts_struct ret; | 640 | struct bts_struct ret; |
572 | int retval; | 641 | const void *bts_record; |
573 | int bts_end; | 642 | size_t bts_index, bts_end; |
574 | int bts_index; | 643 | int error; |
575 | |||
576 | if (!child->thread.ds_area_msr) | ||
577 | return -ENXIO; | ||
578 | 644 | ||
579 | if (index < 0) | 645 | error = ds_get_bts_end(child, &bts_end); |
580 | return -EINVAL; | 646 | if (error < 0) |
647 | return error; | ||
581 | 648 | ||
582 | bts_end = ds_get_bts_end((void *)child->thread.ds_area_msr); | ||
583 | if (bts_end <= index) | 649 | if (bts_end <= index) |
584 | return -EINVAL; | 650 | return -EINVAL; |
585 | 651 | ||
652 | error = ds_get_bts_index(child, &bts_index); | ||
653 | if (error < 0) | ||
654 | return error; | ||
655 | |||
586 | /* translate the ptrace bts index into the ds bts index */ | 656 | /* translate the ptrace bts index into the ds bts index */ |
587 | bts_index = ds_get_bts_index((void *)child->thread.ds_area_msr); | 657 | bts_index += bts_end - (index + 1); |
588 | bts_index -= (index + 1); | 658 | if (bts_end <= bts_index) |
589 | if (bts_index < 0) | 659 | bts_index -= bts_end; |
590 | bts_index += bts_end; | ||
591 | 660 | ||
592 | retval = ds_read_bts((void *)child->thread.ds_area_msr, | 661 | error = ds_access_bts(child, bts_index, &bts_record); |
593 | bts_index, &ret); | 662 | if (error < 0) |
594 | if (retval < 0) | 663 | return error; |
595 | return retval; | 664 | |
665 | ptrace_bts_translate_record(&ret, bts_record); | ||
596 | 666 | ||
597 | if (copy_to_user(out, &ret, sizeof(ret))) | 667 | if (copy_to_user(out, &ret, sizeof(ret))) |
598 | return -EFAULT; | 668 | return -EFAULT; |
@@ -600,101 +670,106 @@ static int ptrace_bts_read_record(struct task_struct *child, | |||
600 | return sizeof(ret); | 670 | return sizeof(ret); |
601 | } | 671 | } |
602 | 672 | ||
603 | static int ptrace_bts_clear(struct task_struct *child) | ||
604 | { | ||
605 | if (!child->thread.ds_area_msr) | ||
606 | return -ENXIO; | ||
607 | |||
608 | return ds_clear((void *)child->thread.ds_area_msr); | ||
609 | } | ||
610 | |||
611 | static int ptrace_bts_drain(struct task_struct *child, | 673 | static int ptrace_bts_drain(struct task_struct *child, |
612 | long size, | 674 | long size, |
613 | struct bts_struct __user *out) | 675 | struct bts_struct __user *out) |
614 | { | 676 | { |
615 | int end, i; | 677 | struct bts_struct ret; |
616 | void *ds = (void *)child->thread.ds_area_msr; | 678 | const unsigned char *raw; |
617 | 679 | size_t end, i; | |
618 | if (!ds) | 680 | int error; |
619 | return -ENXIO; | ||
620 | 681 | ||
621 | end = ds_get_bts_index(ds); | 682 | error = ds_get_bts_index(child, &end); |
622 | if (end <= 0) | 683 | if (error < 0) |
623 | return end; | 684 | return error; |
624 | 685 | ||
625 | if (size < (end * sizeof(struct bts_struct))) | 686 | if (size < (end * sizeof(struct bts_struct))) |
626 | return -EIO; | 687 | return -EIO; |
627 | 688 | ||
628 | for (i = 0; i < end; i++, out++) { | 689 | error = ds_access_bts(child, 0, (const void **)&raw); |
629 | struct bts_struct ret; | 690 | if (error < 0) |
630 | int retval; | 691 | return error; |
631 | 692 | ||
632 | retval = ds_read_bts(ds, i, &ret); | 693 | for (i = 0; i < end; i++, out++, raw += bts_cfg.sizeof_bts) { |
633 | if (retval < 0) | 694 | ptrace_bts_translate_record(&ret, raw); |
634 | return retval; | ||
635 | 695 | ||
636 | if (copy_to_user(out, &ret, sizeof(ret))) | 696 | if (copy_to_user(out, &ret, sizeof(ret))) |
637 | return -EFAULT; | 697 | return -EFAULT; |
638 | } | 698 | } |
639 | 699 | ||
640 | ds_clear(ds); | 700 | error = ds_clear_bts(child); |
701 | if (error < 0) | ||
702 | return error; | ||
641 | 703 | ||
642 | return end; | 704 | return end; |
643 | } | 705 | } |
644 | 706 | ||
707 | static void ptrace_bts_ovfl(struct task_struct *child) | ||
708 | { | ||
709 | send_sig(child->thread.bts_ovfl_signal, child, 0); | ||
710 | } | ||
711 | |||
645 | static int ptrace_bts_config(struct task_struct *child, | 712 | static int ptrace_bts_config(struct task_struct *child, |
646 | long cfg_size, | 713 | long cfg_size, |
647 | const struct ptrace_bts_config __user *ucfg) | 714 | const struct ptrace_bts_config __user *ucfg) |
648 | { | 715 | { |
649 | struct ptrace_bts_config cfg; | 716 | struct ptrace_bts_config cfg; |
650 | int bts_size, ret = 0; | 717 | int error = 0; |
651 | void *ds; | 718 | |
719 | error = -EOPNOTSUPP; | ||
720 | if (!bts_cfg.sizeof_bts) | ||
721 | goto errout; | ||
652 | 722 | ||
723 | error = -EIO; | ||
653 | if (cfg_size < sizeof(cfg)) | 724 | if (cfg_size < sizeof(cfg)) |
654 | return -EIO; | 725 | goto errout; |
655 | 726 | ||
727 | error = -EFAULT; | ||
656 | if (copy_from_user(&cfg, ucfg, sizeof(cfg))) | 728 | if (copy_from_user(&cfg, ucfg, sizeof(cfg))) |
657 | return -EFAULT; | 729 | goto errout; |
658 | 730 | ||
659 | if ((int)cfg.size < 0) | 731 | error = -EINVAL; |
660 | return -EINVAL; | 732 | if ((cfg.flags & PTRACE_BTS_O_SIGNAL) && |
733 | !(cfg.flags & PTRACE_BTS_O_ALLOC)) | ||
734 | goto errout; | ||
661 | 735 | ||
662 | bts_size = 0; | 736 | if (cfg.flags & PTRACE_BTS_O_ALLOC) { |
663 | ds = (void *)child->thread.ds_area_msr; | 737 | ds_ovfl_callback_t ovfl = 0; |
664 | if (ds) { | 738 | unsigned int sig = 0; |
665 | bts_size = ds_get_bts_size(ds); | ||
666 | if (bts_size < 0) | ||
667 | return bts_size; | ||
668 | } | ||
669 | cfg.size = PAGE_ALIGN(cfg.size); | ||
670 | 739 | ||
671 | if (bts_size != cfg.size) { | 740 | /* we ignore the error in case we were not tracing child */ |
672 | ret = ptrace_bts_realloc(child, cfg.size, | 741 | (void)ds_release_bts(child); |
673 | cfg.flags & PTRACE_BTS_O_CUT_SIZE); | 742 | |
674 | if (ret < 0) | 743 | if (cfg.flags & PTRACE_BTS_O_SIGNAL) { |
744 | if (!cfg.signal) | ||
745 | goto errout; | ||
746 | |||
747 | sig = cfg.signal; | ||
748 | ovfl = ptrace_bts_ovfl; | ||
749 | } | ||
750 | |||
751 | error = ds_request_bts(child, /* base = */ 0, cfg.size, ovfl); | ||
752 | if (error < 0) | ||
675 | goto errout; | 753 | goto errout; |
676 | 754 | ||
677 | ds = (void *)child->thread.ds_area_msr; | 755 | child->thread.bts_ovfl_signal = sig; |
678 | } | 756 | } |
679 | 757 | ||
680 | if (cfg.flags & PTRACE_BTS_O_SIGNAL) | 758 | error = -EINVAL; |
681 | ret = ds_set_overflow(ds, DS_O_SIGNAL); | 759 | if (!child->thread.ds_ctx && cfg.flags) |
682 | else | ||
683 | ret = ds_set_overflow(ds, DS_O_WRAP); | ||
684 | if (ret < 0) | ||
685 | goto errout; | 760 | goto errout; |
686 | 761 | ||
687 | if (cfg.flags & PTRACE_BTS_O_TRACE) | 762 | if (cfg.flags & PTRACE_BTS_O_TRACE) |
688 | child->thread.debugctlmsr |= ds_debugctl_mask(); | 763 | child->thread.debugctlmsr |= bts_cfg.debugctl_mask; |
689 | else | 764 | else |
690 | child->thread.debugctlmsr &= ~ds_debugctl_mask(); | 765 | child->thread.debugctlmsr &= ~bts_cfg.debugctl_mask; |
691 | 766 | ||
692 | if (cfg.flags & PTRACE_BTS_O_SCHED) | 767 | if (cfg.flags & PTRACE_BTS_O_SCHED) |
693 | set_tsk_thread_flag(child, TIF_BTS_TRACE_TS); | 768 | set_tsk_thread_flag(child, TIF_BTS_TRACE_TS); |
694 | else | 769 | else |
695 | clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS); | 770 | clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS); |
696 | 771 | ||
697 | ret = sizeof(cfg); | 772 | error = sizeof(cfg); |
698 | 773 | ||
699 | out: | 774 | out: |
700 | if (child->thread.debugctlmsr) | 775 | if (child->thread.debugctlmsr) |
@@ -702,10 +777,10 @@ out: | |||
702 | else | 777 | else |
703 | clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR); | 778 | clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR); |
704 | 779 | ||
705 | return ret; | 780 | return error; |
706 | 781 | ||
707 | errout: | 782 | errout: |
708 | child->thread.debugctlmsr &= ~ds_debugctl_mask(); | 783 | child->thread.debugctlmsr &= ~bts_cfg.debugctl_mask; |
709 | clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS); | 784 | clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS); |
710 | goto out; | 785 | goto out; |
711 | } | 786 | } |
@@ -714,29 +789,40 @@ static int ptrace_bts_status(struct task_struct *child, | |||
714 | long cfg_size, | 789 | long cfg_size, |
715 | struct ptrace_bts_config __user *ucfg) | 790 | struct ptrace_bts_config __user *ucfg) |
716 | { | 791 | { |
717 | void *ds = (void *)child->thread.ds_area_msr; | ||
718 | struct ptrace_bts_config cfg; | 792 | struct ptrace_bts_config cfg; |
793 | size_t end; | ||
794 | const void *base, *max; | ||
795 | int error; | ||
719 | 796 | ||
720 | if (cfg_size < sizeof(cfg)) | 797 | if (cfg_size < sizeof(cfg)) |
721 | return -EIO; | 798 | return -EIO; |
722 | 799 | ||
723 | memset(&cfg, 0, sizeof(cfg)); | 800 | error = ds_get_bts_end(child, &end); |
801 | if (error < 0) | ||
802 | return error; | ||
724 | 803 | ||
725 | if (ds) { | 804 | error = ds_access_bts(child, /* index = */ 0, &base); |
726 | cfg.size = ds_get_bts_size(ds); | 805 | if (error < 0) |
806 | return error; | ||
727 | 807 | ||
728 | if (ds_get_overflow(ds) == DS_O_SIGNAL) | 808 | error = ds_access_bts(child, /* index = */ end, &max); |
729 | cfg.flags |= PTRACE_BTS_O_SIGNAL; | 809 | if (error < 0) |
810 | return error; | ||
730 | 811 | ||
731 | if (test_tsk_thread_flag(child, TIF_DEBUGCTLMSR) && | 812 | memset(&cfg, 0, sizeof(cfg)); |
732 | child->thread.debugctlmsr & ds_debugctl_mask()) | 813 | cfg.size = (max - base); |
733 | cfg.flags |= PTRACE_BTS_O_TRACE; | 814 | cfg.signal = child->thread.bts_ovfl_signal; |
815 | cfg.bts_size = sizeof(struct bts_struct); | ||
734 | 816 | ||
735 | if (test_tsk_thread_flag(child, TIF_BTS_TRACE_TS)) | 817 | if (cfg.signal) |
736 | cfg.flags |= PTRACE_BTS_O_SCHED; | 818 | cfg.flags |= PTRACE_BTS_O_SIGNAL; |
737 | } | ||
738 | 819 | ||
739 | cfg.bts_size = sizeof(struct bts_struct); | 820 | if (test_tsk_thread_flag(child, TIF_DEBUGCTLMSR) && |
821 | child->thread.debugctlmsr & bts_cfg.debugctl_mask) | ||
822 | cfg.flags |= PTRACE_BTS_O_TRACE; | ||
823 | |||
824 | if (test_tsk_thread_flag(child, TIF_BTS_TRACE_TS)) | ||
825 | cfg.flags |= PTRACE_BTS_O_SCHED; | ||
740 | 826 | ||
741 | if (copy_to_user(ucfg, &cfg, sizeof(cfg))) | 827 | if (copy_to_user(ucfg, &cfg, sizeof(cfg))) |
742 | return -EFAULT; | 828 | return -EFAULT; |
@@ -744,89 +830,38 @@ static int ptrace_bts_status(struct task_struct *child, | |||
744 | return sizeof(cfg); | 830 | return sizeof(cfg); |
745 | } | 831 | } |
746 | 832 | ||
747 | |||
748 | static int ptrace_bts_write_record(struct task_struct *child, | 833 | static int ptrace_bts_write_record(struct task_struct *child, |
749 | const struct bts_struct *in) | 834 | const struct bts_struct *in) |
750 | { | 835 | { |
751 | int retval; | 836 | unsigned char bts_record[BTS_MAX_RECORD_SIZE]; |
752 | 837 | ||
753 | if (!child->thread.ds_area_msr) | 838 | BUG_ON(BTS_MAX_RECORD_SIZE < bts_cfg.sizeof_bts); |
754 | return -ENXIO; | ||
755 | 839 | ||
756 | retval = ds_write_bts((void *)child->thread.ds_area_msr, in); | 840 | memset(bts_record, 0, bts_cfg.sizeof_bts); |
757 | if (retval) | 841 | switch (in->qualifier) { |
758 | return retval; | 842 | case BTS_INVALID: |
843 | break; | ||
759 | 844 | ||
760 | return sizeof(*in); | 845 | case BTS_BRANCH: |
761 | } | 846 | bts_set(bts_record, bts_from, in->variant.lbr.from_ip); |
847 | bts_set(bts_record, bts_to, in->variant.lbr.to_ip); | ||
848 | break; | ||
762 | 849 | ||
763 | static int ptrace_bts_realloc(struct task_struct *child, | 850 | case BTS_TASK_ARRIVES: |
764 | int size, int reduce_size) | 851 | case BTS_TASK_DEPARTS: |
765 | { | 852 | bts_set(bts_record, bts_from, bts_escape); |
766 | unsigned long rlim, vm; | 853 | bts_set(bts_record, bts_qual, in->qualifier); |
767 | int ret, old_size; | 854 | bts_set(bts_record, bts_jiffies, in->variant.jiffies); |
855 | break; | ||
768 | 856 | ||
769 | if (size < 0) | 857 | default: |
770 | return -EINVAL; | 858 | return -EINVAL; |
771 | |||
772 | old_size = ds_get_bts_size((void *)child->thread.ds_area_msr); | ||
773 | if (old_size < 0) | ||
774 | return old_size; | ||
775 | |||
776 | ret = ds_free((void **)&child->thread.ds_area_msr); | ||
777 | if (ret < 0) | ||
778 | goto out; | ||
779 | |||
780 | size >>= PAGE_SHIFT; | ||
781 | old_size >>= PAGE_SHIFT; | ||
782 | |||
783 | current->mm->total_vm -= old_size; | ||
784 | current->mm->locked_vm -= old_size; | ||
785 | |||
786 | if (size == 0) | ||
787 | goto out; | ||
788 | |||
789 | rlim = current->signal->rlim[RLIMIT_AS].rlim_cur >> PAGE_SHIFT; | ||
790 | vm = current->mm->total_vm + size; | ||
791 | if (rlim < vm) { | ||
792 | ret = -ENOMEM; | ||
793 | |||
794 | if (!reduce_size) | ||
795 | goto out; | ||
796 | |||
797 | size = rlim - current->mm->total_vm; | ||
798 | if (size <= 0) | ||
799 | goto out; | ||
800 | } | 859 | } |
801 | 860 | ||
802 | rlim = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur >> PAGE_SHIFT; | 861 | /* The writing task will be the switched-to task on a context |
803 | vm = current->mm->locked_vm + size; | 862 | * switch. It needs to write into the switched-from task's BTS |
804 | if (rlim < vm) { | 863 | * buffer. */ |
805 | ret = -ENOMEM; | 864 | return ds_unchecked_write_bts(child, bts_record, bts_cfg.sizeof_bts); |
806 | |||
807 | if (!reduce_size) | ||
808 | goto out; | ||
809 | |||
810 | size = rlim - current->mm->locked_vm; | ||
811 | if (size <= 0) | ||
812 | goto out; | ||
813 | } | ||
814 | |||
815 | ret = ds_allocate((void **)&child->thread.ds_area_msr, | ||
816 | size << PAGE_SHIFT); | ||
817 | if (ret < 0) | ||
818 | goto out; | ||
819 | |||
820 | current->mm->total_vm += size; | ||
821 | current->mm->locked_vm += size; | ||
822 | |||
823 | out: | ||
824 | if (child->thread.ds_area_msr) | ||
825 | set_tsk_thread_flag(child, TIF_DS_AREA_MSR); | ||
826 | else | ||
827 | clear_tsk_thread_flag(child, TIF_DS_AREA_MSR); | ||
828 | |||
829 | return ret; | ||
830 | } | 865 | } |
831 | 866 | ||
832 | void ptrace_bts_take_timestamp(struct task_struct *tsk, | 867 | void ptrace_bts_take_timestamp(struct task_struct *tsk, |
@@ -839,7 +874,66 @@ void ptrace_bts_take_timestamp(struct task_struct *tsk, | |||
839 | 874 | ||
840 | ptrace_bts_write_record(tsk, &rec); | 875 | ptrace_bts_write_record(tsk, &rec); |
841 | } | 876 | } |
842 | #endif /* X86_BTS */ | 877 | |
878 | static const struct bts_configuration bts_cfg_netburst = { | ||
879 | .sizeof_bts = sizeof(long) * 3, | ||
880 | .sizeof_field = sizeof(long), | ||
881 | .debugctl_mask = (1<<2)|(1<<3)|(1<<5) | ||
882 | }; | ||
883 | |||
884 | static const struct bts_configuration bts_cfg_pentium_m = { | ||
885 | .sizeof_bts = sizeof(long) * 3, | ||
886 | .sizeof_field = sizeof(long), | ||
887 | .debugctl_mask = (1<<6)|(1<<7) | ||
888 | }; | ||
889 | |||
890 | static const struct bts_configuration bts_cfg_core2 = { | ||
891 | .sizeof_bts = 8 * 3, | ||
892 | .sizeof_field = 8, | ||
893 | .debugctl_mask = (1<<6)|(1<<7)|(1<<9) | ||
894 | }; | ||
895 | |||
896 | static inline void bts_configure(const struct bts_configuration *cfg) | ||
897 | { | ||
898 | bts_cfg = *cfg; | ||
899 | } | ||
900 | |||
901 | void __cpuinit ptrace_bts_init_intel(struct cpuinfo_x86 *c) | ||
902 | { | ||
903 | switch (c->x86) { | ||
904 | case 0x6: | ||
905 | switch (c->x86_model) { | ||
906 | case 0xD: | ||
907 | case 0xE: /* Pentium M */ | ||
908 | bts_configure(&bts_cfg_pentium_m); | ||
909 | break; | ||
910 | case 0xF: /* Core2 */ | ||
911 | case 0x1C: /* Atom */ | ||
912 | bts_configure(&bts_cfg_core2); | ||
913 | break; | ||
914 | default: | ||
915 | /* sorry, don't know about them */ | ||
916 | break; | ||
917 | } | ||
918 | break; | ||
919 | case 0xF: | ||
920 | switch (c->x86_model) { | ||
921 | case 0x0: | ||
922 | case 0x1: | ||
923 | case 0x2: /* Netburst */ | ||
924 | bts_configure(&bts_cfg_netburst); | ||
925 | break; | ||
926 | default: | ||
927 | /* sorry, don't know about them */ | ||
928 | break; | ||
929 | } | ||
930 | break; | ||
931 | default: | ||
932 | /* sorry, don't know about them */ | ||
933 | break; | ||
934 | } | ||
935 | } | ||
936 | #endif /* CONFIG_X86_PTRACE_BTS */ | ||
843 | 937 | ||
844 | /* | 938 | /* |
845 | * Called by kernel/ptrace.c when detaching.. | 939 | * Called by kernel/ptrace.c when detaching.. |
@@ -852,15 +946,15 @@ void ptrace_disable(struct task_struct *child) | |||
852 | #ifdef TIF_SYSCALL_EMU | 946 | #ifdef TIF_SYSCALL_EMU |
853 | clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); | 947 | clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); |
854 | #endif | 948 | #endif |
855 | if (child->thread.ds_area_msr) { | 949 | #ifdef CONFIG_X86_PTRACE_BTS |
856 | #ifdef X86_BTS | 950 | (void)ds_release_bts(child); |
857 | ptrace_bts_realloc(child, 0, 0); | 951 | |
858 | #endif | 952 | child->thread.debugctlmsr &= ~bts_cfg.debugctl_mask; |
859 | child->thread.debugctlmsr &= ~ds_debugctl_mask(); | 953 | if (!child->thread.debugctlmsr) |
860 | if (!child->thread.debugctlmsr) | 954 | clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR); |
861 | clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR); | 955 | |
862 | clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS); | 956 | clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS); |
863 | } | 957 | #endif /* CONFIG_X86_PTRACE_BTS */ |
864 | } | 958 | } |
865 | 959 | ||
866 | #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION | 960 | #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION |
@@ -980,7 +1074,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
980 | /* | 1074 | /* |
981 | * These bits need more cooking - not enabled yet: | 1075 | * These bits need more cooking - not enabled yet: |
982 | */ | 1076 | */ |
983 | #ifdef X86_BTS | 1077 | #ifdef CONFIG_X86_PTRACE_BTS |
984 | case PTRACE_BTS_CONFIG: | 1078 | case PTRACE_BTS_CONFIG: |
985 | ret = ptrace_bts_config | 1079 | ret = ptrace_bts_config |
986 | (child, data, (struct ptrace_bts_config __user *)addr); | 1080 | (child, data, (struct ptrace_bts_config __user *)addr); |
@@ -992,7 +1086,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
992 | break; | 1086 | break; |
993 | 1087 | ||
994 | case PTRACE_BTS_SIZE: | 1088 | case PTRACE_BTS_SIZE: |
995 | ret = ptrace_bts_get_size(child); | 1089 | ret = ds_get_bts_index(child, /* pos = */ 0); |
996 | break; | 1090 | break; |
997 | 1091 | ||
998 | case PTRACE_BTS_GET: | 1092 | case PTRACE_BTS_GET: |
@@ -1001,14 +1095,14 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
1001 | break; | 1095 | break; |
1002 | 1096 | ||
1003 | case PTRACE_BTS_CLEAR: | 1097 | case PTRACE_BTS_CLEAR: |
1004 | ret = ptrace_bts_clear(child); | 1098 | ret = ds_clear_bts(child); |
1005 | break; | 1099 | break; |
1006 | 1100 | ||
1007 | case PTRACE_BTS_DRAIN: | 1101 | case PTRACE_BTS_DRAIN: |
1008 | ret = ptrace_bts_drain | 1102 | ret = ptrace_bts_drain |
1009 | (child, data, (struct bts_struct __user *) addr); | 1103 | (child, data, (struct bts_struct __user *) addr); |
1010 | break; | 1104 | break; |
1011 | #endif | 1105 | #endif /* CONFIG_X86_PTRACE_BTS */ |
1012 | 1106 | ||
1013 | default: | 1107 | default: |
1014 | ret = ptrace_request(child, request, addr, data); | 1108 | ret = ptrace_request(child, request, addr, data); |