xref: /OK3568_Linux_fs/kernel/tools/testing/selftests/rseq/rseq-arm.h (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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