diff options
author | Gustavo Romero <gromero@linux.vnet.ibm.com> | 2017-11-01 15:23:41 -0400 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2017-11-08 23:50:21 -0500 |
commit | 77fad8bfb1d2f8225b05e4ea34457875fcfae37e (patch) | |
tree | df158bda5a5542d731403989d1a0005dd1cc3fd3 /tools | |
parent | 80eff6c484799722736471d15ff9cc86b64cae7a (diff) |
selftests/powerpc: Check FP/VEC on exception in TM
Add a self test to check if FP/VEC/VSX registers are sane (restored
correctly) after a FP/VEC/VSX unavailable exception is caught during a
transaction.
This test checks all possibilities in a thread regarding the combination
of MSR.[FP|VEC] states in a thread and for each scenario raises a
FP/VEC/VSX unavailable exception in transactional state, verifying if
vs0 and vs32 registers, which are representatives of FP/VEC/VSX reg
sets, are not corrupted.
Signed-off-by: Gustavo Romero <gromero@linux.vnet.ibm.com>
Signed-off-by: Breno Leitao <leitao@debian.org>
Signed-off-by: Cyril Bur <cyrilbur@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/testing/selftests/powerpc/tm/.gitignore | 1 | ||||
-rw-r--r-- | tools/testing/selftests/powerpc/tm/Makefile | 3 | ||||
-rw-r--r-- | tools/testing/selftests/powerpc/tm/tm-unavailable.c | 371 | ||||
-rw-r--r-- | tools/testing/selftests/powerpc/tm/tm.h | 5 |
4 files changed, 379 insertions, 1 deletions
diff --git a/tools/testing/selftests/powerpc/tm/.gitignore b/tools/testing/selftests/powerpc/tm/.gitignore index 2f1f7b013293..241a4a4ee0e4 100644 --- a/tools/testing/selftests/powerpc/tm/.gitignore +++ b/tools/testing/selftests/powerpc/tm/.gitignore | |||
@@ -12,3 +12,4 @@ tm-signal-context-chk-gpr | |||
12 | tm-signal-context-chk-vmx | 12 | tm-signal-context-chk-vmx |
13 | tm-signal-context-chk-vsx | 13 | tm-signal-context-chk-vsx |
14 | tm-vmx-unavail | 14 | tm-vmx-unavail |
15 | tm-unavailable | ||
diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile index 7bfcd454fb2a..24855c0eee53 100644 --- a/tools/testing/selftests/powerpc/tm/Makefile +++ b/tools/testing/selftests/powerpc/tm/Makefile | |||
@@ -2,7 +2,7 @@ SIGNAL_CONTEXT_CHK_TESTS := tm-signal-context-chk-gpr tm-signal-context-chk-fpu | |||
2 | tm-signal-context-chk-vmx tm-signal-context-chk-vsx | 2 | tm-signal-context-chk-vmx tm-signal-context-chk-vsx |
3 | 3 | ||
4 | TEST_GEN_PROGS := tm-resched-dscr tm-syscall tm-signal-msr-resv tm-signal-stack \ | 4 | TEST_GEN_PROGS := tm-resched-dscr tm-syscall tm-signal-msr-resv tm-signal-stack \ |
5 | tm-vmxcopy tm-fork tm-tar tm-tmspr tm-vmx-unavail \ | 5 | tm-vmxcopy tm-fork tm-tar tm-tmspr tm-vmx-unavail tm-unavailable \ |
6 | $(SIGNAL_CONTEXT_CHK_TESTS) | 6 | $(SIGNAL_CONTEXT_CHK_TESTS) |
7 | 7 | ||
8 | include ../../lib.mk | 8 | include ../../lib.mk |
@@ -16,6 +16,7 @@ $(OUTPUT)/tm-syscall: CFLAGS += -I../../../../../usr/include | |||
16 | $(OUTPUT)/tm-tmspr: CFLAGS += -pthread | 16 | $(OUTPUT)/tm-tmspr: CFLAGS += -pthread |
17 | $(OUTPUT)/tm-vmx-unavail: CFLAGS += -pthread -m64 | 17 | $(OUTPUT)/tm-vmx-unavail: CFLAGS += -pthread -m64 |
18 | $(OUTPUT)/tm-resched-dscr: ../pmu/lib.o | 18 | $(OUTPUT)/tm-resched-dscr: ../pmu/lib.o |
19 | $(OUTPUT)/tm-unavailable: CFLAGS += -O0 -pthread -m64 -Wno-error=uninitialized -mvsx | ||
19 | 20 | ||
20 | SIGNAL_CONTEXT_CHK_TESTS := $(patsubst %,$(OUTPUT)/%,$(SIGNAL_CONTEXT_CHK_TESTS)) | 21 | SIGNAL_CONTEXT_CHK_TESTS := $(patsubst %,$(OUTPUT)/%,$(SIGNAL_CONTEXT_CHK_TESTS)) |
21 | $(SIGNAL_CONTEXT_CHK_TESTS): tm-signal.S | 22 | $(SIGNAL_CONTEXT_CHK_TESTS): tm-signal.S |
diff --git a/tools/testing/selftests/powerpc/tm/tm-unavailable.c b/tools/testing/selftests/powerpc/tm/tm-unavailable.c new file mode 100644 index 000000000000..96c37f84ce54 --- /dev/null +++ b/tools/testing/selftests/powerpc/tm/tm-unavailable.c | |||
@@ -0,0 +1,371 @@ | |||
1 | /* | ||
2 | * Copyright 2017, Gustavo Romero, Breno Leitao, Cyril Bur, IBM Corp. | ||
3 | * Licensed under GPLv2. | ||
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 <stdio.h> | ||
19 | #include <stdlib.h> | ||
20 | #include <unistd.h> | ||
21 | #include <inttypes.h> | ||
22 | #include <stdbool.h> | ||
23 | #include <pthread.h> | ||
24 | #include <sched.h> | ||
25 | |||
26 | #include "tm.h" | ||
27 | |||
28 | #define DEBUG 0 | ||
29 | |||
30 | /* Unavailable exceptions to test in HTM */ | ||
31 | #define FP_UNA_EXCEPTION 0 | ||
32 | #define VEC_UNA_EXCEPTION 1 | ||
33 | #define VSX_UNA_EXCEPTION 2 | ||
34 | |||
35 | #define NUM_EXCEPTIONS 3 | ||
36 | |||
37 | struct Flags { | ||
38 | int touch_fp; | ||
39 | int touch_vec; | ||
40 | int result; | ||
41 | int exception; | ||
42 | } flags; | ||
43 | |||
44 | bool expecting_failure(void) | ||
45 | { | ||
46 | if (flags.touch_fp && flags.exception == FP_UNA_EXCEPTION) | ||
47 | return false; | ||
48 | |||
49 | if (flags.touch_vec && flags.exception == VEC_UNA_EXCEPTION) | ||
50 | return false; | ||
51 | |||
52 | /* | ||
53 | * If both FP and VEC are touched it does not mean that touching VSX | ||
54 | * won't raise an exception. However since FP and VEC state are already | ||
55 | * correctly loaded, the transaction is not aborted (i.e. | ||
56 | * treclaimed/trecheckpointed) and MSR.VSX is just set as 1, so a TM | ||
57 | * failure is not expected also in this case. | ||
58 | */ | ||
59 | if ((flags.touch_fp && flags.touch_vec) && | ||
60 | flags.exception == VSX_UNA_EXCEPTION) | ||
61 | return false; | ||
62 | |||
63 | return true; | ||
64 | } | ||
65 | |||
66 | /* Check if failure occurred whilst in transaction. */ | ||
67 | bool is_failure(uint64_t condition_reg) | ||
68 | { | ||
69 | /* | ||
70 | * When failure handling occurs, CR0 is set to 0b1010 (0xa). Otherwise | ||
71 | * transaction completes without failure and hence reaches out 'tend.' | ||
72 | * that sets CR0 to 0b0100 (0x4). | ||
73 | */ | ||
74 | return ((condition_reg >> 28) & 0xa) == 0xa; | ||
75 | } | ||
76 | |||
77 | void *ping(void *input) | ||
78 | { | ||
79 | |||
80 | /* | ||
81 | * Expected values for vs0 and vs32 after a TM failure. They must never | ||
82 | * change, otherwise they got corrupted. | ||
83 | */ | ||
84 | uint64_t high_vs0 = 0x5555555555555555; | ||
85 | uint64_t low_vs0 = 0xffffffffffffffff; | ||
86 | uint64_t high_vs32 = 0x5555555555555555; | ||
87 | uint64_t low_vs32 = 0xffffffffffffffff; | ||
88 | |||
89 | /* Counter for busy wait */ | ||
90 | uint64_t counter = 0x1ff000000; | ||
91 | |||
92 | /* | ||
93 | * Variable to keep a copy of CR register content taken just after we | ||
94 | * leave the transactional state. | ||
95 | */ | ||
96 | uint64_t cr_ = 0; | ||
97 | |||
98 | /* | ||
99 | * Wait a bit so thread can get its name "ping". This is not important | ||
100 | * to reproduce the issue but it's nice to have for systemtap debugging. | ||
101 | */ | ||
102 | if (DEBUG) | ||
103 | sleep(1); | ||
104 | |||
105 | printf("If MSR.FP=%d MSR.VEC=%d: ", flags.touch_fp, flags.touch_vec); | ||
106 | |||
107 | if (flags.exception != FP_UNA_EXCEPTION && | ||
108 | flags.exception != VEC_UNA_EXCEPTION && | ||
109 | flags.exception != VSX_UNA_EXCEPTION) { | ||
110 | printf("No valid exception specified to test.\n"); | ||
111 | return NULL; | ||
112 | } | ||
113 | |||
114 | asm ( | ||
115 | /* Prepare to merge low and high. */ | ||
116 | " mtvsrd 33, %[high_vs0] ;" | ||
117 | " mtvsrd 34, %[low_vs0] ;" | ||
118 | |||
119 | /* | ||
120 | * Adjust VS0 expected value after an TM failure, | ||
121 | * i.e. vs0 = 0x5555555555555555555FFFFFFFFFFFFFFFF | ||
122 | */ | ||
123 | " xxmrghd 0, 33, 34 ;" | ||
124 | |||
125 | /* | ||
126 | * Adjust VS32 expected value after an TM failure, | ||
127 | * i.e. vs32 = 0x5555555555555555555FFFFFFFFFFFFFFFF | ||
128 | */ | ||
129 | " xxmrghd 32, 33, 34 ;" | ||
130 | |||
131 | /* | ||
132 | * Wait an amount of context switches so load_fp and load_vec | ||
133 | * overflow and MSR.FP, MSR.VEC, and MSR.VSX become zero (off). | ||
134 | */ | ||
135 | " mtctr %[counter] ;" | ||
136 | |||
137 | /* Decrement CTR branch if CTR non zero. */ | ||
138 | "1: bdnz 1b ;" | ||
139 | |||
140 | /* | ||
141 | * Check if we want to touch FP prior to the test in order | ||
142 | * to set MSR.FP = 1 before provoking an unavailable | ||
143 | * exception in TM. | ||
144 | */ | ||
145 | " cmpldi %[touch_fp], 0 ;" | ||
146 | " beq no_fp ;" | ||
147 | " fadd 10, 10, 10 ;" | ||
148 | "no_fp: ;" | ||
149 | |||
150 | /* | ||
151 | * Check if we want to touch VEC prior to the test in order | ||
152 | * to set MSR.VEC = 1 before provoking an unavailable | ||
153 | * exception in TM. | ||
154 | */ | ||
155 | " cmpldi %[touch_vec], 0 ;" | ||
156 | " beq no_vec ;" | ||
157 | " vaddcuw 10, 10, 10 ;" | ||
158 | "no_vec: ;" | ||
159 | |||
160 | /* | ||
161 | * Perhaps it would be a better idea to do the | ||
162 | * compares outside transactional context and simply | ||
163 | * duplicate code. | ||
164 | */ | ||
165 | " tbegin. ;" | ||
166 | " beq trans_fail ;" | ||
167 | |||
168 | /* Do we do FP Unavailable? */ | ||
169 | " cmpldi %[exception], %[ex_fp] ;" | ||
170 | " bne 1f ;" | ||
171 | " fadd 10, 10, 10 ;" | ||
172 | " b done ;" | ||
173 | |||
174 | /* Do we do VEC Unavailable? */ | ||
175 | "1: cmpldi %[exception], %[ex_vec] ;" | ||
176 | " bne 2f ;" | ||
177 | " vaddcuw 10, 10, 10 ;" | ||
178 | " b done ;" | ||
179 | |||
180 | /* | ||
181 | * Not FP or VEC, therefore VSX. Ensure this | ||
182 | * instruction always generates a VSX Unavailable. | ||
183 | * ISA 3.0 is tricky here. | ||
184 | * (xxmrghd will on ISA 2.07 and ISA 3.0) | ||
185 | */ | ||
186 | "2: xxmrghd 10, 10, 10 ;" | ||
187 | |||
188 | "done: tend. ;" | ||
189 | |||
190 | "trans_fail: ;" | ||
191 | |||
192 | /* Give values back to C. */ | ||
193 | " mfvsrd %[high_vs0], 0 ;" | ||
194 | " xxsldwi 3, 0, 0, 2 ;" | ||
195 | " mfvsrd %[low_vs0], 3 ;" | ||
196 | " mfvsrd %[high_vs32], 32 ;" | ||
197 | " xxsldwi 3, 32, 32, 2 ;" | ||
198 | " mfvsrd %[low_vs32], 3 ;" | ||
199 | |||
200 | /* Give CR back to C so that it can check what happened. */ | ||
201 | " mfcr %[cr_] ;" | ||
202 | |||
203 | : [high_vs0] "+r" (high_vs0), | ||
204 | [low_vs0] "+r" (low_vs0), | ||
205 | [high_vs32] "=r" (high_vs32), | ||
206 | [low_vs32] "=r" (low_vs32), | ||
207 | [cr_] "+r" (cr_) | ||
208 | : [touch_fp] "r" (flags.touch_fp), | ||
209 | [touch_vec] "r" (flags.touch_vec), | ||
210 | [exception] "r" (flags.exception), | ||
211 | [ex_fp] "i" (FP_UNA_EXCEPTION), | ||
212 | [ex_vec] "i" (VEC_UNA_EXCEPTION), | ||
213 | [ex_vsx] "i" (VSX_UNA_EXCEPTION), | ||
214 | [counter] "r" (counter) | ||
215 | |||
216 | : "cr0", "ctr", "v10", "vs0", "vs10", "vs3", "vs32", "vs33", | ||
217 | "vs34", "fr10" | ||
218 | |||
219 | ); | ||
220 | |||
221 | /* | ||
222 | * Check if we were expecting a failure and it did not occur by checking | ||
223 | * CR0 state just after we leave the transaction. Either way we check if | ||
224 | * vs0 or vs32 got corrupted. | ||
225 | */ | ||
226 | if (expecting_failure() && !is_failure(cr_)) { | ||
227 | printf("\n\tExpecting the transaction to fail, %s", | ||
228 | "but it didn't\n\t"); | ||
229 | flags.result++; | ||
230 | } | ||
231 | |||
232 | /* Check if we were not expecting a failure and a it occurred. */ | ||
233 | if (!expecting_failure() && is_failure(cr_)) { | ||
234 | printf("\n\tUnexpected transaction failure 0x%02lx\n\t", | ||
235 | failure_code()); | ||
236 | return (void *) -1; | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * Check if TM failed due to the cause we were expecting. 0xda is a | ||
241 | * TM_CAUSE_FAC_UNAV cause, otherwise it's an unexpected cause. | ||
242 | */ | ||
243 | if (is_failure(cr_) && !failure_is_unavailable()) { | ||
244 | printf("\n\tUnexpected failure cause 0x%02lx\n\t", | ||
245 | failure_code()); | ||
246 | return (void *) -1; | ||
247 | } | ||
248 | |||
249 | /* 0x4 is a success and 0xa is a fail. See comment in is_failure(). */ | ||
250 | if (DEBUG) | ||
251 | printf("CR0: 0x%1lx ", cr_ >> 28); | ||
252 | |||
253 | /* Check FP (vs0) for the expected value. */ | ||
254 | if (high_vs0 != 0x5555555555555555 || low_vs0 != 0xFFFFFFFFFFFFFFFF) { | ||
255 | printf("FP corrupted!"); | ||
256 | printf(" high = %#16" PRIx64 " low = %#16" PRIx64 " ", | ||
257 | high_vs0, low_vs0); | ||
258 | flags.result++; | ||
259 | } else | ||
260 | printf("FP ok "); | ||
261 | |||
262 | /* Check VEC (vs32) for the expected value. */ | ||
263 | if (high_vs32 != 0x5555555555555555 || low_vs32 != 0xFFFFFFFFFFFFFFFF) { | ||
264 | printf("VEC corrupted!"); | ||
265 | printf(" high = %#16" PRIx64 " low = %#16" PRIx64, | ||
266 | high_vs32, low_vs32); | ||
267 | flags.result++; | ||
268 | } else | ||
269 | printf("VEC ok"); | ||
270 | |||
271 | putchar('\n'); | ||
272 | |||
273 | return NULL; | ||
274 | } | ||
275 | |||
276 | /* Thread to force context switch */ | ||
277 | void *pong(void *not_used) | ||
278 | { | ||
279 | /* Wait thread get its name "pong". */ | ||
280 | if (DEBUG) | ||
281 | sleep(1); | ||
282 | |||
283 | /* Classed as an interactive-like thread. */ | ||
284 | while (1) | ||
285 | sched_yield(); | ||
286 | } | ||
287 | |||
288 | /* Function that creates a thread and launches the "ping" task. */ | ||
289 | void test_fp_vec(int fp, int vec, pthread_attr_t *attr) | ||
290 | { | ||
291 | int retries = 2; | ||
292 | void *ret_value; | ||
293 | pthread_t t0; | ||
294 | |||
295 | flags.touch_fp = fp; | ||
296 | flags.touch_vec = vec; | ||
297 | |||
298 | /* | ||
299 | * Without luck it's possible that the transaction is aborted not due to | ||
300 | * the unavailable exception caught in the middle as we expect but also, | ||
301 | * for instance, due to a context switch or due to a KVM reschedule (if | ||
302 | * it's running on a VM). Thus we try a few times before giving up, | ||
303 | * checking if the failure cause is the one we expect. | ||
304 | */ | ||
305 | do { | ||
306 | /* Bind 'ping' to CPU 0, as specified in 'attr'. */ | ||
307 | pthread_create(&t0, attr, ping, (void *) &flags); | ||
308 | pthread_setname_np(t0, "ping"); | ||
309 | pthread_join(t0, &ret_value); | ||
310 | retries--; | ||
311 | } while (ret_value != NULL && retries); | ||
312 | |||
313 | if (!retries) { | ||
314 | flags.result = 1; | ||
315 | if (DEBUG) | ||
316 | printf("All transactions failed unexpectedly\n"); | ||
317 | |||
318 | } | ||
319 | } | ||
320 | |||
321 | int main(int argc, char **argv) | ||
322 | { | ||
323 | int exception; /* FP = 0, VEC = 1, VSX = 2 */ | ||
324 | pthread_t t1; | ||
325 | pthread_attr_t attr; | ||
326 | cpu_set_t cpuset; | ||
327 | |||
328 | /* Set only CPU 0 in the mask. Both threads will be bound to CPU 0. */ | ||
329 | CPU_ZERO(&cpuset); | ||
330 | CPU_SET(0, &cpuset); | ||
331 | |||
332 | /* Init pthread attribute. */ | ||
333 | pthread_attr_init(&attr); | ||
334 | |||
335 | /* Set CPU 0 mask into the pthread attribute. */ | ||
336 | pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset); | ||
337 | |||
338 | pthread_create(&t1, &attr /* Bind 'pong' to CPU 0 */, pong, NULL); | ||
339 | pthread_setname_np(t1, "pong"); /* Name it for systemtap convenience */ | ||
340 | |||
341 | flags.result = 0; | ||
342 | |||
343 | for (exception = 0; exception < NUM_EXCEPTIONS; exception++) { | ||
344 | printf("Checking if FP/VEC registers are sane after"); | ||
345 | |||
346 | if (exception == FP_UNA_EXCEPTION) | ||
347 | printf(" a FP unavailable exception...\n"); | ||
348 | |||
349 | else if (exception == VEC_UNA_EXCEPTION) | ||
350 | printf(" a VEC unavailable exception...\n"); | ||
351 | |||
352 | else | ||
353 | printf(" a VSX unavailable exception...\n"); | ||
354 | |||
355 | flags.exception = exception; | ||
356 | |||
357 | test_fp_vec(0, 0, &attr); | ||
358 | test_fp_vec(1, 0, &attr); | ||
359 | test_fp_vec(0, 1, &attr); | ||
360 | test_fp_vec(1, 1, &attr); | ||
361 | |||
362 | } | ||
363 | |||
364 | if (flags.result > 0) { | ||
365 | printf("result: failed!\n"); | ||
366 | exit(1); | ||
367 | } else { | ||
368 | printf("result: success\n"); | ||
369 | exit(0); | ||
370 | } | ||
371 | } | ||
diff --git a/tools/testing/selftests/powerpc/tm/tm.h b/tools/testing/selftests/powerpc/tm/tm.h index 0ffff04433c5..df4204247d45 100644 --- a/tools/testing/selftests/powerpc/tm/tm.h +++ b/tools/testing/selftests/powerpc/tm/tm.h | |||
@@ -47,6 +47,11 @@ static inline bool failure_is_syscall(void) | |||
47 | return (failure_code() & TM_CAUSE_SYSCALL) == TM_CAUSE_SYSCALL; | 47 | return (failure_code() & TM_CAUSE_SYSCALL) == TM_CAUSE_SYSCALL; |
48 | } | 48 | } |
49 | 49 | ||
50 | static inline bool failure_is_unavailable(void) | ||
51 | { | ||
52 | return (failure_code() & TM_CAUSE_FAC_UNAV) == TM_CAUSE_FAC_UNAV; | ||
53 | } | ||
54 | |||
50 | static inline bool failure_is_nesting(void) | 55 | static inline bool failure_is_nesting(void) |
51 | { | 56 | { |
52 | return (__builtin_get_texasru() & 0x400000); | 57 | return (__builtin_get_texasru() & 0x400000); |