wp.c 5.1 KB

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