GNU Linux-libre 4.19.245-gnu1
[releases.git] / tools / testing / selftests / rseq / rseq-arm.h
1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
2 /*
3  * rseq-arm.h
4  *
5  * (C) Copyright 2016-2018 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
6  */
7
8 #define RSEQ_SIG        0x53053053
9
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")
13
14 #define rseq_smp_load_acquire(p)                                        \
15 __extension__ ({                                                        \
16         __typeof(*p) ____p1 = RSEQ_READ_ONCE(*p);                       \
17         rseq_smp_mb();                                                  \
18         ____p1;                                                         \
19 })
20
21 #define rseq_smp_acquire__after_ctrl_dep()      rseq_smp_rmb()
22
23 #define rseq_smp_store_release(p, v)                                    \
24 do {                                                                    \
25         rseq_smp_mb();                                                  \
26         RSEQ_WRITE_ONCE(*p, v);                                         \
27 } while (0)
28
29 #ifdef RSEQ_SKIP_FASTPATH
30 #include "rseq-skip.h"
31 #else /* !RSEQ_SKIP_FASTPATH */
32
33 #define __RSEQ_ASM_DEFINE_TABLE(version, flags, start_ip,               \
34                                 post_commit_offset, abort_ip)           \
35                 ".pushsection __rseq_table, \"aw\"\n\t"                 \
36                 ".balign 32\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" \
39                 ".popsection\n\t"
40
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)
44
45 #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs)                \
46                 RSEQ_INJECT_ASM(1)                                      \
47                 "adr r0, " __rseq_str(cs_label) "\n\t"                  \
48                 "str r0, %[" __rseq_str(rseq_cs) "]\n\t"                \
49                 __rseq_str(label) ":\n\t"
50
51 #define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label)              \
52                 RSEQ_INJECT_ASM(2)                                      \
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"
56
57 #define __RSEQ_ASM_DEFINE_ABORT(table_label, label, teardown,           \
58                                 abort_label, version, flags,            \
59                                 start_ip, post_commit_offset, abort_ip) \
60                 ".balign 32\n\t"                                        \
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"                               \
66                 teardown                                                \
67                 "b %l[" __rseq_str(abort_label) "]\n\t"
68
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)
74
75 #define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label)         \
76                 __rseq_str(label) ":\n\t"                               \
77                 teardown                                                \
78                 "b %l[" __rseq_str(cmpfail_label) "]\n\t"
79
80 #define rseq_workaround_gcc_asm_size_guess()    __asm__ __volatile__("")
81
82 static inline __attribute__((always_inline))
83 int rseq_cmpeqv_storev(intptr_t *v, intptr_t expect, intptr_t newv, int cpu)
84 {
85         RSEQ_INJECT_C(9)
86
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)
93                 RSEQ_INJECT_ASM(3)
94                 "ldr r0, %[v]\n\t"
95                 "cmp %[expect], r0\n\t"
96                 "bne %l[cmpfail]\n\t"
97                 RSEQ_INJECT_ASM(4)
98 #ifdef RSEQ_COMPARE_TWICE
99                 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
100                 "ldr r0, %[v]\n\t"
101                 "cmp %[expect], r0\n\t"
102                 "bne %l[error2]\n\t"
103 #endif
104                 /* final store */
105                 "str %[newv], %[v]\n\t"
106                 "2:\n\t"
107                 RSEQ_INJECT_ASM(5)
108                 "b 5f\n\t"
109                 RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
110                 "5:\n\t"
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),
115                   [v]                   "m" (*v),
116                   [expect]              "r" (expect),
117                   [newv]                "r" (newv)
118                   RSEQ_INJECT_INPUT
119                 : "r0", "memory", "cc"
120                   RSEQ_INJECT_CLOBBER
121                 : abort, cmpfail
122 #ifdef RSEQ_COMPARE_TWICE
123                   , error1, error2
124 #endif
125         );
126         rseq_workaround_gcc_asm_size_guess();
127         return 0;
128 abort:
129         rseq_workaround_gcc_asm_size_guess();
130         RSEQ_INJECT_FAILED
131         return -1;
132 cmpfail:
133         rseq_workaround_gcc_asm_size_guess();
134         return 1;
135 #ifdef RSEQ_COMPARE_TWICE
136 error1:
137         rseq_bug("cpu_id comparison failed");
138 error2:
139         rseq_bug("expected value comparison failed");
140 #endif
141 }
142
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)
146 {
147         RSEQ_INJECT_C(9)
148
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)
155                 RSEQ_INJECT_ASM(3)
156                 "ldr r0, %[v]\n\t"
157                 "cmp %[expectnot], r0\n\t"
158                 "beq %l[cmpfail]\n\t"
159                 RSEQ_INJECT_ASM(4)
160 #ifdef RSEQ_COMPARE_TWICE
161                 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
162                 "ldr r0, %[v]\n\t"
163                 "cmp %[expectnot], r0\n\t"
164                 "beq %l[error2]\n\t"
165 #endif
166                 "str r0, %[load]\n\t"
167                 "add r0, %[voffp]\n\t"
168                 "ldr r0, [r0]\n\t"
169                 /* final store */
170                 "str r0, %[v]\n\t"
171                 "2:\n\t"
172                 RSEQ_INJECT_ASM(5)
173                 "b 5f\n\t"
174                 RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
175                 "5:\n\t"
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 */
181                   [v]                   "m" (*v),
182                   [expectnot]           "r" (expectnot),
183                   [voffp]               "Ir" (voffp),
184                   [load]                "m" (*load)
185                   RSEQ_INJECT_INPUT
186                 : "r0", "memory", "cc"
187                   RSEQ_INJECT_CLOBBER
188                 : abort, cmpfail
189 #ifdef RSEQ_COMPARE_TWICE
190                   , error1, error2
191 #endif
192         );
193         rseq_workaround_gcc_asm_size_guess();
194         return 0;
195 abort:
196         rseq_workaround_gcc_asm_size_guess();
197         RSEQ_INJECT_FAILED
198         return -1;
199 cmpfail:
200         rseq_workaround_gcc_asm_size_guess();
201         return 1;
202 #ifdef RSEQ_COMPARE_TWICE
203 error1:
204         rseq_bug("cpu_id comparison failed");
205 error2:
206         rseq_bug("expected value comparison failed");
207 #endif
208 }
209
210 static inline __attribute__((always_inline))
211 int rseq_addv(intptr_t *v, intptr_t count, int cpu)
212 {
213         RSEQ_INJECT_C(9)
214
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)
221                 RSEQ_INJECT_ASM(3)
222 #ifdef RSEQ_COMPARE_TWICE
223                 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
224 #endif
225                 "ldr r0, %[v]\n\t"
226                 "add r0, %[count]\n\t"
227                 /* final store */
228                 "str r0, %[v]\n\t"
229                 "2:\n\t"
230                 RSEQ_INJECT_ASM(4)
231                 "b 5f\n\t"
232                 RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
233                 "5:\n\t"
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                   [v]                   "m" (*v),
239                   [count]               "Ir" (count)
240                   RSEQ_INJECT_INPUT
241                 : "r0", "memory", "cc"
242                   RSEQ_INJECT_CLOBBER
243                 : abort
244 #ifdef RSEQ_COMPARE_TWICE
245                   , error1
246 #endif
247         );
248         rseq_workaround_gcc_asm_size_guess();
249         return 0;
250 abort:
251         rseq_workaround_gcc_asm_size_guess();
252         RSEQ_INJECT_FAILED
253         return -1;
254 #ifdef RSEQ_COMPARE_TWICE
255 error1:
256         rseq_bug("cpu_id comparison failed");
257 #endif
258 }
259
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)
264 {
265         RSEQ_INJECT_C(9)
266
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)
273                 RSEQ_INJECT_ASM(3)
274                 "ldr r0, %[v]\n\t"
275                 "cmp %[expect], r0\n\t"
276                 "bne %l[cmpfail]\n\t"
277                 RSEQ_INJECT_ASM(4)
278 #ifdef RSEQ_COMPARE_TWICE
279                 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
280                 "ldr r0, %[v]\n\t"
281                 "cmp %[expect], r0\n\t"
282                 "bne %l[error2]\n\t"
283 #endif
284                 /* try store */
285                 "str %[newv2], %[v2]\n\t"
286                 RSEQ_INJECT_ASM(5)
287                 /* final store */
288                 "str %[newv], %[v]\n\t"
289                 "2:\n\t"
290                 RSEQ_INJECT_ASM(6)
291                 "b 5f\n\t"
292                 RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
293                 "5:\n\t"
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 */
299                   [v2]                  "m" (*v2),
300                   [newv2]               "r" (newv2),
301                   /* final store input */
302                   [v]                   "m" (*v),
303                   [expect]              "r" (expect),
304                   [newv]                "r" (newv)
305                   RSEQ_INJECT_INPUT
306                 : "r0", "memory", "cc"
307                   RSEQ_INJECT_CLOBBER
308                 : abort, cmpfail
309 #ifdef RSEQ_COMPARE_TWICE
310                   , error1, error2
311 #endif
312         );
313         rseq_workaround_gcc_asm_size_guess();
314         return 0;
315 abort:
316         rseq_workaround_gcc_asm_size_guess();
317         RSEQ_INJECT_FAILED
318         return -1;
319 cmpfail:
320         rseq_workaround_gcc_asm_size_guess();
321         return 1;
322 #ifdef RSEQ_COMPARE_TWICE
323 error1:
324         rseq_bug("cpu_id comparison failed");
325 error2:
326         rseq_bug("expected value comparison failed");
327 #endif
328 }
329
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)
334 {
335         RSEQ_INJECT_C(9)
336
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)
343                 RSEQ_INJECT_ASM(3)
344                 "ldr r0, %[v]\n\t"
345                 "cmp %[expect], r0\n\t"
346                 "bne %l[cmpfail]\n\t"
347                 RSEQ_INJECT_ASM(4)
348 #ifdef RSEQ_COMPARE_TWICE
349                 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
350                 "ldr r0, %[v]\n\t"
351                 "cmp %[expect], r0\n\t"
352                 "bne %l[error2]\n\t"
353 #endif
354                 /* try store */
355                 "str %[newv2], %[v2]\n\t"
356                 RSEQ_INJECT_ASM(5)
357                 "dmb\n\t"       /* full mb provides store-release */
358                 /* final store */
359                 "str %[newv], %[v]\n\t"
360                 "2:\n\t"
361                 RSEQ_INJECT_ASM(6)
362                 "b 5f\n\t"
363                 RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
364                 "5:\n\t"
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 */
370                   [v2]                  "m" (*v2),
371                   [newv2]               "r" (newv2),
372                   /* final store input */
373                   [v]                   "m" (*v),
374                   [expect]              "r" (expect),
375                   [newv]                "r" (newv)
376                   RSEQ_INJECT_INPUT
377                 : "r0", "memory", "cc"
378                   RSEQ_INJECT_CLOBBER
379                 : abort, cmpfail
380 #ifdef RSEQ_COMPARE_TWICE
381                   , error1, error2
382 #endif
383         );
384         rseq_workaround_gcc_asm_size_guess();
385         return 0;
386 abort:
387         rseq_workaround_gcc_asm_size_guess();
388         RSEQ_INJECT_FAILED
389         return -1;
390 cmpfail:
391         rseq_workaround_gcc_asm_size_guess();
392         return 1;
393 #ifdef RSEQ_COMPARE_TWICE
394 error1:
395         rseq_bug("cpu_id comparison failed");
396 error2:
397         rseq_bug("expected value comparison failed");
398 #endif
399 }
400
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)
405 {
406         RSEQ_INJECT_C(9)
407
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)
414                 RSEQ_INJECT_ASM(3)
415                 "ldr r0, %[v]\n\t"
416                 "cmp %[expect], r0\n\t"
417                 "bne %l[cmpfail]\n\t"
418                 RSEQ_INJECT_ASM(4)
419                 "ldr r0, %[v2]\n\t"
420                 "cmp %[expect2], r0\n\t"
421                 "bne %l[cmpfail]\n\t"
422                 RSEQ_INJECT_ASM(5)
423 #ifdef RSEQ_COMPARE_TWICE
424                 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
425                 "ldr r0, %[v]\n\t"
426                 "cmp %[expect], r0\n\t"
427                 "bne %l[error2]\n\t"
428                 "ldr r0, %[v2]\n\t"
429                 "cmp %[expect2], r0\n\t"
430                 "bne %l[error3]\n\t"
431 #endif
432                 /* final store */
433                 "str %[newv], %[v]\n\t"
434                 "2:\n\t"
435                 RSEQ_INJECT_ASM(6)
436                 "b 5f\n\t"
437                 RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
438                 "5:\n\t"
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),
443                   /* cmp2 input */
444                   [v2]                  "m" (*v2),
445                   [expect2]             "r" (expect2),
446                   /* final store input */
447                   [v]                   "m" (*v),
448                   [expect]              "r" (expect),
449                   [newv]                "r" (newv)
450                   RSEQ_INJECT_INPUT
451                 : "r0", "memory", "cc"
452                   RSEQ_INJECT_CLOBBER
453                 : abort, cmpfail
454 #ifdef RSEQ_COMPARE_TWICE
455                   , error1, error2, error3
456 #endif
457         );
458         rseq_workaround_gcc_asm_size_guess();
459         return 0;
460 abort:
461         rseq_workaround_gcc_asm_size_guess();
462         RSEQ_INJECT_FAILED
463         return -1;
464 cmpfail:
465         rseq_workaround_gcc_asm_size_guess();
466         return 1;
467 #ifdef RSEQ_COMPARE_TWICE
468 error1:
469         rseq_bug("cpu_id comparison failed");
470 error2:
471         rseq_bug("1st expected value comparison failed");
472 error3:
473         rseq_bug("2nd expected value comparison failed");
474 #endif
475 }
476
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)
481 {
482         uint32_t rseq_scratch[3];
483
484         RSEQ_INJECT_C(9)
485
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)
495                 RSEQ_INJECT_ASM(3)
496                 "ldr r0, %[v]\n\t"
497                 "cmp %[expect], r0\n\t"
498                 "bne 5f\n\t"
499                 RSEQ_INJECT_ASM(4)
500 #ifdef RSEQ_COMPARE_TWICE
501                 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f)
502                 "ldr r0, %[v]\n\t"
503                 "cmp %[expect], r0\n\t"
504                 "bne 7f\n\t"
505 #endif
506                 /* try memcpy */
507                 "cmp %[len], #0\n\t" \
508                 "beq 333f\n\t" \
509                 "222:\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" \
515                 "bne 222b\n\t" \
516                 "333:\n\t" \
517                 RSEQ_INJECT_ASM(5)
518                 /* final store */
519                 "str %[newv], %[v]\n\t"
520                 "2:\n\t"
521                 RSEQ_INJECT_ASM(6)
522                 /* teardown */
523                 "ldr %[len], %[rseq_scratch2]\n\t"
524                 "ldr %[dst], %[rseq_scratch1]\n\t"
525                 "ldr %[src], %[rseq_scratch0]\n\t"
526                 "b 8f\n\t"
527                 RSEQ_ASM_DEFINE_ABORT(3, 4,
528                                       /* teardown */
529                                       "ldr %[len], %[rseq_scratch2]\n\t"
530                                       "ldr %[dst], %[rseq_scratch1]\n\t"
531                                       "ldr %[src], %[rseq_scratch0]\n\t",
532                                       abort, 1b, 2b, 4f)
533                 RSEQ_ASM_DEFINE_CMPFAIL(5,
534                                         /* teardown */
535                                         "ldr %[len], %[rseq_scratch2]\n\t"
536                                         "ldr %[dst], %[rseq_scratch1]\n\t"
537                                         "ldr %[src], %[rseq_scratch0]\n\t",
538                                         cmpfail)
539 #ifdef RSEQ_COMPARE_TWICE
540                 RSEQ_ASM_DEFINE_CMPFAIL(6,
541                                         /* teardown */
542                                         "ldr %[len], %[rseq_scratch2]\n\t"
543                                         "ldr %[dst], %[rseq_scratch1]\n\t"
544                                         "ldr %[src], %[rseq_scratch0]\n\t",
545                                         error1)
546                 RSEQ_ASM_DEFINE_CMPFAIL(7,
547                                         /* teardown */
548                                         "ldr %[len], %[rseq_scratch2]\n\t"
549                                         "ldr %[dst], %[rseq_scratch1]\n\t"
550                                         "ldr %[src], %[rseq_scratch0]\n\t",
551                                         error2)
552 #endif
553                 "8:\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 */
559                   [v]                   "m" (*v),
560                   [expect]              "r" (expect),
561                   [newv]                "r" (newv),
562                   /* try memcpy input */
563                   [dst]                 "r" (dst),
564                   [src]                 "r" (src),
565                   [len]                 "r" (len),
566                   [rseq_scratch0]       "m" (rseq_scratch[0]),
567                   [rseq_scratch1]       "m" (rseq_scratch[1]),
568                   [rseq_scratch2]       "m" (rseq_scratch[2])
569                   RSEQ_INJECT_INPUT
570                 : "r0", "memory", "cc"
571                   RSEQ_INJECT_CLOBBER
572                 : abort, cmpfail
573 #ifdef RSEQ_COMPARE_TWICE
574                   , error1, error2
575 #endif
576         );
577         rseq_workaround_gcc_asm_size_guess();
578         return 0;
579 abort:
580         rseq_workaround_gcc_asm_size_guess();
581         RSEQ_INJECT_FAILED
582         return -1;
583 cmpfail:
584         rseq_workaround_gcc_asm_size_guess();
585         return 1;
586 #ifdef RSEQ_COMPARE_TWICE
587 error1:
588         rseq_workaround_gcc_asm_size_guess();
589         rseq_bug("cpu_id comparison failed");
590 error2:
591         rseq_workaround_gcc_asm_size_guess();
592         rseq_bug("expected value comparison failed");
593 #endif
594 }
595
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)
600 {
601         uint32_t rseq_scratch[3];
602
603         RSEQ_INJECT_C(9)
604
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)
614                 RSEQ_INJECT_ASM(3)
615                 "ldr r0, %[v]\n\t"
616                 "cmp %[expect], r0\n\t"
617                 "bne 5f\n\t"
618                 RSEQ_INJECT_ASM(4)
619 #ifdef RSEQ_COMPARE_TWICE
620                 RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f)
621                 "ldr r0, %[v]\n\t"
622                 "cmp %[expect], r0\n\t"
623                 "bne 7f\n\t"
624 #endif
625                 /* try memcpy */
626                 "cmp %[len], #0\n\t" \
627                 "beq 333f\n\t" \
628                 "222:\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" \
634                 "bne 222b\n\t" \
635                 "333:\n\t" \
636                 RSEQ_INJECT_ASM(5)
637                 "dmb\n\t"       /* full mb provides store-release */
638                 /* final store */
639                 "str %[newv], %[v]\n\t"
640                 "2:\n\t"
641                 RSEQ_INJECT_ASM(6)
642                 /* teardown */
643                 "ldr %[len], %[rseq_scratch2]\n\t"
644                 "ldr %[dst], %[rseq_scratch1]\n\t"
645                 "ldr %[src], %[rseq_scratch0]\n\t"
646                 "b 8f\n\t"
647                 RSEQ_ASM_DEFINE_ABORT(3, 4,
648                                       /* teardown */
649                                       "ldr %[len], %[rseq_scratch2]\n\t"
650                                       "ldr %[dst], %[rseq_scratch1]\n\t"
651                                       "ldr %[src], %[rseq_scratch0]\n\t",
652                                       abort, 1b, 2b, 4f)
653                 RSEQ_ASM_DEFINE_CMPFAIL(5,
654                                         /* teardown */
655                                         "ldr %[len], %[rseq_scratch2]\n\t"
656                                         "ldr %[dst], %[rseq_scratch1]\n\t"
657                                         "ldr %[src], %[rseq_scratch0]\n\t",
658                                         cmpfail)
659 #ifdef RSEQ_COMPARE_TWICE
660                 RSEQ_ASM_DEFINE_CMPFAIL(6,
661                                         /* teardown */
662                                         "ldr %[len], %[rseq_scratch2]\n\t"
663                                         "ldr %[dst], %[rseq_scratch1]\n\t"
664                                         "ldr %[src], %[rseq_scratch0]\n\t",
665                                         error1)
666                 RSEQ_ASM_DEFINE_CMPFAIL(7,
667                                         /* teardown */
668                                         "ldr %[len], %[rseq_scratch2]\n\t"
669                                         "ldr %[dst], %[rseq_scratch1]\n\t"
670                                         "ldr %[src], %[rseq_scratch0]\n\t",
671                                         error2)
672 #endif
673                 "8:\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 */
679                   [v]                   "m" (*v),
680                   [expect]              "r" (expect),
681                   [newv]                "r" (newv),
682                   /* try memcpy input */
683                   [dst]                 "r" (dst),
684                   [src]                 "r" (src),
685                   [len]                 "r" (len),
686                   [rseq_scratch0]       "m" (rseq_scratch[0]),
687                   [rseq_scratch1]       "m" (rseq_scratch[1]),
688                   [rseq_scratch2]       "m" (rseq_scratch[2])
689                   RSEQ_INJECT_INPUT
690                 : "r0", "memory", "cc"
691                   RSEQ_INJECT_CLOBBER
692                 : abort, cmpfail
693 #ifdef RSEQ_COMPARE_TWICE
694                   , error1, error2
695 #endif
696         );
697         rseq_workaround_gcc_asm_size_guess();
698         return 0;
699 abort:
700         rseq_workaround_gcc_asm_size_guess();
701         RSEQ_INJECT_FAILED
702         return -1;
703 cmpfail:
704         rseq_workaround_gcc_asm_size_guess();
705         return 1;
706 #ifdef RSEQ_COMPARE_TWICE
707 error1:
708         rseq_workaround_gcc_asm_size_guess();
709         rseq_bug("cpu_id comparison failed");
710 error2:
711         rseq_workaround_gcc_asm_size_guess();
712         rseq_bug("expected value comparison failed");
713 #endif
714 }
715
716 #endif /* !RSEQ_SKIP_FASTPATH */