1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
5 * (C) Copyright 2016-2018 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
8 #define RSEQ_SIG 0x53053053
10 #define rseq_smp_mb() __asm__ __volatile__ ("dmb" ::: "memory", "cc")
11 #define rseq_smp_rmb() __asm__ __volatile__ ("dmb" ::: "memory", "cc")
12 #define rseq_smp_wmb() __asm__ __volatile__ ("dmb" ::: "memory", "cc")
14 #define rseq_smp_load_acquire(p) \
16 __typeof(*p) ____p1 = RSEQ_READ_ONCE(*p); \
21 #define rseq_smp_acquire__after_ctrl_dep() rseq_smp_rmb()
23 #define rseq_smp_store_release(p, v) \
26 RSEQ_WRITE_ONCE(*p, v); \
29 #ifdef RSEQ_SKIP_FASTPATH
30 #include "rseq-skip.h"
31 #else /* !RSEQ_SKIP_FASTPATH */
33 #define __RSEQ_ASM_DEFINE_TABLE(version, flags, start_ip, \
34 post_commit_offset, abort_ip) \
35 ".pushsection __rseq_table, \"aw\"\n\t" \
37 ".word " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
38 ".word " __rseq_str(start_ip) ", 0x0, " __rseq_str(post_commit_offset) ", 0x0, " __rseq_str(abort_ip) ", 0x0\n\t" \
41 #define RSEQ_ASM_DEFINE_TABLE(start_ip, post_commit_ip, abort_ip) \
42 __RSEQ_ASM_DEFINE_TABLE(0x0, 0x0, start_ip, \
43 (post_commit_ip - start_ip), abort_ip)
45 #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \
47 "adr r0, " __rseq_str(cs_label) "\n\t" \
48 "str r0, %[" __rseq_str(rseq_cs) "]\n\t" \
49 __rseq_str(label) ":\n\t"
51 #define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label) \
53 "ldr r0, %[" __rseq_str(current_cpu_id) "]\n\t" \
54 "cmp %[" __rseq_str(cpu_id) "], r0\n\t" \
55 "bne " __rseq_str(label) "\n\t"
57 #define __RSEQ_ASM_DEFINE_ABORT(table_label, label, teardown, \
58 abort_label, version, flags, \
59 start_ip, post_commit_offset, abort_ip) \
61 __rseq_str(table_label) ":\n\t" \
62 ".word " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
63 ".word " __rseq_str(start_ip) ", 0x0, " __rseq_str(post_commit_offset) ", 0x0, " __rseq_str(abort_ip) ", 0x0\n\t" \
64 ".word " __rseq_str(RSEQ_SIG) "\n\t" \
65 __rseq_str(label) ":\n\t" \
67 "b %l[" __rseq_str(abort_label) "]\n\t"
69 #define RSEQ_ASM_DEFINE_ABORT(table_label, label, teardown, abort_label, \
70 start_ip, post_commit_ip, abort_ip) \
71 __RSEQ_ASM_DEFINE_ABORT(table_label, label, teardown, \
72 abort_label, 0x0, 0x0, start_ip, \
73 (post_commit_ip - start_ip), abort_ip)
75 #define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label) \
76 __rseq_str(label) ":\n\t" \
78 "b %l[" __rseq_str(cmpfail_label) "]\n\t"
80 #define rseq_workaround_gcc_asm_size_guess() __asm__ __volatile__("")
82 static inline __attribute__((always_inline))
83 int rseq_cmpeqv_storev(intptr_t *v, intptr_t expect, intptr_t newv, int cpu)
87 rseq_workaround_gcc_asm_size_guess();
88 __asm__ __volatile__ goto (
89 RSEQ_ASM_DEFINE_TABLE(1f, 2f, 4f) /* start, commit, abort */
90 /* Start rseq by storing table entry pointer into rseq_cs. */
91 RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
92 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
95 "cmp %[expect], r0\n\t"
98 #ifdef RSEQ_COMPARE_TWICE
99 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
101 "cmp %[expect], r0\n\t"
105 "str %[newv], %[v]\n\t"
109 RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
111 : /* gcc asm goto does not allow outputs */
112 : [cpu_id] "r" (cpu),
113 [current_cpu_id] "m" (__rseq_abi.cpu_id),
114 [rseq_cs] "m" (__rseq_abi.rseq_cs),
116 [expect] "r" (expect),
119 : "r0", "memory", "cc"
122 #ifdef RSEQ_COMPARE_TWICE
126 rseq_workaround_gcc_asm_size_guess();
129 rseq_workaround_gcc_asm_size_guess();
133 rseq_workaround_gcc_asm_size_guess();
135 #ifdef RSEQ_COMPARE_TWICE
137 rseq_bug("cpu_id comparison failed");
139 rseq_bug("expected value comparison failed");
143 static inline __attribute__((always_inline))
144 int rseq_cmpnev_storeoffp_load(intptr_t *v, intptr_t expectnot,
145 off_t voffp, intptr_t *load, int cpu)
149 rseq_workaround_gcc_asm_size_guess();
150 __asm__ __volatile__ goto (
151 RSEQ_ASM_DEFINE_TABLE(1f, 2f, 4f) /* start, commit, abort */
152 /* Start rseq by storing table entry pointer into rseq_cs. */
153 RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
154 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
157 "cmp %[expectnot], r0\n\t"
158 "beq %l[cmpfail]\n\t"
160 #ifdef RSEQ_COMPARE_TWICE
161 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
163 "cmp %[expectnot], r0\n\t"
166 "str r0, %[load]\n\t"
167 "add r0, %[voffp]\n\t"
174 RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
176 : /* gcc asm goto does not allow outputs */
177 : [cpu_id] "r" (cpu),
178 [current_cpu_id] "m" (__rseq_abi.cpu_id),
179 [rseq_cs] "m" (__rseq_abi.rseq_cs),
180 /* final store input */
182 [expectnot] "r" (expectnot),
183 [voffp] "Ir" (voffp),
186 : "r0", "memory", "cc"
189 #ifdef RSEQ_COMPARE_TWICE
193 rseq_workaround_gcc_asm_size_guess();
196 rseq_workaround_gcc_asm_size_guess();
200 rseq_workaround_gcc_asm_size_guess();
202 #ifdef RSEQ_COMPARE_TWICE
204 rseq_bug("cpu_id comparison failed");
206 rseq_bug("expected value comparison failed");
210 static inline __attribute__((always_inline))
211 int rseq_addv(intptr_t *v, intptr_t count, int cpu)
215 rseq_workaround_gcc_asm_size_guess();
216 __asm__ __volatile__ goto (
217 RSEQ_ASM_DEFINE_TABLE(1f, 2f, 4f) /* start, commit, abort */
218 /* Start rseq by storing table entry pointer into rseq_cs. */
219 RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
220 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
222 #ifdef RSEQ_COMPARE_TWICE
223 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
226 "add r0, %[count]\n\t"
232 RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
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),
241 : "r0", "memory", "cc"
244 #ifdef RSEQ_COMPARE_TWICE
248 rseq_workaround_gcc_asm_size_guess();
251 rseq_workaround_gcc_asm_size_guess();
254 #ifdef RSEQ_COMPARE_TWICE
256 rseq_bug("cpu_id comparison failed");
260 static inline __attribute__((always_inline))
261 int rseq_cmpeqv_trystorev_storev(intptr_t *v, intptr_t expect,
262 intptr_t *v2, intptr_t newv2,
263 intptr_t newv, int cpu)
267 rseq_workaround_gcc_asm_size_guess();
268 __asm__ __volatile__ goto (
269 RSEQ_ASM_DEFINE_TABLE(1f, 2f, 4f) /* start, commit, abort */
270 /* Start rseq by storing table entry pointer into rseq_cs. */
271 RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
272 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
275 "cmp %[expect], r0\n\t"
276 "bne %l[cmpfail]\n\t"
278 #ifdef RSEQ_COMPARE_TWICE
279 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
281 "cmp %[expect], r0\n\t"
285 "str %[newv2], %[v2]\n\t"
288 "str %[newv], %[v]\n\t"
292 RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
294 : /* gcc asm goto does not allow outputs */
295 : [cpu_id] "r" (cpu),
296 [current_cpu_id] "m" (__rseq_abi.cpu_id),
297 [rseq_cs] "m" (__rseq_abi.rseq_cs),
298 /* try store input */
301 /* final store input */
303 [expect] "r" (expect),
306 : "r0", "memory", "cc"
309 #ifdef RSEQ_COMPARE_TWICE
313 rseq_workaround_gcc_asm_size_guess();
316 rseq_workaround_gcc_asm_size_guess();
320 rseq_workaround_gcc_asm_size_guess();
322 #ifdef RSEQ_COMPARE_TWICE
324 rseq_bug("cpu_id comparison failed");
326 rseq_bug("expected value comparison failed");
330 static inline __attribute__((always_inline))
331 int rseq_cmpeqv_trystorev_storev_release(intptr_t *v, intptr_t expect,
332 intptr_t *v2, intptr_t newv2,
333 intptr_t newv, int cpu)
337 rseq_workaround_gcc_asm_size_guess();
338 __asm__ __volatile__ goto (
339 RSEQ_ASM_DEFINE_TABLE(1f, 2f, 4f) /* start, commit, abort */
340 /* Start rseq by storing table entry pointer into rseq_cs. */
341 RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
342 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
345 "cmp %[expect], r0\n\t"
346 "bne %l[cmpfail]\n\t"
348 #ifdef RSEQ_COMPARE_TWICE
349 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
351 "cmp %[expect], r0\n\t"
355 "str %[newv2], %[v2]\n\t"
357 "dmb\n\t" /* full mb provides store-release */
359 "str %[newv], %[v]\n\t"
363 RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
365 : /* gcc asm goto does not allow outputs */
366 : [cpu_id] "r" (cpu),
367 [current_cpu_id] "m" (__rseq_abi.cpu_id),
368 [rseq_cs] "m" (__rseq_abi.rseq_cs),
369 /* try store input */
372 /* final store input */
374 [expect] "r" (expect),
377 : "r0", "memory", "cc"
380 #ifdef RSEQ_COMPARE_TWICE
384 rseq_workaround_gcc_asm_size_guess();
387 rseq_workaround_gcc_asm_size_guess();
391 rseq_workaround_gcc_asm_size_guess();
393 #ifdef RSEQ_COMPARE_TWICE
395 rseq_bug("cpu_id comparison failed");
397 rseq_bug("expected value comparison failed");
401 static inline __attribute__((always_inline))
402 int rseq_cmpeqv_cmpeqv_storev(intptr_t *v, intptr_t expect,
403 intptr_t *v2, intptr_t expect2,
404 intptr_t newv, int cpu)
408 rseq_workaround_gcc_asm_size_guess();
409 __asm__ __volatile__ goto (
410 RSEQ_ASM_DEFINE_TABLE(1f, 2f, 4f) /* start, commit, abort */
411 /* Start rseq by storing table entry pointer into rseq_cs. */
412 RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
413 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
416 "cmp %[expect], r0\n\t"
417 "bne %l[cmpfail]\n\t"
420 "cmp %[expect2], r0\n\t"
421 "bne %l[cmpfail]\n\t"
423 #ifdef RSEQ_COMPARE_TWICE
424 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
426 "cmp %[expect], r0\n\t"
429 "cmp %[expect2], r0\n\t"
433 "str %[newv], %[v]\n\t"
437 RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
439 : /* gcc asm goto does not allow outputs */
440 : [cpu_id] "r" (cpu),
441 [current_cpu_id] "m" (__rseq_abi.cpu_id),
442 [rseq_cs] "m" (__rseq_abi.rseq_cs),
445 [expect2] "r" (expect2),
446 /* final store input */
448 [expect] "r" (expect),
451 : "r0", "memory", "cc"
454 #ifdef RSEQ_COMPARE_TWICE
455 , error1, error2, error3
458 rseq_workaround_gcc_asm_size_guess();
461 rseq_workaround_gcc_asm_size_guess();
465 rseq_workaround_gcc_asm_size_guess();
467 #ifdef RSEQ_COMPARE_TWICE
469 rseq_bug("cpu_id comparison failed");
471 rseq_bug("1st expected value comparison failed");
473 rseq_bug("2nd expected value comparison failed");
477 static inline __attribute__((always_inline))
478 int rseq_cmpeqv_trymemcpy_storev(intptr_t *v, intptr_t expect,
479 void *dst, void *src, size_t len,
480 intptr_t newv, int cpu)
482 uint32_t rseq_scratch[3];
486 rseq_workaround_gcc_asm_size_guess();
487 __asm__ __volatile__ goto (
488 RSEQ_ASM_DEFINE_TABLE(1f, 2f, 4f) /* start, commit, abort */
489 "str %[src], %[rseq_scratch0]\n\t"
490 "str %[dst], %[rseq_scratch1]\n\t"
491 "str %[len], %[rseq_scratch2]\n\t"
492 /* Start rseq by storing table entry pointer into rseq_cs. */
493 RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
494 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
497 "cmp %[expect], r0\n\t"
500 #ifdef RSEQ_COMPARE_TWICE
501 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f)
503 "cmp %[expect], r0\n\t"
507 "cmp %[len], #0\n\t" \
510 "ldrb %%r0, [%[src]]\n\t" \
511 "strb %%r0, [%[dst]]\n\t" \
512 "adds %[src], #1\n\t" \
513 "adds %[dst], #1\n\t" \
514 "subs %[len], #1\n\t" \
519 "str %[newv], %[v]\n\t"
523 "ldr %[len], %[rseq_scratch2]\n\t"
524 "ldr %[dst], %[rseq_scratch1]\n\t"
525 "ldr %[src], %[rseq_scratch0]\n\t"
527 RSEQ_ASM_DEFINE_ABORT(3, 4,
529 "ldr %[len], %[rseq_scratch2]\n\t"
530 "ldr %[dst], %[rseq_scratch1]\n\t"
531 "ldr %[src], %[rseq_scratch0]\n\t",
533 RSEQ_ASM_DEFINE_CMPFAIL(5,
535 "ldr %[len], %[rseq_scratch2]\n\t"
536 "ldr %[dst], %[rseq_scratch1]\n\t"
537 "ldr %[src], %[rseq_scratch0]\n\t",
539 #ifdef RSEQ_COMPARE_TWICE
540 RSEQ_ASM_DEFINE_CMPFAIL(6,
542 "ldr %[len], %[rseq_scratch2]\n\t"
543 "ldr %[dst], %[rseq_scratch1]\n\t"
544 "ldr %[src], %[rseq_scratch0]\n\t",
546 RSEQ_ASM_DEFINE_CMPFAIL(7,
548 "ldr %[len], %[rseq_scratch2]\n\t"
549 "ldr %[dst], %[rseq_scratch1]\n\t"
550 "ldr %[src], %[rseq_scratch0]\n\t",
554 : /* gcc asm goto does not allow outputs */
555 : [cpu_id] "r" (cpu),
556 [current_cpu_id] "m" (__rseq_abi.cpu_id),
557 [rseq_cs] "m" (__rseq_abi.rseq_cs),
558 /* final store input */
560 [expect] "r" (expect),
562 /* try memcpy input */
566 [rseq_scratch0] "m" (rseq_scratch[0]),
567 [rseq_scratch1] "m" (rseq_scratch[1]),
568 [rseq_scratch2] "m" (rseq_scratch[2])
570 : "r0", "memory", "cc"
573 #ifdef RSEQ_COMPARE_TWICE
577 rseq_workaround_gcc_asm_size_guess();
580 rseq_workaround_gcc_asm_size_guess();
584 rseq_workaround_gcc_asm_size_guess();
586 #ifdef RSEQ_COMPARE_TWICE
588 rseq_workaround_gcc_asm_size_guess();
589 rseq_bug("cpu_id comparison failed");
591 rseq_workaround_gcc_asm_size_guess();
592 rseq_bug("expected value comparison failed");
596 static inline __attribute__((always_inline))
597 int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v, intptr_t expect,
598 void *dst, void *src, size_t len,
599 intptr_t newv, int cpu)
601 uint32_t rseq_scratch[3];
605 rseq_workaround_gcc_asm_size_guess();
606 __asm__ __volatile__ goto (
607 RSEQ_ASM_DEFINE_TABLE(1f, 2f, 4f) /* start, commit, abort */
608 "str %[src], %[rseq_scratch0]\n\t"
609 "str %[dst], %[rseq_scratch1]\n\t"
610 "str %[len], %[rseq_scratch2]\n\t"
611 /* Start rseq by storing table entry pointer into rseq_cs. */
612 RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
613 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
616 "cmp %[expect], r0\n\t"
619 #ifdef RSEQ_COMPARE_TWICE
620 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f)
622 "cmp %[expect], r0\n\t"
626 "cmp %[len], #0\n\t" \
629 "ldrb %%r0, [%[src]]\n\t" \
630 "strb %%r0, [%[dst]]\n\t" \
631 "adds %[src], #1\n\t" \
632 "adds %[dst], #1\n\t" \
633 "subs %[len], #1\n\t" \
637 "dmb\n\t" /* full mb provides store-release */
639 "str %[newv], %[v]\n\t"
643 "ldr %[len], %[rseq_scratch2]\n\t"
644 "ldr %[dst], %[rseq_scratch1]\n\t"
645 "ldr %[src], %[rseq_scratch0]\n\t"
647 RSEQ_ASM_DEFINE_ABORT(3, 4,
649 "ldr %[len], %[rseq_scratch2]\n\t"
650 "ldr %[dst], %[rseq_scratch1]\n\t"
651 "ldr %[src], %[rseq_scratch0]\n\t",
653 RSEQ_ASM_DEFINE_CMPFAIL(5,
655 "ldr %[len], %[rseq_scratch2]\n\t"
656 "ldr %[dst], %[rseq_scratch1]\n\t"
657 "ldr %[src], %[rseq_scratch0]\n\t",
659 #ifdef RSEQ_COMPARE_TWICE
660 RSEQ_ASM_DEFINE_CMPFAIL(6,
662 "ldr %[len], %[rseq_scratch2]\n\t"
663 "ldr %[dst], %[rseq_scratch1]\n\t"
664 "ldr %[src], %[rseq_scratch0]\n\t",
666 RSEQ_ASM_DEFINE_CMPFAIL(7,
668 "ldr %[len], %[rseq_scratch2]\n\t"
669 "ldr %[dst], %[rseq_scratch1]\n\t"
670 "ldr %[src], %[rseq_scratch0]\n\t",
674 : /* gcc asm goto does not allow outputs */
675 : [cpu_id] "r" (cpu),
676 [current_cpu_id] "m" (__rseq_abi.cpu_id),
677 [rseq_cs] "m" (__rseq_abi.rseq_cs),
678 /* final store input */
680 [expect] "r" (expect),
682 /* try memcpy input */
686 [rseq_scratch0] "m" (rseq_scratch[0]),
687 [rseq_scratch1] "m" (rseq_scratch[1]),
688 [rseq_scratch2] "m" (rseq_scratch[2])
690 : "r0", "memory", "cc"
693 #ifdef RSEQ_COMPARE_TWICE
697 rseq_workaround_gcc_asm_size_guess();
700 rseq_workaround_gcc_asm_size_guess();
704 rseq_workaround_gcc_asm_size_guess();
706 #ifdef RSEQ_COMPARE_TWICE
708 rseq_workaround_gcc_asm_size_guess();
709 rseq_bug("cpu_id comparison failed");
711 rseq_workaround_gcc_asm_size_guess();
712 rseq_bug("expected value comparison failed");
716 #endif /* !RSEQ_SKIP_FASTPATH */