wp.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <stdlib.h>
  3. #include <sys/ioctl.h>
  4. #include <linux/hw_breakpoint.h>
  5. #include "tests.h"
  6. #include "debug.h"
  7. #include "cloexec.h"
  8. #define WP_TEST_ASSERT_VAL(fd, text, val) \
  9. do { \
  10. long long count; \
  11. wp_read(fd, &count, sizeof(long long)); \
  12. TEST_ASSERT_VAL(text, count == val); \
  13. } while (0)
  14. volatile u64 data1;
  15. volatile u8 data2[3];
  16. static int wp_read(int fd, long long *count, int size)
  17. {
  18. int ret = read(fd, count, size);
  19. if (ret != size) {
  20. pr_debug("failed to read: %d\n", ret);
  21. return -1;
  22. }
  23. return 0;
  24. }
  25. static void get__perf_event_attr(struct perf_event_attr *attr, int wp_type,
  26. void *wp_addr, unsigned long wp_len)
  27. {
  28. memset(attr, 0, sizeof(struct perf_event_attr));
  29. attr->type = PERF_TYPE_BREAKPOINT;
  30. attr->size = sizeof(struct perf_event_attr);
  31. attr->config = 0;
  32. attr->bp_type = wp_type;
  33. attr->bp_addr = (unsigned long)wp_addr;
  34. attr->bp_len = wp_len;
  35. attr->sample_period = 1;
  36. attr->sample_type = PERF_SAMPLE_IP;
  37. attr->exclude_kernel = 1;
  38. attr->exclude_hv = 1;
  39. }
  40. static int __event(int wp_type, void *wp_addr, unsigned long wp_len)
  41. {
  42. int fd;
  43. struct perf_event_attr attr;
  44. get__perf_event_attr(&attr, wp_type, wp_addr, wp_len);
  45. fd = sys_perf_event_open(&attr, 0, -1, -1,
  46. perf_event_open_cloexec_flag());
  47. if (fd < 0)
  48. pr_debug("failed opening event %x\n", attr.bp_type);
  49. return fd;
  50. }
  51. static int wp_ro_test(void)
  52. {
  53. int fd;
  54. unsigned long tmp, tmp1 = rand();
  55. fd = __event(HW_BREAKPOINT_R, (void *)&data1, sizeof(data1));
  56. if (fd < 0)
  57. return -1;
  58. tmp = data1;
  59. WP_TEST_ASSERT_VAL(fd, "RO watchpoint", 1);
  60. data1 = tmp1 + tmp;
  61. WP_TEST_ASSERT_VAL(fd, "RO watchpoint", 1);
  62. close(fd);
  63. return 0;
  64. }
  65. static int wp_wo_test(void)
  66. {
  67. int fd;
  68. unsigned long tmp, tmp1 = rand();
  69. fd = __event(HW_BREAKPOINT_W, (void *)&data1, sizeof(data1));
  70. if (fd < 0)
  71. return -1;
  72. tmp = data1;
  73. WP_TEST_ASSERT_VAL(fd, "WO watchpoint", 0);
  74. data1 = tmp1 + tmp;
  75. WP_TEST_ASSERT_VAL(fd, "WO watchpoint", 1);
  76. close(fd);
  77. return 0;
  78. }
  79. static int wp_rw_test(void)
  80. {
  81. int fd;
  82. unsigned long tmp, tmp1 = rand();
  83. fd = __event(HW_BREAKPOINT_R | HW_BREAKPOINT_W, (void *)&data1,
  84. sizeof(data1));
  85. if (fd < 0)
  86. return -1;
  87. tmp = data1;
  88. WP_TEST_ASSERT_VAL(fd, "RW watchpoint", 1);
  89. data1 = tmp1 + tmp;
  90. WP_TEST_ASSERT_VAL(fd, "RW watchpoint", 2);
  91. close(fd);
  92. return 0;
  93. }
  94. static int wp_modify_test(void)
  95. {
  96. int fd, ret;
  97. unsigned long tmp = rand();
  98. struct perf_event_attr new_attr;
  99. fd = __event(HW_BREAKPOINT_W, (void *)&data1, sizeof(data1));
  100. if (fd < 0)
  101. return -1;
  102. data1 = tmp;
  103. WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 1);
  104. /* Modify watchpoint with disabled = 1 */
  105. get__perf_event_attr(&new_attr, HW_BREAKPOINT_W, (void *)&data2[0],
  106. sizeof(u8) * 2);
  107. new_attr.disabled = 1;
  108. ret = ioctl(fd, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &new_attr);
  109. if (ret < 0) {
  110. pr_debug("ioctl(PERF_EVENT_IOC_MODIFY_ATTRIBUTES) failed\n");
  111. close(fd);
  112. return ret;
  113. }
  114. data2[1] = tmp; /* Not Counted */
  115. WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 1);
  116. /* Enable the event */
  117. ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
  118. if (ret < 0) {
  119. pr_debug("Failed to enable event\n");
  120. close(fd);
  121. return ret;
  122. }
  123. data2[1] = tmp; /* Counted */
  124. WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 2);
  125. data2[2] = tmp; /* Not Counted */
  126. WP_TEST_ASSERT_VAL(fd, "Modify watchpoint", 2);
  127. close(fd);
  128. return 0;
  129. }
  130. static bool wp_ro_supported(void)
  131. {
  132. #if defined (__x86_64__) || defined (__i386__)
  133. return false;
  134. #else
  135. return true;
  136. #endif
  137. }
  138. static void wp_ro_skip_msg(void)
  139. {
  140. #if defined (__x86_64__) || defined (__i386__)
  141. pr_debug("Hardware does not support read only watchpoints.\n");
  142. #endif
  143. }
  144. static struct {
  145. const char *desc;
  146. int (*target_func)(void);
  147. bool (*is_supported)(void);
  148. void (*skip_msg)(void);
  149. } wp_testcase_table[] = {
  150. {
  151. .desc = "Read Only Watchpoint",
  152. .target_func = &wp_ro_test,
  153. .is_supported = &wp_ro_supported,
  154. .skip_msg = &wp_ro_skip_msg,
  155. },
  156. {
  157. .desc = "Write Only Watchpoint",
  158. .target_func = &wp_wo_test,
  159. },
  160. {
  161. .desc = "Read / Write Watchpoint",
  162. .target_func = &wp_rw_test,
  163. },
  164. {
  165. .desc = "Modify Watchpoint",
  166. .target_func = &wp_modify_test,
  167. },
  168. };
  169. int test__wp_subtest_get_nr(void)
  170. {
  171. return (int)ARRAY_SIZE(wp_testcase_table);
  172. }
  173. const char *test__wp_subtest_get_desc(int i)
  174. {
  175. if (i < 0 || i >= (int)ARRAY_SIZE(wp_testcase_table))
  176. return NULL;
  177. return wp_testcase_table[i].desc;
  178. }
  179. int test__wp(struct test *test __maybe_unused, int i)
  180. {
  181. if (i < 0 || i >= (int)ARRAY_SIZE(wp_testcase_table))
  182. return TEST_FAIL;
  183. if (wp_testcase_table[i].is_supported &&
  184. !wp_testcase_table[i].is_supported()) {
  185. wp_testcase_table[i].skip_msg();
  186. return TEST_SKIP;
  187. }
  188. return !wp_testcase_table[i].target_func() ? TEST_OK : TEST_FAIL;
  189. }
  190. /* The s390 so far does not have support for
  191. * instruction breakpoint using the perf_event_open() system call.
  192. */
  193. bool test__wp_is_supported(void)
  194. {
  195. #if defined(__s390x__)
  196. return false;
  197. #else
  198. return true;
  199. #endif
  200. }