1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
2 /*
3  * rseq-x86-bits.h
4  *
5  * (C) Copyright 2016-2022 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
6  */
7 
8 #include "rseq-bits-template.h"
9 
10 #ifdef __x86_64__
11 
12 #if defined(RSEQ_TEMPLATE_MO_RELAXED) && \
13 	(defined(RSEQ_TEMPLATE_CPU_ID) || defined(RSEQ_TEMPLATE_MM_CID))
14 
15 static inline __attribute__((always_inline))
RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_storev)16 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_storev)(intptr_t *v, intptr_t expect, intptr_t newv, int cpu)
17 {
18 	RSEQ_INJECT_C(9)
19 
20 	__asm__ __volatile__ goto (
21 		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
22 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
23 #ifdef RSEQ_COMPARE_TWICE
24 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
25 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
26 #endif
27 		/* Start rseq by storing table entry pointer into rseq_cs. */
28 		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_ASM_TP_SEGMENT:RSEQ_CS_OFFSET(%[rseq_offset]))
29 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), 4f)
30 		RSEQ_INJECT_ASM(3)
31 		"cmpq %[v], %[expect]\n\t"
32 		"jnz %l[cmpfail]\n\t"
33 		RSEQ_INJECT_ASM(4)
34 #ifdef RSEQ_COMPARE_TWICE
35 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), %l[error1])
36 		"cmpq %[v], %[expect]\n\t"
37 		"jnz %l[error2]\n\t"
38 #endif
39 		/* final store */
40 		"movq %[newv], %[v]\n\t"
41 		"2:\n\t"
42 		RSEQ_INJECT_ASM(5)
43 		RSEQ_ASM_DEFINE_ABORT(4, "", abort)
44 		: /* gcc asm goto does not allow outputs */
45 		: [cpu_id]		"r" (cpu),
46 		  [rseq_offset]		"r" (rseq_offset),
47 		  [v]			"m" (*v),
48 		  [expect]		"r" (expect),
49 		  [newv]		"r" (newv)
50 		: "memory", "cc", "rax"
51 		  RSEQ_INJECT_CLOBBER
52 		: abort, cmpfail
53 #ifdef RSEQ_COMPARE_TWICE
54 		  , error1, error2
55 #endif
56 	);
57 	rseq_after_asm_goto();
58 	return 0;
59 abort:
60 	rseq_after_asm_goto();
61 	RSEQ_INJECT_FAILED
62 	return -1;
63 cmpfail:
64 	rseq_after_asm_goto();
65 	return 1;
66 #ifdef RSEQ_COMPARE_TWICE
67 error1:
68 	rseq_after_asm_goto();
69 	rseq_bug("cpu_id comparison failed");
70 error2:
71 	rseq_after_asm_goto();
72 	rseq_bug("expected value comparison failed");
73 #endif
74 }
75 
76 /*
77  * Compare @v against @expectnot. When it does _not_ match, load @v
78  * into @load, and store the content of *@v + voffp into @v.
79  */
80 static inline __attribute__((always_inline))
RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpnev_storeoffp_load)81 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpnev_storeoffp_load)(intptr_t *v, intptr_t expectnot,
82 			       long voffp, intptr_t *load, int cpu)
83 {
84 	RSEQ_INJECT_C(9)
85 
86 	__asm__ __volatile__ goto (
87 		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
88 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
89 #ifdef RSEQ_COMPARE_TWICE
90 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
91 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
92 #endif
93 		/* Start rseq by storing table entry pointer into rseq_cs. */
94 		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_ASM_TP_SEGMENT:RSEQ_CS_OFFSET(%[rseq_offset]))
95 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), 4f)
96 		RSEQ_INJECT_ASM(3)
97 		"movq %[v], %%rbx\n\t"
98 		"cmpq %%rbx, %[expectnot]\n\t"
99 		"je %l[cmpfail]\n\t"
100 		RSEQ_INJECT_ASM(4)
101 #ifdef RSEQ_COMPARE_TWICE
102 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), %l[error1])
103 		"movq %[v], %%rbx\n\t"
104 		"cmpq %%rbx, %[expectnot]\n\t"
105 		"je %l[error2]\n\t"
106 #endif
107 		"movq %%rbx, %[load]\n\t"
108 		"addq %[voffp], %%rbx\n\t"
109 		"movq (%%rbx), %%rbx\n\t"
110 		/* final store */
111 		"movq %%rbx, %[v]\n\t"
112 		"2:\n\t"
113 		RSEQ_INJECT_ASM(5)
114 		RSEQ_ASM_DEFINE_ABORT(4, "", abort)
115 		: /* gcc asm goto does not allow outputs */
116 		: [cpu_id]		"r" (cpu),
117 		  [rseq_offset]		"r" (rseq_offset),
118 		  /* final store input */
119 		  [v]			"m" (*v),
120 		  [expectnot]		"r" (expectnot),
121 		  [voffp]		"er" (voffp),
122 		  [load]		"m" (*load)
123 		: "memory", "cc", "rax", "rbx"
124 		  RSEQ_INJECT_CLOBBER
125 		: abort, cmpfail
126 #ifdef RSEQ_COMPARE_TWICE
127 		  , error1, error2
128 #endif
129 	);
130 	rseq_after_asm_goto();
131 	return 0;
132 abort:
133 	rseq_after_asm_goto();
134 	RSEQ_INJECT_FAILED
135 	return -1;
136 cmpfail:
137 	rseq_after_asm_goto();
138 	return 1;
139 #ifdef RSEQ_COMPARE_TWICE
140 error1:
141 	rseq_after_asm_goto();
142 	rseq_bug("cpu_id comparison failed");
143 error2:
144 	rseq_after_asm_goto();
145 	rseq_bug("expected value comparison failed");
146 #endif
147 }
148 
149 static inline __attribute__((always_inline))
RSEQ_TEMPLATE_IDENTIFIER(rseq_addv)150 int RSEQ_TEMPLATE_IDENTIFIER(rseq_addv)(intptr_t *v, intptr_t count, int cpu)
151 {
152 	RSEQ_INJECT_C(9)
153 
154 	__asm__ __volatile__ goto (
155 		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
156 #ifdef RSEQ_COMPARE_TWICE
157 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
158 #endif
159 		/* Start rseq by storing table entry pointer into rseq_cs. */
160 		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_ASM_TP_SEGMENT:RSEQ_CS_OFFSET(%[rseq_offset]))
161 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), 4f)
162 		RSEQ_INJECT_ASM(3)
163 #ifdef RSEQ_COMPARE_TWICE
164 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), %l[error1])
165 #endif
166 		/* final store */
167 		"addq %[count], %[v]\n\t"
168 		"2:\n\t"
169 		RSEQ_INJECT_ASM(4)
170 		RSEQ_ASM_DEFINE_ABORT(4, "", abort)
171 		: /* gcc asm goto does not allow outputs */
172 		: [cpu_id]		"r" (cpu),
173 		  [rseq_offset]		"r" (rseq_offset),
174 		  /* final store input */
175 		  [v]			"m" (*v),
176 		  [count]		"er" (count)
177 		: "memory", "cc", "rax"
178 		  RSEQ_INJECT_CLOBBER
179 		: abort
180 #ifdef RSEQ_COMPARE_TWICE
181 		  , error1
182 #endif
183 	);
184 	rseq_after_asm_goto();
185 	return 0;
186 abort:
187 	rseq_after_asm_goto();
188 	RSEQ_INJECT_FAILED
189 	return -1;
190 #ifdef RSEQ_COMPARE_TWICE
191 error1:
192 	rseq_after_asm_goto();
193 	rseq_bug("cpu_id comparison failed");
194 #endif
195 }
196 
197 #define RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV
198 
199 /*
200  *   pval = *(ptr+off)
201  *  *pval += inc;
202  */
203 static inline __attribute__((always_inline))
RSEQ_TEMPLATE_IDENTIFIER(rseq_offset_deref_addv)204 int RSEQ_TEMPLATE_IDENTIFIER(rseq_offset_deref_addv)(intptr_t *ptr, long off, intptr_t inc, int cpu)
205 {
206 	RSEQ_INJECT_C(9)
207 
208 	__asm__ __volatile__ goto (
209 		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
210 #ifdef RSEQ_COMPARE_TWICE
211 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
212 #endif
213 		/* Start rseq by storing table entry pointer into rseq_cs. */
214 		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_ASM_TP_SEGMENT:RSEQ_CS_OFFSET(%[rseq_offset]))
215 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), 4f)
216 		RSEQ_INJECT_ASM(3)
217 #ifdef RSEQ_COMPARE_TWICE
218 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), %l[error1])
219 #endif
220 		/* get p+v */
221 		"movq %[ptr], %%rbx\n\t"
222 		"addq %[off], %%rbx\n\t"
223 		/* get pv */
224 		"movq (%%rbx), %%rcx\n\t"
225 		/* *pv += inc */
226 		"addq %[inc], (%%rcx)\n\t"
227 		"2:\n\t"
228 		RSEQ_INJECT_ASM(4)
229 		RSEQ_ASM_DEFINE_ABORT(4, "", abort)
230 		: /* gcc asm goto does not allow outputs */
231 		: [cpu_id]		"r" (cpu),
232 		  [rseq_offset]		"r" (rseq_offset),
233 		  /* final store input */
234 		  [ptr]			"m" (*ptr),
235 		  [off]			"er" (off),
236 		  [inc]			"er" (inc)
237 		: "memory", "cc", "rax", "rbx", "rcx"
238 		  RSEQ_INJECT_CLOBBER
239 		: abort
240 #ifdef RSEQ_COMPARE_TWICE
241 		  , error1
242 #endif
243 	);
244 	return 0;
245 abort:
246 	RSEQ_INJECT_FAILED
247 	return -1;
248 #ifdef RSEQ_COMPARE_TWICE
249 error1:
250 	rseq_bug("cpu_id comparison failed");
251 #endif
252 }
253 
254 static inline __attribute__((always_inline))
RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_cmpeqv_storev)255 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_cmpeqv_storev)(intptr_t *v, intptr_t expect,
256 			      intptr_t *v2, intptr_t expect2,
257 			      intptr_t newv, int cpu)
258 {
259 	RSEQ_INJECT_C(9)
260 
261 	__asm__ __volatile__ goto (
262 		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
263 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
264 #ifdef RSEQ_COMPARE_TWICE
265 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
266 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
267 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error3])
268 #endif
269 		/* Start rseq by storing table entry pointer into rseq_cs. */
270 		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_ASM_TP_SEGMENT:RSEQ_CS_OFFSET(%[rseq_offset]))
271 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), 4f)
272 		RSEQ_INJECT_ASM(3)
273 		"cmpq %[v], %[expect]\n\t"
274 		"jnz %l[cmpfail]\n\t"
275 		RSEQ_INJECT_ASM(4)
276 		"cmpq %[v2], %[expect2]\n\t"
277 		"jnz %l[cmpfail]\n\t"
278 		RSEQ_INJECT_ASM(5)
279 #ifdef RSEQ_COMPARE_TWICE
280 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), %l[error1])
281 		"cmpq %[v], %[expect]\n\t"
282 		"jnz %l[error2]\n\t"
283 		"cmpq %[v2], %[expect2]\n\t"
284 		"jnz %l[error3]\n\t"
285 #endif
286 		/* final store */
287 		"movq %[newv], %[v]\n\t"
288 		"2:\n\t"
289 		RSEQ_INJECT_ASM(6)
290 		RSEQ_ASM_DEFINE_ABORT(4, "", abort)
291 		: /* gcc asm goto does not allow outputs */
292 		: [cpu_id]		"r" (cpu),
293 		  [rseq_offset]		"r" (rseq_offset),
294 		  /* cmp2 input */
295 		  [v2]			"m" (*v2),
296 		  [expect2]		"r" (expect2),
297 		  /* final store input */
298 		  [v]			"m" (*v),
299 		  [expect]		"r" (expect),
300 		  [newv]		"r" (newv)
301 		: "memory", "cc", "rax"
302 		  RSEQ_INJECT_CLOBBER
303 		: abort, cmpfail
304 #ifdef RSEQ_COMPARE_TWICE
305 		  , error1, error2, error3
306 #endif
307 	);
308 	rseq_after_asm_goto();
309 	return 0;
310 abort:
311 	rseq_after_asm_goto();
312 	RSEQ_INJECT_FAILED
313 	return -1;
314 cmpfail:
315 	rseq_after_asm_goto();
316 	return 1;
317 #ifdef RSEQ_COMPARE_TWICE
318 error1:
319 	rseq_after_asm_goto();
320 	rseq_bug("cpu_id comparison failed");
321 error2:
322 	rseq_after_asm_goto();
323 	rseq_bug("1st expected value comparison failed");
324 error3:
325 	rseq_after_asm_goto();
326 	rseq_bug("2nd expected value comparison failed");
327 #endif
328 }
329 
330 #endif /* #if defined(RSEQ_TEMPLATE_MO_RELAXED) &&
331 	(defined(RSEQ_TEMPLATE_CPU_ID) || defined(RSEQ_TEMPLATE_MM_CID)) */
332 
333 #if (defined(RSEQ_TEMPLATE_MO_RELAXED) || defined(RSEQ_TEMPLATE_MO_RELEASE)) && \
334 	(defined(RSEQ_TEMPLATE_CPU_ID) || defined(RSEQ_TEMPLATE_MM_CID))
335 
336 static inline __attribute__((always_inline))
RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_trystorev_storev)337 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_trystorev_storev)(intptr_t *v, intptr_t expect,
338 				 intptr_t *v2, intptr_t newv2,
339 				 intptr_t newv, int cpu)
340 {
341 	RSEQ_INJECT_C(9)
342 
343 	__asm__ __volatile__ goto (
344 		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
345 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
346 #ifdef RSEQ_COMPARE_TWICE
347 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
348 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
349 #endif
350 		/* Start rseq by storing table entry pointer into rseq_cs. */
351 		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_ASM_TP_SEGMENT:RSEQ_CS_OFFSET(%[rseq_offset]))
352 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), 4f)
353 		RSEQ_INJECT_ASM(3)
354 		"cmpq %[v], %[expect]\n\t"
355 		"jnz %l[cmpfail]\n\t"
356 		RSEQ_INJECT_ASM(4)
357 #ifdef RSEQ_COMPARE_TWICE
358 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), %l[error1])
359 		"cmpq %[v], %[expect]\n\t"
360 		"jnz %l[error2]\n\t"
361 #endif
362 		/* try store */
363 		"movq %[newv2], %[v2]\n\t"
364 		RSEQ_INJECT_ASM(5)
365 		/* final store */
366 		"movq %[newv], %[v]\n\t"
367 		"2:\n\t"
368 		RSEQ_INJECT_ASM(6)
369 		RSEQ_ASM_DEFINE_ABORT(4, "", abort)
370 		: /* gcc asm goto does not allow outputs */
371 		: [cpu_id]		"r" (cpu),
372 		  [rseq_offset]		"r" (rseq_offset),
373 		  /* try store input */
374 		  [v2]			"m" (*v2),
375 		  [newv2]		"r" (newv2),
376 		  /* final store input */
377 		  [v]			"m" (*v),
378 		  [expect]		"r" (expect),
379 		  [newv]		"r" (newv)
380 		: "memory", "cc", "rax"
381 		  RSEQ_INJECT_CLOBBER
382 		: abort, cmpfail
383 #ifdef RSEQ_COMPARE_TWICE
384 		  , error1, error2
385 #endif
386 	);
387 	rseq_after_asm_goto();
388 	return 0;
389 abort:
390 	rseq_after_asm_goto();
391 	RSEQ_INJECT_FAILED
392 	return -1;
393 cmpfail:
394 	rseq_after_asm_goto();
395 	return 1;
396 #ifdef RSEQ_COMPARE_TWICE
397 error1:
398 	rseq_after_asm_goto();
399 	rseq_bug("cpu_id comparison failed");
400 error2:
401 	rseq_after_asm_goto();
402 	rseq_bug("expected value comparison failed");
403 #endif
404 }
405 
406 static inline __attribute__((always_inline))
RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_trymemcpy_storev)407 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_trymemcpy_storev)(intptr_t *v, intptr_t expect,
408 				 void *dst, void *src, size_t len,
409 				 intptr_t newv, int cpu)
410 {
411 	uint64_t rseq_scratch[3];
412 
413 	RSEQ_INJECT_C(9)
414 
415 	__asm__ __volatile__ goto (
416 		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
417 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
418 #ifdef RSEQ_COMPARE_TWICE
419 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
420 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
421 #endif
422 		"movq %[src], %[rseq_scratch0]\n\t"
423 		"movq %[dst], %[rseq_scratch1]\n\t"
424 		"movq %[len], %[rseq_scratch2]\n\t"
425 		/* Start rseq by storing table entry pointer into rseq_cs. */
426 		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_ASM_TP_SEGMENT:RSEQ_CS_OFFSET(%[rseq_offset]))
427 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), 4f)
428 		RSEQ_INJECT_ASM(3)
429 		"cmpq %[v], %[expect]\n\t"
430 		"jnz 5f\n\t"
431 		RSEQ_INJECT_ASM(4)
432 #ifdef RSEQ_COMPARE_TWICE
433 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), 6f)
434 		"cmpq %[v], %[expect]\n\t"
435 		"jnz 7f\n\t"
436 #endif
437 		/* try memcpy */
438 		"test %[len], %[len]\n\t" \
439 		"jz 333f\n\t" \
440 		"222:\n\t" \
441 		"movb (%[src]), %%al\n\t" \
442 		"movb %%al, (%[dst])\n\t" \
443 		"inc %[src]\n\t" \
444 		"inc %[dst]\n\t" \
445 		"dec %[len]\n\t" \
446 		"jnz 222b\n\t" \
447 		"333:\n\t" \
448 		RSEQ_INJECT_ASM(5)
449 		/* final store */
450 		"movq %[newv], %[v]\n\t"
451 		"2:\n\t"
452 		RSEQ_INJECT_ASM(6)
453 		/* teardown */
454 		"movq %[rseq_scratch2], %[len]\n\t"
455 		"movq %[rseq_scratch1], %[dst]\n\t"
456 		"movq %[rseq_scratch0], %[src]\n\t"
457 		RSEQ_ASM_DEFINE_ABORT(4,
458 			"movq %[rseq_scratch2], %[len]\n\t"
459 			"movq %[rseq_scratch1], %[dst]\n\t"
460 			"movq %[rseq_scratch0], %[src]\n\t",
461 			abort)
462 		RSEQ_ASM_DEFINE_CMPFAIL(5,
463 			"movq %[rseq_scratch2], %[len]\n\t"
464 			"movq %[rseq_scratch1], %[dst]\n\t"
465 			"movq %[rseq_scratch0], %[src]\n\t",
466 			cmpfail)
467 #ifdef RSEQ_COMPARE_TWICE
468 		RSEQ_ASM_DEFINE_CMPFAIL(6,
469 			"movq %[rseq_scratch2], %[len]\n\t"
470 			"movq %[rseq_scratch1], %[dst]\n\t"
471 			"movq %[rseq_scratch0], %[src]\n\t",
472 			error1)
473 		RSEQ_ASM_DEFINE_CMPFAIL(7,
474 			"movq %[rseq_scratch2], %[len]\n\t"
475 			"movq %[rseq_scratch1], %[dst]\n\t"
476 			"movq %[rseq_scratch0], %[src]\n\t",
477 			error2)
478 #endif
479 		: /* gcc asm goto does not allow outputs */
480 		: [cpu_id]		"r" (cpu),
481 		  [rseq_offset]		"r" (rseq_offset),
482 		  /* final store input */
483 		  [v]			"m" (*v),
484 		  [expect]		"r" (expect),
485 		  [newv]		"r" (newv),
486 		  /* try memcpy input */
487 		  [dst]			"r" (dst),
488 		  [src]			"r" (src),
489 		  [len]			"r" (len),
490 		  [rseq_scratch0]	"m" (rseq_scratch[0]),
491 		  [rseq_scratch1]	"m" (rseq_scratch[1]),
492 		  [rseq_scratch2]	"m" (rseq_scratch[2])
493 		: "memory", "cc", "rax"
494 		  RSEQ_INJECT_CLOBBER
495 		: abort, cmpfail
496 #ifdef RSEQ_COMPARE_TWICE
497 		  , error1, error2
498 #endif
499 	);
500 	rseq_after_asm_goto();
501 	return 0;
502 abort:
503 	rseq_after_asm_goto();
504 	RSEQ_INJECT_FAILED
505 	return -1;
506 cmpfail:
507 	rseq_after_asm_goto();
508 	return 1;
509 #ifdef RSEQ_COMPARE_TWICE
510 error1:
511 	rseq_after_asm_goto();
512 	rseq_bug("cpu_id comparison failed");
513 error2:
514 	rseq_after_asm_goto();
515 	rseq_bug("expected value comparison failed");
516 #endif
517 }
518 
519 #endif /* #if (defined(RSEQ_TEMPLATE_MO_RELAXED) || defined(RSEQ_TEMPLATE_MO_RELEASE)) &&
520 	(defined(RSEQ_TEMPLATE_CPU_ID) || defined(RSEQ_TEMPLATE_MM_CID)) */
521 
522 #elif defined(__i386__)
523 
524 #if defined(RSEQ_TEMPLATE_MO_RELAXED) && \
525 	(defined(RSEQ_TEMPLATE_CPU_ID) || defined(RSEQ_TEMPLATE_MM_CID))
526 
527 static inline __attribute__((always_inline))
RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_storev)528 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_storev)(intptr_t *v, intptr_t expect, intptr_t newv, int cpu)
529 {
530 	RSEQ_INJECT_C(9)
531 
532 	__asm__ __volatile__ goto (
533 		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
534 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
535 #ifdef RSEQ_COMPARE_TWICE
536 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
537 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
538 #endif
539 		/* Start rseq by storing table entry pointer into rseq_cs. */
540 		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_ASM_TP_SEGMENT:RSEQ_CS_OFFSET(%[rseq_offset]))
541 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), 4f)
542 		RSEQ_INJECT_ASM(3)
543 		"cmpl %[v], %[expect]\n\t"
544 		"jnz %l[cmpfail]\n\t"
545 		RSEQ_INJECT_ASM(4)
546 #ifdef RSEQ_COMPARE_TWICE
547 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), %l[error1])
548 		"cmpl %[v], %[expect]\n\t"
549 		"jnz %l[error2]\n\t"
550 #endif
551 		/* final store */
552 		"movl %[newv], %[v]\n\t"
553 		"2:\n\t"
554 		RSEQ_INJECT_ASM(5)
555 		RSEQ_ASM_DEFINE_ABORT(4, "", abort)
556 		: /* gcc asm goto does not allow outputs */
557 		: [cpu_id]		"r" (cpu),
558 		  [rseq_offset]		"r" (rseq_offset),
559 		  [v]			"m" (*v),
560 		  [expect]		"r" (expect),
561 		  [newv]		"r" (newv)
562 		: "memory", "cc", "eax"
563 		  RSEQ_INJECT_CLOBBER
564 		: abort, cmpfail
565 #ifdef RSEQ_COMPARE_TWICE
566 		  , error1, error2
567 #endif
568 	);
569 	rseq_after_asm_goto();
570 	return 0;
571 abort:
572 	rseq_after_asm_goto();
573 	RSEQ_INJECT_FAILED
574 	return -1;
575 cmpfail:
576 	rseq_after_asm_goto();
577 	return 1;
578 #ifdef RSEQ_COMPARE_TWICE
579 error1:
580 	rseq_after_asm_goto();
581 	rseq_bug("cpu_id comparison failed");
582 error2:
583 	rseq_after_asm_goto();
584 	rseq_bug("expected value comparison failed");
585 #endif
586 }
587 
588 /*
589  * Compare @v against @expectnot. When it does _not_ match, load @v
590  * into @load, and store the content of *@v + voffp into @v.
591  */
592 static inline __attribute__((always_inline))
RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpnev_storeoffp_load)593 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpnev_storeoffp_load)(intptr_t *v, intptr_t expectnot,
594 			       long voffp, intptr_t *load, int cpu)
595 {
596 	RSEQ_INJECT_C(9)
597 
598 	__asm__ __volatile__ goto (
599 		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
600 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
601 #ifdef RSEQ_COMPARE_TWICE
602 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
603 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
604 #endif
605 		/* Start rseq by storing table entry pointer into rseq_cs. */
606 		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_ASM_TP_SEGMENT:RSEQ_CS_OFFSET(%[rseq_offset]))
607 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), 4f)
608 		RSEQ_INJECT_ASM(3)
609 		"movl %[v], %%ebx\n\t"
610 		"cmpl %%ebx, %[expectnot]\n\t"
611 		"je %l[cmpfail]\n\t"
612 		RSEQ_INJECT_ASM(4)
613 #ifdef RSEQ_COMPARE_TWICE
614 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), %l[error1])
615 		"movl %[v], %%ebx\n\t"
616 		"cmpl %%ebx, %[expectnot]\n\t"
617 		"je %l[error2]\n\t"
618 #endif
619 		"movl %%ebx, %[load]\n\t"
620 		"addl %[voffp], %%ebx\n\t"
621 		"movl (%%ebx), %%ebx\n\t"
622 		/* final store */
623 		"movl %%ebx, %[v]\n\t"
624 		"2:\n\t"
625 		RSEQ_INJECT_ASM(5)
626 		RSEQ_ASM_DEFINE_ABORT(4, "", abort)
627 		: /* gcc asm goto does not allow outputs */
628 		: [cpu_id]		"r" (cpu),
629 		  [rseq_offset]		"r" (rseq_offset),
630 		  /* final store input */
631 		  [v]			"m" (*v),
632 		  [expectnot]		"r" (expectnot),
633 		  [voffp]		"ir" (voffp),
634 		  [load]		"m" (*load)
635 		: "memory", "cc", "eax", "ebx"
636 		  RSEQ_INJECT_CLOBBER
637 		: abort, cmpfail
638 #ifdef RSEQ_COMPARE_TWICE
639 		  , error1, error2
640 #endif
641 	);
642 	rseq_after_asm_goto();
643 	return 0;
644 abort:
645 	rseq_after_asm_goto();
646 	RSEQ_INJECT_FAILED
647 	return -1;
648 cmpfail:
649 	rseq_after_asm_goto();
650 	return 1;
651 #ifdef RSEQ_COMPARE_TWICE
652 error1:
653 	rseq_after_asm_goto();
654 	rseq_bug("cpu_id comparison failed");
655 error2:
656 	rseq_after_asm_goto();
657 	rseq_bug("expected value comparison failed");
658 #endif
659 }
660 
661 static inline __attribute__((always_inline))
RSEQ_TEMPLATE_IDENTIFIER(rseq_addv)662 int RSEQ_TEMPLATE_IDENTIFIER(rseq_addv)(intptr_t *v, intptr_t count, int cpu)
663 {
664 	RSEQ_INJECT_C(9)
665 
666 	__asm__ __volatile__ goto (
667 		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
668 #ifdef RSEQ_COMPARE_TWICE
669 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
670 #endif
671 		/* Start rseq by storing table entry pointer into rseq_cs. */
672 		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_ASM_TP_SEGMENT:RSEQ_CS_OFFSET(%[rseq_offset]))
673 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), 4f)
674 		RSEQ_INJECT_ASM(3)
675 #ifdef RSEQ_COMPARE_TWICE
676 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), %l[error1])
677 #endif
678 		/* final store */
679 		"addl %[count], %[v]\n\t"
680 		"2:\n\t"
681 		RSEQ_INJECT_ASM(4)
682 		RSEQ_ASM_DEFINE_ABORT(4, "", abort)
683 		: /* gcc asm goto does not allow outputs */
684 		: [cpu_id]		"r" (cpu),
685 		  [rseq_offset]		"r" (rseq_offset),
686 		  /* final store input */
687 		  [v]			"m" (*v),
688 		  [count]		"ir" (count)
689 		: "memory", "cc", "eax"
690 		  RSEQ_INJECT_CLOBBER
691 		: abort
692 #ifdef RSEQ_COMPARE_TWICE
693 		  , error1
694 #endif
695 	);
696 	rseq_after_asm_goto();
697 	return 0;
698 abort:
699 	rseq_after_asm_goto();
700 	RSEQ_INJECT_FAILED
701 	return -1;
702 #ifdef RSEQ_COMPARE_TWICE
703 error1:
704 	rseq_after_asm_goto();
705 	rseq_bug("cpu_id comparison failed");
706 #endif
707 }
708 
709 static inline __attribute__((always_inline))
RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_cmpeqv_storev)710 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_cmpeqv_storev)(intptr_t *v, intptr_t expect,
711 			      intptr_t *v2, intptr_t expect2,
712 			      intptr_t newv, int cpu)
713 {
714 	RSEQ_INJECT_C(9)
715 
716 	__asm__ __volatile__ goto (
717 		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
718 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
719 #ifdef RSEQ_COMPARE_TWICE
720 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
721 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
722 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error3])
723 #endif
724 		/* Start rseq by storing table entry pointer into rseq_cs. */
725 		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_ASM_TP_SEGMENT:RSEQ_CS_OFFSET(%[rseq_offset]))
726 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), 4f)
727 		RSEQ_INJECT_ASM(3)
728 		"cmpl %[v], %[expect]\n\t"
729 		"jnz %l[cmpfail]\n\t"
730 		RSEQ_INJECT_ASM(4)
731 		"cmpl %[expect2], %[v2]\n\t"
732 		"jnz %l[cmpfail]\n\t"
733 		RSEQ_INJECT_ASM(5)
734 #ifdef RSEQ_COMPARE_TWICE
735 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), %l[error1])
736 		"cmpl %[v], %[expect]\n\t"
737 		"jnz %l[error2]\n\t"
738 		"cmpl %[expect2], %[v2]\n\t"
739 		"jnz %l[error3]\n\t"
740 #endif
741 		"movl %[newv], %%eax\n\t"
742 		/* final store */
743 		"movl %%eax, %[v]\n\t"
744 		"2:\n\t"
745 		RSEQ_INJECT_ASM(6)
746 		RSEQ_ASM_DEFINE_ABORT(4, "", abort)
747 		: /* gcc asm goto does not allow outputs */
748 		: [cpu_id]		"r" (cpu),
749 		  [rseq_offset]		"r" (rseq_offset),
750 		  /* cmp2 input */
751 		  [v2]			"m" (*v2),
752 		  [expect2]		"r" (expect2),
753 		  /* final store input */
754 		  [v]			"m" (*v),
755 		  [expect]		"r" (expect),
756 		  [newv]		"m" (newv)
757 		: "memory", "cc", "eax"
758 		  RSEQ_INJECT_CLOBBER
759 		: abort, cmpfail
760 #ifdef RSEQ_COMPARE_TWICE
761 		  , error1, error2, error3
762 #endif
763 	);
764 	rseq_after_asm_goto();
765 	return 0;
766 abort:
767 	rseq_after_asm_goto();
768 	RSEQ_INJECT_FAILED
769 	return -1;
770 cmpfail:
771 	rseq_after_asm_goto();
772 	return 1;
773 #ifdef RSEQ_COMPARE_TWICE
774 error1:
775 	rseq_after_asm_goto();
776 	rseq_bug("cpu_id comparison failed");
777 error2:
778 	rseq_after_asm_goto();
779 	rseq_bug("1st expected value comparison failed");
780 error3:
781 	rseq_after_asm_goto();
782 	rseq_bug("2nd expected value comparison failed");
783 #endif
784 }
785 
786 #endif /* #if defined(RSEQ_TEMPLATE_MO_RELAXED) &&
787 	(defined(RSEQ_TEMPLATE_CPU_ID) || defined(RSEQ_TEMPLATE_MM_CID)) */
788 
789 #if (defined(RSEQ_TEMPLATE_MO_RELAXED) || defined(RSEQ_TEMPLATE_MO_RELEASE)) && \
790 	(defined(RSEQ_TEMPLATE_CPU_ID) || defined(RSEQ_TEMPLATE_MM_CID))
791 
792 static inline __attribute__((always_inline))
RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_trystorev_storev)793 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_trystorev_storev)(intptr_t *v, intptr_t expect,
794 					 intptr_t *v2, intptr_t newv2,
795 					 intptr_t newv, int cpu)
796 {
797 	RSEQ_INJECT_C(9)
798 
799 	__asm__ __volatile__ goto (
800 		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
801 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
802 #ifdef RSEQ_COMPARE_TWICE
803 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
804 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
805 #endif
806 		/* Start rseq by storing table entry pointer into rseq_cs. */
807 		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_ASM_TP_SEGMENT:RSEQ_CS_OFFSET(%[rseq_offset]))
808 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), 4f)
809 		RSEQ_INJECT_ASM(3)
810 		"movl %[expect], %%eax\n\t"
811 		"cmpl %[v], %%eax\n\t"
812 		"jnz %l[cmpfail]\n\t"
813 		RSEQ_INJECT_ASM(4)
814 #ifdef RSEQ_COMPARE_TWICE
815 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), %l[error1])
816 		"movl %[expect], %%eax\n\t"
817 		"cmpl %[v], %%eax\n\t"
818 		"jnz %l[error2]\n\t"
819 #endif
820 		/* try store */
821 		"movl %[newv2], %[v2]\n\t"
822 		RSEQ_INJECT_ASM(5)
823 #ifdef RSEQ_TEMPLATE_MO_RELEASE
824 		"lock; addl $0,-128(%%esp)\n\t"
825 #endif
826 		/* final store */
827 		"movl %[newv], %[v]\n\t"
828 		"2:\n\t"
829 		RSEQ_INJECT_ASM(6)
830 		RSEQ_ASM_DEFINE_ABORT(4, "", abort)
831 		: /* gcc asm goto does not allow outputs */
832 		: [cpu_id]		"r" (cpu),
833 		  [rseq_offset]		"r" (rseq_offset),
834 		  /* try store input */
835 		  [v2]			"m" (*v2),
836 		  [newv2]		"r" (newv2),
837 		  /* final store input */
838 		  [v]			"m" (*v),
839 		  [expect]		"m" (expect),
840 		  [newv]		"r" (newv)
841 		: "memory", "cc", "eax"
842 		  RSEQ_INJECT_CLOBBER
843 		: abort, cmpfail
844 #ifdef RSEQ_COMPARE_TWICE
845 		  , error1, error2
846 #endif
847 	);
848 	rseq_after_asm_goto();
849 	return 0;
850 abort:
851 	rseq_after_asm_goto();
852 	RSEQ_INJECT_FAILED
853 	return -1;
854 cmpfail:
855 	rseq_after_asm_goto();
856 	return 1;
857 #ifdef RSEQ_COMPARE_TWICE
858 error1:
859 	rseq_after_asm_goto();
860 	rseq_bug("cpu_id comparison failed");
861 error2:
862 	rseq_after_asm_goto();
863 	rseq_bug("expected value comparison failed");
864 #endif
865 
866 }
867 
868 /* TODO: implement a faster memcpy. */
869 static inline __attribute__((always_inline))
RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_trymemcpy_storev)870 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_trymemcpy_storev)(intptr_t *v, intptr_t expect,
871 					 void *dst, void *src, size_t len,
872 					 intptr_t newv, int cpu)
873 {
874 	uint32_t rseq_scratch[3];
875 
876 	RSEQ_INJECT_C(9)
877 
878 	__asm__ __volatile__ goto (
879 		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
880 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
881 #ifdef RSEQ_COMPARE_TWICE
882 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
883 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
884 #endif
885 		"movl %[src], %[rseq_scratch0]\n\t"
886 		"movl %[dst], %[rseq_scratch1]\n\t"
887 		"movl %[len], %[rseq_scratch2]\n\t"
888 		/* Start rseq by storing table entry pointer into rseq_cs. */
889 		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, RSEQ_ASM_TP_SEGMENT:RSEQ_CS_OFFSET(%[rseq_offset]))
890 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), 4f)
891 		RSEQ_INJECT_ASM(3)
892 		"movl %[expect], %%eax\n\t"
893 		"cmpl %%eax, %[v]\n\t"
894 		"jnz 5f\n\t"
895 		RSEQ_INJECT_ASM(4)
896 #ifdef RSEQ_COMPARE_TWICE
897 		RSEQ_ASM_CMP_CPU_ID(cpu_id, RSEQ_ASM_TP_SEGMENT:RSEQ_TEMPLATE_CPU_ID_OFFSET(%[rseq_offset]), 6f)
898 		"movl %[expect], %%eax\n\t"
899 		"cmpl %%eax, %[v]\n\t"
900 		"jnz 7f\n\t"
901 #endif
902 		/* try memcpy */
903 		"test %[len], %[len]\n\t" \
904 		"jz 333f\n\t" \
905 		"222:\n\t" \
906 		"movb (%[src]), %%al\n\t" \
907 		"movb %%al, (%[dst])\n\t" \
908 		"inc %[src]\n\t" \
909 		"inc %[dst]\n\t" \
910 		"dec %[len]\n\t" \
911 		"jnz 222b\n\t" \
912 		"333:\n\t" \
913 		RSEQ_INJECT_ASM(5)
914 #ifdef RSEQ_TEMPLATE_MO_RELEASE
915 		"lock; addl $0,-128(%%esp)\n\t"
916 #endif
917 		"movl %[newv], %%eax\n\t"
918 		/* final store */
919 		"movl %%eax, %[v]\n\t"
920 		"2:\n\t"
921 		RSEQ_INJECT_ASM(6)
922 		/* teardown */
923 		"movl %[rseq_scratch2], %[len]\n\t"
924 		"movl %[rseq_scratch1], %[dst]\n\t"
925 		"movl %[rseq_scratch0], %[src]\n\t"
926 		RSEQ_ASM_DEFINE_ABORT(4,
927 			"movl %[rseq_scratch2], %[len]\n\t"
928 			"movl %[rseq_scratch1], %[dst]\n\t"
929 			"movl %[rseq_scratch0], %[src]\n\t",
930 			abort)
931 		RSEQ_ASM_DEFINE_CMPFAIL(5,
932 			"movl %[rseq_scratch2], %[len]\n\t"
933 			"movl %[rseq_scratch1], %[dst]\n\t"
934 			"movl %[rseq_scratch0], %[src]\n\t",
935 			cmpfail)
936 #ifdef RSEQ_COMPARE_TWICE
937 		RSEQ_ASM_DEFINE_CMPFAIL(6,
938 			"movl %[rseq_scratch2], %[len]\n\t"
939 			"movl %[rseq_scratch1], %[dst]\n\t"
940 			"movl %[rseq_scratch0], %[src]\n\t",
941 			error1)
942 		RSEQ_ASM_DEFINE_CMPFAIL(7,
943 			"movl %[rseq_scratch2], %[len]\n\t"
944 			"movl %[rseq_scratch1], %[dst]\n\t"
945 			"movl %[rseq_scratch0], %[src]\n\t",
946 			error2)
947 #endif
948 		: /* gcc asm goto does not allow outputs */
949 		: [cpu_id]		"r" (cpu),
950 		  [rseq_offset]		"r" (rseq_offset),
951 		  /* final store input */
952 		  [v]			"m" (*v),
953 		  [expect]		"m" (expect),
954 		  [newv]		"m" (newv),
955 		  /* try memcpy input */
956 		  [dst]			"r" (dst),
957 		  [src]			"r" (src),
958 		  [len]			"r" (len),
959 		  [rseq_scratch0]	"m" (rseq_scratch[0]),
960 		  [rseq_scratch1]	"m" (rseq_scratch[1]),
961 		  [rseq_scratch2]	"m" (rseq_scratch[2])
962 		: "memory", "cc", "eax"
963 		  RSEQ_INJECT_CLOBBER
964 		: abort, cmpfail
965 #ifdef RSEQ_COMPARE_TWICE
966 		  , error1, error2
967 #endif
968 	);
969 	rseq_after_asm_goto();
970 	return 0;
971 abort:
972 	rseq_after_asm_goto();
973 	RSEQ_INJECT_FAILED
974 	return -1;
975 cmpfail:
976 	rseq_after_asm_goto();
977 	return 1;
978 #ifdef RSEQ_COMPARE_TWICE
979 error1:
980 	rseq_after_asm_goto();
981 	rseq_bug("cpu_id comparison failed");
982 error2:
983 	rseq_after_asm_goto();
984 	rseq_bug("expected value comparison failed");
985 #endif
986 }
987 
988 #endif /* #if (defined(RSEQ_TEMPLATE_MO_RELAXED) || defined(RSEQ_TEMPLATE_MO_RELEASE)) &&
989 	(defined(RSEQ_TEMPLATE_CPU_ID) || defined(RSEQ_TEMPLATE_MM_CID)) */
990 
991 #endif
992 
993 #include "rseq-bits-reset.h"
994