tm-poison.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright 2019, Gustavo Romero, Michael Neuling, IBM Corp.
  4. *
  5. * This test will spawn two processes. Both will be attached to the same
  6. * CPU (CPU 0). The child will be in a loop writing to FP register f31 and
  7. * VMX/VEC/Altivec register vr31 a known value, called poison, calling
  8. * sched_yield syscall after to allow the parent to switch on the CPU.
  9. * Parent will set f31 and vr31 to 1 and in a loop will check if f31 and
  10. * vr31 remain 1 as expected until a given timeout (2m). If the issue is
  11. * present child's poison will leak into parent's f31 or vr31 registers,
  12. * otherwise, poison will never leak into parent's f31 and vr31 registers.
  13. */
  14. #define _GNU_SOURCE
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <unistd.h>
  18. #include <inttypes.h>
  19. #include <sched.h>
  20. #include <sys/types.h>
  21. #include <signal.h>
  22. #include "tm.h"
  23. int tm_poison_test(void)
  24. {
  25. int cpu, pid;
  26. cpu_set_t cpuset;
  27. uint64_t poison = 0xdeadbeefc0dec0fe;
  28. uint64_t unknown = 0;
  29. bool fail_fp = false;
  30. bool fail_vr = false;
  31. SKIP_IF(!have_htm());
  32. SKIP_IF(htm_is_synthetic());
  33. cpu = pick_online_cpu();
  34. FAIL_IF(cpu < 0);
  35. // Attach both Child and Parent to the same CPU
  36. CPU_ZERO(&cpuset);
  37. CPU_SET(cpu, &cpuset);
  38. FAIL_IF(sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0);
  39. pid = fork();
  40. if (!pid) {
  41. /**
  42. * child
  43. */
  44. while (1) {
  45. sched_yield();
  46. asm (
  47. "mtvsrd 31, %[poison];" // f31 = poison
  48. "mtvsrd 63, %[poison];" // vr31 = poison
  49. : : [poison] "r" (poison) : );
  50. }
  51. }
  52. /**
  53. * parent
  54. */
  55. asm (
  56. /*
  57. * Set r3, r4, and f31 to known value 1 before entering
  58. * in transaction. They won't be written after that.
  59. */
  60. " li 3, 0x1 ;"
  61. " li 4, 0x1 ;"
  62. " mtvsrd 31, 4 ;"
  63. /*
  64. * The Time Base (TB) is a 64-bit counter register that is
  65. * independent of the CPU clock and which is incremented
  66. * at a frequency of 512000000 Hz, so every 1.953125ns.
  67. * So it's necessary 120s/0.000000001953125s = 61440000000
  68. * increments to get a 2 minutes timeout. Below we set that
  69. * value in r5 and then use r6 to track initial TB value,
  70. * updating TB values in r7 at every iteration and comparing it
  71. * to r6. When r7 (current) - r6 (initial) > 61440000000 we bail
  72. * out since for sure we spent already 2 minutes in the loop.
  73. * SPR 268 is the TB register.
  74. */
  75. " lis 5, 14 ;"
  76. " ori 5, 5, 19996 ;"
  77. " sldi 5, 5, 16 ;" // r5 = 61440000000
  78. " mfspr 6, 268 ;" // r6 (TB initial)
  79. "1: mfspr 7, 268 ;" // r7 (TB current)
  80. " subf 7, 6, 7 ;" // r7 - r6 > 61440000000 ?
  81. " cmpd 7, 5 ;"
  82. " bgt 3f ;" // yes, exit
  83. /*
  84. * Main loop to check f31
  85. */
  86. " tbegin. ;" // no, try again
  87. " beq 1b ;" // restart if no timeout
  88. " mfvsrd 3, 31 ;" // read f31
  89. " cmpd 3, 4 ;" // f31 == 1 ?
  90. " bne 2f ;" // broken :-(
  91. " tabort. 3 ;" // try another transaction
  92. "2: tend. ;" // commit transaction
  93. "3: mr %[unknown], 3 ;" // record r3
  94. : [unknown] "=r" (unknown)
  95. :
  96. : "cr0", "r3", "r4", "r5", "r6", "r7", "vs31"
  97. );
  98. /*
  99. * On leak 'unknown' will contain 'poison' value from child,
  100. * otherwise (no leak) 'unknown' will contain the same value
  101. * as r3 before entering in transactional mode, i.e. 0x1.
  102. */
  103. fail_fp = unknown != 0x1;
  104. if (fail_fp)
  105. printf("Unknown value %#"PRIx64" leaked into f31!\n", unknown);
  106. else
  107. printf("Good, no poison or leaked value into FP registers\n");
  108. asm (
  109. /*
  110. * Set r3, r4, and vr31 to known value 1 before entering
  111. * in transaction. They won't be written after that.
  112. */
  113. " li 3, 0x1 ;"
  114. " li 4, 0x1 ;"
  115. " mtvsrd 63, 4 ;"
  116. " lis 5, 14 ;"
  117. " ori 5, 5, 19996 ;"
  118. " sldi 5, 5, 16 ;" // r5 = 61440000000
  119. " mfspr 6, 268 ;" // r6 (TB initial)
  120. "1: mfspr 7, 268 ;" // r7 (TB current)
  121. " subf 7, 6, 7 ;" // r7 - r6 > 61440000000 ?
  122. " cmpd 7, 5 ;"
  123. " bgt 3f ;" // yes, exit
  124. /*
  125. * Main loop to check vr31
  126. */
  127. " tbegin. ;" // no, try again
  128. " beq 1b ;" // restart if no timeout
  129. " mfvsrd 3, 63 ;" // read vr31
  130. " cmpd 3, 4 ;" // vr31 == 1 ?
  131. " bne 2f ;" // broken :-(
  132. " tabort. 3 ;" // try another transaction
  133. "2: tend. ;" // commit transaction
  134. "3: mr %[unknown], 3 ;" // record r3
  135. : [unknown] "=r" (unknown)
  136. :
  137. : "cr0", "r3", "r4", "r5", "r6", "r7", "vs63"
  138. );
  139. /*
  140. * On leak 'unknown' will contain 'poison' value from child,
  141. * otherwise (no leak) 'unknown' will contain the same value
  142. * as r3 before entering in transactional mode, i.e. 0x1.
  143. */
  144. fail_vr = unknown != 0x1;
  145. if (fail_vr)
  146. printf("Unknown value %#"PRIx64" leaked into vr31!\n", unknown);
  147. else
  148. printf("Good, no poison or leaked value into VEC registers\n");
  149. kill(pid, SIGKILL);
  150. return (fail_fp | fail_vr);
  151. }
  152. int main(int argc, char *argv[])
  153. {
  154. /* Test completes in about 4m */
  155. test_harness_set_timeout(250);
  156. return test_harness(tm_poison_test, "tm_poison_test");
  157. }