1*4882a593Smuzhiyun /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * rseq-arm.h
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * (C) Copyright 2016-2018 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun /*
9*4882a593Smuzhiyun * - ARM little endian
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun * RSEQ_SIG uses the udf A32 instruction with an uncommon immediate operand
12*4882a593Smuzhiyun * value 0x5de3. This traps if user-space reaches this instruction by mistake,
13*4882a593Smuzhiyun * and the uncommon operand ensures the kernel does not move the instruction
14*4882a593Smuzhiyun * pointer to attacker-controlled code on rseq abort.
15*4882a593Smuzhiyun *
16*4882a593Smuzhiyun * The instruction pattern in the A32 instruction set is:
17*4882a593Smuzhiyun *
18*4882a593Smuzhiyun * e7f5def3 udf #24035 ; 0x5de3
19*4882a593Smuzhiyun *
20*4882a593Smuzhiyun * This translates to the following instruction pattern in the T16 instruction
21*4882a593Smuzhiyun * set:
22*4882a593Smuzhiyun *
23*4882a593Smuzhiyun * little endian:
24*4882a593Smuzhiyun * def3 udf #243 ; 0xf3
25*4882a593Smuzhiyun * e7f5 b.n <7f5>
26*4882a593Smuzhiyun *
27*4882a593Smuzhiyun * - ARMv6+ big endian (BE8):
28*4882a593Smuzhiyun *
29*4882a593Smuzhiyun * ARMv6+ -mbig-endian generates mixed endianness code vs data: little-endian
30*4882a593Smuzhiyun * code and big-endian data. The data value of the signature needs to have its
31*4882a593Smuzhiyun * byte order reversed to generate the trap instruction:
32*4882a593Smuzhiyun *
33*4882a593Smuzhiyun * Data: 0xf3def5e7
34*4882a593Smuzhiyun *
35*4882a593Smuzhiyun * Translates to this A32 instruction pattern:
36*4882a593Smuzhiyun *
37*4882a593Smuzhiyun * e7f5def3 udf #24035 ; 0x5de3
38*4882a593Smuzhiyun *
39*4882a593Smuzhiyun * Translates to this T16 instruction pattern:
40*4882a593Smuzhiyun *
41*4882a593Smuzhiyun * def3 udf #243 ; 0xf3
42*4882a593Smuzhiyun * e7f5 b.n <7f5>
43*4882a593Smuzhiyun *
44*4882a593Smuzhiyun * - Prior to ARMv6 big endian (BE32):
45*4882a593Smuzhiyun *
46*4882a593Smuzhiyun * Prior to ARMv6, -mbig-endian generates big-endian code and data
47*4882a593Smuzhiyun * (which match), so the endianness of the data representation of the
48*4882a593Smuzhiyun * signature should not be reversed. However, the choice between BE32
49*4882a593Smuzhiyun * and BE8 is done by the linker, so we cannot know whether code and
50*4882a593Smuzhiyun * data endianness will be mixed before the linker is invoked. So rather
51*4882a593Smuzhiyun * than try to play tricks with the linker, the rseq signature is simply
52*4882a593Smuzhiyun * data (not a trap instruction) prior to ARMv6 on big endian. This is
53*4882a593Smuzhiyun * why the signature is expressed as data (.word) rather than as
54*4882a593Smuzhiyun * instruction (.inst) in assembler.
55*4882a593Smuzhiyun */
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun #ifdef __ARMEB__
58*4882a593Smuzhiyun #define RSEQ_SIG 0xf3def5e7 /* udf #24035 ; 0x5de3 (ARMv6+) */
59*4882a593Smuzhiyun #else
60*4882a593Smuzhiyun #define RSEQ_SIG 0xe7f5def3 /* udf #24035 ; 0x5de3 */
61*4882a593Smuzhiyun #endif
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun #define rseq_smp_mb() __asm__ __volatile__ ("dmb" ::: "memory", "cc")
64*4882a593Smuzhiyun #define rseq_smp_rmb() __asm__ __volatile__ ("dmb" ::: "memory", "cc")
65*4882a593Smuzhiyun #define rseq_smp_wmb() __asm__ __volatile__ ("dmb" ::: "memory", "cc")
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun #define rseq_smp_load_acquire(p) \
68*4882a593Smuzhiyun __extension__ ({ \
69*4882a593Smuzhiyun __typeof(*p) ____p1 = RSEQ_READ_ONCE(*p); \
70*4882a593Smuzhiyun rseq_smp_mb(); \
71*4882a593Smuzhiyun ____p1; \
72*4882a593Smuzhiyun })
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun #define rseq_smp_acquire__after_ctrl_dep() rseq_smp_rmb()
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun #define rseq_smp_store_release(p, v) \
77*4882a593Smuzhiyun do { \
78*4882a593Smuzhiyun rseq_smp_mb(); \
79*4882a593Smuzhiyun RSEQ_WRITE_ONCE(*p, v); \
80*4882a593Smuzhiyun } while (0)
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun #ifdef RSEQ_SKIP_FASTPATH
83*4882a593Smuzhiyun #include "rseq-skip.h"
84*4882a593Smuzhiyun #else /* !RSEQ_SKIP_FASTPATH */
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun #define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, start_ip, \
87*4882a593Smuzhiyun post_commit_offset, abort_ip) \
88*4882a593Smuzhiyun ".pushsection __rseq_cs, \"aw\"\n\t" \
89*4882a593Smuzhiyun ".balign 32\n\t" \
90*4882a593Smuzhiyun __rseq_str(label) ":\n\t" \
91*4882a593Smuzhiyun ".word " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
92*4882a593Smuzhiyun ".word " __rseq_str(start_ip) ", 0x0, " __rseq_str(post_commit_offset) ", 0x0, " __rseq_str(abort_ip) ", 0x0\n\t" \
93*4882a593Smuzhiyun ".popsection\n\t" \
94*4882a593Smuzhiyun ".pushsection __rseq_cs_ptr_array, \"aw\"\n\t" \
95*4882a593Smuzhiyun ".word " __rseq_str(label) "b, 0x0\n\t" \
96*4882a593Smuzhiyun ".popsection\n\t"
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun #define RSEQ_ASM_DEFINE_TABLE(label, start_ip, post_commit_ip, abort_ip) \
99*4882a593Smuzhiyun __RSEQ_ASM_DEFINE_TABLE(label, 0x0, 0x0, start_ip, \
100*4882a593Smuzhiyun (post_commit_ip - start_ip), abort_ip)
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun /*
103*4882a593Smuzhiyun * Exit points of a rseq critical section consist of all instructions outside
104*4882a593Smuzhiyun * of the critical section where a critical section can either branch to or
105*4882a593Smuzhiyun * reach through the normal course of its execution. The abort IP and the
106*4882a593Smuzhiyun * post-commit IP are already part of the __rseq_cs section and should not be
107*4882a593Smuzhiyun * explicitly defined as additional exit points. Knowing all exit points is
108*4882a593Smuzhiyun * useful to assist debuggers stepping over the critical section.
109*4882a593Smuzhiyun */
110*4882a593Smuzhiyun #define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip) \
111*4882a593Smuzhiyun ".pushsection __rseq_exit_point_array, \"aw\"\n\t" \
112*4882a593Smuzhiyun ".word " __rseq_str(start_ip) ", 0x0, " __rseq_str(exit_ip) ", 0x0\n\t" \
113*4882a593Smuzhiyun ".popsection\n\t"
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \
116*4882a593Smuzhiyun RSEQ_INJECT_ASM(1) \
117*4882a593Smuzhiyun "adr r0, " __rseq_str(cs_label) "\n\t" \
118*4882a593Smuzhiyun "str r0, %[" __rseq_str(rseq_cs) "]\n\t" \
119*4882a593Smuzhiyun __rseq_str(label) ":\n\t"
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun #define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label) \
122*4882a593Smuzhiyun RSEQ_INJECT_ASM(2) \
123*4882a593Smuzhiyun "ldr r0, %[" __rseq_str(current_cpu_id) "]\n\t" \
124*4882a593Smuzhiyun "cmp %[" __rseq_str(cpu_id) "], r0\n\t" \
125*4882a593Smuzhiyun "bne " __rseq_str(label) "\n\t"
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun #define __RSEQ_ASM_DEFINE_ABORT(table_label, label, teardown, \
128*4882a593Smuzhiyun abort_label, version, flags, \
129*4882a593Smuzhiyun start_ip, post_commit_offset, abort_ip) \
130*4882a593Smuzhiyun ".balign 32\n\t" \
131*4882a593Smuzhiyun __rseq_str(table_label) ":\n\t" \
132*4882a593Smuzhiyun ".word " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
133*4882a593Smuzhiyun ".word " __rseq_str(start_ip) ", 0x0, " __rseq_str(post_commit_offset) ", 0x0, " __rseq_str(abort_ip) ", 0x0\n\t" \
134*4882a593Smuzhiyun ".word " __rseq_str(RSEQ_SIG) "\n\t" \
135*4882a593Smuzhiyun __rseq_str(label) ":\n\t" \
136*4882a593Smuzhiyun teardown \
137*4882a593Smuzhiyun "b %l[" __rseq_str(abort_label) "]\n\t"
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun #define RSEQ_ASM_DEFINE_ABORT(table_label, label, teardown, abort_label, \
140*4882a593Smuzhiyun start_ip, post_commit_ip, abort_ip) \
141*4882a593Smuzhiyun __RSEQ_ASM_DEFINE_ABORT(table_label, label, teardown, \
142*4882a593Smuzhiyun abort_label, 0x0, 0x0, start_ip, \
143*4882a593Smuzhiyun (post_commit_ip - start_ip), abort_ip)
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun #define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label) \
146*4882a593Smuzhiyun __rseq_str(label) ":\n\t" \
147*4882a593Smuzhiyun teardown \
148*4882a593Smuzhiyun "b %l[" __rseq_str(cmpfail_label) "]\n\t"
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun static inline __attribute__((always_inline))
rseq_cmpeqv_storev(intptr_t * v,intptr_t expect,intptr_t newv,int cpu)151*4882a593Smuzhiyun int rseq_cmpeqv_storev(intptr_t *v, intptr_t expect, intptr_t newv, int cpu)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun RSEQ_INJECT_C(9)
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun __asm__ __volatile__ goto (
156*4882a593Smuzhiyun RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
157*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
158*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
159*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
160*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
161*4882a593Smuzhiyun #endif
162*4882a593Smuzhiyun /* Start rseq by storing table entry pointer into rseq_cs. */
163*4882a593Smuzhiyun RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
164*4882a593Smuzhiyun RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
165*4882a593Smuzhiyun RSEQ_INJECT_ASM(3)
166*4882a593Smuzhiyun "ldr r0, %[v]\n\t"
167*4882a593Smuzhiyun "cmp %[expect], r0\n\t"
168*4882a593Smuzhiyun "bne %l[cmpfail]\n\t"
169*4882a593Smuzhiyun RSEQ_INJECT_ASM(4)
170*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
171*4882a593Smuzhiyun RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
172*4882a593Smuzhiyun "ldr r0, %[v]\n\t"
173*4882a593Smuzhiyun "cmp %[expect], r0\n\t"
174*4882a593Smuzhiyun "bne %l[error2]\n\t"
175*4882a593Smuzhiyun #endif
176*4882a593Smuzhiyun /* final store */
177*4882a593Smuzhiyun "str %[newv], %[v]\n\t"
178*4882a593Smuzhiyun "2:\n\t"
179*4882a593Smuzhiyun RSEQ_INJECT_ASM(5)
180*4882a593Smuzhiyun "b 5f\n\t"
181*4882a593Smuzhiyun RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
182*4882a593Smuzhiyun "5:\n\t"
183*4882a593Smuzhiyun : /* gcc asm goto does not allow outputs */
184*4882a593Smuzhiyun : [cpu_id] "r" (cpu),
185*4882a593Smuzhiyun [current_cpu_id] "m" (rseq_get_abi()->cpu_id),
186*4882a593Smuzhiyun [rseq_cs] "m" (rseq_get_abi()->rseq_cs.arch.ptr),
187*4882a593Smuzhiyun [v] "m" (*v),
188*4882a593Smuzhiyun [expect] "r" (expect),
189*4882a593Smuzhiyun [newv] "r" (newv)
190*4882a593Smuzhiyun RSEQ_INJECT_INPUT
191*4882a593Smuzhiyun : "r0", "memory", "cc"
192*4882a593Smuzhiyun RSEQ_INJECT_CLOBBER
193*4882a593Smuzhiyun : abort, cmpfail
194*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
195*4882a593Smuzhiyun , error1, error2
196*4882a593Smuzhiyun #endif
197*4882a593Smuzhiyun );
198*4882a593Smuzhiyun rseq_after_asm_goto();
199*4882a593Smuzhiyun return 0;
200*4882a593Smuzhiyun abort:
201*4882a593Smuzhiyun rseq_after_asm_goto();
202*4882a593Smuzhiyun RSEQ_INJECT_FAILED
203*4882a593Smuzhiyun return -1;
204*4882a593Smuzhiyun cmpfail:
205*4882a593Smuzhiyun rseq_after_asm_goto();
206*4882a593Smuzhiyun return 1;
207*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
208*4882a593Smuzhiyun error1:
209*4882a593Smuzhiyun rseq_after_asm_goto();
210*4882a593Smuzhiyun rseq_bug("cpu_id comparison failed");
211*4882a593Smuzhiyun error2:
212*4882a593Smuzhiyun rseq_after_asm_goto();
213*4882a593Smuzhiyun rseq_bug("expected value comparison failed");
214*4882a593Smuzhiyun #endif
215*4882a593Smuzhiyun }
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun static inline __attribute__((always_inline))
rseq_cmpnev_storeoffp_load(intptr_t * v,intptr_t expectnot,long voffp,intptr_t * load,int cpu)218*4882a593Smuzhiyun int rseq_cmpnev_storeoffp_load(intptr_t *v, intptr_t expectnot,
219*4882a593Smuzhiyun long voffp, intptr_t *load, int cpu)
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun RSEQ_INJECT_C(9)
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun __asm__ __volatile__ goto (
224*4882a593Smuzhiyun RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
225*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
226*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
227*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
228*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
229*4882a593Smuzhiyun #endif
230*4882a593Smuzhiyun /* Start rseq by storing table entry pointer into rseq_cs. */
231*4882a593Smuzhiyun RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
232*4882a593Smuzhiyun RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
233*4882a593Smuzhiyun RSEQ_INJECT_ASM(3)
234*4882a593Smuzhiyun "ldr r0, %[v]\n\t"
235*4882a593Smuzhiyun "cmp %[expectnot], r0\n\t"
236*4882a593Smuzhiyun "beq %l[cmpfail]\n\t"
237*4882a593Smuzhiyun RSEQ_INJECT_ASM(4)
238*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
239*4882a593Smuzhiyun RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
240*4882a593Smuzhiyun "ldr r0, %[v]\n\t"
241*4882a593Smuzhiyun "cmp %[expectnot], r0\n\t"
242*4882a593Smuzhiyun "beq %l[error2]\n\t"
243*4882a593Smuzhiyun #endif
244*4882a593Smuzhiyun "str r0, %[load]\n\t"
245*4882a593Smuzhiyun "add r0, %[voffp]\n\t"
246*4882a593Smuzhiyun "ldr r0, [r0]\n\t"
247*4882a593Smuzhiyun /* final store */
248*4882a593Smuzhiyun "str r0, %[v]\n\t"
249*4882a593Smuzhiyun "2:\n\t"
250*4882a593Smuzhiyun RSEQ_INJECT_ASM(5)
251*4882a593Smuzhiyun "b 5f\n\t"
252*4882a593Smuzhiyun RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
253*4882a593Smuzhiyun "5:\n\t"
254*4882a593Smuzhiyun : /* gcc asm goto does not allow outputs */
255*4882a593Smuzhiyun : [cpu_id] "r" (cpu),
256*4882a593Smuzhiyun [current_cpu_id] "m" (rseq_get_abi()->cpu_id),
257*4882a593Smuzhiyun [rseq_cs] "m" (rseq_get_abi()->rseq_cs.arch.ptr),
258*4882a593Smuzhiyun /* final store input */
259*4882a593Smuzhiyun [v] "m" (*v),
260*4882a593Smuzhiyun [expectnot] "r" (expectnot),
261*4882a593Smuzhiyun [voffp] "Ir" (voffp),
262*4882a593Smuzhiyun [load] "m" (*load)
263*4882a593Smuzhiyun RSEQ_INJECT_INPUT
264*4882a593Smuzhiyun : "r0", "memory", "cc"
265*4882a593Smuzhiyun RSEQ_INJECT_CLOBBER
266*4882a593Smuzhiyun : abort, cmpfail
267*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
268*4882a593Smuzhiyun , error1, error2
269*4882a593Smuzhiyun #endif
270*4882a593Smuzhiyun );
271*4882a593Smuzhiyun rseq_after_asm_goto();
272*4882a593Smuzhiyun return 0;
273*4882a593Smuzhiyun abort:
274*4882a593Smuzhiyun rseq_after_asm_goto();
275*4882a593Smuzhiyun RSEQ_INJECT_FAILED
276*4882a593Smuzhiyun return -1;
277*4882a593Smuzhiyun cmpfail:
278*4882a593Smuzhiyun rseq_after_asm_goto();
279*4882a593Smuzhiyun return 1;
280*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
281*4882a593Smuzhiyun error1:
282*4882a593Smuzhiyun rseq_after_asm_goto();
283*4882a593Smuzhiyun rseq_bug("cpu_id comparison failed");
284*4882a593Smuzhiyun error2:
285*4882a593Smuzhiyun rseq_after_asm_goto();
286*4882a593Smuzhiyun rseq_bug("expected value comparison failed");
287*4882a593Smuzhiyun #endif
288*4882a593Smuzhiyun }
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun static inline __attribute__((always_inline))
rseq_addv(intptr_t * v,intptr_t count,int cpu)291*4882a593Smuzhiyun int rseq_addv(intptr_t *v, intptr_t count, int cpu)
292*4882a593Smuzhiyun {
293*4882a593Smuzhiyun RSEQ_INJECT_C(9)
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun __asm__ __volatile__ goto (
296*4882a593Smuzhiyun RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
297*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
298*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
299*4882a593Smuzhiyun #endif
300*4882a593Smuzhiyun /* Start rseq by storing table entry pointer into rseq_cs. */
301*4882a593Smuzhiyun RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
302*4882a593Smuzhiyun RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
303*4882a593Smuzhiyun RSEQ_INJECT_ASM(3)
304*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
305*4882a593Smuzhiyun RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
306*4882a593Smuzhiyun #endif
307*4882a593Smuzhiyun "ldr r0, %[v]\n\t"
308*4882a593Smuzhiyun "add r0, %[count]\n\t"
309*4882a593Smuzhiyun /* final store */
310*4882a593Smuzhiyun "str r0, %[v]\n\t"
311*4882a593Smuzhiyun "2:\n\t"
312*4882a593Smuzhiyun RSEQ_INJECT_ASM(4)
313*4882a593Smuzhiyun "b 5f\n\t"
314*4882a593Smuzhiyun RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
315*4882a593Smuzhiyun "5:\n\t"
316*4882a593Smuzhiyun : /* gcc asm goto does not allow outputs */
317*4882a593Smuzhiyun : [cpu_id] "r" (cpu),
318*4882a593Smuzhiyun [current_cpu_id] "m" (rseq_get_abi()->cpu_id),
319*4882a593Smuzhiyun [rseq_cs] "m" (rseq_get_abi()->rseq_cs.arch.ptr),
320*4882a593Smuzhiyun [v] "m" (*v),
321*4882a593Smuzhiyun [count] "Ir" (count)
322*4882a593Smuzhiyun RSEQ_INJECT_INPUT
323*4882a593Smuzhiyun : "r0", "memory", "cc"
324*4882a593Smuzhiyun RSEQ_INJECT_CLOBBER
325*4882a593Smuzhiyun : abort
326*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
327*4882a593Smuzhiyun , error1
328*4882a593Smuzhiyun #endif
329*4882a593Smuzhiyun );
330*4882a593Smuzhiyun rseq_after_asm_goto();
331*4882a593Smuzhiyun return 0;
332*4882a593Smuzhiyun abort:
333*4882a593Smuzhiyun rseq_after_asm_goto();
334*4882a593Smuzhiyun RSEQ_INJECT_FAILED
335*4882a593Smuzhiyun return -1;
336*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
337*4882a593Smuzhiyun error1:
338*4882a593Smuzhiyun rseq_after_asm_goto();
339*4882a593Smuzhiyun rseq_bug("cpu_id comparison failed");
340*4882a593Smuzhiyun #endif
341*4882a593Smuzhiyun }
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun static inline __attribute__((always_inline))
rseq_cmpeqv_trystorev_storev(intptr_t * v,intptr_t expect,intptr_t * v2,intptr_t newv2,intptr_t newv,int cpu)344*4882a593Smuzhiyun int rseq_cmpeqv_trystorev_storev(intptr_t *v, intptr_t expect,
345*4882a593Smuzhiyun intptr_t *v2, intptr_t newv2,
346*4882a593Smuzhiyun intptr_t newv, int cpu)
347*4882a593Smuzhiyun {
348*4882a593Smuzhiyun RSEQ_INJECT_C(9)
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun __asm__ __volatile__ goto (
351*4882a593Smuzhiyun RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
352*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
353*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
354*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
355*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
356*4882a593Smuzhiyun #endif
357*4882a593Smuzhiyun /* Start rseq by storing table entry pointer into rseq_cs. */
358*4882a593Smuzhiyun RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
359*4882a593Smuzhiyun RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
360*4882a593Smuzhiyun RSEQ_INJECT_ASM(3)
361*4882a593Smuzhiyun "ldr r0, %[v]\n\t"
362*4882a593Smuzhiyun "cmp %[expect], r0\n\t"
363*4882a593Smuzhiyun "bne %l[cmpfail]\n\t"
364*4882a593Smuzhiyun RSEQ_INJECT_ASM(4)
365*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
366*4882a593Smuzhiyun RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
367*4882a593Smuzhiyun "ldr r0, %[v]\n\t"
368*4882a593Smuzhiyun "cmp %[expect], r0\n\t"
369*4882a593Smuzhiyun "bne %l[error2]\n\t"
370*4882a593Smuzhiyun #endif
371*4882a593Smuzhiyun /* try store */
372*4882a593Smuzhiyun "str %[newv2], %[v2]\n\t"
373*4882a593Smuzhiyun RSEQ_INJECT_ASM(5)
374*4882a593Smuzhiyun /* final store */
375*4882a593Smuzhiyun "str %[newv], %[v]\n\t"
376*4882a593Smuzhiyun "2:\n\t"
377*4882a593Smuzhiyun RSEQ_INJECT_ASM(6)
378*4882a593Smuzhiyun "b 5f\n\t"
379*4882a593Smuzhiyun RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
380*4882a593Smuzhiyun "5:\n\t"
381*4882a593Smuzhiyun : /* gcc asm goto does not allow outputs */
382*4882a593Smuzhiyun : [cpu_id] "r" (cpu),
383*4882a593Smuzhiyun [current_cpu_id] "m" (rseq_get_abi()->cpu_id),
384*4882a593Smuzhiyun [rseq_cs] "m" (rseq_get_abi()->rseq_cs.arch.ptr),
385*4882a593Smuzhiyun /* try store input */
386*4882a593Smuzhiyun [v2] "m" (*v2),
387*4882a593Smuzhiyun [newv2] "r" (newv2),
388*4882a593Smuzhiyun /* final store input */
389*4882a593Smuzhiyun [v] "m" (*v),
390*4882a593Smuzhiyun [expect] "r" (expect),
391*4882a593Smuzhiyun [newv] "r" (newv)
392*4882a593Smuzhiyun RSEQ_INJECT_INPUT
393*4882a593Smuzhiyun : "r0", "memory", "cc"
394*4882a593Smuzhiyun RSEQ_INJECT_CLOBBER
395*4882a593Smuzhiyun : abort, cmpfail
396*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
397*4882a593Smuzhiyun , error1, error2
398*4882a593Smuzhiyun #endif
399*4882a593Smuzhiyun );
400*4882a593Smuzhiyun rseq_after_asm_goto();
401*4882a593Smuzhiyun return 0;
402*4882a593Smuzhiyun abort:
403*4882a593Smuzhiyun rseq_after_asm_goto();
404*4882a593Smuzhiyun RSEQ_INJECT_FAILED
405*4882a593Smuzhiyun return -1;
406*4882a593Smuzhiyun cmpfail:
407*4882a593Smuzhiyun rseq_after_asm_goto();
408*4882a593Smuzhiyun return 1;
409*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
410*4882a593Smuzhiyun error1:
411*4882a593Smuzhiyun rseq_after_asm_goto();
412*4882a593Smuzhiyun rseq_bug("cpu_id comparison failed");
413*4882a593Smuzhiyun error2:
414*4882a593Smuzhiyun rseq_after_asm_goto();
415*4882a593Smuzhiyun rseq_bug("expected value comparison failed");
416*4882a593Smuzhiyun #endif
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun static inline __attribute__((always_inline))
rseq_cmpeqv_trystorev_storev_release(intptr_t * v,intptr_t expect,intptr_t * v2,intptr_t newv2,intptr_t newv,int cpu)420*4882a593Smuzhiyun int rseq_cmpeqv_trystorev_storev_release(intptr_t *v, intptr_t expect,
421*4882a593Smuzhiyun intptr_t *v2, intptr_t newv2,
422*4882a593Smuzhiyun intptr_t newv, int cpu)
423*4882a593Smuzhiyun {
424*4882a593Smuzhiyun RSEQ_INJECT_C(9)
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun __asm__ __volatile__ goto (
427*4882a593Smuzhiyun RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
428*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
429*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
430*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
431*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
432*4882a593Smuzhiyun #endif
433*4882a593Smuzhiyun /* Start rseq by storing table entry pointer into rseq_cs. */
434*4882a593Smuzhiyun RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
435*4882a593Smuzhiyun RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
436*4882a593Smuzhiyun RSEQ_INJECT_ASM(3)
437*4882a593Smuzhiyun "ldr r0, %[v]\n\t"
438*4882a593Smuzhiyun "cmp %[expect], r0\n\t"
439*4882a593Smuzhiyun "bne %l[cmpfail]\n\t"
440*4882a593Smuzhiyun RSEQ_INJECT_ASM(4)
441*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
442*4882a593Smuzhiyun RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
443*4882a593Smuzhiyun "ldr r0, %[v]\n\t"
444*4882a593Smuzhiyun "cmp %[expect], r0\n\t"
445*4882a593Smuzhiyun "bne %l[error2]\n\t"
446*4882a593Smuzhiyun #endif
447*4882a593Smuzhiyun /* try store */
448*4882a593Smuzhiyun "str %[newv2], %[v2]\n\t"
449*4882a593Smuzhiyun RSEQ_INJECT_ASM(5)
450*4882a593Smuzhiyun "dmb\n\t" /* full mb provides store-release */
451*4882a593Smuzhiyun /* final store */
452*4882a593Smuzhiyun "str %[newv], %[v]\n\t"
453*4882a593Smuzhiyun "2:\n\t"
454*4882a593Smuzhiyun RSEQ_INJECT_ASM(6)
455*4882a593Smuzhiyun "b 5f\n\t"
456*4882a593Smuzhiyun RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
457*4882a593Smuzhiyun "5:\n\t"
458*4882a593Smuzhiyun : /* gcc asm goto does not allow outputs */
459*4882a593Smuzhiyun : [cpu_id] "r" (cpu),
460*4882a593Smuzhiyun [current_cpu_id] "m" (rseq_get_abi()->cpu_id),
461*4882a593Smuzhiyun [rseq_cs] "m" (rseq_get_abi()->rseq_cs.arch.ptr),
462*4882a593Smuzhiyun /* try store input */
463*4882a593Smuzhiyun [v2] "m" (*v2),
464*4882a593Smuzhiyun [newv2] "r" (newv2),
465*4882a593Smuzhiyun /* final store input */
466*4882a593Smuzhiyun [v] "m" (*v),
467*4882a593Smuzhiyun [expect] "r" (expect),
468*4882a593Smuzhiyun [newv] "r" (newv)
469*4882a593Smuzhiyun RSEQ_INJECT_INPUT
470*4882a593Smuzhiyun : "r0", "memory", "cc"
471*4882a593Smuzhiyun RSEQ_INJECT_CLOBBER
472*4882a593Smuzhiyun : abort, cmpfail
473*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
474*4882a593Smuzhiyun , error1, error2
475*4882a593Smuzhiyun #endif
476*4882a593Smuzhiyun );
477*4882a593Smuzhiyun rseq_after_asm_goto();
478*4882a593Smuzhiyun return 0;
479*4882a593Smuzhiyun abort:
480*4882a593Smuzhiyun rseq_after_asm_goto();
481*4882a593Smuzhiyun RSEQ_INJECT_FAILED
482*4882a593Smuzhiyun return -1;
483*4882a593Smuzhiyun cmpfail:
484*4882a593Smuzhiyun rseq_after_asm_goto();
485*4882a593Smuzhiyun return 1;
486*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
487*4882a593Smuzhiyun error1:
488*4882a593Smuzhiyun rseq_after_asm_goto();
489*4882a593Smuzhiyun rseq_bug("cpu_id comparison failed");
490*4882a593Smuzhiyun error2:
491*4882a593Smuzhiyun rseq_after_asm_goto();
492*4882a593Smuzhiyun rseq_bug("expected value comparison failed");
493*4882a593Smuzhiyun #endif
494*4882a593Smuzhiyun }
495*4882a593Smuzhiyun
496*4882a593Smuzhiyun static inline __attribute__((always_inline))
rseq_cmpeqv_cmpeqv_storev(intptr_t * v,intptr_t expect,intptr_t * v2,intptr_t expect2,intptr_t newv,int cpu)497*4882a593Smuzhiyun int rseq_cmpeqv_cmpeqv_storev(intptr_t *v, intptr_t expect,
498*4882a593Smuzhiyun intptr_t *v2, intptr_t expect2,
499*4882a593Smuzhiyun intptr_t newv, int cpu)
500*4882a593Smuzhiyun {
501*4882a593Smuzhiyun RSEQ_INJECT_C(9)
502*4882a593Smuzhiyun
503*4882a593Smuzhiyun __asm__ __volatile__ goto (
504*4882a593Smuzhiyun RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
505*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
506*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
507*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
508*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
509*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error3])
510*4882a593Smuzhiyun #endif
511*4882a593Smuzhiyun /* Start rseq by storing table entry pointer into rseq_cs. */
512*4882a593Smuzhiyun RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
513*4882a593Smuzhiyun RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
514*4882a593Smuzhiyun RSEQ_INJECT_ASM(3)
515*4882a593Smuzhiyun "ldr r0, %[v]\n\t"
516*4882a593Smuzhiyun "cmp %[expect], r0\n\t"
517*4882a593Smuzhiyun "bne %l[cmpfail]\n\t"
518*4882a593Smuzhiyun RSEQ_INJECT_ASM(4)
519*4882a593Smuzhiyun "ldr r0, %[v2]\n\t"
520*4882a593Smuzhiyun "cmp %[expect2], r0\n\t"
521*4882a593Smuzhiyun "bne %l[cmpfail]\n\t"
522*4882a593Smuzhiyun RSEQ_INJECT_ASM(5)
523*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
524*4882a593Smuzhiyun RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
525*4882a593Smuzhiyun "ldr r0, %[v]\n\t"
526*4882a593Smuzhiyun "cmp %[expect], r0\n\t"
527*4882a593Smuzhiyun "bne %l[error2]\n\t"
528*4882a593Smuzhiyun "ldr r0, %[v2]\n\t"
529*4882a593Smuzhiyun "cmp %[expect2], r0\n\t"
530*4882a593Smuzhiyun "bne %l[error3]\n\t"
531*4882a593Smuzhiyun #endif
532*4882a593Smuzhiyun /* final store */
533*4882a593Smuzhiyun "str %[newv], %[v]\n\t"
534*4882a593Smuzhiyun "2:\n\t"
535*4882a593Smuzhiyun RSEQ_INJECT_ASM(6)
536*4882a593Smuzhiyun "b 5f\n\t"
537*4882a593Smuzhiyun RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
538*4882a593Smuzhiyun "5:\n\t"
539*4882a593Smuzhiyun : /* gcc asm goto does not allow outputs */
540*4882a593Smuzhiyun : [cpu_id] "r" (cpu),
541*4882a593Smuzhiyun [current_cpu_id] "m" (rseq_get_abi()->cpu_id),
542*4882a593Smuzhiyun [rseq_cs] "m" (rseq_get_abi()->rseq_cs.arch.ptr),
543*4882a593Smuzhiyun /* cmp2 input */
544*4882a593Smuzhiyun [v2] "m" (*v2),
545*4882a593Smuzhiyun [expect2] "r" (expect2),
546*4882a593Smuzhiyun /* final store input */
547*4882a593Smuzhiyun [v] "m" (*v),
548*4882a593Smuzhiyun [expect] "r" (expect),
549*4882a593Smuzhiyun [newv] "r" (newv)
550*4882a593Smuzhiyun RSEQ_INJECT_INPUT
551*4882a593Smuzhiyun : "r0", "memory", "cc"
552*4882a593Smuzhiyun RSEQ_INJECT_CLOBBER
553*4882a593Smuzhiyun : abort, cmpfail
554*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
555*4882a593Smuzhiyun , error1, error2, error3
556*4882a593Smuzhiyun #endif
557*4882a593Smuzhiyun );
558*4882a593Smuzhiyun rseq_after_asm_goto();
559*4882a593Smuzhiyun return 0;
560*4882a593Smuzhiyun abort:
561*4882a593Smuzhiyun rseq_after_asm_goto();
562*4882a593Smuzhiyun RSEQ_INJECT_FAILED
563*4882a593Smuzhiyun return -1;
564*4882a593Smuzhiyun cmpfail:
565*4882a593Smuzhiyun rseq_after_asm_goto();
566*4882a593Smuzhiyun return 1;
567*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
568*4882a593Smuzhiyun error1:
569*4882a593Smuzhiyun rseq_after_asm_goto();
570*4882a593Smuzhiyun rseq_bug("cpu_id comparison failed");
571*4882a593Smuzhiyun error2:
572*4882a593Smuzhiyun rseq_after_asm_goto();
573*4882a593Smuzhiyun rseq_bug("1st expected value comparison failed");
574*4882a593Smuzhiyun error3:
575*4882a593Smuzhiyun rseq_after_asm_goto();
576*4882a593Smuzhiyun rseq_bug("2nd expected value comparison failed");
577*4882a593Smuzhiyun #endif
578*4882a593Smuzhiyun }
579*4882a593Smuzhiyun
580*4882a593Smuzhiyun static inline __attribute__((always_inline))
rseq_cmpeqv_trymemcpy_storev(intptr_t * v,intptr_t expect,void * dst,void * src,size_t len,intptr_t newv,int cpu)581*4882a593Smuzhiyun int rseq_cmpeqv_trymemcpy_storev(intptr_t *v, intptr_t expect,
582*4882a593Smuzhiyun void *dst, void *src, size_t len,
583*4882a593Smuzhiyun intptr_t newv, int cpu)
584*4882a593Smuzhiyun {
585*4882a593Smuzhiyun uint32_t rseq_scratch[3];
586*4882a593Smuzhiyun
587*4882a593Smuzhiyun RSEQ_INJECT_C(9)
588*4882a593Smuzhiyun
589*4882a593Smuzhiyun __asm__ __volatile__ goto (
590*4882a593Smuzhiyun RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
591*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
592*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
593*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
594*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
595*4882a593Smuzhiyun #endif
596*4882a593Smuzhiyun "str %[src], %[rseq_scratch0]\n\t"
597*4882a593Smuzhiyun "str %[dst], %[rseq_scratch1]\n\t"
598*4882a593Smuzhiyun "str %[len], %[rseq_scratch2]\n\t"
599*4882a593Smuzhiyun /* Start rseq by storing table entry pointer into rseq_cs. */
600*4882a593Smuzhiyun RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
601*4882a593Smuzhiyun RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
602*4882a593Smuzhiyun RSEQ_INJECT_ASM(3)
603*4882a593Smuzhiyun "ldr r0, %[v]\n\t"
604*4882a593Smuzhiyun "cmp %[expect], r0\n\t"
605*4882a593Smuzhiyun "bne 5f\n\t"
606*4882a593Smuzhiyun RSEQ_INJECT_ASM(4)
607*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
608*4882a593Smuzhiyun RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f)
609*4882a593Smuzhiyun "ldr r0, %[v]\n\t"
610*4882a593Smuzhiyun "cmp %[expect], r0\n\t"
611*4882a593Smuzhiyun "bne 7f\n\t"
612*4882a593Smuzhiyun #endif
613*4882a593Smuzhiyun /* try memcpy */
614*4882a593Smuzhiyun "cmp %[len], #0\n\t" \
615*4882a593Smuzhiyun "beq 333f\n\t" \
616*4882a593Smuzhiyun "222:\n\t" \
617*4882a593Smuzhiyun "ldrb %%r0, [%[src]]\n\t" \
618*4882a593Smuzhiyun "strb %%r0, [%[dst]]\n\t" \
619*4882a593Smuzhiyun "adds %[src], #1\n\t" \
620*4882a593Smuzhiyun "adds %[dst], #1\n\t" \
621*4882a593Smuzhiyun "subs %[len], #1\n\t" \
622*4882a593Smuzhiyun "bne 222b\n\t" \
623*4882a593Smuzhiyun "333:\n\t" \
624*4882a593Smuzhiyun RSEQ_INJECT_ASM(5)
625*4882a593Smuzhiyun /* final store */
626*4882a593Smuzhiyun "str %[newv], %[v]\n\t"
627*4882a593Smuzhiyun "2:\n\t"
628*4882a593Smuzhiyun RSEQ_INJECT_ASM(6)
629*4882a593Smuzhiyun /* teardown */
630*4882a593Smuzhiyun "ldr %[len], %[rseq_scratch2]\n\t"
631*4882a593Smuzhiyun "ldr %[dst], %[rseq_scratch1]\n\t"
632*4882a593Smuzhiyun "ldr %[src], %[rseq_scratch0]\n\t"
633*4882a593Smuzhiyun "b 8f\n\t"
634*4882a593Smuzhiyun RSEQ_ASM_DEFINE_ABORT(3, 4,
635*4882a593Smuzhiyun /* teardown */
636*4882a593Smuzhiyun "ldr %[len], %[rseq_scratch2]\n\t"
637*4882a593Smuzhiyun "ldr %[dst], %[rseq_scratch1]\n\t"
638*4882a593Smuzhiyun "ldr %[src], %[rseq_scratch0]\n\t",
639*4882a593Smuzhiyun abort, 1b, 2b, 4f)
640*4882a593Smuzhiyun RSEQ_ASM_DEFINE_CMPFAIL(5,
641*4882a593Smuzhiyun /* teardown */
642*4882a593Smuzhiyun "ldr %[len], %[rseq_scratch2]\n\t"
643*4882a593Smuzhiyun "ldr %[dst], %[rseq_scratch1]\n\t"
644*4882a593Smuzhiyun "ldr %[src], %[rseq_scratch0]\n\t",
645*4882a593Smuzhiyun cmpfail)
646*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
647*4882a593Smuzhiyun RSEQ_ASM_DEFINE_CMPFAIL(6,
648*4882a593Smuzhiyun /* teardown */
649*4882a593Smuzhiyun "ldr %[len], %[rseq_scratch2]\n\t"
650*4882a593Smuzhiyun "ldr %[dst], %[rseq_scratch1]\n\t"
651*4882a593Smuzhiyun "ldr %[src], %[rseq_scratch0]\n\t",
652*4882a593Smuzhiyun error1)
653*4882a593Smuzhiyun RSEQ_ASM_DEFINE_CMPFAIL(7,
654*4882a593Smuzhiyun /* teardown */
655*4882a593Smuzhiyun "ldr %[len], %[rseq_scratch2]\n\t"
656*4882a593Smuzhiyun "ldr %[dst], %[rseq_scratch1]\n\t"
657*4882a593Smuzhiyun "ldr %[src], %[rseq_scratch0]\n\t",
658*4882a593Smuzhiyun error2)
659*4882a593Smuzhiyun #endif
660*4882a593Smuzhiyun "8:\n\t"
661*4882a593Smuzhiyun : /* gcc asm goto does not allow outputs */
662*4882a593Smuzhiyun : [cpu_id] "r" (cpu),
663*4882a593Smuzhiyun [current_cpu_id] "m" (rseq_get_abi()->cpu_id),
664*4882a593Smuzhiyun [rseq_cs] "m" (rseq_get_abi()->rseq_cs.arch.ptr),
665*4882a593Smuzhiyun /* final store input */
666*4882a593Smuzhiyun [v] "m" (*v),
667*4882a593Smuzhiyun [expect] "r" (expect),
668*4882a593Smuzhiyun [newv] "r" (newv),
669*4882a593Smuzhiyun /* try memcpy input */
670*4882a593Smuzhiyun [dst] "r" (dst),
671*4882a593Smuzhiyun [src] "r" (src),
672*4882a593Smuzhiyun [len] "r" (len),
673*4882a593Smuzhiyun [rseq_scratch0] "m" (rseq_scratch[0]),
674*4882a593Smuzhiyun [rseq_scratch1] "m" (rseq_scratch[1]),
675*4882a593Smuzhiyun [rseq_scratch2] "m" (rseq_scratch[2])
676*4882a593Smuzhiyun RSEQ_INJECT_INPUT
677*4882a593Smuzhiyun : "r0", "memory", "cc"
678*4882a593Smuzhiyun RSEQ_INJECT_CLOBBER
679*4882a593Smuzhiyun : abort, cmpfail
680*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
681*4882a593Smuzhiyun , error1, error2
682*4882a593Smuzhiyun #endif
683*4882a593Smuzhiyun );
684*4882a593Smuzhiyun rseq_after_asm_goto();
685*4882a593Smuzhiyun return 0;
686*4882a593Smuzhiyun abort:
687*4882a593Smuzhiyun rseq_after_asm_goto();
688*4882a593Smuzhiyun RSEQ_INJECT_FAILED
689*4882a593Smuzhiyun return -1;
690*4882a593Smuzhiyun cmpfail:
691*4882a593Smuzhiyun rseq_after_asm_goto();
692*4882a593Smuzhiyun return 1;
693*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
694*4882a593Smuzhiyun error1:
695*4882a593Smuzhiyun rseq_after_asm_goto();
696*4882a593Smuzhiyun rseq_bug("cpu_id comparison failed");
697*4882a593Smuzhiyun error2:
698*4882a593Smuzhiyun rseq_after_asm_goto();
699*4882a593Smuzhiyun rseq_bug("expected value comparison failed");
700*4882a593Smuzhiyun #endif
701*4882a593Smuzhiyun }
702*4882a593Smuzhiyun
703*4882a593Smuzhiyun static inline __attribute__((always_inline))
rseq_cmpeqv_trymemcpy_storev_release(intptr_t * v,intptr_t expect,void * dst,void * src,size_t len,intptr_t newv,int cpu)704*4882a593Smuzhiyun int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v, intptr_t expect,
705*4882a593Smuzhiyun void *dst, void *src, size_t len,
706*4882a593Smuzhiyun intptr_t newv, int cpu)
707*4882a593Smuzhiyun {
708*4882a593Smuzhiyun uint32_t rseq_scratch[3];
709*4882a593Smuzhiyun
710*4882a593Smuzhiyun RSEQ_INJECT_C(9)
711*4882a593Smuzhiyun
712*4882a593Smuzhiyun __asm__ __volatile__ goto (
713*4882a593Smuzhiyun RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
714*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
715*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
716*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
717*4882a593Smuzhiyun RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
718*4882a593Smuzhiyun #endif
719*4882a593Smuzhiyun "str %[src], %[rseq_scratch0]\n\t"
720*4882a593Smuzhiyun "str %[dst], %[rseq_scratch1]\n\t"
721*4882a593Smuzhiyun "str %[len], %[rseq_scratch2]\n\t"
722*4882a593Smuzhiyun /* Start rseq by storing table entry pointer into rseq_cs. */
723*4882a593Smuzhiyun RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
724*4882a593Smuzhiyun RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
725*4882a593Smuzhiyun RSEQ_INJECT_ASM(3)
726*4882a593Smuzhiyun "ldr r0, %[v]\n\t"
727*4882a593Smuzhiyun "cmp %[expect], r0\n\t"
728*4882a593Smuzhiyun "bne 5f\n\t"
729*4882a593Smuzhiyun RSEQ_INJECT_ASM(4)
730*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
731*4882a593Smuzhiyun RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f)
732*4882a593Smuzhiyun "ldr r0, %[v]\n\t"
733*4882a593Smuzhiyun "cmp %[expect], r0\n\t"
734*4882a593Smuzhiyun "bne 7f\n\t"
735*4882a593Smuzhiyun #endif
736*4882a593Smuzhiyun /* try memcpy */
737*4882a593Smuzhiyun "cmp %[len], #0\n\t" \
738*4882a593Smuzhiyun "beq 333f\n\t" \
739*4882a593Smuzhiyun "222:\n\t" \
740*4882a593Smuzhiyun "ldrb %%r0, [%[src]]\n\t" \
741*4882a593Smuzhiyun "strb %%r0, [%[dst]]\n\t" \
742*4882a593Smuzhiyun "adds %[src], #1\n\t" \
743*4882a593Smuzhiyun "adds %[dst], #1\n\t" \
744*4882a593Smuzhiyun "subs %[len], #1\n\t" \
745*4882a593Smuzhiyun "bne 222b\n\t" \
746*4882a593Smuzhiyun "333:\n\t" \
747*4882a593Smuzhiyun RSEQ_INJECT_ASM(5)
748*4882a593Smuzhiyun "dmb\n\t" /* full mb provides store-release */
749*4882a593Smuzhiyun /* final store */
750*4882a593Smuzhiyun "str %[newv], %[v]\n\t"
751*4882a593Smuzhiyun "2:\n\t"
752*4882a593Smuzhiyun RSEQ_INJECT_ASM(6)
753*4882a593Smuzhiyun /* teardown */
754*4882a593Smuzhiyun "ldr %[len], %[rseq_scratch2]\n\t"
755*4882a593Smuzhiyun "ldr %[dst], %[rseq_scratch1]\n\t"
756*4882a593Smuzhiyun "ldr %[src], %[rseq_scratch0]\n\t"
757*4882a593Smuzhiyun "b 8f\n\t"
758*4882a593Smuzhiyun RSEQ_ASM_DEFINE_ABORT(3, 4,
759*4882a593Smuzhiyun /* teardown */
760*4882a593Smuzhiyun "ldr %[len], %[rseq_scratch2]\n\t"
761*4882a593Smuzhiyun "ldr %[dst], %[rseq_scratch1]\n\t"
762*4882a593Smuzhiyun "ldr %[src], %[rseq_scratch0]\n\t",
763*4882a593Smuzhiyun abort, 1b, 2b, 4f)
764*4882a593Smuzhiyun RSEQ_ASM_DEFINE_CMPFAIL(5,
765*4882a593Smuzhiyun /* teardown */
766*4882a593Smuzhiyun "ldr %[len], %[rseq_scratch2]\n\t"
767*4882a593Smuzhiyun "ldr %[dst], %[rseq_scratch1]\n\t"
768*4882a593Smuzhiyun "ldr %[src], %[rseq_scratch0]\n\t",
769*4882a593Smuzhiyun cmpfail)
770*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
771*4882a593Smuzhiyun RSEQ_ASM_DEFINE_CMPFAIL(6,
772*4882a593Smuzhiyun /* teardown */
773*4882a593Smuzhiyun "ldr %[len], %[rseq_scratch2]\n\t"
774*4882a593Smuzhiyun "ldr %[dst], %[rseq_scratch1]\n\t"
775*4882a593Smuzhiyun "ldr %[src], %[rseq_scratch0]\n\t",
776*4882a593Smuzhiyun error1)
777*4882a593Smuzhiyun RSEQ_ASM_DEFINE_CMPFAIL(7,
778*4882a593Smuzhiyun /* teardown */
779*4882a593Smuzhiyun "ldr %[len], %[rseq_scratch2]\n\t"
780*4882a593Smuzhiyun "ldr %[dst], %[rseq_scratch1]\n\t"
781*4882a593Smuzhiyun "ldr %[src], %[rseq_scratch0]\n\t",
782*4882a593Smuzhiyun error2)
783*4882a593Smuzhiyun #endif
784*4882a593Smuzhiyun "8:\n\t"
785*4882a593Smuzhiyun : /* gcc asm goto does not allow outputs */
786*4882a593Smuzhiyun : [cpu_id] "r" (cpu),
787*4882a593Smuzhiyun [current_cpu_id] "m" (rseq_get_abi()->cpu_id),
788*4882a593Smuzhiyun [rseq_cs] "m" (rseq_get_abi()->rseq_cs.arch.ptr),
789*4882a593Smuzhiyun /* final store input */
790*4882a593Smuzhiyun [v] "m" (*v),
791*4882a593Smuzhiyun [expect] "r" (expect),
792*4882a593Smuzhiyun [newv] "r" (newv),
793*4882a593Smuzhiyun /* try memcpy input */
794*4882a593Smuzhiyun [dst] "r" (dst),
795*4882a593Smuzhiyun [src] "r" (src),
796*4882a593Smuzhiyun [len] "r" (len),
797*4882a593Smuzhiyun [rseq_scratch0] "m" (rseq_scratch[0]),
798*4882a593Smuzhiyun [rseq_scratch1] "m" (rseq_scratch[1]),
799*4882a593Smuzhiyun [rseq_scratch2] "m" (rseq_scratch[2])
800*4882a593Smuzhiyun RSEQ_INJECT_INPUT
801*4882a593Smuzhiyun : "r0", "memory", "cc"
802*4882a593Smuzhiyun RSEQ_INJECT_CLOBBER
803*4882a593Smuzhiyun : abort, cmpfail
804*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
805*4882a593Smuzhiyun , error1, error2
806*4882a593Smuzhiyun #endif
807*4882a593Smuzhiyun );
808*4882a593Smuzhiyun rseq_after_asm_goto();
809*4882a593Smuzhiyun return 0;
810*4882a593Smuzhiyun abort:
811*4882a593Smuzhiyun rseq_after_asm_goto();
812*4882a593Smuzhiyun RSEQ_INJECT_FAILED
813*4882a593Smuzhiyun return -1;
814*4882a593Smuzhiyun cmpfail:
815*4882a593Smuzhiyun rseq_after_asm_goto();
816*4882a593Smuzhiyun return 1;
817*4882a593Smuzhiyun #ifdef RSEQ_COMPARE_TWICE
818*4882a593Smuzhiyun error1:
819*4882a593Smuzhiyun rseq_after_asm_goto();
820*4882a593Smuzhiyun rseq_bug("cpu_id comparison failed");
821*4882a593Smuzhiyun error2:
822*4882a593Smuzhiyun rseq_after_asm_goto();
823*4882a593Smuzhiyun rseq_bug("expected value comparison failed");
824*4882a593Smuzhiyun #endif
825*4882a593Smuzhiyun }
826*4882a593Smuzhiyun
827*4882a593Smuzhiyun #endif /* !RSEQ_SKIP_FASTPATH */
828