diff options
| -rw-r--r-- | arch/xtensa/Kconfig | 1 | ||||
| -rw-r--r-- | arch/xtensa/include/asm/hw_breakpoint.h | 58 | ||||
| -rw-r--r-- | arch/xtensa/include/asm/irqflags.h | 1 | ||||
| -rw-r--r-- | arch/xtensa/include/asm/processor.h | 9 | ||||
| -rw-r--r-- | arch/xtensa/include/asm/regs.h | 3 | ||||
| -rw-r--r-- | arch/xtensa/include/asm/thread_info.h | 1 | ||||
| -rw-r--r-- | arch/xtensa/include/asm/traps.h | 8 | ||||
| -rw-r--r-- | arch/xtensa/include/uapi/asm/ptrace.h | 2 | ||||
| -rw-r--r-- | arch/xtensa/kernel/Makefile | 1 | ||||
| -rw-r--r-- | arch/xtensa/kernel/asm-offsets.c | 6 | ||||
| -rw-r--r-- | arch/xtensa/kernel/entry.S | 72 | ||||
| -rw-r--r-- | arch/xtensa/kernel/hw_breakpoint.c | 317 | ||||
| -rw-r--r-- | arch/xtensa/kernel/process.c | 5 | ||||
| -rw-r--r-- | arch/xtensa/kernel/ptrace.c | 164 | ||||
| -rw-r--r-- | arch/xtensa/kernel/traps.c | 14 |
15 files changed, 649 insertions, 13 deletions
diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 128e63d3a632..b8e9de1e7832 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig | |||
| @@ -18,6 +18,7 @@ config XTENSA | |||
| 18 | select HAVE_DMA_ATTRS | 18 | select HAVE_DMA_ATTRS |
| 19 | select HAVE_FUNCTION_TRACER | 19 | select HAVE_FUNCTION_TRACER |
| 20 | select HAVE_FUTEX_CMPXCHG if !MMU | 20 | select HAVE_FUTEX_CMPXCHG if !MMU |
| 21 | select HAVE_HW_BREAKPOINT if PERF_EVENTS | ||
| 21 | select HAVE_IRQ_TIME_ACCOUNTING | 22 | select HAVE_IRQ_TIME_ACCOUNTING |
| 22 | select HAVE_OPROFILE | 23 | select HAVE_OPROFILE |
| 23 | select HAVE_PERF_EVENTS | 24 | select HAVE_PERF_EVENTS |
diff --git a/arch/xtensa/include/asm/hw_breakpoint.h b/arch/xtensa/include/asm/hw_breakpoint.h new file mode 100644 index 000000000000..dbe3053b284a --- /dev/null +++ b/arch/xtensa/include/asm/hw_breakpoint.h | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | /* | ||
| 2 | * Xtensa hardware breakpoints/watchpoints handling functions | ||
| 3 | * | ||
| 4 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 5 | * License. See the file "COPYING" in the main directory of this archive | ||
| 6 | * for more details. | ||
| 7 | * | ||
| 8 | * Copyright (C) 2016 Cadence Design Systems Inc. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #ifndef __ASM_XTENSA_HW_BREAKPOINT_H | ||
| 12 | #define __ASM_XTENSA_HW_BREAKPOINT_H | ||
| 13 | |||
| 14 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
| 15 | |||
| 16 | #include <linux/kdebug.h> | ||
| 17 | #include <linux/types.h> | ||
| 18 | #include <uapi/linux/hw_breakpoint.h> | ||
| 19 | |||
| 20 | /* Breakpoint */ | ||
| 21 | #define XTENSA_BREAKPOINT_EXECUTE 0 | ||
| 22 | |||
| 23 | /* Watchpoints */ | ||
| 24 | #define XTENSA_BREAKPOINT_LOAD 1 | ||
| 25 | #define XTENSA_BREAKPOINT_STORE 2 | ||
| 26 | |||
| 27 | struct arch_hw_breakpoint { | ||
| 28 | unsigned long address; | ||
| 29 | u16 len; | ||
| 30 | u16 type; | ||
| 31 | }; | ||
| 32 | |||
| 33 | struct perf_event; | ||
| 34 | struct pt_regs; | ||
| 35 | struct task_struct; | ||
| 36 | |||
| 37 | int hw_breakpoint_slots(int type); | ||
| 38 | int arch_check_bp_in_kernelspace(struct perf_event *bp); | ||
| 39 | int arch_validate_hwbkpt_settings(struct perf_event *bp); | ||
| 40 | int hw_breakpoint_exceptions_notify(struct notifier_block *unused, | ||
| 41 | unsigned long val, void *data); | ||
| 42 | |||
| 43 | int arch_install_hw_breakpoint(struct perf_event *bp); | ||
| 44 | void arch_uninstall_hw_breakpoint(struct perf_event *bp); | ||
| 45 | void hw_breakpoint_pmu_read(struct perf_event *bp); | ||
| 46 | int check_hw_breakpoint(struct pt_regs *regs); | ||
| 47 | void clear_ptrace_hw_breakpoint(struct task_struct *tsk); | ||
| 48 | |||
| 49 | #else | ||
| 50 | |||
| 51 | struct task_struct; | ||
| 52 | |||
| 53 | static inline void clear_ptrace_hw_breakpoint(struct task_struct *tsk) | ||
| 54 | { | ||
| 55 | } | ||
| 56 | |||
| 57 | #endif /* CONFIG_HAVE_HW_BREAKPOINT */ | ||
| 58 | #endif /* __ASM_XTENSA_HW_BREAKPOINT_H */ | ||
diff --git a/arch/xtensa/include/asm/irqflags.h b/arch/xtensa/include/asm/irqflags.h index 8e090c709046..407606e576f8 100644 --- a/arch/xtensa/include/asm/irqflags.h +++ b/arch/xtensa/include/asm/irqflags.h | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #define _XTENSA_IRQFLAGS_H | 13 | #define _XTENSA_IRQFLAGS_H |
| 14 | 14 | ||
| 15 | #include <linux/types.h> | 15 | #include <linux/types.h> |
| 16 | #include <asm/processor.h> | ||
| 16 | 17 | ||
| 17 | static inline unsigned long arch_local_save_flags(void) | 18 | static inline unsigned long arch_local_save_flags(void) |
| 18 | { | 19 | { |
diff --git a/arch/xtensa/include/asm/processor.h b/arch/xtensa/include/asm/processor.h index 744ecf0dc3a4..d2e40d39c615 100644 --- a/arch/xtensa/include/asm/processor.h +++ b/arch/xtensa/include/asm/processor.h | |||
| @@ -130,11 +130,10 @@ struct thread_struct { | |||
| 130 | unsigned long bad_vaddr; /* last user fault */ | 130 | unsigned long bad_vaddr; /* last user fault */ |
| 131 | unsigned long bad_uaddr; /* last kernel fault accessing user space */ | 131 | unsigned long bad_uaddr; /* last kernel fault accessing user space */ |
| 132 | unsigned long error_code; | 132 | unsigned long error_code; |
| 133 | 133 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | |
| 134 | unsigned long ibreak[XCHAL_NUM_IBREAK]; | 134 | struct perf_event *ptrace_bp[XCHAL_NUM_IBREAK]; |
| 135 | unsigned long dbreaka[XCHAL_NUM_DBREAK]; | 135 | struct perf_event *ptrace_wp[XCHAL_NUM_DBREAK]; |
| 136 | unsigned long dbreakc[XCHAL_NUM_DBREAK]; | 136 | #endif |
| 137 | |||
| 138 | /* Make structure 16 bytes aligned. */ | 137 | /* Make structure 16 bytes aligned. */ |
| 139 | int align[0] __attribute__ ((aligned(16))); | 138 | int align[0] __attribute__ ((aligned(16))); |
| 140 | }; | 139 | }; |
diff --git a/arch/xtensa/include/asm/regs.h b/arch/xtensa/include/asm/regs.h index 4ba9f516b0e2..881a1134a4b4 100644 --- a/arch/xtensa/include/asm/regs.h +++ b/arch/xtensa/include/asm/regs.h | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | /* Special registers. */ | 28 | /* Special registers. */ |
| 29 | 29 | ||
| 30 | #define SREG_MR 32 | 30 | #define SREG_MR 32 |
| 31 | #define SREG_IBREAKENABLE 96 | ||
| 31 | #define SREG_IBREAKA 128 | 32 | #define SREG_IBREAKA 128 |
| 32 | #define SREG_DBREAKA 144 | 33 | #define SREG_DBREAKA 144 |
| 33 | #define SREG_DBREAKC 160 | 34 | #define SREG_DBREAKC 160 |
| @@ -103,6 +104,8 @@ | |||
| 103 | 104 | ||
| 104 | /* DEBUGCAUSE register fields. */ | 105 | /* DEBUGCAUSE register fields. */ |
| 105 | 106 | ||
| 107 | #define DEBUGCAUSE_DBNUM_MASK 0xf00 | ||
| 108 | #define DEBUGCAUSE_DBNUM_SHIFT 8 /* First bit of DBNUM field */ | ||
| 106 | #define DEBUGCAUSE_DEBUGINT_BIT 5 /* External debug interrupt */ | 109 | #define DEBUGCAUSE_DEBUGINT_BIT 5 /* External debug interrupt */ |
| 107 | #define DEBUGCAUSE_BREAKN_BIT 4 /* BREAK.N instruction */ | 110 | #define DEBUGCAUSE_BREAKN_BIT 4 /* BREAK.N instruction */ |
| 108 | #define DEBUGCAUSE_BREAK_BIT 3 /* BREAK instruction */ | 111 | #define DEBUGCAUSE_BREAK_BIT 3 /* BREAK instruction */ |
diff --git a/arch/xtensa/include/asm/thread_info.h b/arch/xtensa/include/asm/thread_info.h index 9ad12c617184..7be2400f745a 100644 --- a/arch/xtensa/include/asm/thread_info.h +++ b/arch/xtensa/include/asm/thread_info.h | |||
| @@ -111,6 +111,7 @@ static inline struct thread_info *current_thread_info(void) | |||
| 111 | #define TIF_MEMDIE 5 /* is terminating due to OOM killer */ | 111 | #define TIF_MEMDIE 5 /* is terminating due to OOM killer */ |
| 112 | #define TIF_RESTORE_SIGMASK 6 /* restore signal mask in do_signal() */ | 112 | #define TIF_RESTORE_SIGMASK 6 /* restore signal mask in do_signal() */ |
| 113 | #define TIF_NOTIFY_RESUME 7 /* callback before returning to user */ | 113 | #define TIF_NOTIFY_RESUME 7 /* callback before returning to user */ |
| 114 | #define TIF_DB_DISABLED 8 /* debug trap disabled for syscall */ | ||
| 114 | 115 | ||
| 115 | #define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE) | 116 | #define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE) |
| 116 | #define _TIF_SIGPENDING (1<<TIF_SIGPENDING) | 117 | #define _TIF_SIGPENDING (1<<TIF_SIGPENDING) |
diff --git a/arch/xtensa/include/asm/traps.h b/arch/xtensa/include/asm/traps.h index 3ad151aee6af..2e69aa4b843f 100644 --- a/arch/xtensa/include/asm/traps.h +++ b/arch/xtensa/include/asm/traps.h | |||
| @@ -70,6 +70,14 @@ struct debug_table { | |||
| 70 | void (*debug_exception)(void); | 70 | void (*debug_exception)(void); |
| 71 | /* Temporary register save area */ | 71 | /* Temporary register save area */ |
| 72 | unsigned long debug_save[1]; | 72 | unsigned long debug_save[1]; |
| 73 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
| 74 | /* Save area for DBREAKC registers */ | ||
| 75 | unsigned long dbreakc_save[XCHAL_NUM_DBREAK]; | ||
| 76 | /* Saved ICOUNT register */ | ||
| 77 | unsigned long icount_save; | ||
| 78 | /* Saved ICOUNTLEVEL register */ | ||
| 79 | unsigned long icount_level_save; | ||
| 80 | #endif | ||
| 73 | }; | 81 | }; |
| 74 | 82 | ||
| 75 | void debug_exception(void); | 83 | void debug_exception(void); |
diff --git a/arch/xtensa/include/uapi/asm/ptrace.h b/arch/xtensa/include/uapi/asm/ptrace.h index ee17aa842fdf..6ccbd9e38e35 100644 --- a/arch/xtensa/include/uapi/asm/ptrace.h +++ b/arch/xtensa/include/uapi/asm/ptrace.h | |||
| @@ -72,6 +72,8 @@ | |||
| 72 | #define PTRACE_SETREGS 13 | 72 | #define PTRACE_SETREGS 13 |
| 73 | #define PTRACE_GETXTREGS 18 | 73 | #define PTRACE_GETXTREGS 18 |
| 74 | #define PTRACE_SETXTREGS 19 | 74 | #define PTRACE_SETXTREGS 19 |
| 75 | #define PTRACE_GETHBPREGS 20 | ||
| 76 | #define PTRACE_SETHBPREGS 21 | ||
| 75 | 77 | ||
| 76 | 78 | ||
| 77 | #endif /* _UAPI_XTENSA_PTRACE_H */ | 79 | #endif /* _UAPI_XTENSA_PTRACE_H */ |
diff --git a/arch/xtensa/kernel/Makefile b/arch/xtensa/kernel/Makefile index 17fa04dce3ba..c31f5d5afc7d 100644 --- a/arch/xtensa/kernel/Makefile +++ b/arch/xtensa/kernel/Makefile | |||
| @@ -13,6 +13,7 @@ obj-$(CONFIG_MODULES) += xtensa_ksyms.o module.o | |||
| 13 | obj-$(CONFIG_FUNCTION_TRACER) += mcount.o | 13 | obj-$(CONFIG_FUNCTION_TRACER) += mcount.o |
| 14 | obj-$(CONFIG_SMP) += smp.o mxhead.o | 14 | obj-$(CONFIG_SMP) += smp.o mxhead.o |
| 15 | obj-$(CONFIG_XTENSA_VARIANT_HAVE_PERF_EVENTS) += perf_event.o | 15 | obj-$(CONFIG_XTENSA_VARIANT_HAVE_PERF_EVENTS) += perf_event.o |
| 16 | obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o | ||
| 16 | 17 | ||
| 17 | AFLAGS_head.o += -mtext-section-literals | 18 | AFLAGS_head.o += -mtext-section-literals |
| 18 | AFLAGS_mxhead.o += -mtext-section-literals | 19 | AFLAGS_mxhead.o += -mtext-section-literals |
diff --git a/arch/xtensa/kernel/asm-offsets.c b/arch/xtensa/kernel/asm-offsets.c index 8fd46c6404ea..8e10e357ee32 100644 --- a/arch/xtensa/kernel/asm-offsets.c +++ b/arch/xtensa/kernel/asm-offsets.c | |||
| @@ -122,6 +122,12 @@ int main(void) | |||
| 122 | DEFINE(DT_DEBUG_EXCEPTION, | 122 | DEFINE(DT_DEBUG_EXCEPTION, |
| 123 | offsetof(struct debug_table, debug_exception)); | 123 | offsetof(struct debug_table, debug_exception)); |
| 124 | DEFINE(DT_DEBUG_SAVE, offsetof(struct debug_table, debug_save)); | 124 | DEFINE(DT_DEBUG_SAVE, offsetof(struct debug_table, debug_save)); |
| 125 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
| 126 | DEFINE(DT_DBREAKC_SAVE, offsetof(struct debug_table, dbreakc_save)); | ||
| 127 | DEFINE(DT_ICOUNT_SAVE, offsetof(struct debug_table, icount_save)); | ||
| 128 | DEFINE(DT_ICOUNT_LEVEL_SAVE, | ||
| 129 | offsetof(struct debug_table, icount_level_save)); | ||
| 130 | #endif | ||
| 125 | 131 | ||
| 126 | return 0; | 132 | return 0; |
| 127 | } | 133 | } |
diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S index ab7904fd027f..fe8f7e7efb9d 100644 --- a/arch/xtensa/kernel/entry.S +++ b/arch/xtensa/kernel/entry.S | |||
| @@ -543,6 +543,12 @@ common_exception_return: | |||
| 543 | #endif | 543 | #endif |
| 544 | 544 | ||
| 545 | 5: | 545 | 5: |
| 546 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
| 547 | _bbci.l a4, TIF_DB_DISABLED, 7f | ||
| 548 | movi a4, restore_dbreak | ||
| 549 | callx4 a4 | ||
| 550 | 7: | ||
| 551 | #endif | ||
| 546 | #ifdef CONFIG_DEBUG_TLB_SANITY | 552 | #ifdef CONFIG_DEBUG_TLB_SANITY |
| 547 | l32i a4, a1, PT_DEPC | 553 | l32i a4, a1, PT_DEPC |
| 548 | bgeui a4, VALID_DOUBLE_EXCEPTION_ADDRESS, 4f | 554 | bgeui a4, VALID_DOUBLE_EXCEPTION_ADDRESS, 4f |
| @@ -808,6 +814,18 @@ ENTRY(debug_exception) | |||
| 808 | s32i a0, a2, PT_AREG2 | 814 | s32i a0, a2, PT_AREG2 |
| 809 | mov a1, a2 | 815 | mov a1, a2 |
| 810 | 816 | ||
| 817 | /* Debug exception is handled as an exception, so interrupts will | ||
| 818 | * likely be enabled in the common exception handler. Disable | ||
| 819 | * preemption if we have HW breakpoints to preserve DEBUGCAUSE.DBNUM | ||
| 820 | * meaning. | ||
| 821 | */ | ||
| 822 | #if defined(CONFIG_PREEMPT_COUNT) && defined(CONFIG_HAVE_HW_BREAKPOINT) | ||
| 823 | GET_THREAD_INFO(a2, a1) | ||
| 824 | l32i a3, a2, TI_PRE_COUNT | ||
| 825 | addi a3, a3, 1 | ||
| 826 | s32i a3, a2, TI_PRE_COUNT | ||
| 827 | #endif | ||
| 828 | |||
| 811 | rsr a2, ps | 829 | rsr a2, ps |
| 812 | bbsi.l a2, PS_UM_BIT, _user_exception | 830 | bbsi.l a2, PS_UM_BIT, _user_exception |
| 813 | j _kernel_exception | 831 | j _kernel_exception |
| @@ -816,8 +834,60 @@ ENTRY(debug_exception) | |||
| 816 | l32i a2, a2, EXC_TABLE_KSTK # load kernel stack pointer | 834 | l32i a2, a2, EXC_TABLE_KSTK # load kernel stack pointer |
| 817 | j 3b | 835 | j 3b |
| 818 | 836 | ||
| 819 | /* Debug exception while in exception mode. */ | 837 | #ifdef CONFIG_HAVE_HW_BREAKPOINT |
| 838 | /* Debug exception while in exception mode. This may happen when | ||
| 839 | * window overflow/underflow handler or fast exception handler hits | ||
| 840 | * data breakpoint, in which case save and disable all data | ||
| 841 | * breakpoints, single-step faulting instruction and restore data | ||
| 842 | * breakpoints. | ||
| 843 | */ | ||
| 844 | 1: | ||
| 845 | bbci.l a0, PS_UM_BIT, 1b # jump if kernel mode | ||
| 846 | |||
| 847 | rsr a0, debugcause | ||
| 848 | bbsi.l a0, DEBUGCAUSE_DBREAK_BIT, .Ldebug_save_dbreak | ||
| 849 | |||
| 850 | .set _index, 0 | ||
| 851 | .rept XCHAL_NUM_DBREAK | ||
| 852 | l32i a0, a3, DT_DBREAKC_SAVE + _index * 4 | ||
| 853 | wsr a0, SREG_DBREAKC + _index | ||
| 854 | .set _index, _index + 1 | ||
| 855 | .endr | ||
| 856 | |||
| 857 | l32i a0, a3, DT_ICOUNT_LEVEL_SAVE | ||
| 858 | wsr a0, icountlevel | ||
| 859 | |||
| 860 | l32i a0, a3, DT_ICOUNT_SAVE | ||
| 861 | xsr a0, icount | ||
| 862 | |||
| 863 | l32i a0, a3, DT_DEBUG_SAVE | ||
| 864 | xsr a3, SREG_EXCSAVE + XCHAL_DEBUGLEVEL | ||
| 865 | rfi XCHAL_DEBUGLEVEL | ||
| 866 | |||
| 867 | .Ldebug_save_dbreak: | ||
| 868 | .set _index, 0 | ||
| 869 | .rept XCHAL_NUM_DBREAK | ||
| 870 | movi a0, 0 | ||
| 871 | xsr a0, SREG_DBREAKC + _index | ||
| 872 | s32i a0, a3, DT_DBREAKC_SAVE + _index * 4 | ||
| 873 | .set _index, _index + 1 | ||
| 874 | .endr | ||
| 875 | |||
| 876 | movi a0, XCHAL_EXCM_LEVEL + 1 | ||
| 877 | xsr a0, icountlevel | ||
| 878 | s32i a0, a3, DT_ICOUNT_LEVEL_SAVE | ||
| 879 | |||
| 880 | movi a0, 0xfffffffe | ||
| 881 | xsr a0, icount | ||
| 882 | s32i a0, a3, DT_ICOUNT_SAVE | ||
| 883 | |||
| 884 | l32i a0, a3, DT_DEBUG_SAVE | ||
| 885 | xsr a3, SREG_EXCSAVE + XCHAL_DEBUGLEVEL | ||
| 886 | rfi XCHAL_DEBUGLEVEL | ||
| 887 | #else | ||
| 888 | /* Debug exception while in exception mode. Should not happen. */ | ||
| 820 | 1: j 1b // FIXME!! | 889 | 1: j 1b // FIXME!! |
| 890 | #endif | ||
| 821 | 891 | ||
| 822 | ENDPROC(debug_exception) | 892 | ENDPROC(debug_exception) |
| 823 | 893 | ||
diff --git a/arch/xtensa/kernel/hw_breakpoint.c b/arch/xtensa/kernel/hw_breakpoint.c new file mode 100644 index 000000000000..b35656ab7dbd --- /dev/null +++ b/arch/xtensa/kernel/hw_breakpoint.c | |||
| @@ -0,0 +1,317 @@ | |||
| 1 | /* | ||
| 2 | * Xtensa hardware breakpoints/watchpoints handling functions | ||
| 3 | * | ||
| 4 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 5 | * License. See the file "COPYING" in the main directory of this archive | ||
| 6 | * for more details. | ||
| 7 | * | ||
| 8 | * Copyright (C) 2016 Cadence Design Systems Inc. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/hw_breakpoint.h> | ||
| 12 | #include <linux/log2.h> | ||
| 13 | #include <linux/percpu.h> | ||
| 14 | #include <linux/perf_event.h> | ||
| 15 | #include <variant/core.h> | ||
| 16 | |||
| 17 | /* Breakpoint currently in use for each IBREAKA. */ | ||
| 18 | static DEFINE_PER_CPU(struct perf_event *, bp_on_reg[XCHAL_NUM_IBREAK]); | ||
| 19 | |||
| 20 | /* Watchpoint currently in use for each DBREAKA. */ | ||
| 21 | static DEFINE_PER_CPU(struct perf_event *, wp_on_reg[XCHAL_NUM_DBREAK]); | ||
| 22 | |||
| 23 | int hw_breakpoint_slots(int type) | ||
| 24 | { | ||
| 25 | switch (type) { | ||
| 26 | case TYPE_INST: | ||
| 27 | return XCHAL_NUM_IBREAK; | ||
| 28 | case TYPE_DATA: | ||
| 29 | return XCHAL_NUM_DBREAK; | ||
| 30 | default: | ||
| 31 | pr_warn("unknown slot type: %d\n", type); | ||
| 32 | return 0; | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | int arch_check_bp_in_kernelspace(struct perf_event *bp) | ||
| 37 | { | ||
| 38 | unsigned int len; | ||
| 39 | unsigned long va; | ||
| 40 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); | ||
| 41 | |||
| 42 | va = info->address; | ||
| 43 | len = bp->attr.bp_len; | ||
| 44 | |||
| 45 | return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE); | ||
| 46 | } | ||
| 47 | |||
| 48 | /* | ||
| 49 | * Construct an arch_hw_breakpoint from a perf_event. | ||
| 50 | */ | ||
| 51 | static int arch_build_bp_info(struct perf_event *bp) | ||
| 52 | { | ||
| 53 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); | ||
| 54 | |||
| 55 | /* Type */ | ||
| 56 | switch (bp->attr.bp_type) { | ||
| 57 | case HW_BREAKPOINT_X: | ||
| 58 | info->type = XTENSA_BREAKPOINT_EXECUTE; | ||
| 59 | break; | ||
| 60 | case HW_BREAKPOINT_R: | ||
| 61 | info->type = XTENSA_BREAKPOINT_LOAD; | ||
| 62 | break; | ||
| 63 | case HW_BREAKPOINT_W: | ||
| 64 | info->type = XTENSA_BREAKPOINT_STORE; | ||
| 65 | break; | ||
| 66 | case HW_BREAKPOINT_RW: | ||
| 67 | info->type = XTENSA_BREAKPOINT_LOAD | XTENSA_BREAKPOINT_STORE; | ||
| 68 | break; | ||
| 69 | default: | ||
| 70 | return -EINVAL; | ||
| 71 | } | ||
| 72 | |||
| 73 | /* Len */ | ||
| 74 | info->len = bp->attr.bp_len; | ||
| 75 | if (info->len < 1 || info->len > 64 || !is_power_of_2(info->len)) | ||
| 76 | return -EINVAL; | ||
| 77 | |||
| 78 | /* Address */ | ||
| 79 | info->address = bp->attr.bp_addr; | ||
| 80 | if (info->address & (info->len - 1)) | ||
| 81 | return -EINVAL; | ||
| 82 | |||
| 83 | return 0; | ||
| 84 | } | ||
| 85 | |||
| 86 | int arch_validate_hwbkpt_settings(struct perf_event *bp) | ||
| 87 | { | ||
| 88 | int ret; | ||
| 89 | |||
| 90 | /* Build the arch_hw_breakpoint. */ | ||
| 91 | ret = arch_build_bp_info(bp); | ||
| 92 | return ret; | ||
| 93 | } | ||
| 94 | |||
| 95 | int hw_breakpoint_exceptions_notify(struct notifier_block *unused, | ||
| 96 | unsigned long val, void *data) | ||
| 97 | { | ||
| 98 | return NOTIFY_DONE; | ||
| 99 | } | ||
| 100 | |||
| 101 | static void xtensa_wsr(unsigned long v, u8 sr) | ||
| 102 | { | ||
| 103 | /* We don't have indexed wsr and creating instruction dynamically | ||
| 104 | * doesn't seem worth it given how small XCHAL_NUM_IBREAK and | ||
| 105 | * XCHAL_NUM_DBREAK are. Thus the switch. In case build breaks here | ||
| 106 | * the switch below needs to be extended. | ||
| 107 | */ | ||
| 108 | BUILD_BUG_ON(XCHAL_NUM_IBREAK > 2); | ||
| 109 | BUILD_BUG_ON(XCHAL_NUM_DBREAK > 2); | ||
| 110 | |||
| 111 | switch (sr) { | ||
| 112 | #if XCHAL_NUM_IBREAK > 0 | ||
| 113 | case SREG_IBREAKA + 0: | ||
| 114 | WSR(v, SREG_IBREAKA + 0); | ||
| 115 | break; | ||
| 116 | #endif | ||
| 117 | #if XCHAL_NUM_IBREAK > 1 | ||
| 118 | case SREG_IBREAKA + 1: | ||
| 119 | WSR(v, SREG_IBREAKA + 1); | ||
| 120 | break; | ||
| 121 | #endif | ||
| 122 | |||
| 123 | #if XCHAL_NUM_DBREAK > 0 | ||
| 124 | case SREG_DBREAKA + 0: | ||
| 125 | WSR(v, SREG_DBREAKA + 0); | ||
| 126 | break; | ||
| 127 | case SREG_DBREAKC + 0: | ||
| 128 | WSR(v, SREG_DBREAKC + 0); | ||
| 129 | break; | ||
| 130 | #endif | ||
| 131 | #if XCHAL_NUM_DBREAK > 1 | ||
| 132 | case SREG_DBREAKA + 1: | ||
| 133 | WSR(v, SREG_DBREAKA + 1); | ||
| 134 | break; | ||
| 135 | |||
| 136 | case SREG_DBREAKC + 1: | ||
| 137 | WSR(v, SREG_DBREAKC + 1); | ||
| 138 | break; | ||
| 139 | #endif | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | static int alloc_slot(struct perf_event **slot, size_t n, | ||
| 144 | struct perf_event *bp) | ||
| 145 | { | ||
| 146 | size_t i; | ||
| 147 | |||
| 148 | for (i = 0; i < n; ++i) { | ||
| 149 | if (!slot[i]) { | ||
| 150 | slot[i] = bp; | ||
| 151 | return i; | ||
| 152 | } | ||
| 153 | } | ||
| 154 | return -EBUSY; | ||
| 155 | } | ||
| 156 | |||
| 157 | static void set_ibreak_regs(int reg, struct perf_event *bp) | ||
| 158 | { | ||
| 159 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); | ||
| 160 | unsigned long ibreakenable; | ||
| 161 | |||
| 162 | xtensa_wsr(info->address, SREG_IBREAKA + reg); | ||
| 163 | RSR(ibreakenable, SREG_IBREAKENABLE); | ||
| 164 | WSR(ibreakenable | (1 << reg), SREG_IBREAKENABLE); | ||
| 165 | } | ||
| 166 | |||
| 167 | static void set_dbreak_regs(int reg, struct perf_event *bp) | ||
| 168 | { | ||
| 169 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); | ||
| 170 | unsigned long dbreakc = DBREAKC_MASK_MASK & -info->len; | ||
| 171 | |||
| 172 | if (info->type & XTENSA_BREAKPOINT_LOAD) | ||
| 173 | dbreakc |= DBREAKC_LOAD_MASK; | ||
| 174 | if (info->type & XTENSA_BREAKPOINT_STORE) | ||
| 175 | dbreakc |= DBREAKC_STOR_MASK; | ||
| 176 | |||
| 177 | xtensa_wsr(info->address, SREG_DBREAKA + reg); | ||
| 178 | xtensa_wsr(dbreakc, SREG_DBREAKC + reg); | ||
| 179 | } | ||
| 180 | |||
| 181 | int arch_install_hw_breakpoint(struct perf_event *bp) | ||
| 182 | { | ||
| 183 | int i; | ||
| 184 | |||
| 185 | if (counter_arch_bp(bp)->type == XTENSA_BREAKPOINT_EXECUTE) { | ||
| 186 | /* Breakpoint */ | ||
| 187 | i = alloc_slot(this_cpu_ptr(bp_on_reg), XCHAL_NUM_IBREAK, bp); | ||
| 188 | if (i < 0) | ||
| 189 | return i; | ||
| 190 | set_ibreak_regs(i, bp); | ||
| 191 | |||
| 192 | } else { | ||
| 193 | /* Watchpoint */ | ||
| 194 | i = alloc_slot(this_cpu_ptr(wp_on_reg), XCHAL_NUM_DBREAK, bp); | ||
| 195 | if (i < 0) | ||
| 196 | return i; | ||
| 197 | set_dbreak_regs(i, bp); | ||
| 198 | } | ||
| 199 | return 0; | ||
| 200 | } | ||
| 201 | |||
| 202 | static int free_slot(struct perf_event **slot, size_t n, | ||
| 203 | struct perf_event *bp) | ||
| 204 | { | ||
| 205 | size_t i; | ||
| 206 | |||
| 207 | for (i = 0; i < n; ++i) { | ||
| 208 | if (slot[i] == bp) { | ||
| 209 | slot[i] = NULL; | ||
| 210 | return i; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | return -EBUSY; | ||
| 214 | } | ||
| 215 | |||
| 216 | void arch_uninstall_hw_breakpoint(struct perf_event *bp) | ||
| 217 | { | ||
| 218 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); | ||
| 219 | int i; | ||
| 220 | |||
| 221 | if (info->type == XTENSA_BREAKPOINT_EXECUTE) { | ||
| 222 | unsigned long ibreakenable; | ||
| 223 | |||
| 224 | /* Breakpoint */ | ||
| 225 | i = free_slot(this_cpu_ptr(bp_on_reg), XCHAL_NUM_IBREAK, bp); | ||
| 226 | if (i >= 0) { | ||
| 227 | RSR(ibreakenable, SREG_IBREAKENABLE); | ||
| 228 | WSR(ibreakenable & ~(1 << i), SREG_IBREAKENABLE); | ||
| 229 | } | ||
| 230 | } else { | ||
| 231 | /* Watchpoint */ | ||
| 232 | i = free_slot(this_cpu_ptr(wp_on_reg), XCHAL_NUM_DBREAK, bp); | ||
| 233 | if (i >= 0) | ||
| 234 | xtensa_wsr(0, SREG_DBREAKC + i); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | void hw_breakpoint_pmu_read(struct perf_event *bp) | ||
| 239 | { | ||
| 240 | } | ||
| 241 | |||
| 242 | void flush_ptrace_hw_breakpoint(struct task_struct *tsk) | ||
| 243 | { | ||
| 244 | int i; | ||
| 245 | struct thread_struct *t = &tsk->thread; | ||
| 246 | |||
| 247 | for (i = 0; i < XCHAL_NUM_IBREAK; ++i) { | ||
| 248 | if (t->ptrace_bp[i]) { | ||
| 249 | unregister_hw_breakpoint(t->ptrace_bp[i]); | ||
| 250 | t->ptrace_bp[i] = NULL; | ||
| 251 | } | ||
| 252 | } | ||
| 253 | for (i = 0; i < XCHAL_NUM_DBREAK; ++i) { | ||
| 254 | if (t->ptrace_wp[i]) { | ||
| 255 | unregister_hw_breakpoint(t->ptrace_wp[i]); | ||
| 256 | t->ptrace_wp[i] = NULL; | ||
| 257 | } | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | /* | ||
| 262 | * Set ptrace breakpoint pointers to zero for this task. | ||
| 263 | * This is required in order to prevent child processes from unregistering | ||
| 264 | * breakpoints held by their parent. | ||
| 265 | */ | ||
| 266 | void clear_ptrace_hw_breakpoint(struct task_struct *tsk) | ||
| 267 | { | ||
| 268 | memset(tsk->thread.ptrace_bp, 0, sizeof(tsk->thread.ptrace_bp)); | ||
| 269 | memset(tsk->thread.ptrace_wp, 0, sizeof(tsk->thread.ptrace_wp)); | ||
| 270 | } | ||
| 271 | |||
| 272 | void restore_dbreak(void) | ||
| 273 | { | ||
| 274 | int i; | ||
| 275 | |||
| 276 | for (i = 0; i < XCHAL_NUM_DBREAK; ++i) { | ||
| 277 | struct perf_event *bp = this_cpu_ptr(wp_on_reg)[i]; | ||
| 278 | |||
| 279 | if (bp) | ||
| 280 | set_dbreak_regs(i, bp); | ||
| 281 | } | ||
| 282 | clear_thread_flag(TIF_DB_DISABLED); | ||
| 283 | } | ||
| 284 | |||
| 285 | int check_hw_breakpoint(struct pt_regs *regs) | ||
| 286 | { | ||
| 287 | if (regs->debugcause & BIT(DEBUGCAUSE_IBREAK_BIT)) { | ||
| 288 | int i; | ||
| 289 | struct perf_event **bp = this_cpu_ptr(bp_on_reg); | ||
| 290 | |||
| 291 | for (i = 0; i < XCHAL_NUM_IBREAK; ++i) { | ||
| 292 | if (bp[i] && !bp[i]->attr.disabled && | ||
| 293 | regs->pc == bp[i]->attr.bp_addr) | ||
| 294 | perf_bp_event(bp[i], regs); | ||
| 295 | } | ||
| 296 | return 0; | ||
| 297 | } else if (regs->debugcause & BIT(DEBUGCAUSE_DBREAK_BIT)) { | ||
| 298 | struct perf_event **bp = this_cpu_ptr(wp_on_reg); | ||
| 299 | int dbnum = (regs->debugcause & DEBUGCAUSE_DBNUM_MASK) >> | ||
| 300 | DEBUGCAUSE_DBNUM_SHIFT; | ||
| 301 | |||
| 302 | if (dbnum < XCHAL_NUM_DBREAK && bp[dbnum]) { | ||
| 303 | if (user_mode(regs)) { | ||
| 304 | perf_bp_event(bp[dbnum], regs); | ||
| 305 | } else { | ||
| 306 | set_thread_flag(TIF_DB_DISABLED); | ||
| 307 | xtensa_wsr(0, SREG_DBREAKC + dbnum); | ||
| 308 | } | ||
| 309 | } else { | ||
| 310 | WARN_ONCE(1, | ||
| 311 | "Wrong/unconfigured DBNUM reported in DEBUGCAUSE: %d\n", | ||
| 312 | dbnum); | ||
| 313 | } | ||
| 314 | return 0; | ||
| 315 | } | ||
| 316 | return -ENOENT; | ||
| 317 | } | ||
diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c index 1c85323f01d7..5bbfed81c97b 100644 --- a/arch/xtensa/kernel/process.c +++ b/arch/xtensa/kernel/process.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include <linux/unistd.h> | 24 | #include <linux/unistd.h> |
| 25 | #include <linux/ptrace.h> | 25 | #include <linux/ptrace.h> |
| 26 | #include <linux/elf.h> | 26 | #include <linux/elf.h> |
| 27 | #include <linux/hw_breakpoint.h> | ||
| 27 | #include <linux/init.h> | 28 | #include <linux/init.h> |
| 28 | #include <linux/prctl.h> | 29 | #include <linux/prctl.h> |
| 29 | #include <linux/init_task.h> | 30 | #include <linux/init_task.h> |
| @@ -43,6 +44,7 @@ | |||
| 43 | #include <linux/atomic.h> | 44 | #include <linux/atomic.h> |
| 44 | #include <asm/asm-offsets.h> | 45 | #include <asm/asm-offsets.h> |
| 45 | #include <asm/regs.h> | 46 | #include <asm/regs.h> |
| 47 | #include <asm/hw_breakpoint.h> | ||
| 46 | 48 | ||
| 47 | extern void ret_from_fork(void); | 49 | extern void ret_from_fork(void); |
| 48 | extern void ret_from_kernel_thread(void); | 50 | extern void ret_from_kernel_thread(void); |
| @@ -131,6 +133,7 @@ void flush_thread(void) | |||
| 131 | coprocessor_flush_all(ti); | 133 | coprocessor_flush_all(ti); |
| 132 | coprocessor_release_all(ti); | 134 | coprocessor_release_all(ti); |
| 133 | #endif | 135 | #endif |
| 136 | flush_ptrace_hw_breakpoint(current); | ||
| 134 | } | 137 | } |
| 135 | 138 | ||
| 136 | /* | 139 | /* |
| @@ -273,6 +276,8 @@ int copy_thread(unsigned long clone_flags, unsigned long usp_thread_fn, | |||
| 273 | ti->cpenable = 0; | 276 | ti->cpenable = 0; |
| 274 | #endif | 277 | #endif |
| 275 | 278 | ||
| 279 | clear_ptrace_hw_breakpoint(p); | ||
| 280 | |||
| 276 | return 0; | 281 | return 0; |
| 277 | } | 282 | } |
| 278 | 283 | ||
diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c index 4d54b481123b..a651f3a628ee 100644 --- a/arch/xtensa/kernel/ptrace.c +++ b/arch/xtensa/kernel/ptrace.c | |||
| @@ -13,21 +13,23 @@ | |||
| 13 | * Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca> | 13 | * Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca> |
| 14 | */ | 14 | */ |
| 15 | 15 | ||
| 16 | #include <linux/errno.h> | ||
| 17 | #include <linux/hw_breakpoint.h> | ||
| 16 | #include <linux/kernel.h> | 18 | #include <linux/kernel.h> |
| 17 | #include <linux/sched.h> | ||
| 18 | #include <linux/mm.h> | 19 | #include <linux/mm.h> |
| 19 | #include <linux/errno.h> | 20 | #include <linux/perf_event.h> |
| 20 | #include <linux/ptrace.h> | 21 | #include <linux/ptrace.h> |
| 21 | #include <linux/smp.h> | 22 | #include <linux/sched.h> |
| 22 | #include <linux/security.h> | 23 | #include <linux/security.h> |
| 23 | #include <linux/signal.h> | 24 | #include <linux/signal.h> |
| 25 | #include <linux/smp.h> | ||
| 24 | 26 | ||
| 25 | #include <asm/pgtable.h> | 27 | #include <asm/coprocessor.h> |
| 28 | #include <asm/elf.h> | ||
| 26 | #include <asm/page.h> | 29 | #include <asm/page.h> |
| 27 | #include <asm/uaccess.h> | 30 | #include <asm/pgtable.h> |
| 28 | #include <asm/ptrace.h> | 31 | #include <asm/ptrace.h> |
| 29 | #include <asm/elf.h> | 32 | #include <asm/uaccess.h> |
| 30 | #include <asm/coprocessor.h> | ||
| 31 | 33 | ||
| 32 | 34 | ||
| 33 | void user_enable_single_step(struct task_struct *child) | 35 | void user_enable_single_step(struct task_struct *child) |
| @@ -267,6 +269,146 @@ int ptrace_pokeusr(struct task_struct *child, long regno, long val) | |||
| 267 | return 0; | 269 | return 0; |
| 268 | } | 270 | } |
| 269 | 271 | ||
| 272 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
| 273 | static void ptrace_hbptriggered(struct perf_event *bp, | ||
| 274 | struct perf_sample_data *data, | ||
| 275 | struct pt_regs *regs) | ||
| 276 | { | ||
| 277 | int i; | ||
| 278 | siginfo_t info; | ||
| 279 | struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp); | ||
| 280 | |||
| 281 | if (bp->attr.bp_type & HW_BREAKPOINT_X) { | ||
| 282 | for (i = 0; i < XCHAL_NUM_IBREAK; ++i) | ||
| 283 | if (current->thread.ptrace_bp[i] == bp) | ||
| 284 | break; | ||
| 285 | i <<= 1; | ||
| 286 | } else { | ||
| 287 | for (i = 0; i < XCHAL_NUM_DBREAK; ++i) | ||
| 288 | if (current->thread.ptrace_wp[i] == bp) | ||
| 289 | break; | ||
| 290 | i = (i << 1) | 1; | ||
| 291 | } | ||
| 292 | |||
| 293 | info.si_signo = SIGTRAP; | ||
| 294 | info.si_errno = i; | ||
| 295 | info.si_code = TRAP_HWBKPT; | ||
| 296 | info.si_addr = (void __user *)bkpt->address; | ||
| 297 | |||
| 298 | force_sig_info(SIGTRAP, &info, current); | ||
| 299 | } | ||
| 300 | |||
| 301 | static struct perf_event *ptrace_hbp_create(struct task_struct *tsk, int type) | ||
| 302 | { | ||
| 303 | struct perf_event_attr attr; | ||
| 304 | |||
| 305 | ptrace_breakpoint_init(&attr); | ||
| 306 | |||
| 307 | /* Initialise fields to sane defaults. */ | ||
| 308 | attr.bp_addr = 0; | ||
| 309 | attr.bp_len = 1; | ||
| 310 | attr.bp_type = type; | ||
| 311 | attr.disabled = 1; | ||
| 312 | |||
| 313 | return register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL, | ||
| 314 | tsk); | ||
| 315 | } | ||
| 316 | |||
| 317 | /* | ||
| 318 | * Address bit 0 choose instruction (0) or data (1) break register, bits | ||
| 319 | * 31..1 are the register number. | ||
| 320 | * Both PTRACE_GETHBPREGS and PTRACE_SETHBPREGS transfer two 32-bit words: | ||
| 321 | * address (0) and control (1). | ||
| 322 | * Instruction breakpoint contorl word is 0 to clear breakpoint, 1 to set. | ||
| 323 | * Data breakpoint control word bit 31 is 'trigger on store', bit 30 is | ||
| 324 | * 'trigger on load, bits 29..0 are length. Length 0 is used to clear a | ||
| 325 | * breakpoint. To set a breakpoint length must be a power of 2 in the range | ||
| 326 | * 1..64 and the address must be length-aligned. | ||
| 327 | */ | ||
| 328 | |||
| 329 | static long ptrace_gethbpregs(struct task_struct *child, long addr, | ||
| 330 | long __user *datap) | ||
| 331 | { | ||
| 332 | struct perf_event *bp; | ||
| 333 | u32 user_data[2] = {0}; | ||
| 334 | bool dbreak = addr & 1; | ||
| 335 | unsigned idx = addr >> 1; | ||
| 336 | |||
| 337 | if ((!dbreak && idx >= XCHAL_NUM_IBREAK) || | ||
| 338 | (dbreak && idx >= XCHAL_NUM_DBREAK)) | ||
| 339 | return -EINVAL; | ||
| 340 | |||
| 341 | if (dbreak) | ||
| 342 | bp = child->thread.ptrace_wp[idx]; | ||
| 343 | else | ||
| 344 | bp = child->thread.ptrace_bp[idx]; | ||
| 345 | |||
| 346 | if (bp) { | ||
| 347 | user_data[0] = bp->attr.bp_addr; | ||
| 348 | user_data[1] = bp->attr.disabled ? 0 : bp->attr.bp_len; | ||
| 349 | if (dbreak) { | ||
| 350 | if (bp->attr.bp_type & HW_BREAKPOINT_R) | ||
| 351 | user_data[1] |= DBREAKC_LOAD_MASK; | ||
| 352 | if (bp->attr.bp_type & HW_BREAKPOINT_W) | ||
| 353 | user_data[1] |= DBREAKC_STOR_MASK; | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | if (copy_to_user(datap, user_data, sizeof(user_data))) | ||
| 358 | return -EFAULT; | ||
| 359 | |||
| 360 | return 0; | ||
| 361 | } | ||
| 362 | |||
| 363 | static long ptrace_sethbpregs(struct task_struct *child, long addr, | ||
| 364 | long __user *datap) | ||
| 365 | { | ||
| 366 | struct perf_event *bp; | ||
| 367 | struct perf_event_attr attr; | ||
| 368 | u32 user_data[2]; | ||
| 369 | bool dbreak = addr & 1; | ||
| 370 | unsigned idx = addr >> 1; | ||
| 371 | int bp_type = 0; | ||
| 372 | |||
| 373 | if ((!dbreak && idx >= XCHAL_NUM_IBREAK) || | ||
| 374 | (dbreak && idx >= XCHAL_NUM_DBREAK)) | ||
| 375 | return -EINVAL; | ||
| 376 | |||
| 377 | if (copy_from_user(user_data, datap, sizeof(user_data))) | ||
| 378 | return -EFAULT; | ||
| 379 | |||
| 380 | if (dbreak) { | ||
| 381 | bp = child->thread.ptrace_wp[idx]; | ||
| 382 | if (user_data[1] & DBREAKC_LOAD_MASK) | ||
| 383 | bp_type |= HW_BREAKPOINT_R; | ||
| 384 | if (user_data[1] & DBREAKC_STOR_MASK) | ||
| 385 | bp_type |= HW_BREAKPOINT_W; | ||
| 386 | } else { | ||
| 387 | bp = child->thread.ptrace_bp[idx]; | ||
| 388 | bp_type = HW_BREAKPOINT_X; | ||
| 389 | } | ||
| 390 | |||
| 391 | if (!bp) { | ||
| 392 | bp = ptrace_hbp_create(child, | ||
| 393 | bp_type ? bp_type : HW_BREAKPOINT_RW); | ||
| 394 | if (IS_ERR(bp)) | ||
| 395 | return PTR_ERR(bp); | ||
| 396 | if (dbreak) | ||
| 397 | child->thread.ptrace_wp[idx] = bp; | ||
| 398 | else | ||
| 399 | child->thread.ptrace_bp[idx] = bp; | ||
| 400 | } | ||
| 401 | |||
| 402 | attr = bp->attr; | ||
| 403 | attr.bp_addr = user_data[0]; | ||
| 404 | attr.bp_len = user_data[1] & ~(DBREAKC_LOAD_MASK | DBREAKC_STOR_MASK); | ||
| 405 | attr.bp_type = bp_type; | ||
| 406 | attr.disabled = !attr.bp_len; | ||
| 407 | |||
| 408 | return modify_user_hw_breakpoint(bp, &attr); | ||
| 409 | } | ||
| 410 | #endif | ||
| 411 | |||
| 270 | long arch_ptrace(struct task_struct *child, long request, | 412 | long arch_ptrace(struct task_struct *child, long request, |
| 271 | unsigned long addr, unsigned long data) | 413 | unsigned long addr, unsigned long data) |
| 272 | { | 414 | { |
| @@ -307,7 +449,15 @@ long arch_ptrace(struct task_struct *child, long request, | |||
| 307 | case PTRACE_SETXTREGS: | 449 | case PTRACE_SETXTREGS: |
| 308 | ret = ptrace_setxregs(child, datap); | 450 | ret = ptrace_setxregs(child, datap); |
| 309 | break; | 451 | break; |
| 452 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
| 453 | case PTRACE_GETHBPREGS: | ||
| 454 | ret = ptrace_gethbpregs(child, addr, datap); | ||
| 455 | break; | ||
| 310 | 456 | ||
| 457 | case PTRACE_SETHBPREGS: | ||
| 458 | ret = ptrace_sethbpregs(child, addr, datap); | ||
| 459 | break; | ||
| 460 | #endif | ||
| 311 | default: | 461 | default: |
| 312 | ret = ptrace_request(child, request, addr, data); | 462 | ret = ptrace_request(child, request, addr, data); |
| 313 | break; | 463 | break; |
diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index e4764f216a7a..d02fc304b31c 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c | |||
| @@ -39,6 +39,7 @@ | |||
| 39 | #include <asm/pgtable.h> | 39 | #include <asm/pgtable.h> |
| 40 | #include <asm/processor.h> | 40 | #include <asm/processor.h> |
| 41 | #include <asm/traps.h> | 41 | #include <asm/traps.h> |
| 42 | #include <asm/hw_breakpoint.h> | ||
| 42 | 43 | ||
| 43 | /* | 44 | /* |
| 44 | * Machine specific interrupt handlers | 45 | * Machine specific interrupt handlers |
| @@ -338,9 +339,22 @@ do_unaligned_user (struct pt_regs *regs) | |||
| 338 | } | 339 | } |
| 339 | #endif | 340 | #endif |
| 340 | 341 | ||
| 342 | /* Handle debug events. | ||
| 343 | * When CONFIG_HAVE_HW_BREAKPOINT is on this handler is called with | ||
| 344 | * preemption disabled to avoid rescheduling and keep mapping of hardware | ||
| 345 | * breakpoint structures to debug registers intact, so that | ||
| 346 | * DEBUGCAUSE.DBNUM could be used in case of data breakpoint hit. | ||
| 347 | */ | ||
| 341 | void | 348 | void |
| 342 | do_debug(struct pt_regs *regs) | 349 | do_debug(struct pt_regs *regs) |
| 343 | { | 350 | { |
| 351 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
| 352 | int ret = check_hw_breakpoint(regs); | ||
| 353 | |||
| 354 | preempt_enable(); | ||
| 355 | if (ret == 0) | ||
| 356 | return; | ||
| 357 | #endif | ||
| 344 | __die_if_kernel("Breakpoint in kernel", regs, SIGKILL); | 358 | __die_if_kernel("Breakpoint in kernel", regs, SIGKILL); |
| 345 | 359 | ||
| 346 | /* If in user mode, send SIGTRAP signal to current process */ | 360 | /* If in user mode, send SIGTRAP signal to current process */ |
