Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xtensa: support hardware breakpoints/watchpoints

Use perf framework to manage hardware instruction and data breakpoints.
Add two new ptrace calls: PTRACE_GETHBPREGS and PTRACE_SETHBPREGS to
query and set instruction and data breakpoints.
Address bit 0 choose instruction (0) or data (1) break register, bits
31..1 are the register number.
Both calls transfer two 32-bit words: address (0) and control (1).
Instruction breakpoint contorl word is 0 to clear breakpoint, 1 to set.
Data breakpoint control word bit 31 is 'trigger on store', bit 30 is
'trigger on load, bits 29..0 are length. Length 0 is used to clear a
breakpoint. To set a breakpoint length must be a power of 2 in the range
1..64 and the address must be length-aligned.

Introduce new thread_info flag: TIF_DB_DISABLED. Set it if debug
exception is raised by the kernel code accessing watched userspace
address and disable corresponding data breakpoint. On exit to userspace
check that flag and, if set, restore all data breakpoints.

Handle debug exceptions raised with PS.EXCM set. This may happen when
window overflow/underflow handler or fast exception handler hits data
breakpoint, in which case save and disable all data breakpoints,
single-step faulting instruction and restore data breakpoints.

Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>

authored by

Max Filippov and committed by
Chris Zankel
c91e02bd 6ec7026a

+651 -15
+1
arch/xtensa/Kconfig
··· 18 18 select HAVE_DMA_ATTRS 19 19 select HAVE_FUNCTION_TRACER 20 20 select HAVE_FUTEX_CMPXCHG if !MMU 21 + select HAVE_HW_BREAKPOINT if PERF_EVENTS 21 22 select HAVE_IRQ_TIME_ACCOUNTING 22 23 select HAVE_OPROFILE 23 24 select HAVE_PERF_EVENTS
+58
arch/xtensa/include/asm/hw_breakpoint.h
··· 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 */
+1
arch/xtensa/include/asm/irqflags.h
··· 13 13 #define _XTENSA_IRQFLAGS_H 14 14 15 15 #include <linux/types.h> 16 + #include <asm/processor.h> 16 17 17 18 static inline unsigned long arch_local_save_flags(void) 18 19 {
+4 -5
arch/xtensa/include/asm/processor.h
··· 130 130 unsigned long bad_vaddr; /* last user fault */ 131 131 unsigned long bad_uaddr; /* last kernel fault accessing user space */ 132 132 unsigned long error_code; 133 - 134 - unsigned long ibreak[XCHAL_NUM_IBREAK]; 135 - unsigned long dbreaka[XCHAL_NUM_DBREAK]; 136 - unsigned long dbreakc[XCHAL_NUM_DBREAK]; 137 - 133 + #ifdef CONFIG_HAVE_HW_BREAKPOINT 134 + struct perf_event *ptrace_bp[XCHAL_NUM_IBREAK]; 135 + struct perf_event *ptrace_wp[XCHAL_NUM_DBREAK]; 136 + #endif 138 137 /* Make structure 16 bytes aligned. */ 139 138 int align[0] __attribute__ ((aligned(16))); 140 139 };
+3
arch/xtensa/include/asm/regs.h
··· 28 28 /* Special registers. */ 29 29 30 30 #define SREG_MR 32 31 + #define SREG_IBREAKENABLE 96 31 32 #define SREG_IBREAKA 128 32 33 #define SREG_DBREAKA 144 33 34 #define SREG_DBREAKC 160 ··· 104 103 105 104 /* DEBUGCAUSE register fields. */ 106 105 106 + #define DEBUGCAUSE_DBNUM_MASK 0xf00 107 + #define DEBUGCAUSE_DBNUM_SHIFT 8 /* First bit of DBNUM field */ 107 108 #define DEBUGCAUSE_DEBUGINT_BIT 5 /* External debug interrupt */ 108 109 #define DEBUGCAUSE_BREAKN_BIT 4 /* BREAK.N instruction */ 109 110 #define DEBUGCAUSE_BREAK_BIT 3 /* BREAK instruction */
+1
arch/xtensa/include/asm/thread_info.h
··· 111 111 #define TIF_MEMDIE 5 /* is terminating due to OOM killer */ 112 112 #define TIF_RESTORE_SIGMASK 6 /* restore signal mask in do_signal() */ 113 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 116 #define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE) 116 117 #define _TIF_SIGPENDING (1<<TIF_SIGPENDING)
+8
arch/xtensa/include/asm/traps.h
··· 70 70 void (*debug_exception)(void); 71 71 /* Temporary register save area */ 72 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 83 void debug_exception(void);
+2
arch/xtensa/include/uapi/asm/ptrace.h
··· 72 72 #define PTRACE_SETREGS 13 73 73 #define PTRACE_GETXTREGS 18 74 74 #define PTRACE_SETXTREGS 19 75 + #define PTRACE_GETHBPREGS 20 76 + #define PTRACE_SETHBPREGS 21 75 77 76 78 77 79 #endif /* _UAPI_XTENSA_PTRACE_H */
+1
arch/xtensa/kernel/Makefile
··· 13 13 obj-$(CONFIG_FUNCTION_TRACER) += mcount.o 14 14 obj-$(CONFIG_SMP) += smp.o mxhead.o 15 15 obj-$(CONFIG_XTENSA_VARIANT_HAVE_PERF_EVENTS) += perf_event.o 16 + obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o 16 17 17 18 AFLAGS_head.o += -mtext-section-literals 18 19 AFLAGS_mxhead.o += -mtext-section-literals
+6
arch/xtensa/kernel/asm-offsets.c
··· 122 122 DEFINE(DT_DEBUG_EXCEPTION, 123 123 offsetof(struct debug_table, debug_exception)); 124 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 132 return 0; 127 133 }
+71 -1
arch/xtensa/kernel/entry.S
··· 543 543 #endif 544 544 545 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 552 #ifdef CONFIG_DEBUG_TLB_SANITY 547 553 l32i a4, a1, PT_DEPC 548 554 bgeui a4, VALID_DOUBLE_EXCEPTION_ADDRESS, 4f ··· 814 808 s32i a0, a2, PT_AREG2 815 809 mov a1, a2 816 810 811 + /* Debug exception is handled as an exception, so interrupts will 812 + * likely be enabled in the common exception handler. Disable 813 + * preemption if we have HW breakpoints to preserve DEBUGCAUSE.DBNUM 814 + * meaning. 815 + */ 816 + #if defined(CONFIG_PREEMPT_COUNT) && defined(CONFIG_HAVE_HW_BREAKPOINT) 817 + GET_THREAD_INFO(a2, a1) 818 + l32i a3, a2, TI_PRE_COUNT 819 + addi a3, a3, 1 820 + s32i a3, a2, TI_PRE_COUNT 821 + #endif 822 + 817 823 rsr a2, ps 818 824 bbsi.l a2, PS_UM_BIT, _user_exception 819 825 j _kernel_exception ··· 834 816 l32i a2, a2, EXC_TABLE_KSTK # load kernel stack pointer 835 817 j 3b 836 818 837 - /* Debug exception while in exception mode. */ 819 + #ifdef CONFIG_HAVE_HW_BREAKPOINT 820 + /* Debug exception while in exception mode. This may happen when 821 + * window overflow/underflow handler or fast exception handler hits 822 + * data breakpoint, in which case save and disable all data 823 + * breakpoints, single-step faulting instruction and restore data 824 + * breakpoints. 825 + */ 826 + 1: 827 + bbci.l a0, PS_UM_BIT, 1b # jump if kernel mode 828 + 829 + rsr a0, debugcause 830 + bbsi.l a0, DEBUGCAUSE_DBREAK_BIT, .Ldebug_save_dbreak 831 + 832 + .set _index, 0 833 + .rept XCHAL_NUM_DBREAK 834 + l32i a0, a3, DT_DBREAKC_SAVE + _index * 4 835 + wsr a0, SREG_DBREAKC + _index 836 + .set _index, _index + 1 837 + .endr 838 + 839 + l32i a0, a3, DT_ICOUNT_LEVEL_SAVE 840 + wsr a0, icountlevel 841 + 842 + l32i a0, a3, DT_ICOUNT_SAVE 843 + xsr a0, icount 844 + 845 + l32i a0, a3, DT_DEBUG_SAVE 846 + xsr a3, SREG_EXCSAVE + XCHAL_DEBUGLEVEL 847 + rfi XCHAL_DEBUGLEVEL 848 + 849 + .Ldebug_save_dbreak: 850 + .set _index, 0 851 + .rept XCHAL_NUM_DBREAK 852 + movi a0, 0 853 + xsr a0, SREG_DBREAKC + _index 854 + s32i a0, a3, DT_DBREAKC_SAVE + _index * 4 855 + .set _index, _index + 1 856 + .endr 857 + 858 + movi a0, XCHAL_EXCM_LEVEL + 1 859 + xsr a0, icountlevel 860 + s32i a0, a3, DT_ICOUNT_LEVEL_SAVE 861 + 862 + movi a0, 0xfffffffe 863 + xsr a0, icount 864 + s32i a0, a3, DT_ICOUNT_SAVE 865 + 866 + l32i a0, a3, DT_DEBUG_SAVE 867 + xsr a3, SREG_EXCSAVE + XCHAL_DEBUGLEVEL 868 + rfi XCHAL_DEBUGLEVEL 869 + #else 870 + /* Debug exception while in exception mode. Should not happen. */ 838 871 1: j 1b // FIXME!! 872 + #endif 839 873 840 874 ENDPROC(debug_exception) 841 875
+317
arch/xtensa/kernel/hw_breakpoint.c
··· 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 + }
+5
arch/xtensa/kernel/process.c
··· 24 24 #include <linux/unistd.h> 25 25 #include <linux/ptrace.h> 26 26 #include <linux/elf.h> 27 + #include <linux/hw_breakpoint.h> 27 28 #include <linux/init.h> 28 29 #include <linux/prctl.h> 29 30 #include <linux/init_task.h> ··· 44 43 #include <linux/atomic.h> 45 44 #include <asm/asm-offsets.h> 46 45 #include <asm/regs.h> 46 + #include <asm/hw_breakpoint.h> 47 47 48 48 extern void ret_from_fork(void); 49 49 extern void ret_from_kernel_thread(void); ··· 133 131 coprocessor_flush_all(ti); 134 132 coprocessor_release_all(ti); 135 133 #endif 134 + flush_ptrace_hw_breakpoint(current); 136 135 } 137 136 138 137 /* ··· 275 272 ti = task_thread_info(p); 276 273 ti->cpenable = 0; 277 274 #endif 275 + 276 + clear_ptrace_hw_breakpoint(p); 278 277 279 278 return 0; 280 279 }
+159 -9
arch/xtensa/kernel/ptrace.c
··· 13 13 * Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca> 14 14 */ 15 15 16 - #include <linux/kernel.h> 17 - #include <linux/sched.h> 18 - #include <linux/mm.h> 19 16 #include <linux/errno.h> 17 + #include <linux/hw_breakpoint.h> 18 + #include <linux/kernel.h> 19 + #include <linux/mm.h> 20 + #include <linux/perf_event.h> 20 21 #include <linux/ptrace.h> 21 - #include <linux/smp.h> 22 + #include <linux/sched.h> 22 23 #include <linux/security.h> 23 24 #include <linux/signal.h> 25 + #include <linux/smp.h> 24 26 25 - #include <asm/pgtable.h> 26 - #include <asm/page.h> 27 - #include <asm/uaccess.h> 28 - #include <asm/ptrace.h> 29 - #include <asm/elf.h> 30 27 #include <asm/coprocessor.h> 28 + #include <asm/elf.h> 29 + #include <asm/page.h> 30 + #include <asm/pgtable.h> 31 + #include <asm/ptrace.h> 32 + #include <asm/uaccess.h> 31 33 32 34 33 35 void user_enable_single_step(struct task_struct *child) ··· 269 267 return 0; 270 268 } 271 269 270 + #ifdef CONFIG_HAVE_HW_BREAKPOINT 271 + static void ptrace_hbptriggered(struct perf_event *bp, 272 + struct perf_sample_data *data, 273 + struct pt_regs *regs) 274 + { 275 + int i; 276 + siginfo_t info; 277 + struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp); 278 + 279 + if (bp->attr.bp_type & HW_BREAKPOINT_X) { 280 + for (i = 0; i < XCHAL_NUM_IBREAK; ++i) 281 + if (current->thread.ptrace_bp[i] == bp) 282 + break; 283 + i <<= 1; 284 + } else { 285 + for (i = 0; i < XCHAL_NUM_DBREAK; ++i) 286 + if (current->thread.ptrace_wp[i] == bp) 287 + break; 288 + i = (i << 1) | 1; 289 + } 290 + 291 + info.si_signo = SIGTRAP; 292 + info.si_errno = i; 293 + info.si_code = TRAP_HWBKPT; 294 + info.si_addr = (void __user *)bkpt->address; 295 + 296 + force_sig_info(SIGTRAP, &info, current); 297 + } 298 + 299 + static struct perf_event *ptrace_hbp_create(struct task_struct *tsk, int type) 300 + { 301 + struct perf_event_attr attr; 302 + 303 + ptrace_breakpoint_init(&attr); 304 + 305 + /* Initialise fields to sane defaults. */ 306 + attr.bp_addr = 0; 307 + attr.bp_len = 1; 308 + attr.bp_type = type; 309 + attr.disabled = 1; 310 + 311 + return register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL, 312 + tsk); 313 + } 314 + 315 + /* 316 + * Address bit 0 choose instruction (0) or data (1) break register, bits 317 + * 31..1 are the register number. 318 + * Both PTRACE_GETHBPREGS and PTRACE_SETHBPREGS transfer two 32-bit words: 319 + * address (0) and control (1). 320 + * Instruction breakpoint contorl word is 0 to clear breakpoint, 1 to set. 321 + * Data breakpoint control word bit 31 is 'trigger on store', bit 30 is 322 + * 'trigger on load, bits 29..0 are length. Length 0 is used to clear a 323 + * breakpoint. To set a breakpoint length must be a power of 2 in the range 324 + * 1..64 and the address must be length-aligned. 325 + */ 326 + 327 + static long ptrace_gethbpregs(struct task_struct *child, long addr, 328 + long __user *datap) 329 + { 330 + struct perf_event *bp; 331 + u32 user_data[2] = {0}; 332 + bool dbreak = addr & 1; 333 + unsigned idx = addr >> 1; 334 + 335 + if ((!dbreak && idx >= XCHAL_NUM_IBREAK) || 336 + (dbreak && idx >= XCHAL_NUM_DBREAK)) 337 + return -EINVAL; 338 + 339 + if (dbreak) 340 + bp = child->thread.ptrace_wp[idx]; 341 + else 342 + bp = child->thread.ptrace_bp[idx]; 343 + 344 + if (bp) { 345 + user_data[0] = bp->attr.bp_addr; 346 + user_data[1] = bp->attr.disabled ? 0 : bp->attr.bp_len; 347 + if (dbreak) { 348 + if (bp->attr.bp_type & HW_BREAKPOINT_R) 349 + user_data[1] |= DBREAKC_LOAD_MASK; 350 + if (bp->attr.bp_type & HW_BREAKPOINT_W) 351 + user_data[1] |= DBREAKC_STOR_MASK; 352 + } 353 + } 354 + 355 + if (copy_to_user(datap, user_data, sizeof(user_data))) 356 + return -EFAULT; 357 + 358 + return 0; 359 + } 360 + 361 + static long ptrace_sethbpregs(struct task_struct *child, long addr, 362 + long __user *datap) 363 + { 364 + struct perf_event *bp; 365 + struct perf_event_attr attr; 366 + u32 user_data[2]; 367 + bool dbreak = addr & 1; 368 + unsigned idx = addr >> 1; 369 + int bp_type = 0; 370 + 371 + if ((!dbreak && idx >= XCHAL_NUM_IBREAK) || 372 + (dbreak && idx >= XCHAL_NUM_DBREAK)) 373 + return -EINVAL; 374 + 375 + if (copy_from_user(user_data, datap, sizeof(user_data))) 376 + return -EFAULT; 377 + 378 + if (dbreak) { 379 + bp = child->thread.ptrace_wp[idx]; 380 + if (user_data[1] & DBREAKC_LOAD_MASK) 381 + bp_type |= HW_BREAKPOINT_R; 382 + if (user_data[1] & DBREAKC_STOR_MASK) 383 + bp_type |= HW_BREAKPOINT_W; 384 + } else { 385 + bp = child->thread.ptrace_bp[idx]; 386 + bp_type = HW_BREAKPOINT_X; 387 + } 388 + 389 + if (!bp) { 390 + bp = ptrace_hbp_create(child, 391 + bp_type ? bp_type : HW_BREAKPOINT_RW); 392 + if (IS_ERR(bp)) 393 + return PTR_ERR(bp); 394 + if (dbreak) 395 + child->thread.ptrace_wp[idx] = bp; 396 + else 397 + child->thread.ptrace_bp[idx] = bp; 398 + } 399 + 400 + attr = bp->attr; 401 + attr.bp_addr = user_data[0]; 402 + attr.bp_len = user_data[1] & ~(DBREAKC_LOAD_MASK | DBREAKC_STOR_MASK); 403 + attr.bp_type = bp_type; 404 + attr.disabled = !attr.bp_len; 405 + 406 + return modify_user_hw_breakpoint(bp, &attr); 407 + } 408 + #endif 409 + 272 410 long arch_ptrace(struct task_struct *child, long request, 273 411 unsigned long addr, unsigned long data) 274 412 { ··· 449 307 case PTRACE_SETXTREGS: 450 308 ret = ptrace_setxregs(child, datap); 451 309 break; 310 + #ifdef CONFIG_HAVE_HW_BREAKPOINT 311 + case PTRACE_GETHBPREGS: 312 + ret = ptrace_gethbpregs(child, addr, datap); 313 + break; 452 314 315 + case PTRACE_SETHBPREGS: 316 + ret = ptrace_sethbpregs(child, addr, datap); 317 + break; 318 + #endif 453 319 default: 454 320 ret = ptrace_request(child, request, addr, data); 455 321 break;
+14
arch/xtensa/kernel/traps.c
··· 39 39 #include <asm/pgtable.h> 40 40 #include <asm/processor.h> 41 41 #include <asm/traps.h> 42 + #include <asm/hw_breakpoint.h> 42 43 43 44 /* 44 45 * Machine specific interrupt handlers ··· 339 338 } 340 339 #endif 341 340 341 + /* Handle debug events. 342 + * When CONFIG_HAVE_HW_BREAKPOINT is on this handler is called with 343 + * preemption disabled to avoid rescheduling and keep mapping of hardware 344 + * breakpoint structures to debug registers intact, so that 345 + * DEBUGCAUSE.DBNUM could be used in case of data breakpoint hit. 346 + */ 342 347 void 343 348 do_debug(struct pt_regs *regs) 344 349 { 350 + #ifdef CONFIG_HAVE_HW_BREAKPOINT 351 + int ret = check_hw_breakpoint(regs); 352 + 353 + preempt_enable(); 354 + if (ret == 0) 355 + return; 356 + #endif 345 357 __die_if_kernel("Breakpoint in kernel", regs, SIGKILL); 346 358 347 359 /* If in user mode, send SIGTRAP signal to current process */