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 } 372