1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
5 * (C) Copyright 2016-2018 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
6 * (C) Copyright 2016-2018 - Boqun Feng <boqun.feng@gmail.com>
9 #define RSEQ_SIG 0x53053053
11 #define rseq_smp_mb() __asm__ __volatile__ ("sync" ::: "memory", "cc")
12 #define rseq_smp_lwsync() __asm__ __volatile__ ("lwsync" ::: "memory", "cc")
13 #define rseq_smp_rmb() rseq_smp_lwsync()
14 #define rseq_smp_wmb() rseq_smp_lwsync()
16 #define rseq_smp_load_acquire(p) \
18 __typeof(*p) ____p1 = RSEQ_READ_ONCE(*p); \
23 #define rseq_smp_acquire__after_ctrl_dep() rseq_smp_lwsync()
25 #define rseq_smp_store_release(p, v) \
28 RSEQ_WRITE_ONCE(*p, v); \
31 #ifdef RSEQ_SKIP_FASTPATH
32 #include "rseq-skip.h"
33 #else /* !RSEQ_SKIP_FASTPATH */
36 * The __rseq_table section can be used by debuggers to better handle
37 * single-stepping through the restartable critical sections.
42 #define STORE_WORD "std "
43 #define LOAD_WORD "ld "
44 #define LOADX_WORD "ldx "
45 #define CMP_WORD "cmpd "
47 #define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, \
48 start_ip, post_commit_offset, abort_ip) \
49 ".pushsection __rseq_table, \"aw\"\n\t" \
51 __rseq_str(label) ":\n\t" \
52 ".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
53 ".quad " __rseq_str(start_ip) ", " __rseq_str(post_commit_offset) ", " __rseq_str(abort_ip) "\n\t" \
56 #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \
58 "lis %%r17, (" __rseq_str(cs_label) ")@highest\n\t" \
59 "ori %%r17, %%r17, (" __rseq_str(cs_label) ")@higher\n\t" \
60 "rldicr %%r17, %%r17, 32, 31\n\t" \
61 "oris %%r17, %%r17, (" __rseq_str(cs_label) ")@high\n\t" \
62 "ori %%r17, %%r17, (" __rseq_str(cs_label) ")@l\n\t" \
63 "std %%r17, %[" __rseq_str(rseq_cs) "]\n\t" \
64 __rseq_str(label) ":\n\t"
66 #else /* #ifdef __PPC64__ */
68 #define STORE_WORD "stw "
69 #define LOAD_WORD "lwz "
70 #define LOADX_WORD "lwzx "
71 #define CMP_WORD "cmpw "
73 #define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, \
74 start_ip, post_commit_offset, abort_ip) \
75 ".pushsection __rseq_table, \"aw\"\n\t" \
77 __rseq_str(label) ":\n\t" \
78 ".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
79 /* 32-bit only supported on BE */ \
80 ".long 0x0, " __rseq_str(start_ip) ", 0x0, " __rseq_str(post_commit_offset) ", 0x0, " __rseq_str(abort_ip) "\n\t" \
83 #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \
85 "lis %%r17, (" __rseq_str(cs_label) ")@ha\n\t" \
86 "addi %%r17, %%r17, (" __rseq_str(cs_label) ")@l\n\t" \
87 "stw %%r17, %[" __rseq_str(rseq_cs) "]\n\t" \
88 __rseq_str(label) ":\n\t"
90 #endif /* #ifdef __PPC64__ */
92 #define RSEQ_ASM_DEFINE_TABLE(label, start_ip, post_commit_ip, abort_ip) \
93 __RSEQ_ASM_DEFINE_TABLE(label, 0x0, 0x0, start_ip, \
94 (post_commit_ip - start_ip), abort_ip)
96 #define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label) \
98 "lwz %%r17, %[" __rseq_str(current_cpu_id) "]\n\t" \
99 "cmpw cr7, %[" __rseq_str(cpu_id) "], %%r17\n\t" \
100 "bne- cr7, " __rseq_str(label) "\n\t"
102 #define RSEQ_ASM_DEFINE_ABORT(label, abort_label) \
103 ".pushsection __rseq_failure, \"ax\"\n\t" \
104 ".long " __rseq_str(RSEQ_SIG) "\n\t" \
105 __rseq_str(label) ":\n\t" \
106 "b %l[" __rseq_str(abort_label) "]\n\t" \
110 * RSEQ_ASM_OPs: asm operations for rseq
111 * RSEQ_ASM_OP_R_*: has hard-code registers in it
112 * RSEQ_ASM_OP_* (else): doesn't have hard-code registers(unless cr7)
114 #define RSEQ_ASM_OP_CMPEQ(var, expect, label) \
115 LOAD_WORD "%%r17, %[" __rseq_str(var) "]\n\t" \
116 CMP_WORD "cr7, %%r17, %[" __rseq_str(expect) "]\n\t" \
117 "bne- cr7, " __rseq_str(label) "\n\t"
119 #define RSEQ_ASM_OP_CMPNE(var, expectnot, label) \
120 LOAD_WORD "%%r17, %[" __rseq_str(var) "]\n\t" \
121 CMP_WORD "cr7, %%r17, %[" __rseq_str(expectnot) "]\n\t" \
122 "beq- cr7, " __rseq_str(label) "\n\t"
124 #define RSEQ_ASM_OP_STORE(value, var) \
125 STORE_WORD "%[" __rseq_str(value) "], %[" __rseq_str(var) "]\n\t"
127 /* Load @var to r17 */
128 #define RSEQ_ASM_OP_R_LOAD(var) \
129 LOAD_WORD "%%r17, %[" __rseq_str(var) "]\n\t"
131 /* Store r17 to @var */
132 #define RSEQ_ASM_OP_R_STORE(var) \
133 STORE_WORD "%%r17, %[" __rseq_str(var) "]\n\t"
135 /* Add @count to r17 */
136 #define RSEQ_ASM_OP_R_ADD(count) \
137 "add %%r17, %[" __rseq_str(count) "], %%r17\n\t"
139 /* Load (r17 + voffp) to r17 */
140 #define RSEQ_ASM_OP_R_LOADX(voffp) \
141 LOADX_WORD "%%r17, %[" __rseq_str(voffp) "], %%r17\n\t"
143 /* TODO: implement a faster memcpy. */
144 #define RSEQ_ASM_OP_R_MEMCPY() \
145 "cmpdi %%r19, 0\n\t" \
147 "addi %%r20, %%r20, -1\n\t" \
148 "addi %%r21, %%r21, -1\n\t" \
150 "lbzu %%r18, 1(%%r20)\n\t" \
151 "stbu %%r18, 1(%%r21)\n\t" \
152 "addi %%r19, %%r19, -1\n\t" \
153 "cmpdi %%r19, 0\n\t" \
157 #define RSEQ_ASM_OP_R_FINAL_STORE(var, post_commit_label) \
158 STORE_WORD "%%r17, %[" __rseq_str(var) "]\n\t" \
159 __rseq_str(post_commit_label) ":\n\t"
161 #define RSEQ_ASM_OP_FINAL_STORE(value, var, post_commit_label) \
162 STORE_WORD "%[" __rseq_str(value) "], %[" __rseq_str(var) "]\n\t" \
163 __rseq_str(post_commit_label) ":\n\t"
165 static inline __attribute__((always_inline
))
166 int rseq_cmpeqv_storev(intptr_t *v
, intptr_t expect
, intptr_t newv
, int cpu
)
170 __asm__ __volatile__
goto (
171 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
172 /* Start rseq by storing table entry pointer into rseq_cs. */
173 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
175 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
177 /* cmp @v equal to @expect */
178 RSEQ_ASM_OP_CMPEQ(v
, expect
, %l
[cmpfail
])
180 #ifdef RSEQ_COMPARE_TWICE
182 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
183 /* cmp @v equal to @expect */
184 RSEQ_ASM_OP_CMPEQ(v
, expect
, %l
[error2
])
187 RSEQ_ASM_OP_FINAL_STORE(newv
, v
, 2)
189 RSEQ_ASM_DEFINE_ABORT(4, abort
)
190 : /* gcc asm goto does not allow outputs */
191 : [cpu_id
] "r" (cpu
),
192 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
193 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
195 [expect
] "r" (expect
),
198 : "memory", "cc", "r17"
201 #ifdef RSEQ_COMPARE_TWICE
211 #ifdef RSEQ_COMPARE_TWICE
213 rseq_bug("cpu_id comparison failed");
215 rseq_bug("expected value comparison failed");
219 static inline __attribute__((always_inline
))
220 int rseq_cmpnev_storeoffp_load(intptr_t *v
, intptr_t expectnot
,
221 off_t voffp
, intptr_t *load
, int cpu
)
225 __asm__ __volatile__
goto (
226 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
227 /* Start rseq by storing table entry pointer into rseq_cs. */
228 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
230 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
232 /* cmp @v not equal to @expectnot */
233 RSEQ_ASM_OP_CMPNE(v
, expectnot
, %l
[cmpfail
])
235 #ifdef RSEQ_COMPARE_TWICE
237 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
238 /* cmp @v not equal to @expectnot */
239 RSEQ_ASM_OP_CMPNE(v
, expectnot
, %l
[error2
])
241 /* load the value of @v */
242 RSEQ_ASM_OP_R_LOAD(v
)
243 /* store it in @load */
244 RSEQ_ASM_OP_R_STORE(load
)
245 /* dereference voffp(v) */
246 RSEQ_ASM_OP_R_LOADX(voffp
)
247 /* final store the value at voffp(v) */
248 RSEQ_ASM_OP_R_FINAL_STORE(v
, 2)
250 RSEQ_ASM_DEFINE_ABORT(4, abort
)
251 : /* gcc asm goto does not allow outputs */
252 : [cpu_id
] "r" (cpu
),
253 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
254 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
255 /* final store input */
257 [expectnot
] "r" (expectnot
),
261 : "memory", "cc", "r17"
264 #ifdef RSEQ_COMPARE_TWICE
274 #ifdef RSEQ_COMPARE_TWICE
276 rseq_bug("cpu_id comparison failed");
278 rseq_bug("expected value comparison failed");
282 static inline __attribute__((always_inline
))
283 int rseq_addv(intptr_t *v
, intptr_t count
, int cpu
)
287 __asm__ __volatile__
goto (
288 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
289 /* Start rseq by storing table entry pointer into rseq_cs. */
290 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
292 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
294 #ifdef RSEQ_COMPARE_TWICE
296 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
298 /* load the value of @v */
299 RSEQ_ASM_OP_R_LOAD(v
)
300 /* add @count to it */
301 RSEQ_ASM_OP_R_ADD(count
)
303 RSEQ_ASM_OP_R_FINAL_STORE(v
, 2)
305 RSEQ_ASM_DEFINE_ABORT(4, abort
)
306 : /* gcc asm goto does not allow outputs */
307 : [cpu_id
] "r" (cpu
),
308 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
309 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
310 /* final store input */
314 : "memory", "cc", "r17"
317 #ifdef RSEQ_COMPARE_TWICE
325 #ifdef RSEQ_COMPARE_TWICE
327 rseq_bug("cpu_id comparison failed");
331 static inline __attribute__((always_inline
))
332 int rseq_cmpeqv_trystorev_storev(intptr_t *v
, intptr_t expect
,
333 intptr_t *v2
, intptr_t newv2
,
334 intptr_t newv
, int cpu
)
338 __asm__ __volatile__
goto (
339 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
340 /* Start rseq by storing table entry pointer into rseq_cs. */
341 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
343 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
345 /* cmp @v equal to @expect */
346 RSEQ_ASM_OP_CMPEQ(v
, expect
, %l
[cmpfail
])
348 #ifdef RSEQ_COMPARE_TWICE
350 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
351 /* cmp @v equal to @expect */
352 RSEQ_ASM_OP_CMPEQ(v
, expect
, %l
[error2
])
355 RSEQ_ASM_OP_STORE(newv2
, v2
)
358 RSEQ_ASM_OP_FINAL_STORE(newv
, v
, 2)
360 RSEQ_ASM_DEFINE_ABORT(4, abort
)
361 : /* gcc asm goto does not allow outputs */
362 : [cpu_id
] "r" (cpu
),
363 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
364 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
365 /* try store input */
368 /* final store input */
370 [expect
] "r" (expect
),
373 : "memory", "cc", "r17"
376 #ifdef RSEQ_COMPARE_TWICE
386 #ifdef RSEQ_COMPARE_TWICE
388 rseq_bug("cpu_id comparison failed");
390 rseq_bug("expected value comparison failed");
394 static inline __attribute__((always_inline
))
395 int rseq_cmpeqv_trystorev_storev_release(intptr_t *v
, intptr_t expect
,
396 intptr_t *v2
, intptr_t newv2
,
397 intptr_t newv
, int cpu
)
401 __asm__ __volatile__
goto (
402 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
403 /* Start rseq by storing table entry pointer into rseq_cs. */
404 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
406 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
408 /* cmp @v equal to @expect */
409 RSEQ_ASM_OP_CMPEQ(v
, expect
, %l
[cmpfail
])
411 #ifdef RSEQ_COMPARE_TWICE
413 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
414 /* cmp @v equal to @expect */
415 RSEQ_ASM_OP_CMPEQ(v
, expect
, %l
[error2
])
418 RSEQ_ASM_OP_STORE(newv2
, v2
)
423 RSEQ_ASM_OP_FINAL_STORE(newv
, v
, 2)
425 RSEQ_ASM_DEFINE_ABORT(4, abort
)
426 : /* gcc asm goto does not allow outputs */
427 : [cpu_id
] "r" (cpu
),
428 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
429 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
430 /* try store input */
433 /* final store input */
435 [expect
] "r" (expect
),
438 : "memory", "cc", "r17"
441 #ifdef RSEQ_COMPARE_TWICE
451 #ifdef RSEQ_COMPARE_TWICE
453 rseq_bug("cpu_id comparison failed");
455 rseq_bug("expected value comparison failed");
459 static inline __attribute__((always_inline
))
460 int rseq_cmpeqv_cmpeqv_storev(intptr_t *v
, intptr_t expect
,
461 intptr_t *v2
, intptr_t expect2
,
462 intptr_t newv
, int cpu
)
466 __asm__ __volatile__
goto (
467 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
468 /* Start rseq by storing table entry pointer into rseq_cs. */
469 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
471 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
473 /* cmp @v equal to @expect */
474 RSEQ_ASM_OP_CMPEQ(v
, expect
, %l
[cmpfail
])
476 /* cmp @v2 equal to @expct2 */
477 RSEQ_ASM_OP_CMPEQ(v2
, expect2
, %l
[cmpfail
])
479 #ifdef RSEQ_COMPARE_TWICE
481 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
482 /* cmp @v equal to @expect */
483 RSEQ_ASM_OP_CMPEQ(v
, expect
, %l
[error2
])
484 /* cmp @v2 equal to @expct2 */
485 RSEQ_ASM_OP_CMPEQ(v2
, expect2
, %l
[error3
])
488 RSEQ_ASM_OP_FINAL_STORE(newv
, v
, 2)
490 RSEQ_ASM_DEFINE_ABORT(4, abort
)
491 : /* gcc asm goto does not allow outputs */
492 : [cpu_id
] "r" (cpu
),
493 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
494 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
497 [expect2
] "r" (expect2
),
498 /* final store input */
500 [expect
] "r" (expect
),
503 : "memory", "cc", "r17"
506 #ifdef RSEQ_COMPARE_TWICE
507 , error1
, error2
, error3
516 #ifdef RSEQ_COMPARE_TWICE
518 rseq_bug("cpu_id comparison failed");
520 rseq_bug("1st expected value comparison failed");
522 rseq_bug("2nd expected value comparison failed");
526 static inline __attribute__((always_inline
))
527 int rseq_cmpeqv_trymemcpy_storev(intptr_t *v
, intptr_t expect
,
528 void *dst
, void *src
, size_t len
,
529 intptr_t newv
, int cpu
)
533 __asm__ __volatile__
goto (
534 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
535 /* setup for mempcy */
536 "mr %%r19, %[len]\n\t"
537 "mr %%r20, %[src]\n\t"
538 "mr %%r21, %[dst]\n\t"
539 /* Start rseq by storing table entry pointer into rseq_cs. */
540 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
542 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
544 /* cmp @v equal to @expect */
545 RSEQ_ASM_OP_CMPEQ(v
, expect
, %l
[cmpfail
])
547 #ifdef RSEQ_COMPARE_TWICE
549 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
550 /* cmp @v equal to @expect */
551 RSEQ_ASM_OP_CMPEQ(v
, expect
, %l
[error2
])
554 RSEQ_ASM_OP_R_MEMCPY()
557 RSEQ_ASM_OP_FINAL_STORE(newv
, v
, 2)
560 RSEQ_ASM_DEFINE_ABORT(4, abort
)
561 : /* gcc asm goto does not allow outputs */
562 : [cpu_id
] "r" (cpu
),
563 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
564 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
565 /* final store input */
567 [expect
] "r" (expect
),
569 /* try memcpy input */
574 : "memory", "cc", "r17", "r18", "r19", "r20", "r21"
577 #ifdef RSEQ_COMPARE_TWICE
587 #ifdef RSEQ_COMPARE_TWICE
589 rseq_bug("cpu_id comparison failed");
591 rseq_bug("expected value comparison failed");
595 static inline __attribute__((always_inline
))
596 int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v
, intptr_t expect
,
597 void *dst
, void *src
, size_t len
,
598 intptr_t newv
, int cpu
)
602 __asm__ __volatile__
goto (
603 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
604 /* setup for mempcy */
605 "mr %%r19, %[len]\n\t"
606 "mr %%r20, %[src]\n\t"
607 "mr %%r21, %[dst]\n\t"
608 /* Start rseq by storing table entry pointer into rseq_cs. */
609 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
611 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
613 /* cmp @v equal to @expect */
614 RSEQ_ASM_OP_CMPEQ(v
, expect
, %l
[cmpfail
])
616 #ifdef RSEQ_COMPARE_TWICE
618 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
619 /* cmp @v equal to @expect */
620 RSEQ_ASM_OP_CMPEQ(v
, expect
, %l
[error2
])
623 RSEQ_ASM_OP_R_MEMCPY()
628 RSEQ_ASM_OP_FINAL_STORE(newv
, v
, 2)
631 RSEQ_ASM_DEFINE_ABORT(4, abort
)
632 : /* gcc asm goto does not allow outputs */
633 : [cpu_id
] "r" (cpu
),
634 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
635 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
636 /* final store input */
638 [expect
] "r" (expect
),
640 /* try memcpy input */
645 : "memory", "cc", "r17", "r18", "r19", "r20", "r21"
648 #ifdef RSEQ_COMPARE_TWICE
658 #ifdef RSEQ_COMPARE_TWICE
660 rseq_bug("cpu_id comparison failed");
662 rseq_bug("expected value comparison failed");
671 #endif /* !RSEQ_SKIP_FASTPATH */