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