1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */ 2 3 #include "rseq-bits-template.h" 4 5 #if defined(RSEQ_TEMPLATE_MO_RELAXED) && \ 6 (defined(RSEQ_TEMPLATE_CPU_ID) || defined(RSEQ_TEMPLATE_MM_CID)) 7 8 static inline __attribute__((always_inline)) 9 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_storev)(intptr_t *v, intptr_t expect, intptr_t newv, int cpu) 10 { 11 RSEQ_INJECT_C(9) 12 13 __asm__ __volatile__ goto ( 14 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 15 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) 16 #ifdef RSEQ_COMPARE_TWICE 17 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 18 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) 19 #endif 20 /* Start rseq by storing table entry pointer into rseq_cs. */ 21 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) 22 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) 23 RSEQ_INJECT_ASM(3) 24 LONG_CMP " %[expect], %[v]\n\t" 25 "jnz %l[cmpfail]\n\t" 26 RSEQ_INJECT_ASM(4) 27 #ifdef RSEQ_COMPARE_TWICE 28 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1]) 29 LONG_CMP " %[expect], %[v]\n\t" 30 "jnz %l[error2]\n\t" 31 #endif 32 /* final store */ 33 LONG_S " %[newv], %[v]\n\t" 34 "2:\n\t" 35 RSEQ_INJECT_ASM(5) 36 RSEQ_ASM_DEFINE_ABORT(4, "", abort) 37 : /* gcc asm goto does not allow outputs */ 38 : [cpu_id] "r" (cpu), 39 [current_cpu_id] "m" (rseq_get_abi()->RSEQ_TEMPLATE_CPU_ID_FIELD), 40 [rseq_cs] "m" (rseq_get_abi()->rseq_cs.arch.ptr), 41 [v] "m" (*v), 42 [expect] "r" (expect), 43 [newv] "r" (newv) 44 RSEQ_INJECT_INPUT 45 : "memory", "cc", "r0" 46 RSEQ_INJECT_CLOBBER 47 : abort, cmpfail 48 #ifdef RSEQ_COMPARE_TWICE 49 , error1, error2 50 #endif 51 ); 52 rseq_after_asm_goto(); 53 return 0; 54 abort: 55 rseq_after_asm_goto(); 56 RSEQ_INJECT_FAILED 57 return -1; 58 cmpfail: 59 rseq_after_asm_goto(); 60 return 1; 61 #ifdef RSEQ_COMPARE_TWICE 62 error1: 63 rseq_after_asm_goto(); 64 rseq_bug("cpu_id comparison failed"); 65 error2: 66 rseq_after_asm_goto(); 67 rseq_bug("expected value comparison failed"); 68 #endif 69 } 70 71 /* 72 * Compare @v against @expectnot. When it does _not_ match, load @v 73 * into @load, and store the content of *@v + voffp into @v. 74 */ 75 static inline __attribute__((always_inline)) 76 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpnev_storeoffp_load)(intptr_t *v, intptr_t expectnot, 77 long voffp, intptr_t *load, int cpu) 78 { 79 RSEQ_INJECT_C(9) 80 81 __asm__ __volatile__ goto ( 82 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 83 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) 84 #ifdef RSEQ_COMPARE_TWICE 85 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 86 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) 87 #endif 88 /* Start rseq by storing table entry pointer into rseq_cs. */ 89 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) 90 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) 91 RSEQ_INJECT_ASM(3) 92 LONG_L " %%r1, %[v]\n\t" 93 LONG_CMP_R " %%r1, %[expectnot]\n\t" 94 "je %l[cmpfail]\n\t" 95 RSEQ_INJECT_ASM(4) 96 #ifdef RSEQ_COMPARE_TWICE 97 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1]) 98 LONG_L " %%r1, %[v]\n\t" 99 LONG_CMP_R " %%r1, %[expectnot]\n\t" 100 "je %l[error2]\n\t" 101 #endif 102 LONG_S " %%r1, %[load]\n\t" 103 LONG_ADD_R " %%r1, %[voffp]\n\t" 104 LONG_L " %%r1, 0(%%r1)\n\t" 105 /* final store */ 106 LONG_S " %%r1, %[v]\n\t" 107 "2:\n\t" 108 RSEQ_INJECT_ASM(5) 109 RSEQ_ASM_DEFINE_ABORT(4, "", abort) 110 : /* gcc asm goto does not allow outputs */ 111 : [cpu_id] "r" (cpu), 112 [current_cpu_id] "m" (rseq_get_abi()->RSEQ_TEMPLATE_CPU_ID_FIELD), 113 [rseq_cs] "m" (rseq_get_abi()->rseq_cs.arch.ptr), 114 /* final store input */ 115 [v] "m" (*v), 116 [expectnot] "r" (expectnot), 117 [voffp] "r" (voffp), 118 [load] "m" (*load) 119 RSEQ_INJECT_INPUT 120 : "memory", "cc", "r0", "r1" 121 RSEQ_INJECT_CLOBBER 122 : abort, cmpfail 123 #ifdef RSEQ_COMPARE_TWICE 124 , error1, error2 125 #endif 126 ); 127 rseq_after_asm_goto(); 128 return 0; 129 abort: 130 rseq_after_asm_goto(); 131 RSEQ_INJECT_FAILED 132 return -1; 133 cmpfail: 134 rseq_after_asm_goto(); 135 return 1; 136 #ifdef RSEQ_COMPARE_TWICE 137 error1: 138 rseq_after_asm_goto(); 139 rseq_bug("cpu_id comparison failed"); 140 error2: 141 rseq_after_asm_goto(); 142 rseq_bug("expected value comparison failed"); 143 #endif 144 } 145 146 static inline __attribute__((always_inline)) 147 int RSEQ_TEMPLATE_IDENTIFIER(rseq_addv)(intptr_t *v, intptr_t count, int cpu) 148 { 149 RSEQ_INJECT_C(9) 150 151 __asm__ __volatile__ goto ( 152 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 153 #ifdef RSEQ_COMPARE_TWICE 154 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 155 #endif 156 /* Start rseq by storing table entry pointer into rseq_cs. */ 157 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) 158 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) 159 RSEQ_INJECT_ASM(3) 160 #ifdef RSEQ_COMPARE_TWICE 161 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1]) 162 #endif 163 LONG_L " %%r0, %[v]\n\t" 164 LONG_ADD_R " %%r0, %[count]\n\t" 165 /* final store */ 166 LONG_S " %%r0, %[v]\n\t" 167 "2:\n\t" 168 RSEQ_INJECT_ASM(4) 169 RSEQ_ASM_DEFINE_ABORT(4, "", abort) 170 : /* gcc asm goto does not allow outputs */ 171 : [cpu_id] "r" (cpu), 172 [current_cpu_id] "m" (rseq_get_abi()->RSEQ_TEMPLATE_CPU_ID_FIELD), 173 [rseq_cs] "m" (rseq_get_abi()->rseq_cs.arch.ptr), 174 /* final store input */ 175 [v] "m" (*v), 176 [count] "r" (count) 177 RSEQ_INJECT_INPUT 178 : "memory", "cc", "r0" 179 RSEQ_INJECT_CLOBBER 180 : abort 181 #ifdef RSEQ_COMPARE_TWICE 182 , error1 183 #endif 184 ); 185 rseq_after_asm_goto(); 186 return 0; 187 abort: 188 rseq_after_asm_goto(); 189 RSEQ_INJECT_FAILED 190 return -1; 191 #ifdef RSEQ_COMPARE_TWICE 192 error1: 193 rseq_after_asm_goto(); 194 rseq_bug("cpu_id comparison failed"); 195 #endif 196 } 197 198 static inline __attribute__((always_inline)) 199 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_cmpeqv_storev)(intptr_t *v, intptr_t expect, 200 intptr_t *v2, intptr_t expect2, 201 intptr_t newv, int cpu) 202 { 203 RSEQ_INJECT_C(9) 204 205 __asm__ __volatile__ goto ( 206 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 207 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) 208 #ifdef RSEQ_COMPARE_TWICE 209 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 210 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) 211 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error3]) 212 #endif 213 /* Start rseq by storing table entry pointer into rseq_cs. */ 214 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) 215 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) 216 RSEQ_INJECT_ASM(3) 217 LONG_CMP " %[expect], %[v]\n\t" 218 "jnz %l[cmpfail]\n\t" 219 RSEQ_INJECT_ASM(4) 220 LONG_CMP " %[expect2], %[v2]\n\t" 221 "jnz %l[cmpfail]\n\t" 222 RSEQ_INJECT_ASM(5) 223 #ifdef RSEQ_COMPARE_TWICE 224 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1]) 225 LONG_CMP " %[expect], %[v]\n\t" 226 "jnz %l[error2]\n\t" 227 LONG_CMP " %[expect2], %[v2]\n\t" 228 "jnz %l[error3]\n\t" 229 #endif 230 /* final store */ 231 LONG_S " %[newv], %[v]\n\t" 232 "2:\n\t" 233 RSEQ_INJECT_ASM(6) 234 RSEQ_ASM_DEFINE_ABORT(4, "", abort) 235 : /* gcc asm goto does not allow outputs */ 236 : [cpu_id] "r" (cpu), 237 [current_cpu_id] "m" (rseq_get_abi()->RSEQ_TEMPLATE_CPU_ID_FIELD), 238 [rseq_cs] "m" (rseq_get_abi()->rseq_cs.arch.ptr), 239 /* cmp2 input */ 240 [v2] "m" (*v2), 241 [expect2] "r" (expect2), 242 /* final store input */ 243 [v] "m" (*v), 244 [expect] "r" (expect), 245 [newv] "r" (newv) 246 RSEQ_INJECT_INPUT 247 : "memory", "cc", "r0" 248 RSEQ_INJECT_CLOBBER 249 : abort, cmpfail 250 #ifdef RSEQ_COMPARE_TWICE 251 , error1, error2, error3 252 #endif 253 ); 254 rseq_after_asm_goto(); 255 return 0; 256 abort: 257 rseq_after_asm_goto(); 258 RSEQ_INJECT_FAILED 259 return -1; 260 cmpfail: 261 rseq_after_asm_goto(); 262 return 1; 263 #ifdef RSEQ_COMPARE_TWICE 264 error1: 265 rseq_after_asm_goto(); 266 rseq_bug("cpu_id comparison failed"); 267 error2: 268 rseq_after_asm_goto(); 269 rseq_bug("1st expected value comparison failed"); 270 error3: 271 rseq_after_asm_goto(); 272 rseq_bug("2nd expected value comparison failed"); 273 #endif 274 } 275 276 #endif /* #if defined(RSEQ_TEMPLATE_MO_RELAXED) && 277 (defined(RSEQ_TEMPLATE_CPU_ID) || defined(RSEQ_TEMPLATE_MM_CID)) */ 278 279 #if (defined(RSEQ_TEMPLATE_MO_RELAXED) || defined(RSEQ_TEMPLATE_MO_RELEASE)) && \ 280 (defined(RSEQ_TEMPLATE_CPU_ID) || defined(RSEQ_TEMPLATE_MM_CID)) 281 282 /* s390 is TSO. */ 283 static inline __attribute__((always_inline)) 284 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_trystorev_storev)(intptr_t *v, intptr_t expect, 285 intptr_t *v2, intptr_t newv2, 286 intptr_t newv, int cpu) 287 { 288 RSEQ_INJECT_C(9) 289 290 __asm__ __volatile__ goto ( 291 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 292 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) 293 #ifdef RSEQ_COMPARE_TWICE 294 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 295 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) 296 #endif 297 /* Start rseq by storing table entry pointer into rseq_cs. */ 298 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) 299 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) 300 RSEQ_INJECT_ASM(3) 301 LONG_CMP " %[expect], %[v]\n\t" 302 "jnz %l[cmpfail]\n\t" 303 RSEQ_INJECT_ASM(4) 304 #ifdef RSEQ_COMPARE_TWICE 305 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1]) 306 LONG_CMP " %[expect], %[v]\n\t" 307 "jnz %l[error2]\n\t" 308 #endif 309 /* try store */ 310 LONG_S " %[newv2], %[v2]\n\t" 311 RSEQ_INJECT_ASM(5) 312 /* final store */ 313 LONG_S " %[newv], %[v]\n\t" 314 "2:\n\t" 315 RSEQ_INJECT_ASM(6) 316 RSEQ_ASM_DEFINE_ABORT(4, "", abort) 317 : /* gcc asm goto does not allow outputs */ 318 : [cpu_id] "r" (cpu), 319 [current_cpu_id] "m" (rseq_get_abi()->RSEQ_TEMPLATE_CPU_ID_FIELD), 320 [rseq_cs] "m" (rseq_get_abi()->rseq_cs.arch.ptr), 321 /* try store input */ 322 [v2] "m" (*v2), 323 [newv2] "r" (newv2), 324 /* final store input */ 325 [v] "m" (*v), 326 [expect] "r" (expect), 327 [newv] "r" (newv) 328 RSEQ_INJECT_INPUT 329 : "memory", "cc", "r0" 330 RSEQ_INJECT_CLOBBER 331 : abort, cmpfail 332 #ifdef RSEQ_COMPARE_TWICE 333 , error1, error2 334 #endif 335 ); 336 rseq_after_asm_goto(); 337 return 0; 338 abort: 339 rseq_after_asm_goto(); 340 RSEQ_INJECT_FAILED 341 return -1; 342 cmpfail: 343 rseq_after_asm_goto(); 344 return 1; 345 #ifdef RSEQ_COMPARE_TWICE 346 error1: 347 rseq_after_asm_goto(); 348 rseq_bug("cpu_id comparison failed"); 349 error2: 350 rseq_after_asm_goto(); 351 rseq_bug("expected value comparison failed"); 352 #endif 353 } 354 355 /* s390 is TSO. */ 356 static inline __attribute__((always_inline)) 357 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_trymemcpy_storev)(intptr_t *v, intptr_t expect, 358 void *dst, void *src, size_t len, 359 intptr_t newv, int cpu) 360 { 361 uint64_t rseq_scratch[3]; 362 363 RSEQ_INJECT_C(9) 364 365 __asm__ __volatile__ goto ( 366 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 367 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) 368 #ifdef RSEQ_COMPARE_TWICE 369 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 370 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) 371 #endif 372 LONG_S " %[src], %[rseq_scratch0]\n\t" 373 LONG_S " %[dst], %[rseq_scratch1]\n\t" 374 LONG_S " %[len], %[rseq_scratch2]\n\t" 375 /* Start rseq by storing table entry pointer into rseq_cs. */ 376 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) 377 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) 378 RSEQ_INJECT_ASM(3) 379 LONG_CMP " %[expect], %[v]\n\t" 380 "jnz 5f\n\t" 381 RSEQ_INJECT_ASM(4) 382 #ifdef RSEQ_COMPARE_TWICE 383 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f) 384 LONG_CMP " %[expect], %[v]\n\t" 385 "jnz 7f\n\t" 386 #endif 387 /* try memcpy */ 388 LONG_LT_R " %[len], %[len]\n\t" 389 "jz 333f\n\t" 390 "222:\n\t" 391 "ic %%r0,0(%[src])\n\t" 392 "stc %%r0,0(%[dst])\n\t" 393 LONG_ADDI " %[src], 1\n\t" 394 LONG_ADDI " %[dst], 1\n\t" 395 LONG_ADDI " %[len], -1\n\t" 396 "jnz 222b\n\t" 397 "333:\n\t" 398 RSEQ_INJECT_ASM(5) 399 /* final store */ 400 LONG_S " %[newv], %[v]\n\t" 401 "2:\n\t" 402 RSEQ_INJECT_ASM(6) 403 /* teardown */ 404 LONG_L " %[len], %[rseq_scratch2]\n\t" 405 LONG_L " %[dst], %[rseq_scratch1]\n\t" 406 LONG_L " %[src], %[rseq_scratch0]\n\t" 407 RSEQ_ASM_DEFINE_ABORT(4, 408 LONG_L " %[len], %[rseq_scratch2]\n\t" 409 LONG_L " %[dst], %[rseq_scratch1]\n\t" 410 LONG_L " %[src], %[rseq_scratch0]\n\t", 411 abort) 412 RSEQ_ASM_DEFINE_CMPFAIL(5, 413 LONG_L " %[len], %[rseq_scratch2]\n\t" 414 LONG_L " %[dst], %[rseq_scratch1]\n\t" 415 LONG_L " %[src], %[rseq_scratch0]\n\t", 416 cmpfail) 417 #ifdef RSEQ_COMPARE_TWICE 418 RSEQ_ASM_DEFINE_CMPFAIL(6, 419 LONG_L " %[len], %[rseq_scratch2]\n\t" 420 LONG_L " %[dst], %[rseq_scratch1]\n\t" 421 LONG_L " %[src], %[rseq_scratch0]\n\t", 422 error1) 423 RSEQ_ASM_DEFINE_CMPFAIL(7, 424 LONG_L " %[len], %[rseq_scratch2]\n\t" 425 LONG_L " %[dst], %[rseq_scratch1]\n\t" 426 LONG_L " %[src], %[rseq_scratch0]\n\t", 427 error2) 428 #endif 429 : /* gcc asm goto does not allow outputs */ 430 : [cpu_id] "r" (cpu), 431 [current_cpu_id] "m" (rseq_get_abi()->RSEQ_TEMPLATE_CPU_ID_FIELD), 432 [rseq_cs] "m" (rseq_get_abi()->rseq_cs.arch.ptr), 433 /* final store input */ 434 [v] "m" (*v), 435 [expect] "r" (expect), 436 [newv] "r" (newv), 437 /* try memcpy input */ 438 [dst] "r" (dst), 439 [src] "r" (src), 440 [len] "r" (len), 441 [rseq_scratch0] "m" (rseq_scratch[0]), 442 [rseq_scratch1] "m" (rseq_scratch[1]), 443 [rseq_scratch2] "m" (rseq_scratch[2]) 444 RSEQ_INJECT_INPUT 445 : "memory", "cc", "r0" 446 RSEQ_INJECT_CLOBBER 447 : abort, cmpfail 448 #ifdef RSEQ_COMPARE_TWICE 449 , error1, error2 450 #endif 451 ); 452 rseq_after_asm_goto(); 453 return 0; 454 abort: 455 rseq_after_asm_goto(); 456 RSEQ_INJECT_FAILED 457 return -1; 458 cmpfail: 459 rseq_after_asm_goto(); 460 return 1; 461 #ifdef RSEQ_COMPARE_TWICE 462 error1: 463 rseq_after_asm_goto(); 464 rseq_bug("cpu_id comparison failed"); 465 error2: 466 rseq_after_asm_goto(); 467 rseq_bug("expected value comparison failed"); 468 #endif 469 } 470 471 #endif /* #if (defined(RSEQ_TEMPLATE_MO_RELAXED) || defined(RSEQ_TEMPLATE_MO_RELEASE)) && 472 (defined(RSEQ_TEMPLATE_CPU_ID) || defined(RSEQ_TEMPLATE_MM_CID)) */ 473 474 #include "rseq-bits-reset.h" 475