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