hw_breakpoint.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  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. #include <linux/hw_breakpoint.h>
  11. #include <linux/log2.h>
  12. #include <linux/percpu.h>
  13. #include <linux/perf_event.h>
  14. #include <asm/core.h>
  15. #include <asm/hw_breakpoint.h>
  16. /* Breakpoint currently in use for each IBREAKA. */
  17. static DEFINE_PER_CPU(struct perf_event *, bp_on_reg[XCHAL_NUM_IBREAK]);
  18. /* Watchpoint currently in use for each DBREAKA. */
  19. static DEFINE_PER_CPU(struct perf_event *, wp_on_reg[XCHAL_NUM_DBREAK]);
  20. int hw_breakpoint_slots(int type)
  21. {
  22. switch (type) {
  23. case TYPE_INST:
  24. return XCHAL_NUM_IBREAK;
  25. case TYPE_DATA:
  26. return XCHAL_NUM_DBREAK;
  27. default:
  28. pr_warn("unknown slot type: %d\n", type);
  29. return 0;
  30. }
  31. }
  32. int arch_check_bp_in_kernelspace(struct arch_hw_breakpoint *hw)
  33. {
  34. unsigned int len;
  35. unsigned long va;
  36. va = hw->address;
  37. len = hw->len;
  38. return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE);
  39. }
  40. /*
  41. * Construct an arch_hw_breakpoint from a perf_event.
  42. */
  43. int hw_breakpoint_arch_parse(struct perf_event *bp,
  44. const struct perf_event_attr *attr,
  45. struct arch_hw_breakpoint *hw)
  46. {
  47. /* Type */
  48. switch (attr->bp_type) {
  49. case HW_BREAKPOINT_X:
  50. hw->type = XTENSA_BREAKPOINT_EXECUTE;
  51. break;
  52. case HW_BREAKPOINT_R:
  53. hw->type = XTENSA_BREAKPOINT_LOAD;
  54. break;
  55. case HW_BREAKPOINT_W:
  56. hw->type = XTENSA_BREAKPOINT_STORE;
  57. break;
  58. case HW_BREAKPOINT_RW:
  59. hw->type = XTENSA_BREAKPOINT_LOAD | XTENSA_BREAKPOINT_STORE;
  60. break;
  61. default:
  62. return -EINVAL;
  63. }
  64. /* Len */
  65. hw->len = attr->bp_len;
  66. if (hw->len < 1 || hw->len > 64 || !is_power_of_2(hw->len))
  67. return -EINVAL;
  68. /* Address */
  69. hw->address = attr->bp_addr;
  70. if (hw->address & (hw->len - 1))
  71. return -EINVAL;
  72. return 0;
  73. }
  74. int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
  75. unsigned long val, void *data)
  76. {
  77. return NOTIFY_DONE;
  78. }
  79. static void xtensa_wsr(unsigned long v, u8 sr)
  80. {
  81. /* We don't have indexed wsr and creating instruction dynamically
  82. * doesn't seem worth it given how small XCHAL_NUM_IBREAK and
  83. * XCHAL_NUM_DBREAK are. Thus the switch. In case build breaks here
  84. * the switch below needs to be extended.
  85. */
  86. BUILD_BUG_ON(XCHAL_NUM_IBREAK > 2);
  87. BUILD_BUG_ON(XCHAL_NUM_DBREAK > 2);
  88. switch (sr) {
  89. #if XCHAL_NUM_IBREAK > 0
  90. case SREG_IBREAKA + 0:
  91. xtensa_set_sr(v, SREG_IBREAKA + 0);
  92. break;
  93. #endif
  94. #if XCHAL_NUM_IBREAK > 1
  95. case SREG_IBREAKA + 1:
  96. xtensa_set_sr(v, SREG_IBREAKA + 1);
  97. break;
  98. #endif
  99. #if XCHAL_NUM_DBREAK > 0
  100. case SREG_DBREAKA + 0:
  101. xtensa_set_sr(v, SREG_DBREAKA + 0);
  102. break;
  103. case SREG_DBREAKC + 0:
  104. xtensa_set_sr(v, SREG_DBREAKC + 0);
  105. break;
  106. #endif
  107. #if XCHAL_NUM_DBREAK > 1
  108. case SREG_DBREAKA + 1:
  109. xtensa_set_sr(v, SREG_DBREAKA + 1);
  110. break;
  111. case SREG_DBREAKC + 1:
  112. xtensa_set_sr(v, SREG_DBREAKC + 1);
  113. break;
  114. #endif
  115. }
  116. }
  117. static int alloc_slot(struct perf_event **slot, size_t n,
  118. struct perf_event *bp)
  119. {
  120. size_t i;
  121. for (i = 0; i < n; ++i) {
  122. if (!slot[i]) {
  123. slot[i] = bp;
  124. return i;
  125. }
  126. }
  127. return -EBUSY;
  128. }
  129. static void set_ibreak_regs(int reg, struct perf_event *bp)
  130. {
  131. struct arch_hw_breakpoint *info = counter_arch_bp(bp);
  132. unsigned long ibreakenable;
  133. xtensa_wsr(info->address, SREG_IBREAKA + reg);
  134. ibreakenable = xtensa_get_sr(SREG_IBREAKENABLE);
  135. xtensa_set_sr(ibreakenable | (1 << reg), SREG_IBREAKENABLE);
  136. }
  137. static void set_dbreak_regs(int reg, struct perf_event *bp)
  138. {
  139. struct arch_hw_breakpoint *info = counter_arch_bp(bp);
  140. unsigned long dbreakc = DBREAKC_MASK_MASK & -info->len;
  141. if (info->type & XTENSA_BREAKPOINT_LOAD)
  142. dbreakc |= DBREAKC_LOAD_MASK;
  143. if (info->type & XTENSA_BREAKPOINT_STORE)
  144. dbreakc |= DBREAKC_STOR_MASK;
  145. xtensa_wsr(info->address, SREG_DBREAKA + reg);
  146. xtensa_wsr(dbreakc, SREG_DBREAKC + reg);
  147. }
  148. int arch_install_hw_breakpoint(struct perf_event *bp)
  149. {
  150. int i;
  151. if (counter_arch_bp(bp)->type == XTENSA_BREAKPOINT_EXECUTE) {
  152. /* Breakpoint */
  153. i = alloc_slot(this_cpu_ptr(bp_on_reg), XCHAL_NUM_IBREAK, bp);
  154. if (i < 0)
  155. return i;
  156. set_ibreak_regs(i, bp);
  157. } else {
  158. /* Watchpoint */
  159. i = alloc_slot(this_cpu_ptr(wp_on_reg), XCHAL_NUM_DBREAK, bp);
  160. if (i < 0)
  161. return i;
  162. set_dbreak_regs(i, bp);
  163. }
  164. return 0;
  165. }
  166. static int free_slot(struct perf_event **slot, size_t n,
  167. struct perf_event *bp)
  168. {
  169. size_t i;
  170. for (i = 0; i < n; ++i) {
  171. if (slot[i] == bp) {
  172. slot[i] = NULL;
  173. return i;
  174. }
  175. }
  176. return -EBUSY;
  177. }
  178. void arch_uninstall_hw_breakpoint(struct perf_event *bp)
  179. {
  180. struct arch_hw_breakpoint *info = counter_arch_bp(bp);
  181. int i;
  182. if (info->type == XTENSA_BREAKPOINT_EXECUTE) {
  183. unsigned long ibreakenable;
  184. /* Breakpoint */
  185. i = free_slot(this_cpu_ptr(bp_on_reg), XCHAL_NUM_IBREAK, bp);
  186. if (i >= 0) {
  187. ibreakenable = xtensa_get_sr(SREG_IBREAKENABLE);
  188. xtensa_set_sr(ibreakenable & ~(1 << i),
  189. SREG_IBREAKENABLE);
  190. }
  191. } else {
  192. /* Watchpoint */
  193. i = free_slot(this_cpu_ptr(wp_on_reg), XCHAL_NUM_DBREAK, bp);
  194. if (i >= 0)
  195. xtensa_wsr(0, SREG_DBREAKC + i);
  196. }
  197. }
  198. void hw_breakpoint_pmu_read(struct perf_event *bp)
  199. {
  200. }
  201. void flush_ptrace_hw_breakpoint(struct task_struct *tsk)
  202. {
  203. int i;
  204. struct thread_struct *t = &tsk->thread;
  205. for (i = 0; i < XCHAL_NUM_IBREAK; ++i) {
  206. if (t->ptrace_bp[i]) {
  207. unregister_hw_breakpoint(t->ptrace_bp[i]);
  208. t->ptrace_bp[i] = NULL;
  209. }
  210. }
  211. for (i = 0; i < XCHAL_NUM_DBREAK; ++i) {
  212. if (t->ptrace_wp[i]) {
  213. unregister_hw_breakpoint(t->ptrace_wp[i]);
  214. t->ptrace_wp[i] = NULL;
  215. }
  216. }
  217. }
  218. /*
  219. * Set ptrace breakpoint pointers to zero for this task.
  220. * This is required in order to prevent child processes from unregistering
  221. * breakpoints held by their parent.
  222. */
  223. void clear_ptrace_hw_breakpoint(struct task_struct *tsk)
  224. {
  225. memset(tsk->thread.ptrace_bp, 0, sizeof(tsk->thread.ptrace_bp));
  226. memset(tsk->thread.ptrace_wp, 0, sizeof(tsk->thread.ptrace_wp));
  227. }
  228. void restore_dbreak(void)
  229. {
  230. int i;
  231. for (i = 0; i < XCHAL_NUM_DBREAK; ++i) {
  232. struct perf_event *bp = this_cpu_ptr(wp_on_reg)[i];
  233. if (bp)
  234. set_dbreak_regs(i, bp);
  235. }
  236. clear_thread_flag(TIF_DB_DISABLED);
  237. }
  238. int check_hw_breakpoint(struct pt_regs *regs)
  239. {
  240. if (regs->debugcause & BIT(DEBUGCAUSE_IBREAK_BIT)) {
  241. int i;
  242. struct perf_event **bp = this_cpu_ptr(bp_on_reg);
  243. for (i = 0; i < XCHAL_NUM_IBREAK; ++i) {
  244. if (bp[i] && !bp[i]->attr.disabled &&
  245. regs->pc == bp[i]->attr.bp_addr)
  246. perf_bp_event(bp[i], regs);
  247. }
  248. return 0;
  249. } else if (regs->debugcause & BIT(DEBUGCAUSE_DBREAK_BIT)) {
  250. struct perf_event **bp = this_cpu_ptr(wp_on_reg);
  251. int dbnum = (regs->debugcause & DEBUGCAUSE_DBNUM_MASK) >>
  252. DEBUGCAUSE_DBNUM_SHIFT;
  253. if (dbnum < XCHAL_NUM_DBREAK && bp[dbnum]) {
  254. if (user_mode(regs)) {
  255. perf_bp_event(bp[dbnum], regs);
  256. } else {
  257. set_thread_flag(TIF_DB_DISABLED);
  258. xtensa_wsr(0, SREG_DBREAKC + dbnum);
  259. }
  260. } else {
  261. WARN_ONCE(1,
  262. "Wrong/unconfigured DBNUM reported in DEBUGCAUSE: %d\n",
  263. dbnum);
  264. }
  265. return 0;
  266. }
  267. return -ENOENT;
  268. }