tm-trap.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright 2017, Gustavo Romero, IBM Corp.
  4. *
  5. * Check if thread endianness is flipped inadvertently to BE on trap
  6. * caught in TM whilst MSR.FP and MSR.VEC are zero (i.e. just after
  7. * load_fp and load_vec overflowed).
  8. *
  9. * The issue can be checked on LE machines simply by zeroing load_fp
  10. * and load_vec and then causing a trap in TM. Since the endianness
  11. * changes to BE on return from the signal handler, 'nop' is
  12. * thread as an illegal instruction in following sequence:
  13. * tbegin.
  14. * beq 1f
  15. * trap
  16. * tend.
  17. * 1: nop
  18. *
  19. * However, although the issue is also present on BE machines, it's a
  20. * bit trickier to check it on BE machines because MSR.LE bit is set
  21. * to zero which determines a BE endianness that is the native
  22. * endianness on BE machines, so nothing notably critical happens,
  23. * i.e. no illegal instruction is observed immediately after returning
  24. * from the signal handler (as it happens on LE machines). Thus to test
  25. * it on BE machines LE endianness is forced after a first trap and then
  26. * the endianness is verified on subsequent traps to determine if the
  27. * endianness "flipped back" to the native endianness (BE).
  28. */
  29. #define _GNU_SOURCE
  30. #include <error.h>
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <unistd.h>
  34. #include <htmintrin.h>
  35. #include <inttypes.h>
  36. #include <pthread.h>
  37. #include <sched.h>
  38. #include <signal.h>
  39. #include <stdbool.h>
  40. #include "tm.h"
  41. #include "utils.h"
  42. #define pr_error(error_code, format, ...) \
  43. error_at_line(1, error_code, __FILE__, __LINE__, format, ##__VA_ARGS__)
  44. #define MSR_LE 1UL
  45. #define LE 1UL
  46. pthread_t t0_ping;
  47. pthread_t t1_pong;
  48. int exit_from_pong;
  49. int trap_event;
  50. int le;
  51. bool success;
  52. void trap_signal_handler(int signo, siginfo_t *si, void *uc)
  53. {
  54. ucontext_t *ucp = uc;
  55. uint64_t thread_endianness;
  56. /* Get thread endianness: extract bit LE from MSR */
  57. thread_endianness = MSR_LE & ucp->uc_mcontext.gp_regs[PT_MSR];
  58. /*
  59. * Little-Endian Machine
  60. */
  61. if (le) {
  62. /* First trap event */
  63. if (trap_event == 0) {
  64. /* Do nothing. Since it is returning from this trap
  65. * event that endianness is flipped by the bug, so just
  66. * let the process return from the signal handler and
  67. * check on the second trap event if endianness is
  68. * flipped or not.
  69. */
  70. }
  71. /* Second trap event */
  72. else if (trap_event == 1) {
  73. /*
  74. * Since trap was caught in TM on first trap event, if
  75. * endianness was still LE (not flipped inadvertently)
  76. * after returning from the signal handler instruction
  77. * (1) is executed (basically a 'nop'), as it's located
  78. * at address of tbegin. +4 (rollback addr). As (1) on
  79. * LE endianness does in effect nothing, instruction (2)
  80. * is then executed again as 'trap', generating a second
  81. * trap event (note that in that case 'trap' is caught
  82. * not in transacional mode). On te other hand, if after
  83. * the return from the signal handler the endianness in-
  84. * advertently flipped, instruction (1) is tread as a
  85. * branch instruction, i.e. b .+8, hence instruction (3)
  86. * and (4) are executed (tbegin.; trap;) and we get sim-
  87. * ilaly on the trap signal handler, but now in TM mode.
  88. * Either way, it's now possible to check the MSR LE bit
  89. * once in the trap handler to verify if endianness was
  90. * flipped or not after the return from the second trap
  91. * event. If endianness is flipped, the bug is present.
  92. * Finally, getting a trap in TM mode or not is just
  93. * worth noting because it affects the math to determine
  94. * the offset added to the NIP on return: the NIP for a
  95. * trap caught in TM is the rollback address, i.e. the
  96. * next instruction after 'tbegin.', whilst the NIP for
  97. * a trap caught in non-transactional mode is the very
  98. * same address of the 'trap' instruction that generated
  99. * the trap event.
  100. */
  101. if (thread_endianness == LE) {
  102. /* Go to 'success', i.e. instruction (6) */
  103. ucp->uc_mcontext.gp_regs[PT_NIP] += 16;
  104. } else {
  105. /*
  106. * Thread endianness is BE, so it flipped
  107. * inadvertently. Thus we flip back to LE and
  108. * set NIP to go to 'failure', instruction (5).
  109. */
  110. ucp->uc_mcontext.gp_regs[PT_MSR] |= 1UL;
  111. ucp->uc_mcontext.gp_regs[PT_NIP] += 4;
  112. }
  113. }
  114. }
  115. /*
  116. * Big-Endian Machine
  117. */
  118. else {
  119. /* First trap event */
  120. if (trap_event == 0) {
  121. /*
  122. * Force thread endianness to be LE. Instructions (1),
  123. * (3), and (4) will be executed, generating a second
  124. * trap in TM mode.
  125. */
  126. ucp->uc_mcontext.gp_regs[PT_MSR] |= 1UL;
  127. }
  128. /* Second trap event */
  129. else if (trap_event == 1) {
  130. /*
  131. * Do nothing. If bug is present on return from this
  132. * second trap event endianness will flip back "automat-
  133. * ically" to BE, otherwise thread endianness will
  134. * continue to be LE, just as it was set above.
  135. */
  136. }
  137. /* A third trap event */
  138. else {
  139. /*
  140. * Once here it means that after returning from the sec-
  141. * ond trap event instruction (4) (trap) was executed
  142. * as LE, generating a third trap event. In that case
  143. * endianness is still LE as set on return from the
  144. * first trap event, hence no bug. Otherwise, bug
  145. * flipped back to BE on return from the second trap
  146. * event and instruction (4) was executed as 'tdi' (so
  147. * basically a 'nop') and branch to 'failure' in
  148. * instruction (5) was taken to indicate failure and we
  149. * never get here.
  150. */
  151. /*
  152. * Flip back to BE and go to instruction (6), i.e. go to
  153. * 'success'.
  154. */
  155. ucp->uc_mcontext.gp_regs[PT_MSR] &= ~1UL;
  156. ucp->uc_mcontext.gp_regs[PT_NIP] += 8;
  157. }
  158. }
  159. trap_event++;
  160. }
  161. void usr1_signal_handler(int signo, siginfo_t *si, void *not_used)
  162. {
  163. /* Got a USR1 signal from ping(), so just tell pong() to exit */
  164. exit_from_pong = 1;
  165. }
  166. void *ping(void *not_used)
  167. {
  168. uint64_t i;
  169. trap_event = 0;
  170. /*
  171. * Wait an amount of context switches so load_fp and load_vec overflows
  172. * and MSR_[FP|VEC|V] is 0.
  173. */
  174. for (i = 0; i < 1024*1024*512; i++)
  175. ;
  176. asm goto(
  177. /*
  178. * [NA] means "Native Endianness", i.e. it tells how a
  179. * instruction is executed on machine's native endianness (in
  180. * other words, native endianness matches kernel endianness).
  181. * [OP] means "Opposite Endianness", i.e. on a BE machine, it
  182. * tells how a instruction is executed as a LE instruction; con-
  183. * versely, on a LE machine, it tells how a instruction is
  184. * executed as a BE instruction. When [NA] is omitted, it means
  185. * that the native interpretation of a given instruction is not
  186. * relevant for the test. Likewise when [OP] is omitted.
  187. */
  188. " tbegin. ;" /* (0) tbegin. [NA] */
  189. " tdi 0, 0, 0x48;" /* (1) nop [NA]; b (3) [OP] */
  190. " trap ;" /* (2) trap [NA] */
  191. ".long 0x1D05007C;" /* (3) tbegin. [OP] */
  192. ".long 0x0800E07F;" /* (4) trap [OP]; nop [NA] */
  193. " b %l[failure] ;" /* (5) b [NA]; MSR.LE flipped (bug) */
  194. " b %l[success] ;" /* (6) b [NA]; MSR.LE did not flip (ok)*/
  195. : : : : failure, success);
  196. failure:
  197. success = false;
  198. goto exit_from_ping;
  199. success:
  200. success = true;
  201. exit_from_ping:
  202. /* Tell pong() to exit before leaving */
  203. pthread_kill(t1_pong, SIGUSR1);
  204. return NULL;
  205. }
  206. void *pong(void *not_used)
  207. {
  208. while (!exit_from_pong)
  209. /*
  210. * Induce context switches on ping() thread
  211. * until ping() finishes its job and signs
  212. * to exit from this loop.
  213. */
  214. sched_yield();
  215. return NULL;
  216. }
  217. int tm_trap_test(void)
  218. {
  219. uint16_t k = 1;
  220. int cpu, rc;
  221. pthread_attr_t attr;
  222. cpu_set_t cpuset;
  223. struct sigaction trap_sa;
  224. SKIP_IF(!have_htm());
  225. SKIP_IF(htm_is_synthetic());
  226. trap_sa.sa_flags = SA_SIGINFO;
  227. trap_sa.sa_sigaction = trap_signal_handler;
  228. sigaction(SIGTRAP, &trap_sa, NULL);
  229. struct sigaction usr1_sa;
  230. usr1_sa.sa_flags = SA_SIGINFO;
  231. usr1_sa.sa_sigaction = usr1_signal_handler;
  232. sigaction(SIGUSR1, &usr1_sa, NULL);
  233. cpu = pick_online_cpu();
  234. FAIL_IF(cpu < 0);
  235. // Set only one CPU in the mask. Both threads will be bound to that CPU.
  236. CPU_ZERO(&cpuset);
  237. CPU_SET(cpu, &cpuset);
  238. /* Init pthread attribute */
  239. rc = pthread_attr_init(&attr);
  240. if (rc)
  241. pr_error(rc, "pthread_attr_init()");
  242. /*
  243. * Bind thread ping() and pong() both to CPU 0 so they ping-pong and
  244. * speed up context switches on ping() thread, speeding up the load_fp
  245. * and load_vec overflow.
  246. */
  247. rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
  248. if (rc)
  249. pr_error(rc, "pthread_attr_setaffinity()");
  250. /* Figure out the machine endianness */
  251. le = (int) *(uint8_t *)&k;
  252. printf("%s machine detected. Checking if endianness flips %s",
  253. le ? "Little-Endian" : "Big-Endian",
  254. "inadvertently on trap in TM... ");
  255. rc = fflush(0);
  256. if (rc)
  257. pr_error(rc, "fflush()");
  258. /* Launch ping() */
  259. rc = pthread_create(&t0_ping, &attr, ping, NULL);
  260. if (rc)
  261. pr_error(rc, "pthread_create()");
  262. exit_from_pong = 0;
  263. /* Launch pong() */
  264. rc = pthread_create(&t1_pong, &attr, pong, NULL);
  265. if (rc)
  266. pr_error(rc, "pthread_create()");
  267. rc = pthread_join(t0_ping, NULL);
  268. if (rc)
  269. pr_error(rc, "pthread_join()");
  270. rc = pthread_join(t1_pong, NULL);
  271. if (rc)
  272. pr_error(rc, "pthread_join()");
  273. if (success) {
  274. printf("no.\n"); /* no, endianness did not flip inadvertently */
  275. return EXIT_SUCCESS;
  276. }
  277. printf("yes!\n"); /* yes, endianness did flip inadvertently */
  278. return EXIT_FAILURE;
  279. }
  280. int main(int argc, char **argv)
  281. {
  282. return test_harness(tm_trap_test, "tm_trap_test");
  283. }