1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright 2017, Gustavo Romero, Breno Leitao, Cyril Bur, IBM Corp.
4 *
5 * Force FP, VEC and VSX unavailable exception during transaction in all
6 * possible scenarios regarding the MSR.FP and MSR.VEC state, e.g. when FP
7 * is enable and VEC is disable, when FP is disable and VEC is enable, and
8 * so on. Then we check if the restored state is correctly set for the
9 * FP and VEC registers to the previous state we set just before we entered
10 * in TM, i.e. we check if it corrupts somehow the recheckpointed FP and
11 * VEC/Altivec registers on abortion due to an unavailable exception in TM.
12 * N.B. In this test we do not test all the FP/Altivec/VSX registers for
13 * corruption, but only for registers vs0 and vs32, which are respectively
14 * representatives of FP and VEC/Altivec reg sets.
15 */
16
17 #define _GNU_SOURCE
18 #include <error.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <inttypes.h>
23 #include <stdbool.h>
24 #include <pthread.h>
25 #include <sched.h>
26
27 #include "tm.h"
28
29 #define DEBUG 0
30
31 /* Unavailable exceptions to test in HTM */
32 #define FP_UNA_EXCEPTION 0
33 #define VEC_UNA_EXCEPTION 1
34 #define VSX_UNA_EXCEPTION 2
35
36 #define NUM_EXCEPTIONS 3
37 #define err_at_line(status, errnum, format, ...) \
38 error_at_line(status, errnum, __FILE__, __LINE__, format ##__VA_ARGS__)
39
40 #define pr_warn(code, format, ...) err_at_line(0, code, format, ##__VA_ARGS__)
41 #define pr_err(code, format, ...) err_at_line(1, code, format, ##__VA_ARGS__)
42
43 struct Flags {
44 int touch_fp;
45 int touch_vec;
46 int result;
47 int exception;
48 } flags;
49
expecting_failure(void)50 bool expecting_failure(void)
51 {
52 if (flags.touch_fp && flags.exception == FP_UNA_EXCEPTION)
53 return false;
54
55 if (flags.touch_vec && flags.exception == VEC_UNA_EXCEPTION)
56 return false;
57
58 /*
59 * If both FP and VEC are touched it does not mean that touching VSX
60 * won't raise an exception. However since FP and VEC state are already
61 * correctly loaded, the transaction is not aborted (i.e.
62 * treclaimed/trecheckpointed) and MSR.VSX is just set as 1, so a TM
63 * failure is not expected also in this case.
64 */
65 if ((flags.touch_fp && flags.touch_vec) &&
66 flags.exception == VSX_UNA_EXCEPTION)
67 return false;
68
69 return true;
70 }
71
72 /* Check if failure occurred whilst in transaction. */
is_failure(uint64_t condition_reg)73 bool is_failure(uint64_t condition_reg)
74 {
75 /*
76 * When failure handling occurs, CR0 is set to 0b1010 (0xa). Otherwise
77 * transaction completes without failure and hence reaches out 'tend.'
78 * that sets CR0 to 0b0100 (0x4).
79 */
80 return ((condition_reg >> 28) & 0xa) == 0xa;
81 }
82
tm_una_ping(void * input)83 void *tm_una_ping(void *input)
84 {
85
86 /*
87 * Expected values for vs0 and vs32 after a TM failure. They must never
88 * change, otherwise they got corrupted.
89 */
90 uint64_t high_vs0 = 0x5555555555555555;
91 uint64_t low_vs0 = 0xffffffffffffffff;
92 uint64_t high_vs32 = 0x5555555555555555;
93 uint64_t low_vs32 = 0xffffffffffffffff;
94
95 /* Counter for busy wait */
96 uint64_t counter = 0x1ff000000;
97
98 /*
99 * Variable to keep a copy of CR register content taken just after we
100 * leave the transactional state.
101 */
102 uint64_t cr_ = 0;
103
104 /*
105 * Wait a bit so thread can get its name "ping". This is not important
106 * to reproduce the issue but it's nice to have for systemtap debugging.
107 */
108 if (DEBUG)
109 sleep(1);
110
111 printf("If MSR.FP=%d MSR.VEC=%d: ", flags.touch_fp, flags.touch_vec);
112
113 if (flags.exception != FP_UNA_EXCEPTION &&
114 flags.exception != VEC_UNA_EXCEPTION &&
115 flags.exception != VSX_UNA_EXCEPTION) {
116 printf("No valid exception specified to test.\n");
117 return NULL;
118 }
119
120 asm (
121 /* Prepare to merge low and high. */
122 " mtvsrd 33, %[high_vs0] ;"
123 " mtvsrd 34, %[low_vs0] ;"
124
125 /*
126 * Adjust VS0 expected value after an TM failure,
127 * i.e. vs0 = 0x5555555555555555555FFFFFFFFFFFFFFFF
128 */
129 " xxmrghd 0, 33, 34 ;"
130
131 /*
132 * Adjust VS32 expected value after an TM failure,
133 * i.e. vs32 = 0x5555555555555555555FFFFFFFFFFFFFFFF
134 */
135 " xxmrghd 32, 33, 34 ;"
136
137 /*
138 * Wait an amount of context switches so load_fp and load_vec
139 * overflow and MSR.FP, MSR.VEC, and MSR.VSX become zero (off).
140 */
141 " mtctr %[counter] ;"
142
143 /* Decrement CTR branch if CTR non zero. */
144 "1: bdnz 1b ;"
145
146 /*
147 * Check if we want to touch FP prior to the test in order
148 * to set MSR.FP = 1 before provoking an unavailable
149 * exception in TM.
150 */
151 " cmpldi %[touch_fp], 0 ;"
152 " beq no_fp ;"
153 " fadd 10, 10, 10 ;"
154 "no_fp: ;"
155
156 /*
157 * Check if we want to touch VEC prior to the test in order
158 * to set MSR.VEC = 1 before provoking an unavailable
159 * exception in TM.
160 */
161 " cmpldi %[touch_vec], 0 ;"
162 " beq no_vec ;"
163 " vaddcuw 10, 10, 10 ;"
164 "no_vec: ;"
165
166 /*
167 * Perhaps it would be a better idea to do the
168 * compares outside transactional context and simply
169 * duplicate code.
170 */
171 " tbegin. ;"
172 " beq trans_fail ;"
173
174 /* Do we do FP Unavailable? */
175 " cmpldi %[exception], %[ex_fp] ;"
176 " bne 1f ;"
177 " fadd 10, 10, 10 ;"
178 " b done ;"
179
180 /* Do we do VEC Unavailable? */
181 "1: cmpldi %[exception], %[ex_vec] ;"
182 " bne 2f ;"
183 " vaddcuw 10, 10, 10 ;"
184 " b done ;"
185
186 /*
187 * Not FP or VEC, therefore VSX. Ensure this
188 * instruction always generates a VSX Unavailable.
189 * ISA 3.0 is tricky here.
190 * (xxmrghd will on ISA 2.07 and ISA 3.0)
191 */
192 "2: xxmrghd 10, 10, 10 ;"
193
194 "done: tend. ;"
195
196 "trans_fail: ;"
197
198 /* Give values back to C. */
199 " mfvsrd %[high_vs0], 0 ;"
200 " xxsldwi 3, 0, 0, 2 ;"
201 " mfvsrd %[low_vs0], 3 ;"
202 " mfvsrd %[high_vs32], 32 ;"
203 " xxsldwi 3, 32, 32, 2 ;"
204 " mfvsrd %[low_vs32], 3 ;"
205
206 /* Give CR back to C so that it can check what happened. */
207 " mfcr %[cr_] ;"
208
209 : [high_vs0] "+r" (high_vs0),
210 [low_vs0] "+r" (low_vs0),
211 [high_vs32] "=r" (high_vs32),
212 [low_vs32] "=r" (low_vs32),
213 [cr_] "+r" (cr_)
214 : [touch_fp] "r" (flags.touch_fp),
215 [touch_vec] "r" (flags.touch_vec),
216 [exception] "r" (flags.exception),
217 [ex_fp] "i" (FP_UNA_EXCEPTION),
218 [ex_vec] "i" (VEC_UNA_EXCEPTION),
219 [ex_vsx] "i" (VSX_UNA_EXCEPTION),
220 [counter] "r" (counter)
221
222 : "cr0", "ctr", "v10", "vs0", "vs10", "vs3", "vs32", "vs33",
223 "vs34", "fr10"
224
225 );
226
227 /*
228 * Check if we were expecting a failure and it did not occur by checking
229 * CR0 state just after we leave the transaction. Either way we check if
230 * vs0 or vs32 got corrupted.
231 */
232 if (expecting_failure() && !is_failure(cr_)) {
233 printf("\n\tExpecting the transaction to fail, %s",
234 "but it didn't\n\t");
235 flags.result++;
236 }
237
238 /* Check if we were not expecting a failure and a it occurred. */
239 if (!expecting_failure() && is_failure(cr_) &&
240 !failure_is_reschedule()) {
241 printf("\n\tUnexpected transaction failure 0x%02lx\n\t",
242 failure_code());
243 return (void *) -1;
244 }
245
246 /*
247 * Check if TM failed due to the cause we were expecting. 0xda is a
248 * TM_CAUSE_FAC_UNAV cause, otherwise it's an unexpected cause, unless
249 * it was caused by a reschedule.
250 */
251 if (is_failure(cr_) && !failure_is_unavailable() &&
252 !failure_is_reschedule()) {
253 printf("\n\tUnexpected failure cause 0x%02lx\n\t",
254 failure_code());
255 return (void *) -1;
256 }
257
258 /* 0x4 is a success and 0xa is a fail. See comment in is_failure(). */
259 if (DEBUG)
260 printf("CR0: 0x%1lx ", cr_ >> 28);
261
262 /* Check FP (vs0) for the expected value. */
263 if (high_vs0 != 0x5555555555555555 || low_vs0 != 0xFFFFFFFFFFFFFFFF) {
264 printf("FP corrupted!");
265 printf(" high = %#16" PRIx64 " low = %#16" PRIx64 " ",
266 high_vs0, low_vs0);
267 flags.result++;
268 } else
269 printf("FP ok ");
270
271 /* Check VEC (vs32) for the expected value. */
272 if (high_vs32 != 0x5555555555555555 || low_vs32 != 0xFFFFFFFFFFFFFFFF) {
273 printf("VEC corrupted!");
274 printf(" high = %#16" PRIx64 " low = %#16" PRIx64,
275 high_vs32, low_vs32);
276 flags.result++;
277 } else
278 printf("VEC ok");
279
280 putchar('\n');
281
282 return NULL;
283 }
284
285 /* Thread to force context switch */
tm_una_pong(void * not_used)286 void *tm_una_pong(void *not_used)
287 {
288 /* Wait thread get its name "pong". */
289 if (DEBUG)
290 sleep(1);
291
292 /* Classed as an interactive-like thread. */
293 while (1)
294 sched_yield();
295 }
296
297 /* Function that creates a thread and launches the "ping" task. */
test_fp_vec(int fp,int vec,pthread_attr_t * attr)298 void test_fp_vec(int fp, int vec, pthread_attr_t *attr)
299 {
300 int retries = 2;
301 void *ret_value;
302 pthread_t t0;
303
304 flags.touch_fp = fp;
305 flags.touch_vec = vec;
306
307 /*
308 * Without luck it's possible that the transaction is aborted not due to
309 * the unavailable exception caught in the middle as we expect but also,
310 * for instance, due to a context switch or due to a KVM reschedule (if
311 * it's running on a VM). Thus we try a few times before giving up,
312 * checking if the failure cause is the one we expect.
313 */
314 do {
315 int rc;
316
317 /* Bind to CPU 0, as specified in 'attr'. */
318 rc = pthread_create(&t0, attr, tm_una_ping, (void *) &flags);
319 if (rc)
320 pr_err(rc, "pthread_create()");
321 rc = pthread_setname_np(t0, "tm_una_ping");
322 if (rc)
323 pr_warn(rc, "pthread_setname_np");
324 rc = pthread_join(t0, &ret_value);
325 if (rc)
326 pr_err(rc, "pthread_join");
327
328 retries--;
329 } while (ret_value != NULL && retries);
330
331 if (!retries) {
332 flags.result = 1;
333 if (DEBUG)
334 printf("All transactions failed unexpectedly\n");
335
336 }
337 }
338
tm_unavailable_test(void)339 int tm_unavailable_test(void)
340 {
341 int rc, exception; /* FP = 0, VEC = 1, VSX = 2 */
342 pthread_t t1;
343 pthread_attr_t attr;
344 cpu_set_t cpuset;
345
346 SKIP_IF(!have_htm());
347
348 /* Set only CPU 0 in the mask. Both threads will be bound to CPU 0. */
349 CPU_ZERO(&cpuset);
350 CPU_SET(0, &cpuset);
351
352 /* Init pthread attribute. */
353 rc = pthread_attr_init(&attr);
354 if (rc)
355 pr_err(rc, "pthread_attr_init()");
356
357 /* Set CPU 0 mask into the pthread attribute. */
358 rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
359 if (rc)
360 pr_err(rc, "pthread_attr_setaffinity_np()");
361
362 rc = pthread_create(&t1, &attr /* Bind to CPU 0 */, tm_una_pong, NULL);
363 if (rc)
364 pr_err(rc, "pthread_create()");
365
366 /* Name it for systemtap convenience */
367 rc = pthread_setname_np(t1, "tm_una_pong");
368 if (rc)
369 pr_warn(rc, "pthread_create()");
370
371 flags.result = 0;
372
373 for (exception = 0; exception < NUM_EXCEPTIONS; exception++) {
374 printf("Checking if FP/VEC registers are sane after");
375
376 if (exception == FP_UNA_EXCEPTION)
377 printf(" a FP unavailable exception...\n");
378
379 else if (exception == VEC_UNA_EXCEPTION)
380 printf(" a VEC unavailable exception...\n");
381
382 else
383 printf(" a VSX unavailable exception...\n");
384
385 flags.exception = exception;
386
387 test_fp_vec(0, 0, &attr);
388 test_fp_vec(1, 0, &attr);
389 test_fp_vec(0, 1, &attr);
390 test_fp_vec(1, 1, &attr);
391
392 }
393
394 if (flags.result > 0) {
395 printf("result: failed!\n");
396 exit(1);
397 } else {
398 printf("result: success\n");
399 exit(0);
400 }
401 }
402
main(int argc,char ** argv)403 int main(int argc, char **argv)
404 {
405 test_harness_set_timeout(220);
406 return test_harness(tm_unavailable_test, "tm_unavailable_test");
407 }
408