1/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
2/*
3 * rseq-ppc.h
4 *
5 * (C) Copyright 2016-2018 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
6 * (C) Copyright 2016-2018 - Boqun Feng <boqun.feng@gmail.com>
7 */
8
9/*
10 * RSEQ_SIG is used with the following trap instruction:
11 *
12 * powerpc-be:    0f e5 00 0b           twui   r5,11
13 * powerpc64-le:  0b 00 e5 0f           twui   r5,11
14 * powerpc64-be:  0f e5 00 0b           twui   r5,11
15 */
16
17#define RSEQ_SIG	0x0fe5000b
18
19#define rseq_smp_mb()		__asm__ __volatile__ ("sync"	::: "memory", "cc")
20#define rseq_smp_lwsync()	__asm__ __volatile__ ("lwsync"	::: "memory", "cc")
21#define rseq_smp_rmb()		rseq_smp_lwsync()
22#define rseq_smp_wmb()		rseq_smp_lwsync()
23
24#define rseq_smp_load_acquire(p)					\
25__extension__ ({							\
26	__typeof(*p) ____p1 = RSEQ_READ_ONCE(*p);			\
27	rseq_smp_lwsync();						\
28	____p1;								\
29})
30
31#define rseq_smp_acquire__after_ctrl_dep()	rseq_smp_lwsync()
32
33#define rseq_smp_store_release(p, v)					\
34do {									\
35	rseq_smp_lwsync();						\
36	RSEQ_WRITE_ONCE(*p, v);						\
37} while (0)
38
39#ifdef RSEQ_SKIP_FASTPATH
40#include "rseq-skip.h"
41#else /* !RSEQ_SKIP_FASTPATH */
42
43/*
44 * The __rseq_cs_ptr_array and __rseq_cs sections can be used by debuggers to
45 * better handle single-stepping through the restartable critical sections.
46 */
47
48#ifdef __PPC64__
49
50#define RSEQ_STORE_LONG(arg)	"std%U[" __rseq_str(arg) "]%X[" __rseq_str(arg) "] "	/* To memory ("m" constraint) */
51#define RSEQ_STORE_INT(arg)	"stw%U[" __rseq_str(arg) "]%X[" __rseq_str(arg) "] "	/* To memory ("m" constraint) */
52#define RSEQ_LOAD_LONG(arg)	"ld%U[" __rseq_str(arg) "]%X[" __rseq_str(arg) "] "	/* From memory ("m" constraint) */
53#define RSEQ_LOAD_INT(arg)	"lwz%U[" __rseq_str(arg) "]%X[" __rseq_str(arg) "] "	/* From memory ("m" constraint) */
54#define RSEQ_LOADX_LONG		"ldx "							/* From base register ("b" constraint) */
55#define RSEQ_CMP_LONG		"cmpd "
56#define RSEQ_CMP_LONG_INT	"cmpdi "
57
58#define __RSEQ_ASM_DEFINE_TABLE(label, version, flags,				\
59			start_ip, post_commit_offset, abort_ip)			\
60		".pushsection __rseq_cs, \"aw\"\n\t"				\
61		".balign 32\n\t"						\
62		__rseq_str(label) ":\n\t"					\
63		".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t"	\
64		".quad " __rseq_str(start_ip) ", " __rseq_str(post_commit_offset) ", " __rseq_str(abort_ip) "\n\t" \
65		".popsection\n\t"						\
66		".pushsection __rseq_cs_ptr_array, \"aw\"\n\t"			\
67		".quad " __rseq_str(label) "b\n\t"				\
68		".popsection\n\t"
69
70#define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs)			\
71		RSEQ_INJECT_ASM(1)						\
72		"lis %%r17, (" __rseq_str(cs_label) ")@highest\n\t"		\
73		"ori %%r17, %%r17, (" __rseq_str(cs_label) ")@higher\n\t"	\
74		"rldicr %%r17, %%r17, 32, 31\n\t"				\
75		"oris %%r17, %%r17, (" __rseq_str(cs_label) ")@high\n\t"	\
76		"ori %%r17, %%r17, (" __rseq_str(cs_label) ")@l\n\t"		\
77		"std %%r17, %[" __rseq_str(rseq_cs) "]\n\t"			\
78		__rseq_str(label) ":\n\t"
79
80/*
81 * Exit points of a rseq critical section consist of all instructions outside
82 * of the critical section where a critical section can either branch to or
83 * reach through the normal course of its execution. The abort IP and the
84 * post-commit IP are already part of the __rseq_cs section and should not be
85 * explicitly defined as additional exit points. Knowing all exit points is
86 * useful to assist debuggers stepping over the critical section.
87 */
88#define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip)			\
89		".pushsection __rseq_exit_point_array, \"aw\"\n\t"	\
90		".quad " __rseq_str(start_ip) ", " __rseq_str(exit_ip) "\n\t" \
91		".popsection\n\t"
92
93#else /* #ifdef __PPC64__ */
94
95#define RSEQ_STORE_LONG(arg)	"stw%U[" __rseq_str(arg) "]%X[" __rseq_str(arg) "] "	/* To memory ("m" constraint) */
96#define RSEQ_STORE_INT(arg)	RSEQ_STORE_LONG(arg)					/* To memory ("m" constraint) */
97#define RSEQ_LOAD_LONG(arg)	"lwz%U[" __rseq_str(arg) "]%X[" __rseq_str(arg) "] "	/* From memory ("m" constraint) */
98#define RSEQ_LOAD_INT(arg)	RSEQ_LOAD_LONG(arg)					/* From memory ("m" constraint) */
99#define RSEQ_LOADX_LONG		"lwzx "							/* From base register ("b" constraint) */
100#define RSEQ_CMP_LONG		"cmpw "
101#define RSEQ_CMP_LONG_INT	"cmpwi "
102
103#define __RSEQ_ASM_DEFINE_TABLE(label, version, flags,				\
104			start_ip, post_commit_offset, abort_ip)			\
105		".pushsection __rseq_cs, \"aw\"\n\t"				\
106		".balign 32\n\t"						\
107		__rseq_str(label) ":\n\t"					\
108		".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t"	\
109		/* 32-bit only supported on BE */				\
110		".long 0x0, " __rseq_str(start_ip) ", 0x0, " __rseq_str(post_commit_offset) ", 0x0, " __rseq_str(abort_ip) "\n\t" \
111		".popsection\n\t"					\
112		".pushsection __rseq_cs_ptr_array, \"aw\"\n\t"		\
113		".long 0x0, " __rseq_str(label) "b\n\t"			\
114		".popsection\n\t"
115
116/*
117 * Exit points of a rseq critical section consist of all instructions outside
118 * of the critical section where a critical section can either branch to or
119 * reach through the normal course of its execution. The abort IP and the
120 * post-commit IP are already part of the __rseq_cs section and should not be
121 * explicitly defined as additional exit points. Knowing all exit points is
122 * useful to assist debuggers stepping over the critical section.
123 */
124#define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip)				\
125		".pushsection __rseq_exit_point_array, \"aw\"\n\t"		\
126		/* 32-bit only supported on BE */				\
127		".long 0x0, " __rseq_str(start_ip) ", 0x0, " __rseq_str(exit_ip) "\n\t"	\
128		".popsection\n\t"
129
130#define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs)			\
131		RSEQ_INJECT_ASM(1)						\
132		"lis %%r17, (" __rseq_str(cs_label) ")@ha\n\t"			\
133		"addi %%r17, %%r17, (" __rseq_str(cs_label) ")@l\n\t"		\
134		RSEQ_STORE_INT(rseq_cs) "%%r17, %[" __rseq_str(rseq_cs) "]\n\t"	\
135		__rseq_str(label) ":\n\t"
136
137#endif /* #ifdef __PPC64__ */
138
139#define RSEQ_ASM_DEFINE_TABLE(label, start_ip, post_commit_ip, abort_ip)	\
140		__RSEQ_ASM_DEFINE_TABLE(label, 0x0, 0x0, start_ip,		\
141					(post_commit_ip - start_ip), abort_ip)
142
143#define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label)			\
144		RSEQ_INJECT_ASM(2)						\
145		RSEQ_LOAD_INT(current_cpu_id) "%%r17, %[" __rseq_str(current_cpu_id) "]\n\t" \
146		"cmpw cr7, %[" __rseq_str(cpu_id) "], %%r17\n\t"		\
147		"bne- cr7, " __rseq_str(label) "\n\t"
148
149#define RSEQ_ASM_DEFINE_ABORT(label, abort_label)				\
150		".pushsection __rseq_failure, \"ax\"\n\t"			\
151		".long " __rseq_str(RSEQ_SIG) "\n\t"				\
152		__rseq_str(label) ":\n\t"					\
153		"b %l[" __rseq_str(abort_label) "]\n\t"				\
154		".popsection\n\t"
155
156/*
157 * RSEQ_ASM_OPs: asm operations for rseq
158 * 	RSEQ_ASM_OP_R_*: has hard-code registers in it
159 * 	RSEQ_ASM_OP_* (else): doesn't have hard-code registers(unless cr7)
160 */
161#define RSEQ_ASM_OP_CMPEQ(var, expect, label)					\
162		RSEQ_LOAD_LONG(var) "%%r17, %[" __rseq_str(var) "]\n\t"		\
163		RSEQ_CMP_LONG "cr7, %%r17, %[" __rseq_str(expect) "]\n\t"		\
164		"bne- cr7, " __rseq_str(label) "\n\t"
165
166#define RSEQ_ASM_OP_CMPNE(var, expectnot, label)				\
167		RSEQ_LOAD_LONG(var) "%%r17, %[" __rseq_str(var) "]\n\t"		\
168		RSEQ_CMP_LONG "cr7, %%r17, %[" __rseq_str(expectnot) "]\n\t"		\
169		"beq- cr7, " __rseq_str(label) "\n\t"
170
171#define RSEQ_ASM_OP_STORE(value, var)						\
172		RSEQ_STORE_LONG(var) "%[" __rseq_str(value) "], %[" __rseq_str(var) "]\n\t"
173
174/* Load @var to r17 */
175#define RSEQ_ASM_OP_R_LOAD(var)							\
176		RSEQ_LOAD_LONG(var) "%%r17, %[" __rseq_str(var) "]\n\t"
177
178/* Store r17 to @var */
179#define RSEQ_ASM_OP_R_STORE(var)						\
180		RSEQ_STORE_LONG(var) "%%r17, %[" __rseq_str(var) "]\n\t"
181
182/* Add @count to r17 */
183#define RSEQ_ASM_OP_R_ADD(count)						\
184		"add %%r17, %[" __rseq_str(count) "], %%r17\n\t"
185
186/* Load (r17 + voffp) to r17 */
187#define RSEQ_ASM_OP_R_LOADX(voffp)						\
188		RSEQ_LOADX_LONG "%%r17, %[" __rseq_str(voffp) "], %%r17\n\t"
189
190/* TODO: implement a faster memcpy. */
191#define RSEQ_ASM_OP_R_MEMCPY() \
192		RSEQ_CMP_LONG_INT "%%r19, 0\n\t" \
193		"beq 333f\n\t" \
194		"addi %%r20, %%r20, -1\n\t" \
195		"addi %%r21, %%r21, -1\n\t" \
196		"222:\n\t" \
197		"lbzu %%r18, 1(%%r20)\n\t" \
198		"stbu %%r18, 1(%%r21)\n\t" \
199		"addi %%r19, %%r19, -1\n\t" \
200		RSEQ_CMP_LONG_INT "%%r19, 0\n\t" \
201		"bne 222b\n\t" \
202		"333:\n\t" \
203
204#define RSEQ_ASM_OP_R_FINAL_STORE(var, post_commit_label)			\
205		RSEQ_STORE_LONG(var) "%%r17, %[" __rseq_str(var) "]\n\t"			\
206		__rseq_str(post_commit_label) ":\n\t"
207
208#define RSEQ_ASM_OP_FINAL_STORE(value, var, post_commit_label)			\
209		RSEQ_STORE_LONG(var) "%[" __rseq_str(value) "], %[" __rseq_str(var) "]\n\t" \
210		__rseq_str(post_commit_label) ":\n\t"
211
212static inline __attribute__((always_inline))
213int rseq_cmpeqv_storev(intptr_t *v, intptr_t expect, intptr_t newv, int cpu)
214{
215	RSEQ_INJECT_C(9)
216
217	__asm__ __volatile__ goto (
218		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
219		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
220#ifdef RSEQ_COMPARE_TWICE
221		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
222		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
223#endif
224		/* Start rseq by storing table entry pointer into rseq_cs. */
225		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
226		/* cmp cpuid */
227		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
228		RSEQ_INJECT_ASM(3)
229		/* cmp @v equal to @expect */
230		RSEQ_ASM_OP_CMPEQ(v, expect, %l[cmpfail])
231		RSEQ_INJECT_ASM(4)
232#ifdef RSEQ_COMPARE_TWICE
233		/* cmp cpuid */
234		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
235		/* cmp @v equal to @expect */
236		RSEQ_ASM_OP_CMPEQ(v, expect, %l[error2])
237#endif
238		/* final store */
239		RSEQ_ASM_OP_FINAL_STORE(newv, v, 2)
240		RSEQ_INJECT_ASM(5)
241		RSEQ_ASM_DEFINE_ABORT(4, abort)
242		: /* gcc asm goto does not allow outputs */
243		: [cpu_id]		"r" (cpu),
244		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
245		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
246		  [v]			"m" (*v),
247		  [expect]		"r" (expect),
248		  [newv]		"r" (newv)
249		  RSEQ_INJECT_INPUT
250		: "memory", "cc", "r17"
251		  RSEQ_INJECT_CLOBBER
252		: abort, cmpfail
253#ifdef RSEQ_COMPARE_TWICE
254		  , error1, error2
255#endif
256	);
257	rseq_after_asm_goto();
258	return 0;
259abort:
260	rseq_after_asm_goto();
261	RSEQ_INJECT_FAILED
262	return -1;
263cmpfail:
264	rseq_after_asm_goto();
265	return 1;
266#ifdef RSEQ_COMPARE_TWICE
267error1:
268	rseq_after_asm_goto();
269	rseq_bug("cpu_id comparison failed");
270error2:
271	rseq_after_asm_goto();
272	rseq_bug("expected value comparison failed");
273#endif
274}
275
276static inline __attribute__((always_inline))
277int rseq_cmpnev_storeoffp_load(intptr_t *v, intptr_t expectnot,
278			       long voffp, intptr_t *load, int cpu)
279{
280	RSEQ_INJECT_C(9)
281
282	__asm__ __volatile__ goto (
283		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
284		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
285#ifdef RSEQ_COMPARE_TWICE
286		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
287		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
288#endif
289		/* Start rseq by storing table entry pointer into rseq_cs. */
290		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
291		/* cmp cpuid */
292		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
293		RSEQ_INJECT_ASM(3)
294		/* cmp @v not equal to @expectnot */
295		RSEQ_ASM_OP_CMPNE(v, expectnot, %l[cmpfail])
296		RSEQ_INJECT_ASM(4)
297#ifdef RSEQ_COMPARE_TWICE
298		/* cmp cpuid */
299		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
300		/* cmp @v not equal to @expectnot */
301		RSEQ_ASM_OP_CMPNE(v, expectnot, %l[error2])
302#endif
303		/* load the value of @v */
304		RSEQ_ASM_OP_R_LOAD(v)
305		/* store it in @load */
306		RSEQ_ASM_OP_R_STORE(load)
307		/* dereference voffp(v) */
308		RSEQ_ASM_OP_R_LOADX(voffp)
309		/* final store the value at voffp(v) */
310		RSEQ_ASM_OP_R_FINAL_STORE(v, 2)
311		RSEQ_INJECT_ASM(5)
312		RSEQ_ASM_DEFINE_ABORT(4, abort)
313		: /* gcc asm goto does not allow outputs */
314		: [cpu_id]		"r" (cpu),
315		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
316		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
317		  /* final store input */
318		  [v]			"m" (*v),
319		  [expectnot]		"r" (expectnot),
320		  [voffp]		"b" (voffp),
321		  [load]		"m" (*load)
322		  RSEQ_INJECT_INPUT
323		: "memory", "cc", "r17"
324		  RSEQ_INJECT_CLOBBER
325		: abort, cmpfail
326#ifdef RSEQ_COMPARE_TWICE
327		  , error1, error2
328#endif
329	);
330	rseq_after_asm_goto();
331	return 0;
332abort:
333	rseq_after_asm_goto();
334	RSEQ_INJECT_FAILED
335	return -1;
336cmpfail:
337	rseq_after_asm_goto();
338	return 1;
339#ifdef RSEQ_COMPARE_TWICE
340error1:
341	rseq_after_asm_goto();
342	rseq_bug("cpu_id comparison failed");
343error2:
344	rseq_after_asm_goto();
345	rseq_bug("expected value comparison failed");
346#endif
347}
348
349static inline __attribute__((always_inline))
350int rseq_addv(intptr_t *v, intptr_t count, int cpu)
351{
352	RSEQ_INJECT_C(9)
353
354	__asm__ __volatile__ goto (
355		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
356#ifdef RSEQ_COMPARE_TWICE
357		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
358#endif
359		/* Start rseq by storing table entry pointer into rseq_cs. */
360		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
361		/* cmp cpuid */
362		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
363		RSEQ_INJECT_ASM(3)
364#ifdef RSEQ_COMPARE_TWICE
365		/* cmp cpuid */
366		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
367#endif
368		/* load the value of @v */
369		RSEQ_ASM_OP_R_LOAD(v)
370		/* add @count to it */
371		RSEQ_ASM_OP_R_ADD(count)
372		/* final store */
373		RSEQ_ASM_OP_R_FINAL_STORE(v, 2)
374		RSEQ_INJECT_ASM(4)
375		RSEQ_ASM_DEFINE_ABORT(4, abort)
376		: /* gcc asm goto does not allow outputs */
377		: [cpu_id]		"r" (cpu),
378		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
379		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
380		  /* final store input */
381		  [v]			"m" (*v),
382		  [count]		"r" (count)
383		  RSEQ_INJECT_INPUT
384		: "memory", "cc", "r17"
385		  RSEQ_INJECT_CLOBBER
386		: abort
387#ifdef RSEQ_COMPARE_TWICE
388		  , error1
389#endif
390	);
391	rseq_after_asm_goto();
392	return 0;
393abort:
394	rseq_after_asm_goto();
395	RSEQ_INJECT_FAILED
396	return -1;
397#ifdef RSEQ_COMPARE_TWICE
398error1:
399	rseq_after_asm_goto();
400	rseq_bug("cpu_id comparison failed");
401#endif
402}
403
404static inline __attribute__((always_inline))
405int rseq_cmpeqv_trystorev_storev(intptr_t *v, intptr_t expect,
406				 intptr_t *v2, intptr_t newv2,
407				 intptr_t newv, int cpu)
408{
409	RSEQ_INJECT_C(9)
410
411	__asm__ __volatile__ goto (
412		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
413		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
414#ifdef RSEQ_COMPARE_TWICE
415		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
416		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
417#endif
418		/* Start rseq by storing table entry pointer into rseq_cs. */
419		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
420		/* cmp cpuid */
421		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
422		RSEQ_INJECT_ASM(3)
423		/* cmp @v equal to @expect */
424		RSEQ_ASM_OP_CMPEQ(v, expect, %l[cmpfail])
425		RSEQ_INJECT_ASM(4)
426#ifdef RSEQ_COMPARE_TWICE
427		/* cmp cpuid */
428		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
429		/* cmp @v equal to @expect */
430		RSEQ_ASM_OP_CMPEQ(v, expect, %l[error2])
431#endif
432		/* try store */
433		RSEQ_ASM_OP_STORE(newv2, v2)
434		RSEQ_INJECT_ASM(5)
435		/* final store */
436		RSEQ_ASM_OP_FINAL_STORE(newv, v, 2)
437		RSEQ_INJECT_ASM(6)
438		RSEQ_ASM_DEFINE_ABORT(4, abort)
439		: /* gcc asm goto does not allow outputs */
440		: [cpu_id]		"r" (cpu),
441		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
442		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
443		  /* try store input */
444		  [v2]			"m" (*v2),
445		  [newv2]		"r" (newv2),
446		  /* final store input */
447		  [v]			"m" (*v),
448		  [expect]		"r" (expect),
449		  [newv]		"r" (newv)
450		  RSEQ_INJECT_INPUT
451		: "memory", "cc", "r17"
452		  RSEQ_INJECT_CLOBBER
453		: abort, cmpfail
454#ifdef RSEQ_COMPARE_TWICE
455		  , error1, error2
456#endif
457	);
458	rseq_after_asm_goto();
459	return 0;
460abort:
461	rseq_after_asm_goto();
462	RSEQ_INJECT_FAILED
463	return -1;
464cmpfail:
465	rseq_after_asm_goto();
466	return 1;
467#ifdef RSEQ_COMPARE_TWICE
468error1:
469	rseq_after_asm_goto();
470	rseq_bug("cpu_id comparison failed");
471error2:
472	rseq_after_asm_goto();
473	rseq_bug("expected value comparison failed");
474#endif
475}
476
477static inline __attribute__((always_inline))
478int rseq_cmpeqv_trystorev_storev_release(intptr_t *v, intptr_t expect,
479					 intptr_t *v2, intptr_t newv2,
480					 intptr_t newv, int cpu)
481{
482	RSEQ_INJECT_C(9)
483
484	__asm__ __volatile__ goto (
485		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
486		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
487#ifdef RSEQ_COMPARE_TWICE
488		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
489		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
490#endif
491		/* Start rseq by storing table entry pointer into rseq_cs. */
492		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
493		/* cmp cpuid */
494		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
495		RSEQ_INJECT_ASM(3)
496		/* cmp @v equal to @expect */
497		RSEQ_ASM_OP_CMPEQ(v, expect, %l[cmpfail])
498		RSEQ_INJECT_ASM(4)
499#ifdef RSEQ_COMPARE_TWICE
500		/* cmp cpuid */
501		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
502		/* cmp @v equal to @expect */
503		RSEQ_ASM_OP_CMPEQ(v, expect, %l[error2])
504#endif
505		/* try store */
506		RSEQ_ASM_OP_STORE(newv2, v2)
507		RSEQ_INJECT_ASM(5)
508		/* for 'release' */
509		"lwsync\n\t"
510		/* final store */
511		RSEQ_ASM_OP_FINAL_STORE(newv, v, 2)
512		RSEQ_INJECT_ASM(6)
513		RSEQ_ASM_DEFINE_ABORT(4, abort)
514		: /* gcc asm goto does not allow outputs */
515		: [cpu_id]		"r" (cpu),
516		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
517		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
518		  /* try store input */
519		  [v2]			"m" (*v2),
520		  [newv2]		"r" (newv2),
521		  /* final store input */
522		  [v]			"m" (*v),
523		  [expect]		"r" (expect),
524		  [newv]		"r" (newv)
525		  RSEQ_INJECT_INPUT
526		: "memory", "cc", "r17"
527		  RSEQ_INJECT_CLOBBER
528		: abort, cmpfail
529#ifdef RSEQ_COMPARE_TWICE
530		  , error1, error2
531#endif
532	);
533	rseq_after_asm_goto();
534	return 0;
535abort:
536	rseq_after_asm_goto();
537	RSEQ_INJECT_FAILED
538	return -1;
539cmpfail:
540	rseq_after_asm_goto();
541	return 1;
542#ifdef RSEQ_COMPARE_TWICE
543error1:
544	rseq_after_asm_goto();
545	rseq_bug("cpu_id comparison failed");
546error2:
547	rseq_after_asm_goto();
548	rseq_bug("expected value comparison failed");
549#endif
550}
551
552static inline __attribute__((always_inline))
553int rseq_cmpeqv_cmpeqv_storev(intptr_t *v, intptr_t expect,
554			      intptr_t *v2, intptr_t expect2,
555			      intptr_t newv, int cpu)
556{
557	RSEQ_INJECT_C(9)
558
559	__asm__ __volatile__ goto (
560		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
561		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
562#ifdef RSEQ_COMPARE_TWICE
563		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
564		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
565		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error3])
566#endif
567		/* Start rseq by storing table entry pointer into rseq_cs. */
568		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
569		/* cmp cpuid */
570		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
571		RSEQ_INJECT_ASM(3)
572		/* cmp @v equal to @expect */
573		RSEQ_ASM_OP_CMPEQ(v, expect, %l[cmpfail])
574		RSEQ_INJECT_ASM(4)
575		/* cmp @v2 equal to @expct2 */
576		RSEQ_ASM_OP_CMPEQ(v2, expect2, %l[cmpfail])
577		RSEQ_INJECT_ASM(5)
578#ifdef RSEQ_COMPARE_TWICE
579		/* cmp cpuid */
580		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
581		/* cmp @v equal to @expect */
582		RSEQ_ASM_OP_CMPEQ(v, expect, %l[error2])
583		/* cmp @v2 equal to @expct2 */
584		RSEQ_ASM_OP_CMPEQ(v2, expect2, %l[error3])
585#endif
586		/* final store */
587		RSEQ_ASM_OP_FINAL_STORE(newv, v, 2)
588		RSEQ_INJECT_ASM(6)
589		RSEQ_ASM_DEFINE_ABORT(4, abort)
590		: /* gcc asm goto does not allow outputs */
591		: [cpu_id]		"r" (cpu),
592		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
593		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
594		  /* cmp2 input */
595		  [v2]			"m" (*v2),
596		  [expect2]		"r" (expect2),
597		  /* final store input */
598		  [v]			"m" (*v),
599		  [expect]		"r" (expect),
600		  [newv]		"r" (newv)
601		  RSEQ_INJECT_INPUT
602		: "memory", "cc", "r17"
603		  RSEQ_INJECT_CLOBBER
604		: abort, cmpfail
605#ifdef RSEQ_COMPARE_TWICE
606		  , error1, error2, error3
607#endif
608	);
609	rseq_after_asm_goto();
610	return 0;
611abort:
612	rseq_after_asm_goto();
613	RSEQ_INJECT_FAILED
614	return -1;
615cmpfail:
616	rseq_after_asm_goto();
617	return 1;
618#ifdef RSEQ_COMPARE_TWICE
619error1:
620	rseq_after_asm_goto();
621	rseq_bug("cpu_id comparison failed");
622error2:
623	rseq_after_asm_goto();
624	rseq_bug("1st expected value comparison failed");
625error3:
626	rseq_after_asm_goto();
627	rseq_bug("2nd expected value comparison failed");
628#endif
629}
630
631static inline __attribute__((always_inline))
632int rseq_cmpeqv_trymemcpy_storev(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(3, 1f, 2f, 4f) /* start, commit, abort */
640		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
641#ifdef RSEQ_COMPARE_TWICE
642		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
643		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
644#endif
645		/* setup for mempcy */
646		"mr %%r19, %[len]\n\t"
647		"mr %%r20, %[src]\n\t"
648		"mr %%r21, %[dst]\n\t"
649		/* Start rseq by storing table entry pointer into rseq_cs. */
650		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
651		/* cmp cpuid */
652		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
653		RSEQ_INJECT_ASM(3)
654		/* cmp @v equal to @expect */
655		RSEQ_ASM_OP_CMPEQ(v, expect, %l[cmpfail])
656		RSEQ_INJECT_ASM(4)
657#ifdef RSEQ_COMPARE_TWICE
658		/* cmp cpuid */
659		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
660		/* cmp @v equal to @expect */
661		RSEQ_ASM_OP_CMPEQ(v, expect, %l[error2])
662#endif
663		/* try memcpy */
664		RSEQ_ASM_OP_R_MEMCPY()
665		RSEQ_INJECT_ASM(5)
666		/* final store */
667		RSEQ_ASM_OP_FINAL_STORE(newv, v, 2)
668		RSEQ_INJECT_ASM(6)
669		/* teardown */
670		RSEQ_ASM_DEFINE_ABORT(4, abort)
671		: /* gcc asm goto does not allow outputs */
672		: [cpu_id]		"r" (cpu),
673		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
674		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
675		  /* final store input */
676		  [v]			"m" (*v),
677		  [expect]		"r" (expect),
678		  [newv]		"r" (newv),
679		  /* try memcpy input */
680		  [dst]			"r" (dst),
681		  [src]			"r" (src),
682		  [len]			"r" (len)
683		  RSEQ_INJECT_INPUT
684		: "memory", "cc", "r17", "r18", "r19", "r20", "r21"
685		  RSEQ_INJECT_CLOBBER
686		: abort, cmpfail
687#ifdef RSEQ_COMPARE_TWICE
688		  , error1, error2
689#endif
690	);
691	rseq_after_asm_goto();
692	return 0;
693abort:
694	rseq_after_asm_goto();
695	RSEQ_INJECT_FAILED
696	return -1;
697cmpfail:
698	rseq_after_asm_goto();
699	return 1;
700#ifdef RSEQ_COMPARE_TWICE
701error1:
702	rseq_after_asm_goto();
703	rseq_bug("cpu_id comparison failed");
704error2:
705	rseq_after_asm_goto();
706	rseq_bug("expected value comparison failed");
707#endif
708}
709
710static inline __attribute__((always_inline))
711int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v, intptr_t expect,
712					 void *dst, void *src, size_t len,
713					 intptr_t newv, int cpu)
714{
715	RSEQ_INJECT_C(9)
716
717	__asm__ __volatile__ goto (
718		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
719		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
720#ifdef RSEQ_COMPARE_TWICE
721		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
722		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
723#endif
724		/* setup for mempcy */
725		"mr %%r19, %[len]\n\t"
726		"mr %%r20, %[src]\n\t"
727		"mr %%r21, %[dst]\n\t"
728		/* Start rseq by storing table entry pointer into rseq_cs. */
729		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
730		/* cmp cpuid */
731		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
732		RSEQ_INJECT_ASM(3)
733		/* cmp @v equal to @expect */
734		RSEQ_ASM_OP_CMPEQ(v, expect, %l[cmpfail])
735		RSEQ_INJECT_ASM(4)
736#ifdef RSEQ_COMPARE_TWICE
737		/* cmp cpuid */
738		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
739		/* cmp @v equal to @expect */
740		RSEQ_ASM_OP_CMPEQ(v, expect, %l[error2])
741#endif
742		/* try memcpy */
743		RSEQ_ASM_OP_R_MEMCPY()
744		RSEQ_INJECT_ASM(5)
745		/* for 'release' */
746		"lwsync\n\t"
747		/* final store */
748		RSEQ_ASM_OP_FINAL_STORE(newv, v, 2)
749		RSEQ_INJECT_ASM(6)
750		/* teardown */
751		RSEQ_ASM_DEFINE_ABORT(4, abort)
752		: /* gcc asm goto does not allow outputs */
753		: [cpu_id]		"r" (cpu),
754		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
755		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
756		  /* final store input */
757		  [v]			"m" (*v),
758		  [expect]		"r" (expect),
759		  [newv]		"r" (newv),
760		  /* try memcpy input */
761		  [dst]			"r" (dst),
762		  [src]			"r" (src),
763		  [len]			"r" (len)
764		  RSEQ_INJECT_INPUT
765		: "memory", "cc", "r17", "r18", "r19", "r20", "r21"
766		  RSEQ_INJECT_CLOBBER
767		: abort, cmpfail
768#ifdef RSEQ_COMPARE_TWICE
769		  , error1, error2
770#endif
771	);
772	rseq_after_asm_goto();
773	return 0;
774abort:
775	rseq_after_asm_goto();
776	RSEQ_INJECT_FAILED
777	return -1;
778cmpfail:
779	rseq_after_asm_goto();
780	return 1;
781#ifdef RSEQ_COMPARE_TWICE
782error1:
783	rseq_after_asm_goto();
784	rseq_bug("cpu_id comparison failed");
785error2:
786	rseq_after_asm_goto();
787	rseq_bug("expected value comparison failed");
788#endif
789}
790
791#endif /* !RSEQ_SKIP_FASTPATH */
792