1 /* SPDX-License-Identifier: LGPL-2.1-only OR MIT */
3 #define RSEQ_SIG 0x53053053
5 #define rseq_smp_mb() __asm__ __volatile__ ("bcr 15,0" ::: "memory")
6 #define rseq_smp_rmb() rseq_smp_mb()
7 #define rseq_smp_wmb() rseq_smp_mb()
9 #define rseq_smp_load_acquire(p) \
11 __typeof(*p) ____p1 = RSEQ_READ_ONCE(*p); \
16 #define rseq_smp_acquire__after_ctrl_dep() rseq_smp_rmb()
18 #define rseq_smp_store_release(p, v) \
21 RSEQ_WRITE_ONCE(*p, v); \
24 #ifdef RSEQ_SKIP_FASTPATH
25 #include "rseq-skip.h"
26 #else /* !RSEQ_SKIP_FASTPATH */
32 #define LONG_LT_R "ltgr"
34 #define LONG_CMP_R "cgr"
35 #define LONG_ADDI "aghi"
36 #define LONG_ADD_R "agr"
38 #define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, \
39 start_ip, post_commit_offset, abort_ip) \
40 ".pushsection __rseq_cs, \"aw\"\n\t" \
42 __rseq_str(label) ":\n\t" \
43 ".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
44 ".quad " __rseq_str(start_ip) ", " __rseq_str(post_commit_offset) ", " __rseq_str(abort_ip) "\n\t" \
46 ".pushsection __rseq_cs_ptr_array, \"aw\"\n\t" \
47 ".quad " __rseq_str(label) "b\n\t" \
51 * Exit points of a rseq critical section consist of all instructions outside
52 * of the critical section where a critical section can branch to. The abort IP
53 * is already part of the __rseq_cs section and should not be explicitly
54 * defined as an additional exit point. Knowing all exit points is useful to
55 * assist debuggers stepping over the critical section.
57 #define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip) \
58 ".pushsection __rseq_exit_point_array, \"aw\"\n\t" \
59 ".quad " __rseq_str(start_ip) ", " __rseq_str(exit_ip) "\n\t" \
64 #define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, \
65 start_ip, post_commit_offset, abort_ip) \
66 ".pushsection __rseq_cs, \"aw\"\n\t" \
68 __rseq_str(label) ":\n\t" \
69 ".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
70 ".long 0x0, " __rseq_str(start_ip) ", 0x0, " __rseq_str(post_commit_offset) ", 0x0, " __rseq_str(abort_ip) "\n\t" \
72 ".pushsection __rseq_cs_ptr_array, \"aw\"\n\t" \
73 ".long 0x0, " __rseq_str(label) "b\n\t" \
77 * Exit points of a rseq critical section consist of all instructions outside
78 * of the critical section where a critical section can branch to. The abort IP
79 * is already part of the __rseq_cs and should not be explicitly defined as
80 * an additional exit point. Knowing all exit points is useful to assist
81 * debuggers stepping over the critical section.
83 #define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip) \
84 ".pushsection __rseq_exit_point_array, \"aw\"\n\t" \
85 ".long 0x0, " __rseq_str(start_ip) ", 0x0, " __rseq_str(exit_ip) "\n\t" \
90 #define LONG_LT_R "ltr"
92 #define LONG_CMP_R "cr"
93 #define LONG_ADDI "ahi"
94 #define LONG_ADD_R "ar"
98 #define RSEQ_ASM_DEFINE_TABLE(label, start_ip, post_commit_ip, abort_ip) \
99 __RSEQ_ASM_DEFINE_TABLE(label, 0x0, 0x0, start_ip, \
100 (post_commit_ip - start_ip), abort_ip)
102 #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \
104 "larl %%r0, " __rseq_str(cs_label) "\n\t" \
105 LONG_S " %%r0, %[" __rseq_str(rseq_cs) "]\n\t" \
106 __rseq_str(label) ":\n\t"
108 #define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label) \
110 "c %[" __rseq_str(cpu_id) "], %[" __rseq_str(current_cpu_id) "]\n\t" \
111 "jnz " __rseq_str(label) "\n\t"
113 #define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label) \
114 ".pushsection __rseq_failure, \"ax\"\n\t" \
115 ".long " __rseq_str(RSEQ_SIG) "\n\t" \
116 __rseq_str(label) ":\n\t" \
118 "j %l[" __rseq_str(abort_label) "]\n\t" \
121 #define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label) \
122 ".pushsection __rseq_failure, \"ax\"\n\t" \
123 __rseq_str(label) ":\n\t" \
125 "j %l[" __rseq_str(cmpfail_label) "]\n\t" \
128 static inline __attribute__((always_inline
))
129 int rseq_cmpeqv_storev(intptr_t *v
, intptr_t expect
, intptr_t newv
, int cpu
)
133 __asm__ __volatile__
goto (
134 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
135 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
136 #ifdef RSEQ_COMPARE_TWICE
137 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
138 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
140 /* Start rseq by storing table entry pointer into rseq_cs. */
141 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
142 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
144 LONG_CMP
" %[expect], %[v]\n\t"
145 "jnz %l[cmpfail]\n\t"
147 #ifdef RSEQ_COMPARE_TWICE
148 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
149 LONG_CMP
" %[expect], %[v]\n\t"
153 LONG_S
" %[newv], %[v]\n\t"
156 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
157 : /* gcc asm goto does not allow outputs */
158 : [cpu_id
] "r" (cpu
),
159 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
160 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
162 [expect
] "r" (expect
),
165 : "memory", "cc", "r0"
168 #ifdef RSEQ_COMPARE_TWICE
178 #ifdef RSEQ_COMPARE_TWICE
180 rseq_bug("cpu_id comparison failed");
182 rseq_bug("expected value comparison failed");
187 * Compare @v against @expectnot. When it does _not_ match, load @v
188 * into @load, and store the content of *@v + voffp into @v.
190 static inline __attribute__((always_inline
))
191 int rseq_cmpnev_storeoffp_load(intptr_t *v
, intptr_t expectnot
,
192 off_t voffp
, intptr_t *load
, int cpu
)
196 __asm__ __volatile__
goto (
197 RSEQ_ASM_DEFINE_TABLE(3, 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
])
203 /* Start rseq by storing table entry pointer into rseq_cs. */
204 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
205 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
207 LONG_L
" %%r1, %[v]\n\t"
208 LONG_CMP_R
" %%r1, %[expectnot]\n\t"
211 #ifdef RSEQ_COMPARE_TWICE
212 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
213 LONG_L
" %%r1, %[v]\n\t"
214 LONG_CMP_R
" %%r1, %[expectnot]\n\t"
217 LONG_S
" %%r1, %[load]\n\t"
218 LONG_ADD_R
" %%r1, %[voffp]\n\t"
219 LONG_L
" %%r1, 0(%%r1)\n\t"
221 LONG_S
" %%r1, %[v]\n\t"
224 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
225 : /* gcc asm goto does not allow outputs */
226 : [cpu_id
] "r" (cpu
),
227 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
228 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
229 /* final store input */
231 [expectnot
] "r" (expectnot
),
235 : "memory", "cc", "r0", "r1"
238 #ifdef RSEQ_COMPARE_TWICE
248 #ifdef RSEQ_COMPARE_TWICE
250 rseq_bug("cpu_id comparison failed");
252 rseq_bug("expected value comparison failed");
256 static inline __attribute__((always_inline
))
257 int rseq_addv(intptr_t *v
, intptr_t count
, int cpu
)
261 __asm__ __volatile__
goto (
262 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
263 #ifdef RSEQ_COMPARE_TWICE
264 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
266 /* Start rseq by storing table entry pointer into rseq_cs. */
267 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
268 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
270 #ifdef RSEQ_COMPARE_TWICE
271 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
273 LONG_L
" %%r0, %[v]\n\t"
274 LONG_ADD_R
" %%r0, %[count]\n\t"
276 LONG_S
" %%r0, %[v]\n\t"
279 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
280 : /* gcc asm goto does not allow outputs */
281 : [cpu_id
] "r" (cpu
),
282 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
283 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
284 /* final store input */
288 : "memory", "cc", "r0"
291 #ifdef RSEQ_COMPARE_TWICE
299 #ifdef RSEQ_COMPARE_TWICE
301 rseq_bug("cpu_id comparison failed");
305 static inline __attribute__((always_inline
))
306 int rseq_cmpeqv_trystorev_storev(intptr_t *v
, intptr_t expect
,
307 intptr_t *v2
, intptr_t newv2
,
308 intptr_t newv
, int cpu
)
312 __asm__ __volatile__
goto (
313 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
314 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
315 #ifdef RSEQ_COMPARE_TWICE
316 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
317 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
319 /* Start rseq by storing table entry pointer into rseq_cs. */
320 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
321 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
323 LONG_CMP
" %[expect], %[v]\n\t"
324 "jnz %l[cmpfail]\n\t"
326 #ifdef RSEQ_COMPARE_TWICE
327 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
328 LONG_CMP
" %[expect], %[v]\n\t"
332 LONG_S
" %[newv2], %[v2]\n\t"
335 LONG_S
" %[newv], %[v]\n\t"
338 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
339 : /* gcc asm goto does not allow outputs */
340 : [cpu_id
] "r" (cpu
),
341 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
342 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
343 /* try store input */
346 /* final store input */
348 [expect
] "r" (expect
),
351 : "memory", "cc", "r0"
354 #ifdef RSEQ_COMPARE_TWICE
364 #ifdef RSEQ_COMPARE_TWICE
366 rseq_bug("cpu_id comparison failed");
368 rseq_bug("expected value comparison failed");
373 static inline __attribute__((always_inline
))
374 int rseq_cmpeqv_trystorev_storev_release(intptr_t *v
, intptr_t expect
,
375 intptr_t *v2
, intptr_t newv2
,
376 intptr_t newv
, int cpu
)
378 return rseq_cmpeqv_trystorev_storev(v
, expect
, v2
, newv2
, newv
, cpu
);
381 static inline __attribute__((always_inline
))
382 int rseq_cmpeqv_cmpeqv_storev(intptr_t *v
, intptr_t expect
,
383 intptr_t *v2
, intptr_t expect2
,
384 intptr_t newv
, int cpu
)
388 __asm__ __volatile__
goto (
389 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
390 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
391 #ifdef RSEQ_COMPARE_TWICE
392 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
393 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
394 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error3
])
396 /* Start rseq by storing table entry pointer into rseq_cs. */
397 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
398 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
400 LONG_CMP
" %[expect], %[v]\n\t"
401 "jnz %l[cmpfail]\n\t"
403 LONG_CMP
" %[expect2], %[v2]\n\t"
404 "jnz %l[cmpfail]\n\t"
406 #ifdef RSEQ_COMPARE_TWICE
407 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
408 LONG_CMP
" %[expect], %[v]\n\t"
410 LONG_CMP
" %[expect2], %[v2]\n\t"
414 LONG_S
" %[newv], %[v]\n\t"
417 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
418 : /* gcc asm goto does not allow outputs */
419 : [cpu_id
] "r" (cpu
),
420 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
421 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
424 [expect2
] "r" (expect2
),
425 /* final store input */
427 [expect
] "r" (expect
),
430 : "memory", "cc", "r0"
433 #ifdef RSEQ_COMPARE_TWICE
434 , error1
, error2
, error3
443 #ifdef RSEQ_COMPARE_TWICE
445 rseq_bug("cpu_id comparison failed");
447 rseq_bug("1st expected value comparison failed");
449 rseq_bug("2nd expected value comparison failed");
453 static inline __attribute__((always_inline
))
454 int rseq_cmpeqv_trymemcpy_storev(intptr_t *v
, intptr_t expect
,
455 void *dst
, void *src
, size_t len
,
456 intptr_t newv
, int cpu
)
458 uint64_t rseq_scratch
[3];
462 __asm__ __volatile__
goto (
463 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
464 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
465 #ifdef RSEQ_COMPARE_TWICE
466 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
467 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
469 LONG_S
" %[src], %[rseq_scratch0]\n\t"
470 LONG_S
" %[dst], %[rseq_scratch1]\n\t"
471 LONG_S
" %[len], %[rseq_scratch2]\n\t"
472 /* Start rseq by storing table entry pointer into rseq_cs. */
473 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
474 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
476 LONG_CMP
" %[expect], %[v]\n\t"
479 #ifdef RSEQ_COMPARE_TWICE
480 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 6f
)
481 LONG_CMP
" %[expect], %[v]\n\t"
485 LONG_LT_R
" %[len], %[len]\n\t"
488 "ic %%r0,0(%[src])\n\t"
489 "stc %%r0,0(%[dst])\n\t"
490 LONG_ADDI
" %[src], 1\n\t"
491 LONG_ADDI
" %[dst], 1\n\t"
492 LONG_ADDI
" %[len], -1\n\t"
497 LONG_S
" %[newv], %[v]\n\t"
501 LONG_L
" %[len], %[rseq_scratch2]\n\t"
502 LONG_L
" %[dst], %[rseq_scratch1]\n\t"
503 LONG_L
" %[src], %[rseq_scratch0]\n\t"
504 RSEQ_ASM_DEFINE_ABORT(4,
505 LONG_L
" %[len], %[rseq_scratch2]\n\t"
506 LONG_L
" %[dst], %[rseq_scratch1]\n\t"
507 LONG_L
" %[src], %[rseq_scratch0]\n\t",
509 RSEQ_ASM_DEFINE_CMPFAIL(5,
510 LONG_L
" %[len], %[rseq_scratch2]\n\t"
511 LONG_L
" %[dst], %[rseq_scratch1]\n\t"
512 LONG_L
" %[src], %[rseq_scratch0]\n\t",
514 #ifdef RSEQ_COMPARE_TWICE
515 RSEQ_ASM_DEFINE_CMPFAIL(6,
516 LONG_L
" %[len], %[rseq_scratch2]\n\t"
517 LONG_L
" %[dst], %[rseq_scratch1]\n\t"
518 LONG_L
" %[src], %[rseq_scratch0]\n\t",
520 RSEQ_ASM_DEFINE_CMPFAIL(7,
521 LONG_L
" %[len], %[rseq_scratch2]\n\t"
522 LONG_L
" %[dst], %[rseq_scratch1]\n\t"
523 LONG_L
" %[src], %[rseq_scratch0]\n\t",
526 : /* gcc asm goto does not allow outputs */
527 : [cpu_id
] "r" (cpu
),
528 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
529 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
530 /* final store input */
532 [expect
] "r" (expect
),
534 /* try memcpy input */
538 [rseq_scratch0
] "m" (rseq_scratch
[0]),
539 [rseq_scratch1
] "m" (rseq_scratch
[1]),
540 [rseq_scratch2
] "m" (rseq_scratch
[2])
542 : "memory", "cc", "r0"
545 #ifdef RSEQ_COMPARE_TWICE
555 #ifdef RSEQ_COMPARE_TWICE
557 rseq_bug("cpu_id comparison failed");
559 rseq_bug("expected value comparison failed");
564 static inline __attribute__((always_inline
))
565 int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v
, intptr_t expect
,
566 void *dst
, void *src
, size_t len
,
567 intptr_t newv
, int cpu
)
569 return rseq_cmpeqv_trymemcpy_storev(v
, expect
, dst
, src
, len
,
572 #endif /* !RSEQ_SKIP_FASTPATH */
This page took 0.04304 seconds and 5 git commands to generate.