Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
13static inline __attribute__((always_inline))
14int 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;
60abort:
61 RSEQ_INJECT_FAILED
62 return -1;
63cmpfail:
64 return 1;
65#ifdef RSEQ_COMPARE_TWICE
66error1:
67 rseq_bug("cpu_id comparison failed");
68error2:
69 rseq_bug("expected value comparison failed");
70#endif
71}
72
73static inline __attribute__((always_inline))
74int 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;
126abort:
127 RSEQ_INJECT_FAILED
128 return -1;
129cmpfail:
130 return 1;
131#ifdef RSEQ_COMPARE_TWICE
132error1:
133 rseq_bug("cpu_id comparison failed");
134error2:
135 rseq_bug("expected value comparison failed");
136#endif
137}
138
139static inline __attribute__((always_inline))
140int 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;
180abort:
181 RSEQ_INJECT_FAILED
182 return -1;
183#ifdef RSEQ_COMPARE_TWICE
184error1:
185 rseq_bug("cpu_id comparison failed");
186#endif
187}
188
189static inline __attribute__((always_inline))
190int 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;
248abort:
249 RSEQ_INJECT_FAILED
250 return -1;
251cmpfail:
252 return 1;
253#ifdef RSEQ_COMPARE_TWICE
254error1:
255 rseq_bug("cpu_id comparison failed");
256error2:
257 rseq_bug("1st expected value comparison failed");
258error3:
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
269static inline __attribute__((always_inline))
270int 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;
328abort:
329 RSEQ_INJECT_FAILED
330 return -1;
331cmpfail:
332 return 1;
333#ifdef RSEQ_COMPARE_TWICE
334error1:
335 rseq_bug("cpu_id comparison failed");
336error2:
337 rseq_bug("expected value comparison failed");
338#endif
339}
340
341static inline __attribute__((always_inline))
342int 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;
446abort:
447 RSEQ_INJECT_FAILED
448 return -1;
449cmpfail:
450 return 1;
451#ifdef RSEQ_COMPARE_TWICE
452error1:
453 rseq_bug("cpu_id comparison failed");
454error2:
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"