1 /* SPDX-License-Identifier: MIT */
2 /* SPDX-FileCopyrightText: 2016-2018 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> */
8 #include "rseq/arch/templates/bits.h"
11 * Refer to rseq/pseudocode.h for documentation and pseudo-code of the
12 * rseq critical section helpers.
14 #include "rseq/pseudocode.h"
16 #if defined(RSEQ_TEMPLATE_MO_RELAXED) && \
17 (defined(RSEQ_TEMPLATE_INDEX_CPU_ID) || defined(RSEQ_TEMPLATE_INDEX_MM_CID))
19 static inline __attribute__((always_inline
))
20 int RSEQ_TEMPLATE_IDENTIFIER(rseq_load_cbne_store__ptr
)(intptr_t *v
, intptr_t expect
, intptr_t newv
, int cpu
)
24 __asm__ __volatile__
goto (
25 RSEQ_ASM_DEFINE_TABLE(9, 1f
, 2f
, 4f
) /* start, commit, abort */
26 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[ne
])
27 #ifdef RSEQ_COMPARE_TWICE
28 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
29 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
31 /* Start rseq by storing table entry pointer into rseq_cs. */
32 RSEQ_ASM_STORE_RSEQ_CS(1, 3f
, rseq_cs
)
33 RSEQ_ASM_CBNE_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
36 "cmp %[expect], r0\n\t"
39 #ifdef RSEQ_COMPARE_TWICE
40 RSEQ_ASM_CBNE_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
42 "cmp %[expect], r0\n\t"
46 "str %[newv], %[v]\n\t"
50 RSEQ_ASM_DEFINE_ABORT(4, "", abort
, 3, 1b
, 2b
, 4f
)
52 : /* gcc asm goto does not allow outputs */
54 [current_cpu_id
] "m" (rseq_get_abi()->RSEQ_TEMPLATE_INDEX_CPU_ID_FIELD
),
55 [rseq_cs
] "m" (rseq_get_abi()->rseq_cs
.arch
.ptr
),
57 [expect
] "r" (expect
),
60 : "r0", "memory", "cc"
63 #ifdef RSEQ_COMPARE_TWICE
67 rseq_after_asm_goto();
70 rseq_after_asm_goto();
74 rseq_after_asm_goto();
76 #ifdef RSEQ_COMPARE_TWICE
78 rseq_after_asm_goto();
79 rseq_bug("cpu_id comparison failed");
81 rseq_after_asm_goto();
82 rseq_bug("expected value comparison failed");
86 static inline __attribute__((always_inline
))
87 int RSEQ_TEMPLATE_IDENTIFIER(rseq_load_cbeq_store_add_load_store__ptr
)(intptr_t *v
, intptr_t expectnot
,
88 long voffp
, intptr_t *load
, int cpu
)
92 __asm__ __volatile__
goto (
93 RSEQ_ASM_DEFINE_TABLE(9, 1f
, 2f
, 4f
) /* start, commit, abort */
94 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[eq
])
95 #ifdef RSEQ_COMPARE_TWICE
96 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
97 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
99 /* Start rseq by storing table entry pointer into rseq_cs. */
100 RSEQ_ASM_STORE_RSEQ_CS(1, 3f
, rseq_cs
)
101 RSEQ_ASM_CBNE_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
104 "cmp %[expectnot], r0\n\t"
107 #ifdef RSEQ_COMPARE_TWICE
108 RSEQ_ASM_CBNE_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
110 "cmp %[expectnot], r0\n\t"
113 "str r0, %[load]\n\t"
114 "add r0, %[voffp]\n\t"
121 RSEQ_ASM_DEFINE_ABORT(4, "", abort
, 3, 1b
, 2b
, 4f
)
123 : /* gcc asm goto does not allow outputs */
124 : [cpu_id
] "r" (cpu
),
125 [current_cpu_id
] "m" (rseq_get_abi()->RSEQ_TEMPLATE_INDEX_CPU_ID_FIELD
),
126 [rseq_cs
] "m" (rseq_get_abi()->rseq_cs
.arch
.ptr
),
127 /* final store input */
129 [expectnot
] "r" (expectnot
),
130 [voffp
] "Ir" (voffp
),
133 : "r0", "memory", "cc"
136 #ifdef RSEQ_COMPARE_TWICE
140 rseq_after_asm_goto();
143 rseq_after_asm_goto();
147 rseq_after_asm_goto();
149 #ifdef RSEQ_COMPARE_TWICE
151 rseq_after_asm_goto();
152 rseq_bug("cpu_id comparison failed");
154 rseq_after_asm_goto();
155 rseq_bug("expected value comparison failed");
159 static inline __attribute__((always_inline
))
160 int RSEQ_TEMPLATE_IDENTIFIER(rseq_load_add_store__ptr
)(intptr_t *v
, intptr_t count
, int cpu
)
164 __asm__ __volatile__
goto (
165 RSEQ_ASM_DEFINE_TABLE(9, 1f
, 2f
, 4f
) /* start, commit, abort */
166 #ifdef RSEQ_COMPARE_TWICE
167 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
169 /* Start rseq by storing table entry pointer into rseq_cs. */
170 RSEQ_ASM_STORE_RSEQ_CS(1, 3f
, rseq_cs
)
171 RSEQ_ASM_CBNE_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
173 #ifdef RSEQ_COMPARE_TWICE
174 RSEQ_ASM_CBNE_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
177 "add r0, %[count]\n\t"
183 RSEQ_ASM_DEFINE_ABORT(4, "", abort
, 3, 1b
, 2b
, 4f
)
185 : /* gcc asm goto does not allow outputs */
186 : [cpu_id
] "r" (cpu
),
187 [current_cpu_id
] "m" (rseq_get_abi()->RSEQ_TEMPLATE_INDEX_CPU_ID_FIELD
),
188 [rseq_cs
] "m" (rseq_get_abi()->rseq_cs
.arch
.ptr
),
192 : "r0", "memory", "cc"
195 #ifdef RSEQ_COMPARE_TWICE
199 rseq_after_asm_goto();
202 rseq_after_asm_goto();
205 #ifdef RSEQ_COMPARE_TWICE
207 rseq_after_asm_goto();
208 rseq_bug("cpu_id comparison failed");
212 static inline __attribute__((always_inline
))
213 int RSEQ_TEMPLATE_IDENTIFIER(rseq_load_cbne_load_cbne_store__ptr
)(intptr_t *v
, intptr_t expect
,
214 intptr_t *v2
, intptr_t expect2
,
215 intptr_t newv
, int cpu
)
219 __asm__ __volatile__
goto (
220 RSEQ_ASM_DEFINE_TABLE(9, 1f
, 2f
, 4f
) /* start, commit, abort */
221 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[ne
])
222 #ifdef RSEQ_COMPARE_TWICE
223 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
224 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
225 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error3
])
227 /* Start rseq by storing table entry pointer into rseq_cs. */
228 RSEQ_ASM_STORE_RSEQ_CS(1, 3f
, rseq_cs
)
229 RSEQ_ASM_CBNE_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
232 "cmp %[expect], r0\n\t"
236 "cmp %[expect2], r0\n\t"
239 #ifdef RSEQ_COMPARE_TWICE
240 RSEQ_ASM_CBNE_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
242 "cmp %[expect], r0\n\t"
245 "cmp %[expect2], r0\n\t"
249 "str %[newv], %[v]\n\t"
253 RSEQ_ASM_DEFINE_ABORT(4, "", abort
, 3, 1b
, 2b
, 4f
)
255 : /* gcc asm goto does not allow outputs */
256 : [cpu_id
] "r" (cpu
),
257 [current_cpu_id
] "m" (rseq_get_abi()->RSEQ_TEMPLATE_INDEX_CPU_ID_FIELD
),
258 [rseq_cs
] "m" (rseq_get_abi()->rseq_cs
.arch
.ptr
),
261 [expect2
] "r" (expect2
),
262 /* final store input */
264 [expect
] "r" (expect
),
267 : "r0", "memory", "cc"
270 #ifdef RSEQ_COMPARE_TWICE
271 , error1
, error2
, error3
274 rseq_after_asm_goto();
277 rseq_after_asm_goto();
281 rseq_after_asm_goto();
283 #ifdef RSEQ_COMPARE_TWICE
285 rseq_after_asm_goto();
286 rseq_bug("cpu_id comparison failed");
288 rseq_after_asm_goto();
289 rseq_bug("1st expected value comparison failed");
291 rseq_after_asm_goto();
292 rseq_bug("2nd expected value comparison failed");
296 #endif /* #if defined(RSEQ_TEMPLATE_MO_RELAXED) &&
297 (defined(RSEQ_TEMPLATE_INDEX_CPU_ID) || defined(RSEQ_TEMPLATE_INDEX_MM_CID)) */
299 #if (defined(RSEQ_TEMPLATE_MO_RELAXED) || defined(RSEQ_TEMPLATE_MO_RELEASE)) && \
300 (defined(RSEQ_TEMPLATE_INDEX_CPU_ID) || defined(RSEQ_TEMPLATE_INDEX_MM_CID))
302 static inline __attribute__((always_inline
))
303 int RSEQ_TEMPLATE_IDENTIFIER(rseq_load_cbne_store_store__ptr
)(intptr_t *v
, intptr_t expect
,
304 intptr_t *v2
, intptr_t newv2
,
305 intptr_t newv
, int cpu
)
309 __asm__ __volatile__
goto (
310 RSEQ_ASM_DEFINE_TABLE(9, 1f
, 2f
, 4f
) /* start, commit, abort */
311 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[ne
])
312 #ifdef RSEQ_COMPARE_TWICE
313 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
314 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
316 /* Start rseq by storing table entry pointer into rseq_cs. */
317 RSEQ_ASM_STORE_RSEQ_CS(1, 3f
, rseq_cs
)
318 RSEQ_ASM_CBNE_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
321 "cmp %[expect], r0\n\t"
324 #ifdef RSEQ_COMPARE_TWICE
325 RSEQ_ASM_CBNE_CPU_ID(cpu_id
, current_cpu_id
, %l
[error1
])
327 "cmp %[expect], r0\n\t"
331 "str %[newv2], %[v2]\n\t"
333 #ifdef RSEQ_TEMPLATE_MO_RELEASE
334 "dmb\n\t" /* full mb provides store-release */
337 "str %[newv], %[v]\n\t"
341 RSEQ_ASM_DEFINE_ABORT(4, "", abort
, 3, 1b
, 2b
, 4f
)
343 : /* gcc asm goto does not allow outputs */
344 : [cpu_id
] "r" (cpu
),
345 [current_cpu_id
] "m" (rseq_get_abi()->RSEQ_TEMPLATE_INDEX_CPU_ID_FIELD
),
346 [rseq_cs
] "m" (rseq_get_abi()->rseq_cs
.arch
.ptr
),
347 /* try store input */
350 /* final store input */
352 [expect
] "r" (expect
),
355 : "r0", "memory", "cc"
358 #ifdef RSEQ_COMPARE_TWICE
362 rseq_after_asm_goto();
365 rseq_after_asm_goto();
369 rseq_after_asm_goto();
371 #ifdef RSEQ_COMPARE_TWICE
373 rseq_after_asm_goto();
374 rseq_bug("cpu_id comparison failed");
376 rseq_after_asm_goto();
377 rseq_bug("expected value comparison failed");
382 static inline __attribute__((always_inline
))
383 int RSEQ_TEMPLATE_IDENTIFIER(rseq_load_cbne_memcpy_store__ptr
)(intptr_t *v
, intptr_t expect
,
384 void *dst
, void *src
, size_t len
,
385 intptr_t newv
, int cpu
)
387 uint32_t rseq_scratch
[3];
391 __asm__ __volatile__
goto (
392 RSEQ_ASM_DEFINE_TABLE(9, 1f
, 2f
, 4f
) /* start, commit, abort */
393 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[ne
])
394 #ifdef RSEQ_COMPARE_TWICE
395 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
396 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
398 "str %[src], %[rseq_scratch0]\n\t"
399 "str %[dst], %[rseq_scratch1]\n\t"
400 "str %[len], %[rseq_scratch2]\n\t"
401 /* Start rseq by storing table entry pointer into rseq_cs. */
402 RSEQ_ASM_STORE_RSEQ_CS(1, 3f
, rseq_cs
)
403 RSEQ_ASM_CBNE_CPU_ID(cpu_id
, current_cpu_id
, 4f
)
406 "cmp %[expect], r0\n\t"
409 #ifdef RSEQ_COMPARE_TWICE
410 RSEQ_ASM_CBNE_CPU_ID(cpu_id
, current_cpu_id
, 6f
)
412 "cmp %[expect], r0\n\t"
416 "cmp %[len], #0\n\t" \
419 "ldrb %%r0, [%[src]]\n\t" \
420 "strb %%r0, [%[dst]]\n\t" \
421 "adds %[src], #1\n\t" \
422 "adds %[dst], #1\n\t" \
423 "subs %[len], #1\n\t" \
427 #ifdef RSEQ_TEMPLATE_MO_RELEASE
428 "dmb\n\t" /* full mb provides store-release */
431 "str %[newv], %[v]\n\t"
435 "ldr %[len], %[rseq_scratch2]\n\t"
436 "ldr %[dst], %[rseq_scratch1]\n\t"
437 "ldr %[src], %[rseq_scratch0]\n\t"
439 RSEQ_ASM_DEFINE_ABORT(4,
441 "ldr %[len], %[rseq_scratch2]\n\t"
442 "ldr %[dst], %[rseq_scratch1]\n\t"
443 "ldr %[src], %[rseq_scratch0]\n\t",
444 abort
, 3, 1b
, 2b
, 4f
)
445 RSEQ_ASM_DEFINE_TEARDOWN(5,
447 "ldr %[len], %[rseq_scratch2]\n\t"
448 "ldr %[dst], %[rseq_scratch1]\n\t"
449 "ldr %[src], %[rseq_scratch0]\n\t",
451 #ifdef RSEQ_COMPARE_TWICE
452 RSEQ_ASM_DEFINE_TEARDOWN(6,
454 "ldr %[len], %[rseq_scratch2]\n\t"
455 "ldr %[dst], %[rseq_scratch1]\n\t"
456 "ldr %[src], %[rseq_scratch0]\n\t",
458 RSEQ_ASM_DEFINE_TEARDOWN(7,
460 "ldr %[len], %[rseq_scratch2]\n\t"
461 "ldr %[dst], %[rseq_scratch1]\n\t"
462 "ldr %[src], %[rseq_scratch0]\n\t",
466 : /* gcc asm goto does not allow outputs */
467 : [cpu_id
] "r" (cpu
),
468 [current_cpu_id
] "m" (rseq_get_abi()->RSEQ_TEMPLATE_INDEX_CPU_ID_FIELD
),
469 [rseq_cs
] "m" (rseq_get_abi()->rseq_cs
.arch
.ptr
),
470 /* final store input */
472 [expect
] "r" (expect
),
474 /* try memcpy input */
478 [rseq_scratch0
] "m" (rseq_scratch
[0]),
479 [rseq_scratch1
] "m" (rseq_scratch
[1]),
480 [rseq_scratch2
] "m" (rseq_scratch
[2])
482 : "r0", "memory", "cc"
485 #ifdef RSEQ_COMPARE_TWICE
489 rseq_after_asm_goto();
492 rseq_after_asm_goto();
496 rseq_after_asm_goto();
498 #ifdef RSEQ_COMPARE_TWICE
500 rseq_after_asm_goto();
501 rseq_bug("cpu_id comparison failed");
503 rseq_after_asm_goto();
504 rseq_bug("expected value comparison failed");
508 #endif /* #if (defined(RSEQ_TEMPLATE_MO_RELAXED) || defined(RSEQ_TEMPLATE_MO_RELEASE)) &&
509 (defined(RSEQ_TEMPLATE_INDEX_CPU_ID) || defined(RSEQ_TEMPLATE_INDEX_MM_CID)) */
511 #include "rseq/arch/templates/bits-reset.h"