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_cs, \"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" \
53 ".pushsection __rseq_cs_ptr_array, \"aw\"\n\t" \
54 ".quad " __rseq_str(label) "b\n\t" \
58 * Exit points of a rseq critical section consist of all instructions outside
59 * of the critical section where a critical section can either branch to or
60 * reach through the normal course of its execution. The abort IP and the
61 * post-commit IP are already part of the __rseq_cs section and should not be
62 * explicitly defined as additional exit points. Knowing all exit points is
63 * useful to assist debuggers stepping over the critical section.
65 #define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip) \
66 ".pushsection __rseq_exit_point_array, \"aw\"\n\t" \
67 ".quad " __rseq_str(start_ip) ", " __rseq_str(exit_ip) "\n\t" \
72 #define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, \
73 start_ip, post_commit_offset, abort_ip) \
74 ".pushsection __rseq_cs, \"aw\"\n\t" \
76 __rseq_str(label) ":\n\t" \
77 ".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
78 ".long 0x0, " __rseq_str(start_ip) ", 0x0, " __rseq_str(post_commit_offset) ", 0x0, " __rseq_str(abort_ip) "\n\t" \
80 ".pushsection __rseq_cs_ptr_array, \"aw\"\n\t" \
81 ".long 0x0, " __rseq_str(label) "b\n\t" \
85 * Exit points of a rseq critical section consist of all instructions outside
86 * of the critical section where a critical section can either branch to or
87 * reach through the normal course of its execution. The abort IP and the
88 * post-commit IP are already part of the __rseq_cs section and should not be
89 * explicitly defined as additional exit points. Knowing all exit points is
90 * useful to assist debuggers stepping over the critical section.
92 #define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip) \
93 ".pushsection __rseq_exit_point_array, \"aw\"\n\t" \
94 ".long 0x0, " __rseq_str(start_ip) ", 0x0, " __rseq_str(exit_ip) "\n\t" \
99 #define LONG_LT_R "ltr"
101 #define LONG_CMP_R "cr"
102 #define LONG_ADDI "ahi"
103 #define LONG_ADD_R "ar"
107 #define RSEQ_ASM_DEFINE_TABLE(label, start_ip, post_commit_ip, abort_ip) \
108 __RSEQ_ASM_DEFINE_TABLE(label, 0x0, 0x0, start_ip, \
109 (post_commit_ip - start_ip), abort_ip)
111 #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \
113 "larl %%r0, " __rseq_str(cs_label) "\n\t" \
114 LONG_S " %%r0, %[" __rseq_str(rseq_cs) "]\n\t" \
115 __rseq_str(label) ":\n\t"
117 #define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label) \
119 "c %[" __rseq_str(cpu_id) "], %[" __rseq_str(current_cpu_id) "]\n\t" \
120 "jnz " __rseq_str(label) "\n\t"
122 #define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label) \
123 ".pushsection __rseq_failure, \"ax\"\n\t" \
124 ".long " __rseq_str(RSEQ_SIG) "\n\t" \
125 __rseq_str(label) ":\n\t" \
127 "jg %l[" __rseq_str(abort_label) "]\n\t" \
130 #define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label) \
131 ".pushsection __rseq_failure, \"ax\"\n\t" \
132 __rseq_str(label) ":\n\t" \
134 "jg %l[" __rseq_str(cmpfail_label) "]\n\t" \
137 static inline __attribute__((always_inline
))
138 int rseq_cmpeqv_storev(intptr_t *v
, intptr_t expect
, intptr_t newv
, int cpu
)
142 __asm__ __volatile__
goto (
143 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
144 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
145 #ifdef RSEQ_COMPARE_TWICE
146 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
147 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
149 /* Start rseq by storing table entry pointer into rseq_cs. */
150 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
151 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
153 LONG_CMP
" %[expect], %[v]\n\t"
154 "jnz %l[cmpfail]\n\t"
156 #ifdef RSEQ_COMPARE_TWICE
157 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
158 LONG_CMP
" %[expect], %[v]\n\t"
162 LONG_S
" %[newv], %[v]\n\t"
165 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
166 : /* gcc asm goto does not allow outputs */
167 : [cpu_id
] "r" (cpu
),
168 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
169 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
171 [expect
] "r" (expect
),
174 : "memory", "cc", "r0"
177 #ifdef RSEQ_COMPARE_TWICE
187 #ifdef RSEQ_COMPARE_TWICE
189 rseq_bug("cpu_id comparison failed");
191 rseq_bug("expected value comparison failed");
196 * Compare @v against @expectnot. When it does _not_ match, load @v
197 * into @load, and store the content of *@v + voffp into @v.
199 static inline __attribute__((always_inline
))
200 int rseq_cmpnev_storeoffp_load(intptr_t *v
, intptr_t expectnot
,
201 off_t voffp
, intptr_t *load
, int cpu
)
205 __asm__ __volatile__
goto (
206 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
207 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
208 #ifdef RSEQ_COMPARE_TWICE
209 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
210 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
212 /* Start rseq by storing table entry pointer into rseq_cs. */
213 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
214 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
216 LONG_L
" %%r1, %[v]\n\t"
217 LONG_CMP_R
" %%r1, %[expectnot]\n\t"
220 #ifdef RSEQ_COMPARE_TWICE
221 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
222 LONG_L
" %%r1, %[v]\n\t"
223 LONG_CMP_R
" %%r1, %[expectnot]\n\t"
226 LONG_S
" %%r1, %[load]\n\t"
227 LONG_ADD_R
" %%r1, %[voffp]\n\t"
228 LONG_L
" %%r1, 0(%%r1)\n\t"
230 LONG_S
" %%r1, %[v]\n\t"
233 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
234 : /* gcc asm goto does not allow outputs */
235 : [cpu_id
] "r" (cpu
),
236 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
237 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
238 /* final store input */
240 [expectnot
] "r" (expectnot
),
244 : "memory", "cc", "r0", "r1"
247 #ifdef RSEQ_COMPARE_TWICE
257 #ifdef RSEQ_COMPARE_TWICE
259 rseq_bug("cpu_id comparison failed");
261 rseq_bug("expected value comparison failed");
265 static inline __attribute__((always_inline
))
266 int rseq_addv(intptr_t *v
, intptr_t count
, int cpu
)
270 __asm__ __volatile__
goto (
271 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
272 #ifdef RSEQ_COMPARE_TWICE
273 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
275 /* Start rseq by storing table entry pointer into rseq_cs. */
276 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
277 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
279 #ifdef RSEQ_COMPARE_TWICE
280 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
282 LONG_L
" %%r0, %[v]\n\t"
283 LONG_ADD_R
" %%r0, %[count]\n\t"
285 LONG_S
" %%r0, %[v]\n\t"
288 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
289 : /* gcc asm goto does not allow outputs */
290 : [cpu_id
] "r" (cpu
),
291 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
292 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
293 /* final store input */
297 : "memory", "cc", "r0"
300 #ifdef RSEQ_COMPARE_TWICE
308 #ifdef RSEQ_COMPARE_TWICE
310 rseq_bug("cpu_id comparison failed");
314 static inline __attribute__((always_inline
))
315 int rseq_cmpeqv_trystorev_storev(intptr_t *v
, intptr_t expect
,
316 intptr_t *v2
, intptr_t newv2
,
317 intptr_t newv
, int cpu
)
321 __asm__ __volatile__
goto (
322 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
323 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
324 #ifdef RSEQ_COMPARE_TWICE
325 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
326 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
328 /* Start rseq by storing table entry pointer into rseq_cs. */
329 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
330 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
332 LONG_CMP
" %[expect], %[v]\n\t"
333 "jnz %l[cmpfail]\n\t"
335 #ifdef RSEQ_COMPARE_TWICE
336 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
337 LONG_CMP
" %[expect], %[v]\n\t"
341 LONG_S
" %[newv2], %[v2]\n\t"
344 LONG_S
" %[newv], %[v]\n\t"
347 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
348 : /* gcc asm goto does not allow outputs */
349 : [cpu_id
] "r" (cpu
),
350 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
351 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
352 /* try store input */
355 /* final store input */
357 [expect
] "r" (expect
),
360 : "memory", "cc", "r0"
363 #ifdef RSEQ_COMPARE_TWICE
373 #ifdef RSEQ_COMPARE_TWICE
375 rseq_bug("cpu_id comparison failed");
377 rseq_bug("expected value comparison failed");
382 static inline __attribute__((always_inline
))
383 int rseq_cmpeqv_trystorev_storev_release(intptr_t *v
, intptr_t expect
,
384 intptr_t *v2
, intptr_t newv2
,
385 intptr_t newv
, int cpu
)
387 return rseq_cmpeqv_trystorev_storev(v
, expect
, v2
, newv2
, newv
, cpu
);
390 static inline __attribute__((always_inline
))
391 int rseq_cmpeqv_cmpeqv_storev(intptr_t *v
, intptr_t expect
,
392 intptr_t *v2
, intptr_t expect2
,
393 intptr_t newv
, int cpu
)
397 __asm__ __volatile__
goto (
398 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
399 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
400 #ifdef RSEQ_COMPARE_TWICE
401 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
402 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
403 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error3
])
405 /* Start rseq by storing table entry pointer into rseq_cs. */
406 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
407 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
409 LONG_CMP
" %[expect], %[v]\n\t"
410 "jnz %l[cmpfail]\n\t"
412 LONG_CMP
" %[expect2], %[v2]\n\t"
413 "jnz %l[cmpfail]\n\t"
415 #ifdef RSEQ_COMPARE_TWICE
416 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
417 LONG_CMP
" %[expect], %[v]\n\t"
419 LONG_CMP
" %[expect2], %[v2]\n\t"
423 LONG_S
" %[newv], %[v]\n\t"
426 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
427 : /* gcc asm goto does not allow outputs */
428 : [cpu_id
] "r" (cpu
),
429 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
430 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
433 [expect2
] "r" (expect2
),
434 /* final store input */
436 [expect
] "r" (expect
),
439 : "memory", "cc", "r0"
442 #ifdef RSEQ_COMPARE_TWICE
443 , error1
, error2
, error3
452 #ifdef RSEQ_COMPARE_TWICE
454 rseq_bug("cpu_id comparison failed");
456 rseq_bug("1st expected value comparison failed");
458 rseq_bug("2nd expected value comparison failed");
462 static inline __attribute__((always_inline
))
463 int rseq_cmpeqv_trymemcpy_storev(intptr_t *v
, intptr_t expect
,
464 void *dst
, void *src
, size_t len
,
465 intptr_t newv
, int cpu
)
467 uint64_t rseq_scratch
[3];
471 __asm__ __volatile__
goto (
472 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
473 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
474 #ifdef RSEQ_COMPARE_TWICE
475 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
476 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
478 LONG_S
" %[src], %[rseq_scratch0]\n\t"
479 LONG_S
" %[dst], %[rseq_scratch1]\n\t"
480 LONG_S
" %[len], %[rseq_scratch2]\n\t"
481 /* Start rseq by storing table entry pointer into rseq_cs. */
482 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, rseq_cs
)
483 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
485 LONG_CMP
" %[expect], %[v]\n\t"
488 #ifdef RSEQ_COMPARE_TWICE
489 RSEQ_ASM_CMP_CPU_ID(cpu_id
, current_cpu_id
, 6f
)
490 LONG_CMP
" %[expect], %[v]\n\t"
494 LONG_LT_R
" %[len], %[len]\n\t"
497 "ic %%r0,0(%[src])\n\t"
498 "stc %%r0,0(%[dst])\n\t"
499 LONG_ADDI
" %[src], 1\n\t"
500 LONG_ADDI
" %[dst], 1\n\t"
501 LONG_ADDI
" %[len], -1\n\t"
506 LONG_S
" %[newv], %[v]\n\t"
510 LONG_L
" %[len], %[rseq_scratch2]\n\t"
511 LONG_L
" %[dst], %[rseq_scratch1]\n\t"
512 LONG_L
" %[src], %[rseq_scratch0]\n\t"
513 RSEQ_ASM_DEFINE_ABORT(4,
514 LONG_L
" %[len], %[rseq_scratch2]\n\t"
515 LONG_L
" %[dst], %[rseq_scratch1]\n\t"
516 LONG_L
" %[src], %[rseq_scratch0]\n\t",
518 RSEQ_ASM_DEFINE_CMPFAIL(5,
519 LONG_L
" %[len], %[rseq_scratch2]\n\t"
520 LONG_L
" %[dst], %[rseq_scratch1]\n\t"
521 LONG_L
" %[src], %[rseq_scratch0]\n\t",
523 #ifdef RSEQ_COMPARE_TWICE
524 RSEQ_ASM_DEFINE_CMPFAIL(6,
525 LONG_L
" %[len], %[rseq_scratch2]\n\t"
526 LONG_L
" %[dst], %[rseq_scratch1]\n\t"
527 LONG_L
" %[src], %[rseq_scratch0]\n\t",
529 RSEQ_ASM_DEFINE_CMPFAIL(7,
530 LONG_L
" %[len], %[rseq_scratch2]\n\t"
531 LONG_L
" %[dst], %[rseq_scratch1]\n\t"
532 LONG_L
" %[src], %[rseq_scratch0]\n\t",
535 : /* gcc asm goto does not allow outputs */
536 : [cpu_id
] "r" (cpu
),
537 [current_cpu_id
] "m" (__rseq_abi
.cpu_id
),
538 [rseq_cs
] "m" (__rseq_abi
.rseq_cs
),
539 /* final store input */
541 [expect
] "r" (expect
),
543 /* try memcpy input */
547 [rseq_scratch0
] "m" (rseq_scratch
[0]),
548 [rseq_scratch1
] "m" (rseq_scratch
[1]),
549 [rseq_scratch2
] "m" (rseq_scratch
[2])
551 : "memory", "cc", "r0"
554 #ifdef RSEQ_COMPARE_TWICE
564 #ifdef RSEQ_COMPARE_TWICE
566 rseq_bug("cpu_id comparison failed");
568 rseq_bug("expected value comparison failed");
573 static inline __attribute__((always_inline
))
574 int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v
, intptr_t expect
,
575 void *dst
, void *src
, size_t len
,
576 intptr_t newv
, int cpu
)
578 return rseq_cmpeqv_trymemcpy_storev(v
, expect
, dst
, src
, len
,
581 #endif /* !RSEQ_SKIP_FASTPATH */