1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
4 * RSEQ_SIG uses the trap4 instruction. As Linux does not make use of the
5 * access-register mode nor the linkage stack this instruction will always
6 * cause a special-operation exception (the trap-enabled bit in the DUCT
7 * is and will stay 0). The instruction pattern is
8 * b2 ff 0f ff trap4 4095(%r0)
10 #define RSEQ_SIG 0xB2FF0FFF
12 #define rseq_smp_mb() __asm__ __volatile__ ("bcr 15,0" ::: "memory")
13 #define rseq_smp_rmb() rseq_smp_mb()
14 #define rseq_smp_wmb() rseq_smp_mb()
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_rmb()
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 */
39 #define LONG_LT_R "ltgr"
41 #define LONG_CMP_R "cgr"
42 #define LONG_ADDI "aghi"
43 #define LONG_ADD_R "agr"
45 #define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, \
46 start_ip, post_commit_offset, abort_ip) \
47 ".pushsection __rseq_table, \"aw\"\n\t" \
49 __rseq_str(label) ":\n\t" \
50 ".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
51 ".quad " __rseq_str(start_ip) ", " __rseq_str(post_commit_offset) ", " __rseq_str(abort_ip) "\n\t" \
56 #define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, \
57 start_ip, post_commit_offset, abort_ip) \
58 ".pushsection __rseq_table, \"aw\"\n\t" \
60 __rseq_str(label) ":\n\t" \
61 ".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
62 ".long 0x0, " __rseq_str(start_ip) ", 0x0, " __rseq_str(post_commit_offset) ", 0x0, " __rseq_str(abort_ip) "\n\t" \
67 #define LONG_LT_R "ltr"
69 #define LONG_CMP_R "cr"
70 #define LONG_ADDI "ahi"
71 #define LONG_ADD_R "ar"
75 #define RSEQ_ASM_DEFINE_TABLE(label, start_ip, post_commit_ip, abort_ip) \
76 __RSEQ_ASM_DEFINE_TABLE(label, 0x0, 0x0, start_ip, \
77 (post_commit_ip - start_ip), abort_ip)
79 #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \
81 "larl %%r0, " __rseq_str(cs_label) "\n\t" \
82 LONG_S " %%r0, %[" __rseq_str(rseq_cs) "]\n\t" \
83 __rseq_str(label) ":\n\t"
85 #define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label) \
87 "c %[" __rseq_str(cpu_id) "], %[" __rseq_str(current_cpu_id) "]\n\t" \
88 "jnz " __rseq_str(label) "\n\t"
90 #define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label) \
91 ".pushsection __rseq_failure, \"ax\"\n\t" \
92 ".long " __rseq_str(RSEQ_SIG) "\n\t" \
93 __rseq_str(label) ":\n\t" \
95 "j %l[" __rseq_str(abort_label) "]\n\t" \
98 #define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label) \
99 ".pushsection __rseq_failure, \"ax\"\n\t" \
100 __rseq_str(label) ":\n\t" \
102 "j %l[" __rseq_str(cmpfail_label) "]\n\t" \
105 static inline __attribute__((always_inline))
106 int rseq_cmpeqv_storev(intptr_t *v, intptr_t expect, intptr_t newv, int cpu)
110 __asm__ __volatile__ goto (
111 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
112 /* Start rseq by storing table entry pointer into rseq_cs. */
113 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
114 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
116 LONG_CMP " %[expect], %[v]\n\t"
117 "jnz %l[cmpfail]\n\t"
119 #ifdef RSEQ_COMPARE_TWICE
120 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
121 LONG_CMP " %[expect], %[v]\n\t"
125 LONG_S " %[newv], %[v]\n\t"
128 RSEQ_ASM_DEFINE_ABORT(4, "", abort)
129 : /* gcc asm goto does not allow outputs */
130 : [cpu_id] "r" (cpu),
131 [current_cpu_id] "m" (__rseq_abi.cpu_id),
132 [rseq_cs] "m" (__rseq_abi.rseq_cs),
134 [expect] "r" (expect),
137 : "memory", "cc", "r0"
140 #ifdef RSEQ_COMPARE_TWICE
150 #ifdef RSEQ_COMPARE_TWICE
152 rseq_bug("cpu_id comparison failed");
154 rseq_bug("expected value comparison failed");
159 * Compare @v against @expectnot. When it does _not_ match, load @v
160 * into @load, and store the content of *@v + voffp into @v.
162 static inline __attribute__((always_inline))
163 int rseq_cmpnev_storeoffp_load(intptr_t *v, intptr_t expectnot,
164 off_t voffp, intptr_t *load, int cpu)
168 __asm__ __volatile__ goto (
169 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
170 /* Start rseq by storing table entry pointer into rseq_cs. */
171 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
172 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
174 LONG_L " %%r1, %[v]\n\t"
175 LONG_CMP_R " %%r1, %[expectnot]\n\t"
178 #ifdef RSEQ_COMPARE_TWICE
179 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
180 LONG_L " %%r1, %[v]\n\t"
181 LONG_CMP_R " %%r1, %[expectnot]\n\t"
184 LONG_S " %%r1, %[load]\n\t"
185 LONG_ADD_R " %%r1, %[voffp]\n\t"
186 LONG_L " %%r1, 0(%%r1)\n\t"
188 LONG_S " %%r1, %[v]\n\t"
191 RSEQ_ASM_DEFINE_ABORT(4, "", abort)
192 : /* gcc asm goto does not allow outputs */
193 : [cpu_id] "r" (cpu),
194 [current_cpu_id] "m" (__rseq_abi.cpu_id),
195 [rseq_cs] "m" (__rseq_abi.rseq_cs),
196 /* final store input */
198 [expectnot] "r" (expectnot),
202 : "memory", "cc", "r0", "r1"
205 #ifdef RSEQ_COMPARE_TWICE
215 #ifdef RSEQ_COMPARE_TWICE
217 rseq_bug("cpu_id comparison failed");
219 rseq_bug("expected value comparison failed");
223 static inline __attribute__((always_inline))
224 int rseq_addv(intptr_t *v, intptr_t count, int cpu)
228 __asm__ __volatile__ goto (
229 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
230 /* Start rseq by storing table entry pointer into rseq_cs. */
231 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
232 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
234 #ifdef RSEQ_COMPARE_TWICE
235 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
237 LONG_L " %%r0, %[v]\n\t"
238 LONG_ADD_R " %%r0, %[count]\n\t"
240 LONG_S " %%r0, %[v]\n\t"
243 RSEQ_ASM_DEFINE_ABORT(4, "", abort)
244 : /* gcc asm goto does not allow outputs */
245 : [cpu_id] "r" (cpu),
246 [current_cpu_id] "m" (__rseq_abi.cpu_id),
247 [rseq_cs] "m" (__rseq_abi.rseq_cs),
248 /* final store input */
252 : "memory", "cc", "r0"
255 #ifdef RSEQ_COMPARE_TWICE
263 #ifdef RSEQ_COMPARE_TWICE
265 rseq_bug("cpu_id comparison failed");
269 static inline __attribute__((always_inline))
270 int rseq_cmpeqv_trystorev_storev(intptr_t *v, intptr_t expect,
271 intptr_t *v2, intptr_t newv2,
272 intptr_t newv, int cpu)
276 __asm__ __volatile__ goto (
277 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
278 /* Start rseq by storing table entry pointer into rseq_cs. */
279 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
280 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
282 LONG_CMP " %[expect], %[v]\n\t"
283 "jnz %l[cmpfail]\n\t"
285 #ifdef RSEQ_COMPARE_TWICE
286 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
287 LONG_CMP " %[expect], %[v]\n\t"
291 LONG_S " %[newv2], %[v2]\n\t"
294 LONG_S " %[newv], %[v]\n\t"
297 RSEQ_ASM_DEFINE_ABORT(4, "", abort)
298 : /* gcc asm goto does not allow outputs */
299 : [cpu_id] "r" (cpu),
300 [current_cpu_id] "m" (__rseq_abi.cpu_id),
301 [rseq_cs] "m" (__rseq_abi.rseq_cs),
302 /* try store input */
305 /* final store input */
307 [expect] "r" (expect),
310 : "memory", "cc", "r0"
313 #ifdef RSEQ_COMPARE_TWICE
323 #ifdef RSEQ_COMPARE_TWICE
325 rseq_bug("cpu_id comparison failed");
327 rseq_bug("expected value comparison failed");
332 static inline __attribute__((always_inline))
333 int rseq_cmpeqv_trystorev_storev_release(intptr_t *v, intptr_t expect,
334 intptr_t *v2, intptr_t newv2,
335 intptr_t newv, int cpu)
337 return rseq_cmpeqv_trystorev_storev(v, expect, v2, newv2, newv, cpu);
340 static inline __attribute__((always_inline))
341 int rseq_cmpeqv_cmpeqv_storev(intptr_t *v, intptr_t expect,
342 intptr_t *v2, intptr_t expect2,
343 intptr_t newv, int cpu)
347 __asm__ __volatile__ goto (
348 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
349 /* Start rseq by storing table entry pointer into rseq_cs. */
350 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
351 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
353 LONG_CMP " %[expect], %[v]\n\t"
354 "jnz %l[cmpfail]\n\t"
356 LONG_CMP " %[expect2], %[v2]\n\t"
357 "jnz %l[cmpfail]\n\t"
359 #ifdef RSEQ_COMPARE_TWICE
360 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
361 LONG_CMP " %[expect], %[v]\n\t"
363 LONG_CMP " %[expect2], %[v2]\n\t"
367 LONG_S " %[newv], %[v]\n\t"
370 RSEQ_ASM_DEFINE_ABORT(4, "", abort)
371 : /* gcc asm goto does not allow outputs */
372 : [cpu_id] "r" (cpu),
373 [current_cpu_id] "m" (__rseq_abi.cpu_id),
374 [rseq_cs] "m" (__rseq_abi.rseq_cs),
377 [expect2] "r" (expect2),
378 /* final store input */
380 [expect] "r" (expect),
383 : "memory", "cc", "r0"
386 #ifdef RSEQ_COMPARE_TWICE
387 , error1, error2, error3
396 #ifdef RSEQ_COMPARE_TWICE
398 rseq_bug("cpu_id comparison failed");
400 rseq_bug("1st expected value comparison failed");
402 rseq_bug("2nd expected value comparison failed");
406 static inline __attribute__((always_inline))
407 int rseq_cmpeqv_trymemcpy_storev(intptr_t *v, intptr_t expect,
408 void *dst, void *src, size_t len,
409 intptr_t newv, int cpu)
411 uint64_t rseq_scratch[3];
415 __asm__ __volatile__ goto (
416 RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
417 LONG_S " %[src], %[rseq_scratch0]\n\t"
418 LONG_S " %[dst], %[rseq_scratch1]\n\t"
419 LONG_S " %[len], %[rseq_scratch2]\n\t"
420 /* Start rseq by storing table entry pointer into rseq_cs. */
421 RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
422 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
424 LONG_CMP " %[expect], %[v]\n\t"
427 #ifdef RSEQ_COMPARE_TWICE
428 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f)
429 LONG_CMP " %[expect], %[v]\n\t"
433 LONG_LT_R " %[len], %[len]\n\t"
436 "ic %%r0,0(%[src])\n\t"
437 "stc %%r0,0(%[dst])\n\t"
438 LONG_ADDI " %[src], 1\n\t"
439 LONG_ADDI " %[dst], 1\n\t"
440 LONG_ADDI " %[len], -1\n\t"
445 LONG_S " %[newv], %[v]\n\t"
449 LONG_L " %[len], %[rseq_scratch2]\n\t"
450 LONG_L " %[dst], %[rseq_scratch1]\n\t"
451 LONG_L " %[src], %[rseq_scratch0]\n\t"
452 RSEQ_ASM_DEFINE_ABORT(4,
453 LONG_L " %[len], %[rseq_scratch2]\n\t"
454 LONG_L " %[dst], %[rseq_scratch1]\n\t"
455 LONG_L " %[src], %[rseq_scratch0]\n\t",
457 RSEQ_ASM_DEFINE_CMPFAIL(5,
458 LONG_L " %[len], %[rseq_scratch2]\n\t"
459 LONG_L " %[dst], %[rseq_scratch1]\n\t"
460 LONG_L " %[src], %[rseq_scratch0]\n\t",
462 #ifdef RSEQ_COMPARE_TWICE
463 RSEQ_ASM_DEFINE_CMPFAIL(6,
464 LONG_L " %[len], %[rseq_scratch2]\n\t"
465 LONG_L " %[dst], %[rseq_scratch1]\n\t"
466 LONG_L " %[src], %[rseq_scratch0]\n\t",
468 RSEQ_ASM_DEFINE_CMPFAIL(7,
469 LONG_L " %[len], %[rseq_scratch2]\n\t"
470 LONG_L " %[dst], %[rseq_scratch1]\n\t"
471 LONG_L " %[src], %[rseq_scratch0]\n\t",
474 : /* gcc asm goto does not allow outputs */
475 : [cpu_id] "r" (cpu),
476 [current_cpu_id] "m" (__rseq_abi.cpu_id),
477 [rseq_cs] "m" (__rseq_abi.rseq_cs),
478 /* final store input */
480 [expect] "r" (expect),
482 /* try memcpy input */
486 [rseq_scratch0] "m" (rseq_scratch[0]),
487 [rseq_scratch1] "m" (rseq_scratch[1]),
488 [rseq_scratch2] "m" (rseq_scratch[2])
490 : "memory", "cc", "r0"
493 #ifdef RSEQ_COMPARE_TWICE
503 #ifdef RSEQ_COMPARE_TWICE
505 rseq_bug("cpu_id comparison failed");
507 rseq_bug("expected value comparison failed");
512 static inline __attribute__((always_inline))
513 int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v, intptr_t expect,
514 void *dst, void *src, size_t len,
515 intptr_t newv, int cpu)
517 return rseq_cmpeqv_trymemcpy_storev(v, expect, dst, src, len,
520 #endif /* !RSEQ_SKIP_FASTPATH */