1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
2 /*
3  * Author: Paul Burton <paul.burton@mips.com>
4  * (C) Copyright 2018 MIPS Tech LLC
5  * (C) Copyright 2016-2022 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
6  */
7 
8 #include "rseq-bits-template.h"
9 
10 #if defined(RSEQ_TEMPLATE_MO_RELAXED) && \
11 	(defined(RSEQ_TEMPLATE_CPU_ID) || defined(RSEQ_TEMPLATE_MM_CID))
12 
13 static inline __attribute__((always_inline))
14 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_storev)(intptr_t *v, intptr_t expect, intptr_t newv, int cpu)
15 {
16 	RSEQ_INJECT_C(9)
17 
18 	__asm__ __volatile__ goto (
19 		RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
20 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
21 #ifdef RSEQ_COMPARE_TWICE
22 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
23 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
24 #endif
25 		/* Start rseq by storing table entry pointer into rseq_cs. */
26 		RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
27 		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
28 		RSEQ_INJECT_ASM(3)
29 		LONG_L " $4, %[v]\n\t"
30 		"bne $4, %[expect], %l[cmpfail]\n\t"
31 		RSEQ_INJECT_ASM(4)
32 #ifdef RSEQ_COMPARE_TWICE
33 		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
34 		LONG_L " $4, %[v]\n\t"
35 		"bne $4, %[expect], %l[error2]\n\t"
36 #endif
37 		/* final store */
38 		LONG_S " %[newv], %[v]\n\t"
39 		"2:\n\t"
40 		RSEQ_INJECT_ASM(5)
41 		"b 5f\n\t"
42 		RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
43 		"5:\n\t"
44 		: /* gcc asm goto does not allow outputs */
45 		: [cpu_id]		"r" (cpu),
46 		  [current_cpu_id]	"m" (rseq_get_abi()->RSEQ_TEMPLATE_CPU_ID_FIELD),
47 		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
48 		  [v]			"m" (*v),
49 		  [expect]		"r" (expect),
50 		  [newv]		"r" (newv)
51 		  RSEQ_INJECT_INPUT
52 		: "$4", "memory"
53 		  RSEQ_INJECT_CLOBBER
54 		: abort, cmpfail
55 #ifdef RSEQ_COMPARE_TWICE
56 		  , error1, error2
57 #endif
58 	);
59 	return 0;
60 abort:
61 	RSEQ_INJECT_FAILED
62 	return -1;
63 cmpfail:
64 	return 1;
65 #ifdef RSEQ_COMPARE_TWICE
66 error1:
67 	rseq_bug("cpu_id comparison failed");
68 error2:
69 	rseq_bug("expected value comparison failed");
70 #endif
71 }
72 
73 static inline __attribute__((always_inline))
74 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpnev_storeoffp_load)(intptr_t *v, intptr_t expectnot,
75 			       long voffp, intptr_t *load, int cpu)
76 {
77 	RSEQ_INJECT_C(9)
78 
79 	__asm__ __volatile__ goto (
80 		RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
81 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
82 #ifdef RSEQ_COMPARE_TWICE
83 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
84 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
85 #endif
86 		/* Start rseq by storing table entry pointer into rseq_cs. */
87 		RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
88 		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
89 		RSEQ_INJECT_ASM(3)
90 		LONG_L " $4, %[v]\n\t"
91 		"beq $4, %[expectnot], %l[cmpfail]\n\t"
92 		RSEQ_INJECT_ASM(4)
93 #ifdef RSEQ_COMPARE_TWICE
94 		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
95 		LONG_L " $4, %[v]\n\t"
96 		"beq $4, %[expectnot], %l[error2]\n\t"
97 #endif
98 		LONG_S " $4, %[load]\n\t"
99 		LONG_ADDI " $4, %[voffp]\n\t"
100 		LONG_L " $4, 0($4)\n\t"
101 		/* final store */
102 		LONG_S " $4, %[v]\n\t"
103 		"2:\n\t"
104 		RSEQ_INJECT_ASM(5)
105 		"b 5f\n\t"
106 		RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
107 		"5:\n\t"
108 		: /* gcc asm goto does not allow outputs */
109 		: [cpu_id]		"r" (cpu),
110 		  [current_cpu_id]	"m" (rseq_get_abi()->RSEQ_TEMPLATE_CPU_ID_FIELD),
111 		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
112 		  /* final store input */
113 		  [v]			"m" (*v),
114 		  [expectnot]		"r" (expectnot),
115 		  [voffp]		"Ir" (voffp),
116 		  [load]		"m" (*load)
117 		  RSEQ_INJECT_INPUT
118 		: "$4", "memory"
119 		  RSEQ_INJECT_CLOBBER
120 		: abort, cmpfail
121 #ifdef RSEQ_COMPARE_TWICE
122 		  , error1, error2
123 #endif
124 	);
125 	return 0;
126 abort:
127 	RSEQ_INJECT_FAILED
128 	return -1;
129 cmpfail:
130 	return 1;
131 #ifdef RSEQ_COMPARE_TWICE
132 error1:
133 	rseq_bug("cpu_id comparison failed");
134 error2:
135 	rseq_bug("expected value comparison failed");
136 #endif
137 }
138 
139 static inline __attribute__((always_inline))
140 int RSEQ_TEMPLATE_IDENTIFIER(rseq_addv)(intptr_t *v, intptr_t count, int cpu)
141 {
142 	RSEQ_INJECT_C(9)
143 
144 	__asm__ __volatile__ goto (
145 		RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
146 #ifdef RSEQ_COMPARE_TWICE
147 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
148 #endif
149 		/* Start rseq by storing table entry pointer into rseq_cs. */
150 		RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
151 		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
152 		RSEQ_INJECT_ASM(3)
153 #ifdef RSEQ_COMPARE_TWICE
154 		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
155 #endif
156 		LONG_L " $4, %[v]\n\t"
157 		LONG_ADDI " $4, %[count]\n\t"
158 		/* final store */
159 		LONG_S " $4, %[v]\n\t"
160 		"2:\n\t"
161 		RSEQ_INJECT_ASM(4)
162 		"b 5f\n\t"
163 		RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
164 		"5:\n\t"
165 		: /* gcc asm goto does not allow outputs */
166 		: [cpu_id]		"r" (cpu),
167 		  [current_cpu_id]	"m" (rseq_get_abi()->RSEQ_TEMPLATE_CPU_ID_FIELD),
168 		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
169 		  [v]			"m" (*v),
170 		  [count]		"Ir" (count)
171 		  RSEQ_INJECT_INPUT
172 		: "$4", "memory"
173 		  RSEQ_INJECT_CLOBBER
174 		: abort
175 #ifdef RSEQ_COMPARE_TWICE
176 		  , error1
177 #endif
178 	);
179 	return 0;
180 abort:
181 	RSEQ_INJECT_FAILED
182 	return -1;
183 #ifdef RSEQ_COMPARE_TWICE
184 error1:
185 	rseq_bug("cpu_id comparison failed");
186 #endif
187 }
188 
189 static inline __attribute__((always_inline))
190 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_cmpeqv_storev)(intptr_t *v, intptr_t expect,
191 			      intptr_t *v2, intptr_t expect2,
192 			      intptr_t newv, int cpu)
193 {
194 	RSEQ_INJECT_C(9)
195 
196 	__asm__ __volatile__ goto (
197 		RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
198 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
199 #ifdef RSEQ_COMPARE_TWICE
200 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
201 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
202 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error3])
203 #endif
204 		/* Start rseq by storing table entry pointer into rseq_cs. */
205 		RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
206 		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
207 		RSEQ_INJECT_ASM(3)
208 		LONG_L " $4, %[v]\n\t"
209 		"bne $4, %[expect], %l[cmpfail]\n\t"
210 		RSEQ_INJECT_ASM(4)
211 		LONG_L " $4, %[v2]\n\t"
212 		"bne $4, %[expect2], %l[cmpfail]\n\t"
213 		RSEQ_INJECT_ASM(5)
214 #ifdef RSEQ_COMPARE_TWICE
215 		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
216 		LONG_L " $4, %[v]\n\t"
217 		"bne $4, %[expect], %l[error2]\n\t"
218 		LONG_L " $4, %[v2]\n\t"
219 		"bne $4, %[expect2], %l[error3]\n\t"
220 #endif
221 		/* final store */
222 		LONG_S " %[newv], %[v]\n\t"
223 		"2:\n\t"
224 		RSEQ_INJECT_ASM(6)
225 		"b 5f\n\t"
226 		RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
227 		"5:\n\t"
228 		: /* gcc asm goto does not allow outputs */
229 		: [cpu_id]		"r" (cpu),
230 		  [current_cpu_id]	"m" (rseq_get_abi()->RSEQ_TEMPLATE_CPU_ID_FIELD),
231 		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
232 		  /* cmp2 input */
233 		  [v2]			"m" (*v2),
234 		  [expect2]		"r" (expect2),
235 		  /* final store input */
236 		  [v]			"m" (*v),
237 		  [expect]		"r" (expect),
238 		  [newv]		"r" (newv)
239 		  RSEQ_INJECT_INPUT
240 		: "$4", "memory"
241 		  RSEQ_INJECT_CLOBBER
242 		: abort, cmpfail
243 #ifdef RSEQ_COMPARE_TWICE
244 		  , error1, error2, error3
245 #endif
246 	);
247 	return 0;
248 abort:
249 	RSEQ_INJECT_FAILED
250 	return -1;
251 cmpfail:
252 	return 1;
253 #ifdef RSEQ_COMPARE_TWICE
254 error1:
255 	rseq_bug("cpu_id comparison failed");
256 error2:
257 	rseq_bug("1st expected value comparison failed");
258 error3:
259 	rseq_bug("2nd expected value comparison failed");
260 #endif
261 }
262 
263 #endif /* #if defined(RSEQ_TEMPLATE_MO_RELAXED) &&
264 	(defined(RSEQ_TEMPLATE_CPU_ID) || defined(RSEQ_TEMPLATE_MM_CID)) */
265 
266 #if (defined(RSEQ_TEMPLATE_MO_RELAXED) || defined(RSEQ_TEMPLATE_MO_RELEASE)) && \
267 	(defined(RSEQ_TEMPLATE_CPU_ID) || defined(RSEQ_TEMPLATE_MM_CID))
268 
269 static inline __attribute__((always_inline))
270 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_trystorev_storev)(intptr_t *v, intptr_t expect,
271 				 intptr_t *v2, intptr_t newv2,
272 				 intptr_t newv, int cpu)
273 {
274 	RSEQ_INJECT_C(9)
275 
276 	__asm__ __volatile__ goto (
277 		RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
278 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
279 #ifdef RSEQ_COMPARE_TWICE
280 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
281 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
282 #endif
283 		/* Start rseq by storing table entry pointer into rseq_cs. */
284 		RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
285 		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
286 		RSEQ_INJECT_ASM(3)
287 		LONG_L " $4, %[v]\n\t"
288 		"bne $4, %[expect], %l[cmpfail]\n\t"
289 		RSEQ_INJECT_ASM(4)
290 #ifdef RSEQ_COMPARE_TWICE
291 		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
292 		LONG_L " $4, %[v]\n\t"
293 		"bne $4, %[expect], %l[error2]\n\t"
294 #endif
295 		/* try store */
296 		LONG_S " %[newv2], %[v2]\n\t"
297 		RSEQ_INJECT_ASM(5)
298 #ifdef RSEQ_TEMPLATE_MO_RELEASE
299 		"sync\n\t"	/* full sync provides store-release */
300 #endif
301 		/* final store */
302 		LONG_S " %[newv], %[v]\n\t"
303 		"2:\n\t"
304 		RSEQ_INJECT_ASM(6)
305 		"b 5f\n\t"
306 		RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
307 		"5:\n\t"
308 		: /* gcc asm goto does not allow outputs */
309 		: [cpu_id]		"r" (cpu),
310 		  [current_cpu_id]	"m" (rseq_get_abi()->RSEQ_TEMPLATE_CPU_ID_FIELD),
311 		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
312 		  /* try store input */
313 		  [v2]			"m" (*v2),
314 		  [newv2]		"r" (newv2),
315 		  /* final store input */
316 		  [v]			"m" (*v),
317 		  [expect]		"r" (expect),
318 		  [newv]		"r" (newv)
319 		  RSEQ_INJECT_INPUT
320 		: "$4", "memory"
321 		  RSEQ_INJECT_CLOBBER
322 		: abort, cmpfail
323 #ifdef RSEQ_COMPARE_TWICE
324 		  , error1, error2
325 #endif
326 	);
327 	return 0;
328 abort:
329 	RSEQ_INJECT_FAILED
330 	return -1;
331 cmpfail:
332 	return 1;
333 #ifdef RSEQ_COMPARE_TWICE
334 error1:
335 	rseq_bug("cpu_id comparison failed");
336 error2:
337 	rseq_bug("expected value comparison failed");
338 #endif
339 }
340 
341 static inline __attribute__((always_inline))
342 int RSEQ_TEMPLATE_IDENTIFIER(rseq_cmpeqv_trymemcpy_storev)(intptr_t *v, intptr_t expect,
343 				 void *dst, void *src, size_t len,
344 				 intptr_t newv, int cpu)
345 {
346 	uintptr_t rseq_scratch[3];
347 
348 	RSEQ_INJECT_C(9)
349 
350 	__asm__ __volatile__ goto (
351 		RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
352 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
353 #ifdef RSEQ_COMPARE_TWICE
354 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
355 		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
356 #endif
357 		LONG_S " %[src], %[rseq_scratch0]\n\t"
358 		LONG_S "  %[dst], %[rseq_scratch1]\n\t"
359 		LONG_S " %[len], %[rseq_scratch2]\n\t"
360 		/* Start rseq by storing table entry pointer into rseq_cs. */
361 		RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
362 		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
363 		RSEQ_INJECT_ASM(3)
364 		LONG_L " $4, %[v]\n\t"
365 		"bne $4, %[expect], 5f\n\t"
366 		RSEQ_INJECT_ASM(4)
367 #ifdef RSEQ_COMPARE_TWICE
368 		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f)
369 		LONG_L " $4, %[v]\n\t"
370 		"bne $4, %[expect], 7f\n\t"
371 #endif
372 		/* try memcpy */
373 		"beqz %[len], 333f\n\t" \
374 		"222:\n\t" \
375 		"lb   $4, 0(%[src])\n\t" \
376 		"sb   $4, 0(%[dst])\n\t" \
377 		LONG_ADDI " %[src], 1\n\t" \
378 		LONG_ADDI " %[dst], 1\n\t" \
379 		LONG_ADDI " %[len], -1\n\t" \
380 		"bnez %[len], 222b\n\t" \
381 		"333:\n\t" \
382 		RSEQ_INJECT_ASM(5)
383 #ifdef RSEQ_TEMPLATE_MO_RELEASE
384 		"sync\n\t"	/* full sync provides store-release */
385 #endif
386 		/* final store */
387 		LONG_S " %[newv], %[v]\n\t"
388 		"2:\n\t"
389 		RSEQ_INJECT_ASM(6)
390 		/* teardown */
391 		LONG_L " %[len], %[rseq_scratch2]\n\t"
392 		LONG_L " %[dst], %[rseq_scratch1]\n\t"
393 		LONG_L " %[src], %[rseq_scratch0]\n\t"
394 		"b 8f\n\t"
395 		RSEQ_ASM_DEFINE_ABORT(3, 4,
396 				      /* teardown */
397 				      LONG_L " %[len], %[rseq_scratch2]\n\t"
398 				      LONG_L " %[dst], %[rseq_scratch1]\n\t"
399 				      LONG_L " %[src], %[rseq_scratch0]\n\t",
400 				      abort, 1b, 2b, 4f)
401 		RSEQ_ASM_DEFINE_CMPFAIL(5,
402 					/* teardown */
403 					LONG_L " %[len], %[rseq_scratch2]\n\t"
404 					LONG_L " %[dst], %[rseq_scratch1]\n\t"
405 					LONG_L " %[src], %[rseq_scratch0]\n\t",
406 					cmpfail)
407 #ifdef RSEQ_COMPARE_TWICE
408 		RSEQ_ASM_DEFINE_CMPFAIL(6,
409 					/* teardown */
410 					LONG_L " %[len], %[rseq_scratch2]\n\t"
411 					LONG_L " %[dst], %[rseq_scratch1]\n\t"
412 					LONG_L " %[src], %[rseq_scratch0]\n\t",
413 					error1)
414 		RSEQ_ASM_DEFINE_CMPFAIL(7,
415 					/* teardown */
416 					LONG_L " %[len], %[rseq_scratch2]\n\t"
417 					LONG_L " %[dst], %[rseq_scratch1]\n\t"
418 					LONG_L " %[src], %[rseq_scratch0]\n\t",
419 					error2)
420 #endif
421 		"8:\n\t"
422 		: /* gcc asm goto does not allow outputs */
423 		: [cpu_id]		"r" (cpu),
424 		  [current_cpu_id]	"m" (rseq_get_abi()->RSEQ_TEMPLATE_CPU_ID_FIELD),
425 		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
426 		  /* final store input */
427 		  [v]			"m" (*v),
428 		  [expect]		"r" (expect),
429 		  [newv]		"r" (newv),
430 		  /* try memcpy input */
431 		  [dst]			"r" (dst),
432 		  [src]			"r" (src),
433 		  [len]			"r" (len),
434 		  [rseq_scratch0]	"m" (rseq_scratch[0]),
435 		  [rseq_scratch1]	"m" (rseq_scratch[1]),
436 		  [rseq_scratch2]	"m" (rseq_scratch[2])
437 		  RSEQ_INJECT_INPUT
438 		: "$4", "memory"
439 		  RSEQ_INJECT_CLOBBER
440 		: abort, cmpfail
441 #ifdef RSEQ_COMPARE_TWICE
442 		  , error1, error2
443 #endif
444 	);
445 	return 0;
446 abort:
447 	RSEQ_INJECT_FAILED
448 	return -1;
449 cmpfail:
450 	return 1;
451 #ifdef RSEQ_COMPARE_TWICE
452 error1:
453 	rseq_bug("cpu_id comparison failed");
454 error2:
455 	rseq_bug("expected value comparison failed");
456 #endif
457 }
458 
459 #endif /* #if (defined(RSEQ_TEMPLATE_MO_RELAXED) || defined(RSEQ_TEMPLATE_MO_RELEASE)) &&
460 	(defined(RSEQ_TEMPLATE_CPU_ID) || defined(RSEQ_TEMPLATE_MM_CID)) */
461 
462 #include "rseq-bits-reset.h"
463