Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.17-rc4 1293 lines 36 kB view raw
1/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ 2/* 3 * rseq-x86.h 4 * 5 * (C) Copyright 2016-2018 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> 6 */ 7 8#include <stdint.h> 9 10/* 11 * RSEQ_SIG is used with the following reserved undefined instructions, which 12 * trap in user-space: 13 * 14 * x86-32: 0f b9 3d 53 30 05 53 ud1 0x53053053,%edi 15 * x86-64: 0f b9 3d 53 30 05 53 ud1 0x53053053(%rip),%edi 16 */ 17#define RSEQ_SIG 0x53053053 18 19/* 20 * Due to a compiler optimization bug in gcc-8 with asm goto and TLS asm input 21 * operands, we cannot use "m" input operands, and rather pass the __rseq_abi 22 * address through a "r" input operand. 23 */ 24 25/* Offset of cpu_id and rseq_cs fields in struct rseq. */ 26#define RSEQ_CPU_ID_OFFSET 4 27#define RSEQ_CS_OFFSET 8 28 29#ifdef __x86_64__ 30 31#define rseq_smp_mb() \ 32 __asm__ __volatile__ ("lock; addl $0,-128(%%rsp)" ::: "memory", "cc") 33#define rseq_smp_rmb() rseq_barrier() 34#define rseq_smp_wmb() rseq_barrier() 35 36#define rseq_smp_load_acquire(p) \ 37__extension__ ({ \ 38 __typeof(*p) ____p1 = RSEQ_READ_ONCE(*p); \ 39 rseq_barrier(); \ 40 ____p1; \ 41}) 42 43#define rseq_smp_acquire__after_ctrl_dep() rseq_smp_rmb() 44 45#define rseq_smp_store_release(p, v) \ 46do { \ 47 rseq_barrier(); \ 48 RSEQ_WRITE_ONCE(*p, v); \ 49} while (0) 50 51#ifdef RSEQ_SKIP_FASTPATH 52#include "rseq-skip.h" 53#else /* !RSEQ_SKIP_FASTPATH */ 54 55#define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, \ 56 start_ip, post_commit_offset, abort_ip) \ 57 ".pushsection __rseq_cs, \"aw\"\n\t" \ 58 ".balign 32\n\t" \ 59 __rseq_str(label) ":\n\t" \ 60 ".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \ 61 ".quad " __rseq_str(start_ip) ", " __rseq_str(post_commit_offset) ", " __rseq_str(abort_ip) "\n\t" \ 62 ".popsection\n\t" \ 63 ".pushsection __rseq_cs_ptr_array, \"aw\"\n\t" \ 64 ".quad " __rseq_str(label) "b\n\t" \ 65 ".popsection\n\t" 66 67 68#define RSEQ_ASM_DEFINE_TABLE(label, start_ip, post_commit_ip, abort_ip) \ 69 __RSEQ_ASM_DEFINE_TABLE(label, 0x0, 0x0, start_ip, \ 70 (post_commit_ip - start_ip), abort_ip) 71 72/* 73 * Exit points of a rseq critical section consist of all instructions outside 74 * of the critical section where a critical section can either branch to or 75 * reach through the normal course of its execution. The abort IP and the 76 * post-commit IP are already part of the __rseq_cs section and should not be 77 * explicitly defined as additional exit points. Knowing all exit points is 78 * useful to assist debuggers stepping over the critical section. 79 */ 80#define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip) \ 81 ".pushsection __rseq_exit_point_array, \"aw\"\n\t" \ 82 ".quad " __rseq_str(start_ip) ", " __rseq_str(exit_ip) "\n\t" \ 83 ".popsection\n\t" 84 85#define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \ 86 RSEQ_INJECT_ASM(1) \ 87 "leaq " __rseq_str(cs_label) "(%%rip), %%rax\n\t" \ 88 "movq %%rax, " __rseq_str(rseq_cs) "\n\t" \ 89 __rseq_str(label) ":\n\t" 90 91#define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label) \ 92 RSEQ_INJECT_ASM(2) \ 93 "cmpl %[" __rseq_str(cpu_id) "], " __rseq_str(current_cpu_id) "\n\t" \ 94 "jnz " __rseq_str(label) "\n\t" 95 96#define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label) \ 97 ".pushsection __rseq_failure, \"ax\"\n\t" \ 98 /* Disassembler-friendly signature: ud1 <sig>(%rip),%edi. */ \ 99 ".byte 0x0f, 0xb9, 0x3d\n\t" \ 100 ".long " __rseq_str(RSEQ_SIG) "\n\t" \ 101 __rseq_str(label) ":\n\t" \ 102 teardown \ 103 "jmp %l[" __rseq_str(abort_label) "]\n\t" \ 104 ".popsection\n\t" 105 106#define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label) \ 107 ".pushsection __rseq_failure, \"ax\"\n\t" \ 108 __rseq_str(label) ":\n\t" \ 109 teardown \ 110 "jmp %l[" __rseq_str(cmpfail_label) "]\n\t" \ 111 ".popsection\n\t" 112 113static inline __attribute__((always_inline)) 114int rseq_cmpeqv_storev(intptr_t *v, intptr_t expect, intptr_t newv, int cpu) 115{ 116 RSEQ_INJECT_C(9) 117 118 __asm__ __volatile__ goto ( 119 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 120 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) 121#ifdef RSEQ_COMPARE_TWICE 122 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 123 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) 124#endif 125 /* Start rseq by storing table entry pointer into rseq_cs. */ 126 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi])) 127 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f) 128 RSEQ_INJECT_ASM(3) 129 "cmpq %[v], %[expect]\n\t" 130 "jnz %l[cmpfail]\n\t" 131 RSEQ_INJECT_ASM(4) 132#ifdef RSEQ_COMPARE_TWICE 133 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1]) 134 "cmpq %[v], %[expect]\n\t" 135 "jnz %l[error2]\n\t" 136#endif 137 /* final store */ 138 "movq %[newv], %[v]\n\t" 139 "2:\n\t" 140 RSEQ_INJECT_ASM(5) 141 RSEQ_ASM_DEFINE_ABORT(4, "", abort) 142 : /* gcc asm goto does not allow outputs */ 143 : [cpu_id] "r" (cpu), 144 [rseq_abi] "r" (&__rseq_abi), 145 [v] "m" (*v), 146 [expect] "r" (expect), 147 [newv] "r" (newv) 148 : "memory", "cc", "rax" 149 RSEQ_INJECT_CLOBBER 150 : abort, cmpfail 151#ifdef RSEQ_COMPARE_TWICE 152 , error1, error2 153#endif 154 ); 155 return 0; 156abort: 157 RSEQ_INJECT_FAILED 158 return -1; 159cmpfail: 160 return 1; 161#ifdef RSEQ_COMPARE_TWICE 162error1: 163 rseq_bug("cpu_id comparison failed"); 164error2: 165 rseq_bug("expected value comparison failed"); 166#endif 167} 168 169/* 170 * Compare @v against @expectnot. When it does _not_ match, load @v 171 * into @load, and store the content of *@v + voffp into @v. 172 */ 173static inline __attribute__((always_inline)) 174int rseq_cmpnev_storeoffp_load(intptr_t *v, intptr_t expectnot, 175 off_t voffp, intptr_t *load, int cpu) 176{ 177 RSEQ_INJECT_C(9) 178 179 __asm__ __volatile__ goto ( 180 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 181 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) 182#ifdef RSEQ_COMPARE_TWICE 183 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 184 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) 185#endif 186 /* Start rseq by storing table entry pointer into rseq_cs. */ 187 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi])) 188 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f) 189 RSEQ_INJECT_ASM(3) 190 "movq %[v], %%rbx\n\t" 191 "cmpq %%rbx, %[expectnot]\n\t" 192 "je %l[cmpfail]\n\t" 193 RSEQ_INJECT_ASM(4) 194#ifdef RSEQ_COMPARE_TWICE 195 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1]) 196 "movq %[v], %%rbx\n\t" 197 "cmpq %%rbx, %[expectnot]\n\t" 198 "je %l[error2]\n\t" 199#endif 200 "movq %%rbx, %[load]\n\t" 201 "addq %[voffp], %%rbx\n\t" 202 "movq (%%rbx), %%rbx\n\t" 203 /* final store */ 204 "movq %%rbx, %[v]\n\t" 205 "2:\n\t" 206 RSEQ_INJECT_ASM(5) 207 RSEQ_ASM_DEFINE_ABORT(4, "", abort) 208 : /* gcc asm goto does not allow outputs */ 209 : [cpu_id] "r" (cpu), 210 [rseq_abi] "r" (&__rseq_abi), 211 /* final store input */ 212 [v] "m" (*v), 213 [expectnot] "r" (expectnot), 214 [voffp] "er" (voffp), 215 [load] "m" (*load) 216 : "memory", "cc", "rax", "rbx" 217 RSEQ_INJECT_CLOBBER 218 : abort, cmpfail 219#ifdef RSEQ_COMPARE_TWICE 220 , error1, error2 221#endif 222 ); 223 return 0; 224abort: 225 RSEQ_INJECT_FAILED 226 return -1; 227cmpfail: 228 return 1; 229#ifdef RSEQ_COMPARE_TWICE 230error1: 231 rseq_bug("cpu_id comparison failed"); 232error2: 233 rseq_bug("expected value comparison failed"); 234#endif 235} 236 237static inline __attribute__((always_inline)) 238int rseq_addv(intptr_t *v, intptr_t count, int cpu) 239{ 240 RSEQ_INJECT_C(9) 241 242 __asm__ __volatile__ goto ( 243 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 244#ifdef RSEQ_COMPARE_TWICE 245 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 246#endif 247 /* Start rseq by storing table entry pointer into rseq_cs. */ 248 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi])) 249 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f) 250 RSEQ_INJECT_ASM(3) 251#ifdef RSEQ_COMPARE_TWICE 252 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1]) 253#endif 254 /* final store */ 255 "addq %[count], %[v]\n\t" 256 "2:\n\t" 257 RSEQ_INJECT_ASM(4) 258 RSEQ_ASM_DEFINE_ABORT(4, "", abort) 259 : /* gcc asm goto does not allow outputs */ 260 : [cpu_id] "r" (cpu), 261 [rseq_abi] "r" (&__rseq_abi), 262 /* final store input */ 263 [v] "m" (*v), 264 [count] "er" (count) 265 : "memory", "cc", "rax" 266 RSEQ_INJECT_CLOBBER 267 : abort 268#ifdef RSEQ_COMPARE_TWICE 269 , error1 270#endif 271 ); 272 return 0; 273abort: 274 RSEQ_INJECT_FAILED 275 return -1; 276#ifdef RSEQ_COMPARE_TWICE 277error1: 278 rseq_bug("cpu_id comparison failed"); 279#endif 280} 281 282#define RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV 283 284/* 285 * pval = *(ptr+off) 286 * *pval += inc; 287 */ 288static inline __attribute__((always_inline)) 289int rseq_offset_deref_addv(intptr_t *ptr, off_t off, intptr_t inc, int cpu) 290{ 291 RSEQ_INJECT_C(9) 292 293 __asm__ __volatile__ goto ( 294 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 295#ifdef RSEQ_COMPARE_TWICE 296 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 297#endif 298 /* Start rseq by storing table entry pointer into rseq_cs. */ 299 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi])) 300 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f) 301 RSEQ_INJECT_ASM(3) 302#ifdef RSEQ_COMPARE_TWICE 303 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1]) 304#endif 305 /* get p+v */ 306 "movq %[ptr], %%rbx\n\t" 307 "addq %[off], %%rbx\n\t" 308 /* get pv */ 309 "movq (%%rbx), %%rcx\n\t" 310 /* *pv += inc */ 311 "addq %[inc], (%%rcx)\n\t" 312 "2:\n\t" 313 RSEQ_INJECT_ASM(4) 314 RSEQ_ASM_DEFINE_ABORT(4, "", abort) 315 : /* gcc asm goto does not allow outputs */ 316 : [cpu_id] "r" (cpu), 317 [rseq_abi] "r" (&__rseq_abi), 318 /* final store input */ 319 [ptr] "m" (*ptr), 320 [off] "er" (off), 321 [inc] "er" (inc) 322 : "memory", "cc", "rax", "rbx", "rcx" 323 RSEQ_INJECT_CLOBBER 324 : abort 325#ifdef RSEQ_COMPARE_TWICE 326 , error1 327#endif 328 ); 329 return 0; 330abort: 331 RSEQ_INJECT_FAILED 332 return -1; 333#ifdef RSEQ_COMPARE_TWICE 334error1: 335 rseq_bug("cpu_id comparison failed"); 336#endif 337} 338 339static inline __attribute__((always_inline)) 340int rseq_cmpeqv_trystorev_storev(intptr_t *v, intptr_t expect, 341 intptr_t *v2, intptr_t newv2, 342 intptr_t newv, int cpu) 343{ 344 RSEQ_INJECT_C(9) 345 346 __asm__ __volatile__ goto ( 347 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 348 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) 349#ifdef RSEQ_COMPARE_TWICE 350 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 351 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) 352#endif 353 /* Start rseq by storing table entry pointer into rseq_cs. */ 354 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi])) 355 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f) 356 RSEQ_INJECT_ASM(3) 357 "cmpq %[v], %[expect]\n\t" 358 "jnz %l[cmpfail]\n\t" 359 RSEQ_INJECT_ASM(4) 360#ifdef RSEQ_COMPARE_TWICE 361 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1]) 362 "cmpq %[v], %[expect]\n\t" 363 "jnz %l[error2]\n\t" 364#endif 365 /* try store */ 366 "movq %[newv2], %[v2]\n\t" 367 RSEQ_INJECT_ASM(5) 368 /* final store */ 369 "movq %[newv], %[v]\n\t" 370 "2:\n\t" 371 RSEQ_INJECT_ASM(6) 372 RSEQ_ASM_DEFINE_ABORT(4, "", abort) 373 : /* gcc asm goto does not allow outputs */ 374 : [cpu_id] "r" (cpu), 375 [rseq_abi] "r" (&__rseq_abi), 376 /* try store input */ 377 [v2] "m" (*v2), 378 [newv2] "r" (newv2), 379 /* final store input */ 380 [v] "m" (*v), 381 [expect] "r" (expect), 382 [newv] "r" (newv) 383 : "memory", "cc", "rax" 384 RSEQ_INJECT_CLOBBER 385 : abort, cmpfail 386#ifdef RSEQ_COMPARE_TWICE 387 , error1, error2 388#endif 389 ); 390 return 0; 391abort: 392 RSEQ_INJECT_FAILED 393 return -1; 394cmpfail: 395 return 1; 396#ifdef RSEQ_COMPARE_TWICE 397error1: 398 rseq_bug("cpu_id comparison failed"); 399error2: 400 rseq_bug("expected value comparison failed"); 401#endif 402} 403 404/* x86-64 is TSO. */ 405static inline __attribute__((always_inline)) 406int rseq_cmpeqv_trystorev_storev_release(intptr_t *v, intptr_t expect, 407 intptr_t *v2, intptr_t newv2, 408 intptr_t newv, int cpu) 409{ 410 return rseq_cmpeqv_trystorev_storev(v, expect, v2, newv2, newv, cpu); 411} 412 413static inline __attribute__((always_inline)) 414int rseq_cmpeqv_cmpeqv_storev(intptr_t *v, intptr_t expect, 415 intptr_t *v2, intptr_t expect2, 416 intptr_t newv, int cpu) 417{ 418 RSEQ_INJECT_C(9) 419 420 __asm__ __volatile__ goto ( 421 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 422 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) 423#ifdef RSEQ_COMPARE_TWICE 424 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 425 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) 426 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error3]) 427#endif 428 /* Start rseq by storing table entry pointer into rseq_cs. */ 429 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi])) 430 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f) 431 RSEQ_INJECT_ASM(3) 432 "cmpq %[v], %[expect]\n\t" 433 "jnz %l[cmpfail]\n\t" 434 RSEQ_INJECT_ASM(4) 435 "cmpq %[v2], %[expect2]\n\t" 436 "jnz %l[cmpfail]\n\t" 437 RSEQ_INJECT_ASM(5) 438#ifdef RSEQ_COMPARE_TWICE 439 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1]) 440 "cmpq %[v], %[expect]\n\t" 441 "jnz %l[error2]\n\t" 442 "cmpq %[v2], %[expect2]\n\t" 443 "jnz %l[error3]\n\t" 444#endif 445 /* final store */ 446 "movq %[newv], %[v]\n\t" 447 "2:\n\t" 448 RSEQ_INJECT_ASM(6) 449 RSEQ_ASM_DEFINE_ABORT(4, "", abort) 450 : /* gcc asm goto does not allow outputs */ 451 : [cpu_id] "r" (cpu), 452 [rseq_abi] "r" (&__rseq_abi), 453 /* cmp2 input */ 454 [v2] "m" (*v2), 455 [expect2] "r" (expect2), 456 /* final store input */ 457 [v] "m" (*v), 458 [expect] "r" (expect), 459 [newv] "r" (newv) 460 : "memory", "cc", "rax" 461 RSEQ_INJECT_CLOBBER 462 : abort, cmpfail 463#ifdef RSEQ_COMPARE_TWICE 464 , error1, error2, error3 465#endif 466 ); 467 return 0; 468abort: 469 RSEQ_INJECT_FAILED 470 return -1; 471cmpfail: 472 return 1; 473#ifdef RSEQ_COMPARE_TWICE 474error1: 475 rseq_bug("cpu_id comparison failed"); 476error2: 477 rseq_bug("1st expected value comparison failed"); 478error3: 479 rseq_bug("2nd expected value comparison failed"); 480#endif 481} 482 483static inline __attribute__((always_inline)) 484int rseq_cmpeqv_trymemcpy_storev(intptr_t *v, intptr_t expect, 485 void *dst, void *src, size_t len, 486 intptr_t newv, int cpu) 487{ 488 uint64_t rseq_scratch[3]; 489 490 RSEQ_INJECT_C(9) 491 492 __asm__ __volatile__ goto ( 493 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 494 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) 495#ifdef RSEQ_COMPARE_TWICE 496 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 497 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) 498#endif 499 "movq %[src], %[rseq_scratch0]\n\t" 500 "movq %[dst], %[rseq_scratch1]\n\t" 501 "movq %[len], %[rseq_scratch2]\n\t" 502 /* Start rseq by storing table entry pointer into rseq_cs. */ 503 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi])) 504 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f) 505 RSEQ_INJECT_ASM(3) 506 "cmpq %[v], %[expect]\n\t" 507 "jnz 5f\n\t" 508 RSEQ_INJECT_ASM(4) 509#ifdef RSEQ_COMPARE_TWICE 510 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 6f) 511 "cmpq %[v], %[expect]\n\t" 512 "jnz 7f\n\t" 513#endif 514 /* try memcpy */ 515 "test %[len], %[len]\n\t" \ 516 "jz 333f\n\t" \ 517 "222:\n\t" \ 518 "movb (%[src]), %%al\n\t" \ 519 "movb %%al, (%[dst])\n\t" \ 520 "inc %[src]\n\t" \ 521 "inc %[dst]\n\t" \ 522 "dec %[len]\n\t" \ 523 "jnz 222b\n\t" \ 524 "333:\n\t" \ 525 RSEQ_INJECT_ASM(5) 526 /* final store */ 527 "movq %[newv], %[v]\n\t" 528 "2:\n\t" 529 RSEQ_INJECT_ASM(6) 530 /* teardown */ 531 "movq %[rseq_scratch2], %[len]\n\t" 532 "movq %[rseq_scratch1], %[dst]\n\t" 533 "movq %[rseq_scratch0], %[src]\n\t" 534 RSEQ_ASM_DEFINE_ABORT(4, 535 "movq %[rseq_scratch2], %[len]\n\t" 536 "movq %[rseq_scratch1], %[dst]\n\t" 537 "movq %[rseq_scratch0], %[src]\n\t", 538 abort) 539 RSEQ_ASM_DEFINE_CMPFAIL(5, 540 "movq %[rseq_scratch2], %[len]\n\t" 541 "movq %[rseq_scratch1], %[dst]\n\t" 542 "movq %[rseq_scratch0], %[src]\n\t", 543 cmpfail) 544#ifdef RSEQ_COMPARE_TWICE 545 RSEQ_ASM_DEFINE_CMPFAIL(6, 546 "movq %[rseq_scratch2], %[len]\n\t" 547 "movq %[rseq_scratch1], %[dst]\n\t" 548 "movq %[rseq_scratch0], %[src]\n\t", 549 error1) 550 RSEQ_ASM_DEFINE_CMPFAIL(7, 551 "movq %[rseq_scratch2], %[len]\n\t" 552 "movq %[rseq_scratch1], %[dst]\n\t" 553 "movq %[rseq_scratch0], %[src]\n\t", 554 error2) 555#endif 556 : /* gcc asm goto does not allow outputs */ 557 : [cpu_id] "r" (cpu), 558 [rseq_abi] "r" (&__rseq_abi), 559 /* final store input */ 560 [v] "m" (*v), 561 [expect] "r" (expect), 562 [newv] "r" (newv), 563 /* try memcpy input */ 564 [dst] "r" (dst), 565 [src] "r" (src), 566 [len] "r" (len), 567 [rseq_scratch0] "m" (rseq_scratch[0]), 568 [rseq_scratch1] "m" (rseq_scratch[1]), 569 [rseq_scratch2] "m" (rseq_scratch[2]) 570 : "memory", "cc", "rax" 571 RSEQ_INJECT_CLOBBER 572 : abort, cmpfail 573#ifdef RSEQ_COMPARE_TWICE 574 , error1, error2 575#endif 576 ); 577 return 0; 578abort: 579 RSEQ_INJECT_FAILED 580 return -1; 581cmpfail: 582 return 1; 583#ifdef RSEQ_COMPARE_TWICE 584error1: 585 rseq_bug("cpu_id comparison failed"); 586error2: 587 rseq_bug("expected value comparison failed"); 588#endif 589} 590 591/* x86-64 is TSO. */ 592static inline __attribute__((always_inline)) 593int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v, intptr_t expect, 594 void *dst, void *src, size_t len, 595 intptr_t newv, int cpu) 596{ 597 return rseq_cmpeqv_trymemcpy_storev(v, expect, dst, src, len, 598 newv, cpu); 599} 600 601#endif /* !RSEQ_SKIP_FASTPATH */ 602 603#elif __i386__ 604 605#define rseq_smp_mb() \ 606 __asm__ __volatile__ ("lock; addl $0,-128(%%esp)" ::: "memory", "cc") 607#define rseq_smp_rmb() \ 608 __asm__ __volatile__ ("lock; addl $0,-128(%%esp)" ::: "memory", "cc") 609#define rseq_smp_wmb() \ 610 __asm__ __volatile__ ("lock; addl $0,-128(%%esp)" ::: "memory", "cc") 611 612#define rseq_smp_load_acquire(p) \ 613__extension__ ({ \ 614 __typeof(*p) ____p1 = RSEQ_READ_ONCE(*p); \ 615 rseq_smp_mb(); \ 616 ____p1; \ 617}) 618 619#define rseq_smp_acquire__after_ctrl_dep() rseq_smp_rmb() 620 621#define rseq_smp_store_release(p, v) \ 622do { \ 623 rseq_smp_mb(); \ 624 RSEQ_WRITE_ONCE(*p, v); \ 625} while (0) 626 627#ifdef RSEQ_SKIP_FASTPATH 628#include "rseq-skip.h" 629#else /* !RSEQ_SKIP_FASTPATH */ 630 631/* 632 * Use eax as scratch register and take memory operands as input to 633 * lessen register pressure. Especially needed when compiling in O0. 634 */ 635#define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, \ 636 start_ip, post_commit_offset, abort_ip) \ 637 ".pushsection __rseq_cs, \"aw\"\n\t" \ 638 ".balign 32\n\t" \ 639 __rseq_str(label) ":\n\t" \ 640 ".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \ 641 ".long " __rseq_str(start_ip) ", 0x0, " __rseq_str(post_commit_offset) ", 0x0, " __rseq_str(abort_ip) ", 0x0\n\t" \ 642 ".popsection\n\t" \ 643 ".pushsection __rseq_cs_ptr_array, \"aw\"\n\t" \ 644 ".long " __rseq_str(label) "b, 0x0\n\t" \ 645 ".popsection\n\t" 646 647#define RSEQ_ASM_DEFINE_TABLE(label, start_ip, post_commit_ip, abort_ip) \ 648 __RSEQ_ASM_DEFINE_TABLE(label, 0x0, 0x0, start_ip, \ 649 (post_commit_ip - start_ip), abort_ip) 650 651/* 652 * Exit points of a rseq critical section consist of all instructions outside 653 * of the critical section where a critical section can either branch to or 654 * reach through the normal course of its execution. The abort IP and the 655 * post-commit IP are already part of the __rseq_cs section and should not be 656 * explicitly defined as additional exit points. Knowing all exit points is 657 * useful to assist debuggers stepping over the critical section. 658 */ 659#define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip) \ 660 ".pushsection __rseq_exit_point_array, \"aw\"\n\t" \ 661 ".long " __rseq_str(start_ip) ", 0x0, " __rseq_str(exit_ip) ", 0x0\n\t" \ 662 ".popsection\n\t" 663 664#define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \ 665 RSEQ_INJECT_ASM(1) \ 666 "movl $" __rseq_str(cs_label) ", " __rseq_str(rseq_cs) "\n\t" \ 667 __rseq_str(label) ":\n\t" 668 669#define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label) \ 670 RSEQ_INJECT_ASM(2) \ 671 "cmpl %[" __rseq_str(cpu_id) "], " __rseq_str(current_cpu_id) "\n\t" \ 672 "jnz " __rseq_str(label) "\n\t" 673 674#define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label) \ 675 ".pushsection __rseq_failure, \"ax\"\n\t" \ 676 /* Disassembler-friendly signature: ud1 <sig>,%edi. */ \ 677 ".byte 0x0f, 0xb9, 0x3d\n\t" \ 678 ".long " __rseq_str(RSEQ_SIG) "\n\t" \ 679 __rseq_str(label) ":\n\t" \ 680 teardown \ 681 "jmp %l[" __rseq_str(abort_label) "]\n\t" \ 682 ".popsection\n\t" 683 684#define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label) \ 685 ".pushsection __rseq_failure, \"ax\"\n\t" \ 686 __rseq_str(label) ":\n\t" \ 687 teardown \ 688 "jmp %l[" __rseq_str(cmpfail_label) "]\n\t" \ 689 ".popsection\n\t" 690 691static inline __attribute__((always_inline)) 692int rseq_cmpeqv_storev(intptr_t *v, intptr_t expect, intptr_t newv, int cpu) 693{ 694 RSEQ_INJECT_C(9) 695 696 __asm__ __volatile__ goto ( 697 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 698 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) 699#ifdef RSEQ_COMPARE_TWICE 700 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 701 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) 702#endif 703 /* Start rseq by storing table entry pointer into rseq_cs. */ 704 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi])) 705 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f) 706 RSEQ_INJECT_ASM(3) 707 "cmpl %[v], %[expect]\n\t" 708 "jnz %l[cmpfail]\n\t" 709 RSEQ_INJECT_ASM(4) 710#ifdef RSEQ_COMPARE_TWICE 711 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1]) 712 "cmpl %[v], %[expect]\n\t" 713 "jnz %l[error2]\n\t" 714#endif 715 /* final store */ 716 "movl %[newv], %[v]\n\t" 717 "2:\n\t" 718 RSEQ_INJECT_ASM(5) 719 RSEQ_ASM_DEFINE_ABORT(4, "", abort) 720 : /* gcc asm goto does not allow outputs */ 721 : [cpu_id] "r" (cpu), 722 [rseq_abi] "r" (&__rseq_abi), 723 [v] "m" (*v), 724 [expect] "r" (expect), 725 [newv] "r" (newv) 726 : "memory", "cc", "eax" 727 RSEQ_INJECT_CLOBBER 728 : abort, cmpfail 729#ifdef RSEQ_COMPARE_TWICE 730 , error1, error2 731#endif 732 ); 733 return 0; 734abort: 735 RSEQ_INJECT_FAILED 736 return -1; 737cmpfail: 738 return 1; 739#ifdef RSEQ_COMPARE_TWICE 740error1: 741 rseq_bug("cpu_id comparison failed"); 742error2: 743 rseq_bug("expected value comparison failed"); 744#endif 745} 746 747/* 748 * Compare @v against @expectnot. When it does _not_ match, load @v 749 * into @load, and store the content of *@v + voffp into @v. 750 */ 751static inline __attribute__((always_inline)) 752int rseq_cmpnev_storeoffp_load(intptr_t *v, intptr_t expectnot, 753 off_t voffp, intptr_t *load, int cpu) 754{ 755 RSEQ_INJECT_C(9) 756 757 __asm__ __volatile__ goto ( 758 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 759 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) 760#ifdef RSEQ_COMPARE_TWICE 761 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 762 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) 763#endif 764 /* Start rseq by storing table entry pointer into rseq_cs. */ 765 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi])) 766 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f) 767 RSEQ_INJECT_ASM(3) 768 "movl %[v], %%ebx\n\t" 769 "cmpl %%ebx, %[expectnot]\n\t" 770 "je %l[cmpfail]\n\t" 771 RSEQ_INJECT_ASM(4) 772#ifdef RSEQ_COMPARE_TWICE 773 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1]) 774 "movl %[v], %%ebx\n\t" 775 "cmpl %%ebx, %[expectnot]\n\t" 776 "je %l[error2]\n\t" 777#endif 778 "movl %%ebx, %[load]\n\t" 779 "addl %[voffp], %%ebx\n\t" 780 "movl (%%ebx), %%ebx\n\t" 781 /* final store */ 782 "movl %%ebx, %[v]\n\t" 783 "2:\n\t" 784 RSEQ_INJECT_ASM(5) 785 RSEQ_ASM_DEFINE_ABORT(4, "", abort) 786 : /* gcc asm goto does not allow outputs */ 787 : [cpu_id] "r" (cpu), 788 [rseq_abi] "r" (&__rseq_abi), 789 /* final store input */ 790 [v] "m" (*v), 791 [expectnot] "r" (expectnot), 792 [voffp] "ir" (voffp), 793 [load] "m" (*load) 794 : "memory", "cc", "eax", "ebx" 795 RSEQ_INJECT_CLOBBER 796 : abort, cmpfail 797#ifdef RSEQ_COMPARE_TWICE 798 , error1, error2 799#endif 800 ); 801 return 0; 802abort: 803 RSEQ_INJECT_FAILED 804 return -1; 805cmpfail: 806 return 1; 807#ifdef RSEQ_COMPARE_TWICE 808error1: 809 rseq_bug("cpu_id comparison failed"); 810error2: 811 rseq_bug("expected value comparison failed"); 812#endif 813} 814 815static inline __attribute__((always_inline)) 816int rseq_addv(intptr_t *v, intptr_t count, int cpu) 817{ 818 RSEQ_INJECT_C(9) 819 820 __asm__ __volatile__ goto ( 821 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 822#ifdef RSEQ_COMPARE_TWICE 823 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 824#endif 825 /* Start rseq by storing table entry pointer into rseq_cs. */ 826 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi])) 827 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f) 828 RSEQ_INJECT_ASM(3) 829#ifdef RSEQ_COMPARE_TWICE 830 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1]) 831#endif 832 /* final store */ 833 "addl %[count], %[v]\n\t" 834 "2:\n\t" 835 RSEQ_INJECT_ASM(4) 836 RSEQ_ASM_DEFINE_ABORT(4, "", abort) 837 : /* gcc asm goto does not allow outputs */ 838 : [cpu_id] "r" (cpu), 839 [rseq_abi] "r" (&__rseq_abi), 840 /* final store input */ 841 [v] "m" (*v), 842 [count] "ir" (count) 843 : "memory", "cc", "eax" 844 RSEQ_INJECT_CLOBBER 845 : abort 846#ifdef RSEQ_COMPARE_TWICE 847 , error1 848#endif 849 ); 850 return 0; 851abort: 852 RSEQ_INJECT_FAILED 853 return -1; 854#ifdef RSEQ_COMPARE_TWICE 855error1: 856 rseq_bug("cpu_id comparison failed"); 857#endif 858} 859 860static inline __attribute__((always_inline)) 861int rseq_cmpeqv_trystorev_storev(intptr_t *v, intptr_t expect, 862 intptr_t *v2, intptr_t newv2, 863 intptr_t newv, int cpu) 864{ 865 RSEQ_INJECT_C(9) 866 867 __asm__ __volatile__ goto ( 868 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 869 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) 870#ifdef RSEQ_COMPARE_TWICE 871 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 872 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) 873#endif 874 /* Start rseq by storing table entry pointer into rseq_cs. */ 875 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi])) 876 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f) 877 RSEQ_INJECT_ASM(3) 878 "cmpl %[v], %[expect]\n\t" 879 "jnz %l[cmpfail]\n\t" 880 RSEQ_INJECT_ASM(4) 881#ifdef RSEQ_COMPARE_TWICE 882 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1]) 883 "cmpl %[v], %[expect]\n\t" 884 "jnz %l[error2]\n\t" 885#endif 886 /* try store */ 887 "movl %[newv2], %%eax\n\t" 888 "movl %%eax, %[v2]\n\t" 889 RSEQ_INJECT_ASM(5) 890 /* final store */ 891 "movl %[newv], %[v]\n\t" 892 "2:\n\t" 893 RSEQ_INJECT_ASM(6) 894 RSEQ_ASM_DEFINE_ABORT(4, "", abort) 895 : /* gcc asm goto does not allow outputs */ 896 : [cpu_id] "r" (cpu), 897 [rseq_abi] "r" (&__rseq_abi), 898 /* try store input */ 899 [v2] "m" (*v2), 900 [newv2] "m" (newv2), 901 /* final store input */ 902 [v] "m" (*v), 903 [expect] "r" (expect), 904 [newv] "r" (newv) 905 : "memory", "cc", "eax" 906 RSEQ_INJECT_CLOBBER 907 : abort, cmpfail 908#ifdef RSEQ_COMPARE_TWICE 909 , error1, error2 910#endif 911 ); 912 return 0; 913abort: 914 RSEQ_INJECT_FAILED 915 return -1; 916cmpfail: 917 return 1; 918#ifdef RSEQ_COMPARE_TWICE 919error1: 920 rseq_bug("cpu_id comparison failed"); 921error2: 922 rseq_bug("expected value comparison failed"); 923#endif 924} 925 926static inline __attribute__((always_inline)) 927int rseq_cmpeqv_trystorev_storev_release(intptr_t *v, intptr_t expect, 928 intptr_t *v2, intptr_t newv2, 929 intptr_t newv, int cpu) 930{ 931 RSEQ_INJECT_C(9) 932 933 __asm__ __volatile__ goto ( 934 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 935 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) 936#ifdef RSEQ_COMPARE_TWICE 937 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 938 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) 939#endif 940 /* Start rseq by storing table entry pointer into rseq_cs. */ 941 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi])) 942 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f) 943 RSEQ_INJECT_ASM(3) 944 "movl %[expect], %%eax\n\t" 945 "cmpl %[v], %%eax\n\t" 946 "jnz %l[cmpfail]\n\t" 947 RSEQ_INJECT_ASM(4) 948#ifdef RSEQ_COMPARE_TWICE 949 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1]) 950 "movl %[expect], %%eax\n\t" 951 "cmpl %[v], %%eax\n\t" 952 "jnz %l[error2]\n\t" 953#endif 954 /* try store */ 955 "movl %[newv2], %[v2]\n\t" 956 RSEQ_INJECT_ASM(5) 957 "lock; addl $0,-128(%%esp)\n\t" 958 /* final store */ 959 "movl %[newv], %[v]\n\t" 960 "2:\n\t" 961 RSEQ_INJECT_ASM(6) 962 RSEQ_ASM_DEFINE_ABORT(4, "", abort) 963 : /* gcc asm goto does not allow outputs */ 964 : [cpu_id] "r" (cpu), 965 [rseq_abi] "r" (&__rseq_abi), 966 /* try store input */ 967 [v2] "m" (*v2), 968 [newv2] "r" (newv2), 969 /* final store input */ 970 [v] "m" (*v), 971 [expect] "m" (expect), 972 [newv] "r" (newv) 973 : "memory", "cc", "eax" 974 RSEQ_INJECT_CLOBBER 975 : abort, cmpfail 976#ifdef RSEQ_COMPARE_TWICE 977 , error1, error2 978#endif 979 ); 980 return 0; 981abort: 982 RSEQ_INJECT_FAILED 983 return -1; 984cmpfail: 985 return 1; 986#ifdef RSEQ_COMPARE_TWICE 987error1: 988 rseq_bug("cpu_id comparison failed"); 989error2: 990 rseq_bug("expected value comparison failed"); 991#endif 992 993} 994 995static inline __attribute__((always_inline)) 996int rseq_cmpeqv_cmpeqv_storev(intptr_t *v, intptr_t expect, 997 intptr_t *v2, intptr_t expect2, 998 intptr_t newv, int cpu) 999{ 1000 RSEQ_INJECT_C(9) 1001 1002 __asm__ __volatile__ goto ( 1003 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 1004 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) 1005#ifdef RSEQ_COMPARE_TWICE 1006 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 1007 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) 1008 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error3]) 1009#endif 1010 /* Start rseq by storing table entry pointer into rseq_cs. */ 1011 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi])) 1012 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f) 1013 RSEQ_INJECT_ASM(3) 1014 "cmpl %[v], %[expect]\n\t" 1015 "jnz %l[cmpfail]\n\t" 1016 RSEQ_INJECT_ASM(4) 1017 "cmpl %[expect2], %[v2]\n\t" 1018 "jnz %l[cmpfail]\n\t" 1019 RSEQ_INJECT_ASM(5) 1020#ifdef RSEQ_COMPARE_TWICE 1021 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), %l[error1]) 1022 "cmpl %[v], %[expect]\n\t" 1023 "jnz %l[error2]\n\t" 1024 "cmpl %[expect2], %[v2]\n\t" 1025 "jnz %l[error3]\n\t" 1026#endif 1027 "movl %[newv], %%eax\n\t" 1028 /* final store */ 1029 "movl %%eax, %[v]\n\t" 1030 "2:\n\t" 1031 RSEQ_INJECT_ASM(6) 1032 RSEQ_ASM_DEFINE_ABORT(4, "", abort) 1033 : /* gcc asm goto does not allow outputs */ 1034 : [cpu_id] "r" (cpu), 1035 [rseq_abi] "r" (&__rseq_abi), 1036 /* cmp2 input */ 1037 [v2] "m" (*v2), 1038 [expect2] "r" (expect2), 1039 /* final store input */ 1040 [v] "m" (*v), 1041 [expect] "r" (expect), 1042 [newv] "m" (newv) 1043 : "memory", "cc", "eax" 1044 RSEQ_INJECT_CLOBBER 1045 : abort, cmpfail 1046#ifdef RSEQ_COMPARE_TWICE 1047 , error1, error2, error3 1048#endif 1049 ); 1050 return 0; 1051abort: 1052 RSEQ_INJECT_FAILED 1053 return -1; 1054cmpfail: 1055 return 1; 1056#ifdef RSEQ_COMPARE_TWICE 1057error1: 1058 rseq_bug("cpu_id comparison failed"); 1059error2: 1060 rseq_bug("1st expected value comparison failed"); 1061error3: 1062 rseq_bug("2nd expected value comparison failed"); 1063#endif 1064} 1065 1066/* TODO: implement a faster memcpy. */ 1067static inline __attribute__((always_inline)) 1068int rseq_cmpeqv_trymemcpy_storev(intptr_t *v, intptr_t expect, 1069 void *dst, void *src, size_t len, 1070 intptr_t newv, int cpu) 1071{ 1072 uint32_t rseq_scratch[3]; 1073 1074 RSEQ_INJECT_C(9) 1075 1076 __asm__ __volatile__ goto ( 1077 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 1078 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) 1079#ifdef RSEQ_COMPARE_TWICE 1080 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 1081 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) 1082#endif 1083 "movl %[src], %[rseq_scratch0]\n\t" 1084 "movl %[dst], %[rseq_scratch1]\n\t" 1085 "movl %[len], %[rseq_scratch2]\n\t" 1086 /* Start rseq by storing table entry pointer into rseq_cs. */ 1087 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi])) 1088 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f) 1089 RSEQ_INJECT_ASM(3) 1090 "movl %[expect], %%eax\n\t" 1091 "cmpl %%eax, %[v]\n\t" 1092 "jnz 5f\n\t" 1093 RSEQ_INJECT_ASM(4) 1094#ifdef RSEQ_COMPARE_TWICE 1095 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 6f) 1096 "movl %[expect], %%eax\n\t" 1097 "cmpl %%eax, %[v]\n\t" 1098 "jnz 7f\n\t" 1099#endif 1100 /* try memcpy */ 1101 "test %[len], %[len]\n\t" \ 1102 "jz 333f\n\t" \ 1103 "222:\n\t" \ 1104 "movb (%[src]), %%al\n\t" \ 1105 "movb %%al, (%[dst])\n\t" \ 1106 "inc %[src]\n\t" \ 1107 "inc %[dst]\n\t" \ 1108 "dec %[len]\n\t" \ 1109 "jnz 222b\n\t" \ 1110 "333:\n\t" \ 1111 RSEQ_INJECT_ASM(5) 1112 "movl %[newv], %%eax\n\t" 1113 /* final store */ 1114 "movl %%eax, %[v]\n\t" 1115 "2:\n\t" 1116 RSEQ_INJECT_ASM(6) 1117 /* teardown */ 1118 "movl %[rseq_scratch2], %[len]\n\t" 1119 "movl %[rseq_scratch1], %[dst]\n\t" 1120 "movl %[rseq_scratch0], %[src]\n\t" 1121 RSEQ_ASM_DEFINE_ABORT(4, 1122 "movl %[rseq_scratch2], %[len]\n\t" 1123 "movl %[rseq_scratch1], %[dst]\n\t" 1124 "movl %[rseq_scratch0], %[src]\n\t", 1125 abort) 1126 RSEQ_ASM_DEFINE_CMPFAIL(5, 1127 "movl %[rseq_scratch2], %[len]\n\t" 1128 "movl %[rseq_scratch1], %[dst]\n\t" 1129 "movl %[rseq_scratch0], %[src]\n\t", 1130 cmpfail) 1131#ifdef RSEQ_COMPARE_TWICE 1132 RSEQ_ASM_DEFINE_CMPFAIL(6, 1133 "movl %[rseq_scratch2], %[len]\n\t" 1134 "movl %[rseq_scratch1], %[dst]\n\t" 1135 "movl %[rseq_scratch0], %[src]\n\t", 1136 error1) 1137 RSEQ_ASM_DEFINE_CMPFAIL(7, 1138 "movl %[rseq_scratch2], %[len]\n\t" 1139 "movl %[rseq_scratch1], %[dst]\n\t" 1140 "movl %[rseq_scratch0], %[src]\n\t", 1141 error2) 1142#endif 1143 : /* gcc asm goto does not allow outputs */ 1144 : [cpu_id] "r" (cpu), 1145 [rseq_abi] "r" (&__rseq_abi), 1146 /* final store input */ 1147 [v] "m" (*v), 1148 [expect] "m" (expect), 1149 [newv] "m" (newv), 1150 /* try memcpy input */ 1151 [dst] "r" (dst), 1152 [src] "r" (src), 1153 [len] "r" (len), 1154 [rseq_scratch0] "m" (rseq_scratch[0]), 1155 [rseq_scratch1] "m" (rseq_scratch[1]), 1156 [rseq_scratch2] "m" (rseq_scratch[2]) 1157 : "memory", "cc", "eax" 1158 RSEQ_INJECT_CLOBBER 1159 : abort, cmpfail 1160#ifdef RSEQ_COMPARE_TWICE 1161 , error1, error2 1162#endif 1163 ); 1164 return 0; 1165abort: 1166 RSEQ_INJECT_FAILED 1167 return -1; 1168cmpfail: 1169 return 1; 1170#ifdef RSEQ_COMPARE_TWICE 1171error1: 1172 rseq_bug("cpu_id comparison failed"); 1173error2: 1174 rseq_bug("expected value comparison failed"); 1175#endif 1176} 1177 1178/* TODO: implement a faster memcpy. */ 1179static inline __attribute__((always_inline)) 1180int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v, intptr_t expect, 1181 void *dst, void *src, size_t len, 1182 intptr_t newv, int cpu) 1183{ 1184 uint32_t rseq_scratch[3]; 1185 1186 RSEQ_INJECT_C(9) 1187 1188 __asm__ __volatile__ goto ( 1189 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ 1190 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) 1191#ifdef RSEQ_COMPARE_TWICE 1192 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) 1193 RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) 1194#endif 1195 "movl %[src], %[rseq_scratch0]\n\t" 1196 "movl %[dst], %[rseq_scratch1]\n\t" 1197 "movl %[len], %[rseq_scratch2]\n\t" 1198 /* Start rseq by storing table entry pointer into rseq_cs. */ 1199 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_CS_OFFSET(%[rseq_abi])) 1200 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 4f) 1201 RSEQ_INJECT_ASM(3) 1202 "movl %[expect], %%eax\n\t" 1203 "cmpl %%eax, %[v]\n\t" 1204 "jnz 5f\n\t" 1205 RSEQ_INJECT_ASM(4) 1206#ifdef RSEQ_COMPARE_TWICE 1207 RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_CPU_ID_OFFSET(%[rseq_abi]), 6f) 1208 "movl %[expect], %%eax\n\t" 1209 "cmpl %%eax, %[v]\n\t" 1210 "jnz 7f\n\t" 1211#endif 1212 /* try memcpy */ 1213 "test %[len], %[len]\n\t" \ 1214 "jz 333f\n\t" \ 1215 "222:\n\t" \ 1216 "movb (%[src]), %%al\n\t" \ 1217 "movb %%al, (%[dst])\n\t" \ 1218 "inc %[src]\n\t" \ 1219 "inc %[dst]\n\t" \ 1220 "dec %[len]\n\t" \ 1221 "jnz 222b\n\t" \ 1222 "333:\n\t" \ 1223 RSEQ_INJECT_ASM(5) 1224 "lock; addl $0,-128(%%esp)\n\t" 1225 "movl %[newv], %%eax\n\t" 1226 /* final store */ 1227 "movl %%eax, %[v]\n\t" 1228 "2:\n\t" 1229 RSEQ_INJECT_ASM(6) 1230 /* teardown */ 1231 "movl %[rseq_scratch2], %[len]\n\t" 1232 "movl %[rseq_scratch1], %[dst]\n\t" 1233 "movl %[rseq_scratch0], %[src]\n\t" 1234 RSEQ_ASM_DEFINE_ABORT(4, 1235 "movl %[rseq_scratch2], %[len]\n\t" 1236 "movl %[rseq_scratch1], %[dst]\n\t" 1237 "movl %[rseq_scratch0], %[src]\n\t", 1238 abort) 1239 RSEQ_ASM_DEFINE_CMPFAIL(5, 1240 "movl %[rseq_scratch2], %[len]\n\t" 1241 "movl %[rseq_scratch1], %[dst]\n\t" 1242 "movl %[rseq_scratch0], %[src]\n\t", 1243 cmpfail) 1244#ifdef RSEQ_COMPARE_TWICE 1245 RSEQ_ASM_DEFINE_CMPFAIL(6, 1246 "movl %[rseq_scratch2], %[len]\n\t" 1247 "movl %[rseq_scratch1], %[dst]\n\t" 1248 "movl %[rseq_scratch0], %[src]\n\t", 1249 error1) 1250 RSEQ_ASM_DEFINE_CMPFAIL(7, 1251 "movl %[rseq_scratch2], %[len]\n\t" 1252 "movl %[rseq_scratch1], %[dst]\n\t" 1253 "movl %[rseq_scratch0], %[src]\n\t", 1254 error2) 1255#endif 1256 : /* gcc asm goto does not allow outputs */ 1257 : [cpu_id] "r" (cpu), 1258 [rseq_abi] "r" (&__rseq_abi), 1259 /* final store input */ 1260 [v] "m" (*v), 1261 [expect] "m" (expect), 1262 [newv] "m" (newv), 1263 /* try memcpy input */ 1264 [dst] "r" (dst), 1265 [src] "r" (src), 1266 [len] "r" (len), 1267 [rseq_scratch0] "m" (rseq_scratch[0]), 1268 [rseq_scratch1] "m" (rseq_scratch[1]), 1269 [rseq_scratch2] "m" (rseq_scratch[2]) 1270 : "memory", "cc", "eax" 1271 RSEQ_INJECT_CLOBBER 1272 : abort, cmpfail 1273#ifdef RSEQ_COMPARE_TWICE 1274 , error1, error2 1275#endif 1276 ); 1277 return 0; 1278abort: 1279 RSEQ_INJECT_FAILED 1280 return -1; 1281cmpfail: 1282 return 1; 1283#ifdef RSEQ_COMPARE_TWICE 1284error1: 1285 rseq_bug("cpu_id comparison failed"); 1286error2: 1287 rseq_bug("expected value comparison failed"); 1288#endif 1289} 1290 1291#endif /* !RSEQ_SKIP_FASTPATH */ 1292 1293#endif