1/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
2/*
3 * rseq-arm64.h
4 *
5 * (C) Copyright 2016-2018 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
6 * (C) Copyright 2018 - Will Deacon <will.deacon@arm.com>
7 */
8
9/*
10 * aarch64 -mbig-endian generates mixed endianness code vs data:
11 * little-endian code and big-endian data. Ensure the RSEQ_SIG signature
12 * matches code endianness.
13 */
14#define RSEQ_SIG_CODE	0xd428bc00	/* BRK #0x45E0.  */
15
16#ifdef __AARCH64EB__
17#define RSEQ_SIG_DATA	0x00bc28d4	/* BRK #0x45E0.  */
18#else
19#define RSEQ_SIG_DATA	RSEQ_SIG_CODE
20#endif
21
22#define RSEQ_SIG	RSEQ_SIG_DATA
23
24#define rseq_smp_mb()	__asm__ __volatile__ ("dmb ish" ::: "memory")
25#define rseq_smp_rmb()	__asm__ __volatile__ ("dmb ishld" ::: "memory")
26#define rseq_smp_wmb()	__asm__ __volatile__ ("dmb ishst" ::: "memory")
27
28#define rseq_smp_load_acquire(p)						\
29__extension__ ({								\
30	__typeof(*p) ____p1;							\
31	switch (sizeof(*p)) {							\
32	case 1:									\
33		asm volatile ("ldarb %w0, %1"					\
34			: "=r" (*(__u8 *)p)					\
35			: "Q" (*p) : "memory");					\
36		break;								\
37	case 2:									\
38		asm volatile ("ldarh %w0, %1"					\
39			: "=r" (*(__u16 *)p)					\
40			: "Q" (*p) : "memory");					\
41		break;								\
42	case 4:									\
43		asm volatile ("ldar %w0, %1"					\
44			: "=r" (*(__u32 *)p)					\
45			: "Q" (*p) : "memory");					\
46		break;								\
47	case 8:									\
48		asm volatile ("ldar %0, %1"					\
49			: "=r" (*(__u64 *)p)					\
50			: "Q" (*p) : "memory");					\
51		break;								\
52	}									\
53	____p1;									\
54})
55
56#define rseq_smp_acquire__after_ctrl_dep()	rseq_smp_rmb()
57
58#define rseq_smp_store_release(p, v)						\
59do {										\
60	switch (sizeof(*p)) {							\
61	case 1:									\
62		asm volatile ("stlrb %w1, %0"					\
63				: "=Q" (*p)					\
64				: "r" ((__u8)v)					\
65				: "memory");					\
66		break;								\
67	case 2:									\
68		asm volatile ("stlrh %w1, %0"					\
69				: "=Q" (*p)					\
70				: "r" ((__u16)v)				\
71				: "memory");					\
72		break;								\
73	case 4:									\
74		asm volatile ("stlr %w1, %0"					\
75				: "=Q" (*p)					\
76				: "r" ((__u32)v)				\
77				: "memory");					\
78		break;								\
79	case 8:									\
80		asm volatile ("stlr %1, %0"					\
81				: "=Q" (*p)					\
82				: "r" ((__u64)v)				\
83				: "memory");					\
84		break;								\
85	}									\
86} while (0)
87
88#ifdef RSEQ_SKIP_FASTPATH
89#include "rseq-skip.h"
90#else /* !RSEQ_SKIP_FASTPATH */
91
92#define RSEQ_ASM_TMP_REG32	"w15"
93#define RSEQ_ASM_TMP_REG	"x15"
94#define RSEQ_ASM_TMP_REG_2	"x14"
95
96#define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, start_ip,		\
97				post_commit_offset, abort_ip)			\
98	"	.pushsection	__rseq_cs, \"aw\"\n"				\
99	"	.balign	32\n"							\
100	__rseq_str(label) ":\n"							\
101	"	.long	" __rseq_str(version) ", " __rseq_str(flags) "\n"	\
102	"	.quad	" __rseq_str(start_ip) ", "				\
103			  __rseq_str(post_commit_offset) ", "			\
104			  __rseq_str(abort_ip) "\n"				\
105	"	.popsection\n\t"						\
106	"	.pushsection __rseq_cs_ptr_array, \"aw\"\n"				\
107	"	.quad " __rseq_str(label) "b\n"					\
108	"	.popsection\n"
109
110#define RSEQ_ASM_DEFINE_TABLE(label, start_ip, post_commit_ip, abort_ip)	\
111	__RSEQ_ASM_DEFINE_TABLE(label, 0x0, 0x0, start_ip,			\
112				(post_commit_ip - start_ip), abort_ip)
113
114/*
115 * Exit points of a rseq critical section consist of all instructions outside
116 * of the critical section where a critical section can either branch to or
117 * reach through the normal course of its execution. The abort IP and the
118 * post-commit IP are already part of the __rseq_cs section and should not be
119 * explicitly defined as additional exit points. Knowing all exit points is
120 * useful to assist debuggers stepping over the critical section.
121 */
122#define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip)				\
123	"	.pushsection __rseq_exit_point_array, \"aw\"\n"			\
124	"	.quad " __rseq_str(start_ip) ", " __rseq_str(exit_ip) "\n"	\
125	"	.popsection\n"
126
127#define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs)			\
128	RSEQ_INJECT_ASM(1)							\
129	"	adrp	" RSEQ_ASM_TMP_REG ", " __rseq_str(cs_label) "\n"	\
130	"	add	" RSEQ_ASM_TMP_REG ", " RSEQ_ASM_TMP_REG		\
131			", :lo12:" __rseq_str(cs_label) "\n"			\
132	"	str	" RSEQ_ASM_TMP_REG ", %[" __rseq_str(rseq_cs) "]\n"	\
133	__rseq_str(label) ":\n"
134
135#define RSEQ_ASM_DEFINE_ABORT(label, abort_label)				\
136	"	b	222f\n"							\
137	"	.inst 	"	__rseq_str(RSEQ_SIG_CODE) "\n"			\
138	__rseq_str(label) ":\n"							\
139	"	b	%l[" __rseq_str(abort_label) "]\n"			\
140	"222:\n"
141
142#define RSEQ_ASM_OP_STORE(value, var)						\
143	"	str	%[" __rseq_str(value) "], %[" __rseq_str(var) "]\n"
144
145#define RSEQ_ASM_OP_STORE_RELEASE(value, var)					\
146	"	stlr	%[" __rseq_str(value) "], %[" __rseq_str(var) "]\n"
147
148#define RSEQ_ASM_OP_FINAL_STORE(value, var, post_commit_label)			\
149	RSEQ_ASM_OP_STORE(value, var)						\
150	__rseq_str(post_commit_label) ":\n"
151
152#define RSEQ_ASM_OP_FINAL_STORE_RELEASE(value, var, post_commit_label)		\
153	RSEQ_ASM_OP_STORE_RELEASE(value, var)					\
154	__rseq_str(post_commit_label) ":\n"
155
156#define RSEQ_ASM_OP_CMPEQ(var, expect, label)					\
157	"	ldr	" RSEQ_ASM_TMP_REG ", %[" __rseq_str(var) "]\n"		\
158	"	sub	" RSEQ_ASM_TMP_REG ", " RSEQ_ASM_TMP_REG		\
159			", %[" __rseq_str(expect) "]\n"				\
160	"	cbnz	" RSEQ_ASM_TMP_REG ", " __rseq_str(label) "\n"
161
162#define RSEQ_ASM_OP_CMPEQ32(var, expect, label)					\
163	"	ldr	" RSEQ_ASM_TMP_REG32 ", %[" __rseq_str(var) "]\n"	\
164	"	sub	" RSEQ_ASM_TMP_REG32 ", " RSEQ_ASM_TMP_REG32		\
165			", %w[" __rseq_str(expect) "]\n"			\
166	"	cbnz	" RSEQ_ASM_TMP_REG32 ", " __rseq_str(label) "\n"
167
168#define RSEQ_ASM_OP_CMPNE(var, expect, label)					\
169	"	ldr	" RSEQ_ASM_TMP_REG ", %[" __rseq_str(var) "]\n"		\
170	"	sub	" RSEQ_ASM_TMP_REG ", " RSEQ_ASM_TMP_REG		\
171			", %[" __rseq_str(expect) "]\n"				\
172	"	cbz	" RSEQ_ASM_TMP_REG ", " __rseq_str(label) "\n"
173
174#define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label)			\
175	RSEQ_INJECT_ASM(2)							\
176	RSEQ_ASM_OP_CMPEQ32(current_cpu_id, cpu_id, label)
177
178#define RSEQ_ASM_OP_R_LOAD(var)							\
179	"	ldr	" RSEQ_ASM_TMP_REG ", %[" __rseq_str(var) "]\n"
180
181#define RSEQ_ASM_OP_R_STORE(var)						\
182	"	str	" RSEQ_ASM_TMP_REG ", %[" __rseq_str(var) "]\n"
183
184#define RSEQ_ASM_OP_R_LOAD_OFF(offset)						\
185	"	ldr	" RSEQ_ASM_TMP_REG ", [" RSEQ_ASM_TMP_REG		\
186			", %[" __rseq_str(offset) "]]\n"
187
188#define RSEQ_ASM_OP_R_ADD(count)						\
189	"	add	" RSEQ_ASM_TMP_REG ", " RSEQ_ASM_TMP_REG		\
190			", %[" __rseq_str(count) "]\n"
191
192#define RSEQ_ASM_OP_R_FINAL_STORE(var, post_commit_label)			\
193	"	str	" RSEQ_ASM_TMP_REG ", %[" __rseq_str(var) "]\n"		\
194	__rseq_str(post_commit_label) ":\n"
195
196#define RSEQ_ASM_OP_R_BAD_MEMCPY(dst, src, len)					\
197	"	cbz	%[" __rseq_str(len) "], 333f\n"				\
198	"	mov	" RSEQ_ASM_TMP_REG_2 ", %[" __rseq_str(len) "]\n"	\
199	"222:	sub	" RSEQ_ASM_TMP_REG_2 ", " RSEQ_ASM_TMP_REG_2 ", #1\n"	\
200	"	ldrb	" RSEQ_ASM_TMP_REG32 ", [%[" __rseq_str(src) "]"	\
201			", " RSEQ_ASM_TMP_REG_2 "]\n"				\
202	"	strb	" RSEQ_ASM_TMP_REG32 ", [%[" __rseq_str(dst) "]"	\
203			", " RSEQ_ASM_TMP_REG_2 "]\n"				\
204	"	cbnz	" RSEQ_ASM_TMP_REG_2 ", 222b\n"				\
205	"333:\n"
206
207static inline __attribute__((always_inline))
208int rseq_cmpeqv_storev(intptr_t *v, intptr_t expect, intptr_t newv, int cpu)
209{
210	RSEQ_INJECT_C(9)
211
212	__asm__ __volatile__ goto (
213		RSEQ_ASM_DEFINE_TABLE(1, 2f, 3f, 4f)
214		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[cmpfail])
215#ifdef RSEQ_COMPARE_TWICE
216		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[error1])
217		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[error2])
218#endif
219		RSEQ_ASM_STORE_RSEQ_CS(2, 1b, rseq_cs)
220		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
221		RSEQ_INJECT_ASM(3)
222		RSEQ_ASM_OP_CMPEQ(v, expect, %l[cmpfail])
223		RSEQ_INJECT_ASM(4)
224#ifdef RSEQ_COMPARE_TWICE
225		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
226		RSEQ_ASM_OP_CMPEQ(v, expect, %l[error2])
227#endif
228		RSEQ_ASM_OP_FINAL_STORE(newv, v, 3)
229		RSEQ_INJECT_ASM(5)
230		RSEQ_ASM_DEFINE_ABORT(4, abort)
231		: /* gcc asm goto does not allow outputs */
232		: [cpu_id]		"r" (cpu),
233		  [current_cpu_id]	"Qo" (rseq_get_abi()->cpu_id),
234		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
235		  [v]			"Qo" (*v),
236		  [expect]		"r" (expect),
237		  [newv]		"r" (newv)
238		  RSEQ_INJECT_INPUT
239		: "memory", RSEQ_ASM_TMP_REG
240		: abort, cmpfail
241#ifdef RSEQ_COMPARE_TWICE
242		  , error1, error2
243#endif
244	);
245	rseq_after_asm_goto();
246	return 0;
247abort:
248	rseq_after_asm_goto();
249	RSEQ_INJECT_FAILED
250	return -1;
251cmpfail:
252	rseq_after_asm_goto();
253	return 1;
254#ifdef RSEQ_COMPARE_TWICE
255error1:
256	rseq_after_asm_goto();
257	rseq_bug("cpu_id comparison failed");
258error2:
259	rseq_after_asm_goto();
260	rseq_bug("expected value comparison failed");
261#endif
262}
263
264static inline __attribute__((always_inline))
265int rseq_cmpnev_storeoffp_load(intptr_t *v, intptr_t expectnot,
266			       long voffp, intptr_t *load, int cpu)
267{
268	RSEQ_INJECT_C(9)
269
270	__asm__ __volatile__ goto (
271		RSEQ_ASM_DEFINE_TABLE(1, 2f, 3f, 4f)
272		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[cmpfail])
273#ifdef RSEQ_COMPARE_TWICE
274		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[error1])
275		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[error2])
276#endif
277		RSEQ_ASM_STORE_RSEQ_CS(2, 1b, rseq_cs)
278		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
279		RSEQ_INJECT_ASM(3)
280		RSEQ_ASM_OP_CMPNE(v, expectnot, %l[cmpfail])
281		RSEQ_INJECT_ASM(4)
282#ifdef RSEQ_COMPARE_TWICE
283		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
284		RSEQ_ASM_OP_CMPNE(v, expectnot, %l[error2])
285#endif
286		RSEQ_ASM_OP_R_LOAD(v)
287		RSEQ_ASM_OP_R_STORE(load)
288		RSEQ_ASM_OP_R_LOAD_OFF(voffp)
289		RSEQ_ASM_OP_R_FINAL_STORE(v, 3)
290		RSEQ_INJECT_ASM(5)
291		RSEQ_ASM_DEFINE_ABORT(4, abort)
292		: /* gcc asm goto does not allow outputs */
293		: [cpu_id]		"r" (cpu),
294		  [current_cpu_id]	"Qo" (rseq_get_abi()->cpu_id),
295		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
296		  [v]			"Qo" (*v),
297		  [expectnot]		"r" (expectnot),
298		  [load]		"Qo" (*load),
299		  [voffp]		"r" (voffp)
300		  RSEQ_INJECT_INPUT
301		: "memory", RSEQ_ASM_TMP_REG
302		: abort, cmpfail
303#ifdef RSEQ_COMPARE_TWICE
304		  , error1, error2
305#endif
306	);
307	rseq_after_asm_goto();
308	return 0;
309abort:
310	rseq_after_asm_goto();
311	RSEQ_INJECT_FAILED
312	return -1;
313cmpfail:
314	rseq_after_asm_goto();
315	return 1;
316#ifdef RSEQ_COMPARE_TWICE
317error1:
318	rseq_after_asm_goto();
319	rseq_bug("cpu_id comparison failed");
320error2:
321	rseq_after_asm_goto();
322	rseq_bug("expected value comparison failed");
323#endif
324}
325
326static inline __attribute__((always_inline))
327int rseq_addv(intptr_t *v, intptr_t count, int cpu)
328{
329	RSEQ_INJECT_C(9)
330
331	__asm__ __volatile__ goto (
332		RSEQ_ASM_DEFINE_TABLE(1, 2f, 3f, 4f)
333#ifdef RSEQ_COMPARE_TWICE
334		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[error1])
335#endif
336		RSEQ_ASM_STORE_RSEQ_CS(2, 1b, rseq_cs)
337		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
338		RSEQ_INJECT_ASM(3)
339#ifdef RSEQ_COMPARE_TWICE
340		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
341#endif
342		RSEQ_ASM_OP_R_LOAD(v)
343		RSEQ_ASM_OP_R_ADD(count)
344		RSEQ_ASM_OP_R_FINAL_STORE(v, 3)
345		RSEQ_INJECT_ASM(4)
346		RSEQ_ASM_DEFINE_ABORT(4, abort)
347		: /* gcc asm goto does not allow outputs */
348		: [cpu_id]		"r" (cpu),
349		  [current_cpu_id]	"Qo" (rseq_get_abi()->cpu_id),
350		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
351		  [v]			"Qo" (*v),
352		  [count]		"r" (count)
353		  RSEQ_INJECT_INPUT
354		: "memory", RSEQ_ASM_TMP_REG
355		: abort
356#ifdef RSEQ_COMPARE_TWICE
357		  , error1
358#endif
359	);
360	rseq_after_asm_goto();
361	return 0;
362abort:
363	rseq_after_asm_goto();
364	RSEQ_INJECT_FAILED
365	return -1;
366#ifdef RSEQ_COMPARE_TWICE
367error1:
368	rseq_after_asm_goto();
369	rseq_bug("cpu_id comparison failed");
370#endif
371}
372
373static inline __attribute__((always_inline))
374int rseq_cmpeqv_trystorev_storev(intptr_t *v, intptr_t expect,
375				 intptr_t *v2, intptr_t newv2,
376				 intptr_t newv, int cpu)
377{
378	RSEQ_INJECT_C(9)
379
380	__asm__ __volatile__ goto (
381		RSEQ_ASM_DEFINE_TABLE(1, 2f, 3f, 4f)
382		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[cmpfail])
383#ifdef RSEQ_COMPARE_TWICE
384		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[error1])
385		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[error2])
386#endif
387		RSEQ_ASM_STORE_RSEQ_CS(2, 1b, rseq_cs)
388		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
389		RSEQ_INJECT_ASM(3)
390		RSEQ_ASM_OP_CMPEQ(v, expect, %l[cmpfail])
391		RSEQ_INJECT_ASM(4)
392#ifdef RSEQ_COMPARE_TWICE
393		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
394		RSEQ_ASM_OP_CMPEQ(v, expect, %l[error2])
395#endif
396		RSEQ_ASM_OP_STORE(newv2, v2)
397		RSEQ_INJECT_ASM(5)
398		RSEQ_ASM_OP_FINAL_STORE(newv, v, 3)
399		RSEQ_INJECT_ASM(6)
400		RSEQ_ASM_DEFINE_ABORT(4, abort)
401		: /* gcc asm goto does not allow outputs */
402		: [cpu_id]		"r" (cpu),
403		  [current_cpu_id]	"Qo" (rseq_get_abi()->cpu_id),
404		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
405		  [expect]		"r" (expect),
406		  [v]			"Qo" (*v),
407		  [newv]		"r" (newv),
408		  [v2]			"Qo" (*v2),
409		  [newv2]		"r" (newv2)
410		  RSEQ_INJECT_INPUT
411		: "memory", RSEQ_ASM_TMP_REG
412		: abort, cmpfail
413#ifdef RSEQ_COMPARE_TWICE
414		  , error1, error2
415#endif
416	);
417	rseq_after_asm_goto();
418	return 0;
419abort:
420	rseq_after_asm_goto();
421	RSEQ_INJECT_FAILED
422	return -1;
423cmpfail:
424	rseq_after_asm_goto();
425	return 1;
426#ifdef RSEQ_COMPARE_TWICE
427error1:
428	rseq_after_asm_goto();
429	rseq_bug("cpu_id comparison failed");
430error2:
431	rseq_after_asm_goto();
432	rseq_bug("expected value comparison failed");
433#endif
434}
435
436static inline __attribute__((always_inline))
437int rseq_cmpeqv_trystorev_storev_release(intptr_t *v, intptr_t expect,
438					 intptr_t *v2, intptr_t newv2,
439					 intptr_t newv, int cpu)
440{
441	RSEQ_INJECT_C(9)
442
443	__asm__ __volatile__ goto (
444		RSEQ_ASM_DEFINE_TABLE(1, 2f, 3f, 4f)
445		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[cmpfail])
446#ifdef RSEQ_COMPARE_TWICE
447		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[error1])
448		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[error2])
449#endif
450		RSEQ_ASM_STORE_RSEQ_CS(2, 1b, rseq_cs)
451		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
452		RSEQ_INJECT_ASM(3)
453		RSEQ_ASM_OP_CMPEQ(v, expect, %l[cmpfail])
454		RSEQ_INJECT_ASM(4)
455#ifdef RSEQ_COMPARE_TWICE
456		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
457		RSEQ_ASM_OP_CMPEQ(v, expect, %l[error2])
458#endif
459		RSEQ_ASM_OP_STORE(newv2, v2)
460		RSEQ_INJECT_ASM(5)
461		RSEQ_ASM_OP_FINAL_STORE_RELEASE(newv, v, 3)
462		RSEQ_INJECT_ASM(6)
463		RSEQ_ASM_DEFINE_ABORT(4, abort)
464		: /* gcc asm goto does not allow outputs */
465		: [cpu_id]		"r" (cpu),
466		  [current_cpu_id]	"Qo" (rseq_get_abi()->cpu_id),
467		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
468		  [expect]		"r" (expect),
469		  [v]			"Qo" (*v),
470		  [newv]		"r" (newv),
471		  [v2]			"Qo" (*v2),
472		  [newv2]		"r" (newv2)
473		  RSEQ_INJECT_INPUT
474		: "memory", RSEQ_ASM_TMP_REG
475		: abort, cmpfail
476#ifdef RSEQ_COMPARE_TWICE
477		  , error1, error2
478#endif
479	);
480	rseq_after_asm_goto();
481	return 0;
482abort:
483	rseq_after_asm_goto();
484	RSEQ_INJECT_FAILED
485	return -1;
486cmpfail:
487	rseq_after_asm_goto();
488	return 1;
489#ifdef RSEQ_COMPARE_TWICE
490error1:
491	rseq_after_asm_goto();
492	rseq_bug("cpu_id comparison failed");
493error2:
494	rseq_after_asm_goto();
495	rseq_bug("expected value comparison failed");
496#endif
497}
498
499static inline __attribute__((always_inline))
500int rseq_cmpeqv_cmpeqv_storev(intptr_t *v, intptr_t expect,
501			      intptr_t *v2, intptr_t expect2,
502			      intptr_t newv, int cpu)
503{
504	RSEQ_INJECT_C(9)
505
506	__asm__ __volatile__ goto (
507		RSEQ_ASM_DEFINE_TABLE(1, 2f, 3f, 4f)
508		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[cmpfail])
509#ifdef RSEQ_COMPARE_TWICE
510		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[error1])
511		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[error2])
512		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[error3])
513#endif
514		RSEQ_ASM_STORE_RSEQ_CS(2, 1b, rseq_cs)
515		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
516		RSEQ_INJECT_ASM(3)
517		RSEQ_ASM_OP_CMPEQ(v, expect, %l[cmpfail])
518		RSEQ_INJECT_ASM(4)
519		RSEQ_ASM_OP_CMPEQ(v2, expect2, %l[cmpfail])
520		RSEQ_INJECT_ASM(5)
521#ifdef RSEQ_COMPARE_TWICE
522		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
523		RSEQ_ASM_OP_CMPEQ(v, expect, %l[error2])
524		RSEQ_ASM_OP_CMPEQ(v2, expect2, %l[error3])
525#endif
526		RSEQ_ASM_OP_FINAL_STORE(newv, v, 3)
527		RSEQ_INJECT_ASM(6)
528		RSEQ_ASM_DEFINE_ABORT(4, abort)
529		: /* gcc asm goto does not allow outputs */
530		: [cpu_id]		"r" (cpu),
531		  [current_cpu_id]	"Qo" (rseq_get_abi()->cpu_id),
532		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
533		  [v]			"Qo" (*v),
534		  [expect]		"r" (expect),
535		  [v2]			"Qo" (*v2),
536		  [expect2]		"r" (expect2),
537		  [newv]		"r" (newv)
538		  RSEQ_INJECT_INPUT
539		: "memory", RSEQ_ASM_TMP_REG
540		: abort, cmpfail
541#ifdef RSEQ_COMPARE_TWICE
542		  , error1, error2, error3
543#endif
544	);
545	rseq_after_asm_goto();
546	return 0;
547abort:
548	rseq_after_asm_goto();
549	RSEQ_INJECT_FAILED
550	return -1;
551cmpfail:
552	rseq_after_asm_goto();
553	return 1;
554#ifdef RSEQ_COMPARE_TWICE
555error1:
556	rseq_after_asm_goto();
557	rseq_bug("cpu_id comparison failed");
558error2:
559	rseq_after_asm_goto();
560	rseq_bug("expected value comparison failed");
561error3:
562	rseq_after_asm_goto();
563	rseq_bug("2nd expected value comparison failed");
564#endif
565}
566
567static inline __attribute__((always_inline))
568int rseq_cmpeqv_trymemcpy_storev(intptr_t *v, intptr_t expect,
569				 void *dst, void *src, size_t len,
570				 intptr_t newv, int cpu)
571{
572	RSEQ_INJECT_C(9)
573
574	__asm__ __volatile__ goto (
575		RSEQ_ASM_DEFINE_TABLE(1, 2f, 3f, 4f)
576		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[cmpfail])
577#ifdef RSEQ_COMPARE_TWICE
578		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[error1])
579		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[error2])
580#endif
581		RSEQ_ASM_STORE_RSEQ_CS(2, 1b, rseq_cs)
582		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
583		RSEQ_INJECT_ASM(3)
584		RSEQ_ASM_OP_CMPEQ(v, expect, %l[cmpfail])
585		RSEQ_INJECT_ASM(4)
586#ifdef RSEQ_COMPARE_TWICE
587		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
588		RSEQ_ASM_OP_CMPEQ(v, expect, %l[error2])
589#endif
590		RSEQ_ASM_OP_R_BAD_MEMCPY(dst, src, len)
591		RSEQ_INJECT_ASM(5)
592		RSEQ_ASM_OP_FINAL_STORE(newv, v, 3)
593		RSEQ_INJECT_ASM(6)
594		RSEQ_ASM_DEFINE_ABORT(4, abort)
595		: /* gcc asm goto does not allow outputs */
596		: [cpu_id]		"r" (cpu),
597		  [current_cpu_id]	"Qo" (rseq_get_abi()->cpu_id),
598		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
599		  [expect]		"r" (expect),
600		  [v]			"Qo" (*v),
601		  [newv]		"r" (newv),
602		  [dst]			"r" (dst),
603		  [src]			"r" (src),
604		  [len]			"r" (len)
605		  RSEQ_INJECT_INPUT
606		: "memory", RSEQ_ASM_TMP_REG, RSEQ_ASM_TMP_REG_2
607		: abort, cmpfail
608#ifdef RSEQ_COMPARE_TWICE
609		  , error1, error2
610#endif
611	);
612	rseq_after_asm_goto();
613	return 0;
614abort:
615	rseq_after_asm_goto();
616	RSEQ_INJECT_FAILED
617	return -1;
618cmpfail:
619	rseq_after_asm_goto();
620	return 1;
621#ifdef RSEQ_COMPARE_TWICE
622error1:
623	rseq_after_asm_goto();
624	rseq_bug("cpu_id comparison failed");
625error2:
626	rseq_after_asm_goto();
627	rseq_bug("expected value comparison failed");
628#endif
629}
630
631static inline __attribute__((always_inline))
632int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v, intptr_t expect,
633					 void *dst, void *src, size_t len,
634					 intptr_t newv, int cpu)
635{
636	RSEQ_INJECT_C(9)
637
638	__asm__ __volatile__ goto (
639		RSEQ_ASM_DEFINE_TABLE(1, 2f, 3f, 4f)
640		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[cmpfail])
641#ifdef RSEQ_COMPARE_TWICE
642		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[error1])
643		RSEQ_ASM_DEFINE_EXIT_POINT(2f, %l[error2])
644#endif
645		RSEQ_ASM_STORE_RSEQ_CS(2, 1b, rseq_cs)
646		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
647		RSEQ_INJECT_ASM(3)
648		RSEQ_ASM_OP_CMPEQ(v, expect, %l[cmpfail])
649		RSEQ_INJECT_ASM(4)
650#ifdef RSEQ_COMPARE_TWICE
651		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
652		RSEQ_ASM_OP_CMPEQ(v, expect, %l[error2])
653#endif
654		RSEQ_ASM_OP_R_BAD_MEMCPY(dst, src, len)
655		RSEQ_INJECT_ASM(5)
656		RSEQ_ASM_OP_FINAL_STORE_RELEASE(newv, v, 3)
657		RSEQ_INJECT_ASM(6)
658		RSEQ_ASM_DEFINE_ABORT(4, abort)
659		: /* gcc asm goto does not allow outputs */
660		: [cpu_id]		"r" (cpu),
661		  [current_cpu_id]	"Qo" (rseq_get_abi()->cpu_id),
662		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
663		  [expect]		"r" (expect),
664		  [v]			"Qo" (*v),
665		  [newv]		"r" (newv),
666		  [dst]			"r" (dst),
667		  [src]			"r" (src),
668		  [len]			"r" (len)
669		  RSEQ_INJECT_INPUT
670		: "memory", RSEQ_ASM_TMP_REG, RSEQ_ASM_TMP_REG_2
671		: abort, cmpfail
672#ifdef RSEQ_COMPARE_TWICE
673		  , error1, error2
674#endif
675	);
676	rseq_after_asm_goto();
677	return 0;
678abort:
679	rseq_after_asm_goto();
680	RSEQ_INJECT_FAILED
681	return -1;
682cmpfail:
683	rseq_after_asm_goto();
684	return 1;
685#ifdef RSEQ_COMPARE_TWICE
686error1:
687	rseq_after_asm_goto();
688	rseq_bug("cpu_id comparison failed");
689error2:
690	rseq_after_asm_goto();
691	rseq_bug("expected value comparison failed");
692#endif
693}
694
695#endif /* !RSEQ_SKIP_FASTPATH */
696