1 /* SPDX-License-Identifier: LGPL-2.1 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_table, \"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" \
49 #define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, \
50 start_ip, post_commit_offset, abort_ip) \
51 ".pushsection __rseq_table, \"aw\"\n\t" \
53 __rseq_str(label) ":\n\t" \
54 ".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
55 ".long 0x0, " __rseq_str(start_ip) ", 0x0, " __rseq_str(post_commit_offset) ", 0x0, " __rseq_str(abort_ip) "\n\t" \
60 #define LONG_LT_R "ltr"
62 #define LONG_CMP_R "cr"
63 #define LONG_ADDI "ahi"
64 #define LONG_ADD_R "ar"
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)
72 #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \
74 "larl %%r0, " __rseq_str(cs_label) "\n\t" \
75 LONG_S " %%r0, %[" __rseq_str(rseq_cs) "]\n\t" \
76 __rseq_str(label) ":\n\t"
78 #define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label) \
80 "c %[" __rseq_str(cpu_id) "], %[" __rseq_str(current_cpu_id) "]\n\t" \
81 "jnz " __rseq_str(label) "\n\t"
83 #define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label) \
84 ".pushsection __rseq_failure, \"ax\"\n\t" \
85 ".long " __rseq_str(RSEQ_SIG) "\n\t" \
86 __rseq_str(label) ":\n\t" \
88 "j %l[" __rseq_str(abort_label) "]\n\t" \
91 #define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label) \
92 ".pushsection __rseq_failure, \"ax\"\n\t" \
93 __rseq_str(label) ":\n\t" \
95 "j %l[" __rseq_str(cmpfail_label) "]\n\t" \
98 static inline __attribute__((always_inline
))
99 int rseq_cmpeqv_storev(intptr_t *v
, intptr_t expect
, intptr_t newv
, int cpu
)
103 __asm__ __volatile__
goto (
104 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
105 /* Start rseq by storing table entry pointer into rseq_cs. */
106 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
107 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
109 LONG_CMP
" %[expect], %[v]\n\t"
110 "jnz %l[cmpfail]\n\t"
112 #ifdef RSEQ_COMPARE_TWICE
113 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
114 LONG_CMP
" %[expect], %[v]\n\t"
118 LONG_S
" %[newv], %[v]\n\t"
121 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
122 : /* gcc asm goto does not allow outputs */
123 : [cpu_id
] "r" (cpu
),
124 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
125 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
127 [expect
] "r" (expect
),
130 : "memory", "cc", "r0"
133 #ifdef RSEQ_COMPARE_TWICE
143 #ifdef RSEQ_COMPARE_TWICE
145 rseq_bug("cpu_id comparison failed");
147 rseq_bug("expected value comparison failed");
152 * Compare @v against @expectnot. When it does _not_ match, load @v
153 * into @load, and store the content of *@v + voffp into @v.
155 static inline __attribute__((always_inline
))
156 int rseq_cmpnev_storeoffp_load(intptr_t *v
, intptr_t expectnot
,
157 off_t voffp
, intptr_t *load
, int cpu
)
161 __asm__ __volatile__
goto (
162 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
163 /* Start rseq by storing table entry pointer into rseq_cs. */
164 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
165 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
167 LONG_L
" %%r1, %[v]\n\t"
168 LONG_CMP_R
" %%r1, %[expectnot]\n\t"
171 #ifdef RSEQ_COMPARE_TWICE
172 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
173 LONG_L
" %%r1, %[v]\n\t"
174 LONG_CMP_R
" %%r1, %[expectnot]\n\t"
177 LONG_S
" %%r1, %[load]\n\t"
178 LONG_ADD_R
" %%r1, %[voffp]\n\t"
179 LONG_L
" %%r1, 0(%%r1)\n\t"
181 LONG_S
" %%r1, %[v]\n\t"
184 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
185 : /* gcc asm goto does not allow outputs */
186 : [cpu_id
] "r" (cpu
),
187 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
188 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
189 /* final store input */
191 [expectnot
] "r" (expectnot
),
195 : "memory", "cc", "r0", "r1"
198 #ifdef RSEQ_COMPARE_TWICE
208 #ifdef RSEQ_COMPARE_TWICE
210 rseq_bug("cpu_id comparison failed");
212 rseq_bug("expected value comparison failed");
216 static inline __attribute__((always_inline
))
217 int rseq_addv(intptr_t *v
, intptr_t count
, int cpu
)
221 __asm__ __volatile__
goto (
222 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
223 /* Start rseq by storing table entry pointer into rseq_cs. */
224 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
225 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
227 #ifdef RSEQ_COMPARE_TWICE
228 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
230 LONG_L
" %%r0, %[v]\n\t"
231 LONG_ADD_R
" %%r0, %[count]\n\t"
233 LONG_S
" %%r0, %[v]\n\t"
236 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
237 : /* gcc asm goto does not allow outputs */
238 : [cpu_id
] "r" (cpu
),
239 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
240 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
241 /* final store input */
245 : "memory", "cc", "r0"
248 #ifdef RSEQ_COMPARE_TWICE
256 #ifdef RSEQ_COMPARE_TWICE
258 rseq_bug("cpu_id comparison failed");
262 static inline __attribute__((always_inline
))
263 int rseq_cmpeqv_trystorev_storev(intptr_t *v
, intptr_t expect
,
264 intptr_t *v2
, intptr_t newv2
,
265 intptr_t newv
, int cpu
)
269 __asm__ __volatile__
goto (
270 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
271 /* Start rseq by storing table entry pointer into rseq_cs. */
272 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
273 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
275 LONG_CMP
" %[expect], %[v]\n\t"
276 "jnz %l[cmpfail]\n\t"
278 #ifdef RSEQ_COMPARE_TWICE
279 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
280 LONG_CMP
" %[expect], %[v]\n\t"
284 LONG_S
" %[newv2], %[v2]\n\t"
287 LONG_S
" %[newv], %[v]\n\t"
290 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
291 : /* gcc asm goto does not allow outputs */
292 : [cpu_id
] "r" (cpu
),
293 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
294 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
295 /* try store input */
298 /* final store input */
300 [expect
] "r" (expect
),
303 : "memory", "cc", "r0"
306 #ifdef RSEQ_COMPARE_TWICE
316 #ifdef RSEQ_COMPARE_TWICE
318 rseq_bug("cpu_id comparison failed");
320 rseq_bug("expected value comparison failed");
325 static inline __attribute__((always_inline
))
326 int rseq_cmpeqv_trystorev_storev_release(intptr_t *v
, intptr_t expect
,
327 intptr_t *v2
, intptr_t newv2
,
328 intptr_t newv
, int cpu
)
330 return rseq_cmpeqv_trystorev_storev(v
, expect
, v2
, newv2
, newv
, cpu
);
333 static inline __attribute__((always_inline
))
334 int rseq_cmpeqv_cmpeqv_storev(intptr_t *v
, intptr_t expect
,
335 intptr_t *v2
, intptr_t expect2
,
336 intptr_t newv
, int cpu
)
340 __asm__ __volatile__
goto (
341 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
342 /* Start rseq by storing table entry pointer into rseq_cs. */
343 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
344 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
346 LONG_CMP
" %[expect], %[v]\n\t"
347 "jnz %l[cmpfail]\n\t"
349 LONG_CMP
" %[expect2], %[v2]\n\t"
350 "jnz %l[cmpfail]\n\t"
352 #ifdef RSEQ_COMPARE_TWICE
353 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
354 LONG_CMP
" %[expect], %[v]\n\t"
356 LONG_CMP
" %[expect2], %[v2]\n\t"
360 LONG_S
" %[newv], %[v]\n\t"
363 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
364 : /* gcc asm goto does not allow outputs */
365 : [cpu_id
] "r" (cpu
),
366 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
367 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
370 [expect2
] "r" (expect2
),
371 /* final store input */
373 [expect
] "r" (expect
),
376 : "memory", "cc", "r0"
379 #ifdef RSEQ_COMPARE_TWICE
380 , error1
, error2
, error3
389 #ifdef RSEQ_COMPARE_TWICE
391 rseq_bug("cpu_id comparison failed");
393 rseq_bug("1st expected value comparison failed");
395 rseq_bug("2nd expected value comparison failed");
399 static inline __attribute__((always_inline
))
400 int rseq_cmpeqv_trymemcpy_storev(intptr_t *v
, intptr_t expect
,
401 void *dst
, void *src
, size_t len
,
402 intptr_t newv
, int cpu
)
404 uint64_t rseq_scratch
[3];
408 __asm__ __volatile__
goto (
409 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
410 LONG_S
" %[src], %[rseq_scratch0]\n\t"
411 LONG_S
" %[dst], %[rseq_scratch1]\n\t"
412 LONG_S
" %[len], %[rseq_scratch2]\n\t"
413 /* Start rseq by storing table entry pointer into rseq_cs. */
414 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
415 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
417 LONG_CMP
" %[expect], %[v]\n\t"
420 #ifdef RSEQ_COMPARE_TWICE
421 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 6f
)
422 LONG_CMP
" %[expect], %[v]\n\t"
426 LONG_LT_R
" %[len], %[len]\n\t"
429 "ic %%r0,0(%[src])\n\t"
430 "stc %%r0,0(%[dst])\n\t"
431 LONG_ADDI
" %[src], 1\n\t"
432 LONG_ADDI
" %[dst], 1\n\t"
433 LONG_ADDI
" %[len], -1\n\t"
438 LONG_S
" %[newv], %[v]\n\t"
442 LONG_L
" %[len], %[rseq_scratch2]\n\t"
443 LONG_L
" %[dst], %[rseq_scratch1]\n\t"
444 LONG_L
" %[src], %[rseq_scratch0]\n\t"
445 RSEQ_ASM_DEFINE_ABORT(4,
446 LONG_L
" %[len], %[rseq_scratch2]\n\t"
447 LONG_L
" %[dst], %[rseq_scratch1]\n\t"
448 LONG_L
" %[src], %[rseq_scratch0]\n\t",
450 RSEQ_ASM_DEFINE_CMPFAIL(5,
451 LONG_L
" %[len], %[rseq_scratch2]\n\t"
452 LONG_L
" %[dst], %[rseq_scratch1]\n\t"
453 LONG_L
" %[src], %[rseq_scratch0]\n\t",
455 #ifdef RSEQ_COMPARE_TWICE
456 RSEQ_ASM_DEFINE_CMPFAIL(6,
457 LONG_L
" %[len], %[rseq_scratch2]\n\t"
458 LONG_L
" %[dst], %[rseq_scratch1]\n\t"
459 LONG_L
" %[src], %[rseq_scratch0]\n\t",
461 RSEQ_ASM_DEFINE_CMPFAIL(7,
462 LONG_L
" %[len], %[rseq_scratch2]\n\t"
463 LONG_L
" %[dst], %[rseq_scratch1]\n\t"
464 LONG_L
" %[src], %[rseq_scratch0]\n\t",
467 : /* gcc asm goto does not allow outputs */
468 : [cpu_id
] "r" (cpu
),
469 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
470 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
471 /* final store input */
473 [expect
] "r" (expect
),
475 /* try memcpy input */
479 [rseq_scratch0
] "m" (rseq_scratch
[0]),
480 [rseq_scratch1
] "m" (rseq_scratch
[1]),
481 [rseq_scratch2
] "m" (rseq_scratch
[2])
483 : "memory", "cc", "r0"
486 #ifdef RSEQ_COMPARE_TWICE
496 #ifdef RSEQ_COMPARE_TWICE
498 rseq_bug("cpu_id comparison failed");
500 rseq_bug("expected value comparison failed");
505 static inline __attribute__((always_inline
))
506 int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v
, intptr_t expect
,
507 void *dst
, void *src
, size_t len
,
508 intptr_t newv
, int cpu
)
510 return rseq_cmpeqv_trymemcpy_storev(v
, expect
, dst
, src
, len
,
513 #endif /* !RSEQ_SKIP_FASTPATH */