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