Commit | Line | Data |
---|---|---|
744d0b8b | 1 | /* SPDX-License-Identifier: LGPL-2.1-only OR MIT */ |
784b0012 MD |
2 | /* |
3 | * rseq-x86.h | |
4 | * | |
5 | * (C) Copyright 2016-2018 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | |
6 | */ | |
7 | ||
8 | #include <stdint.h> | |
9 | ||
10 | #define RSEQ_SIG 0x53053053 | |
11 | ||
12 | #ifdef __x86_64__ | |
13 | ||
14 | #define rseq_smp_mb() \ | |
15 | __asm__ __volatile__ ("lock; addl $0,-128(%%rsp)" ::: "memory", "cc") | |
16 | #define rseq_smp_rmb() rseq_barrier() | |
17 | #define rseq_smp_wmb() rseq_barrier() | |
18 | ||
19 | #define rseq_smp_load_acquire(p) \ | |
20 | __extension__ ({ \ | |
21 | __typeof(*p) ____p1 = RSEQ_READ_ONCE(*p); \ | |
22 | rseq_barrier(); \ | |
23 | ____p1; \ | |
24 | }) | |
25 | ||
26 | #define rseq_smp_acquire__after_ctrl_dep() rseq_smp_rmb() | |
27 | ||
28 | #define rseq_smp_store_release(p, v) \ | |
29 | do { \ | |
30 | rseq_barrier(); \ | |
31 | RSEQ_WRITE_ONCE(*p, v); \ | |
32 | } while (0) | |
33 | ||
34 | #ifdef RSEQ_SKIP_FASTPATH | |
35 | #include "rseq-skip.h" | |
36 | #else /* !RSEQ_SKIP_FASTPATH */ | |
37 | ||
38 | #define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, \ | |
39 | start_ip, post_commit_offset, abort_ip) \ | |
40 | ".pushsection __rseq_table, \"aw\"\n\t" \ | |
41 | ".balign 32\n\t" \ | |
42 | __rseq_str(label) ":\n\t" \ | |
43 | ".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \ | |
44 | ".quad " __rseq_str(start_ip) ", " __rseq_str(post_commit_offset) ", " __rseq_str(abort_ip) "\n\t" \ | |
45 | ".popsection\n\t" | |
46 | ||
47 | #define RSEQ_ASM_DEFINE_TABLE(label, start_ip, post_commit_ip, abort_ip) \ | |
48 | __RSEQ_ASM_DEFINE_TABLE(label, 0x0, 0x0, start_ip, \ | |
49 | (post_commit_ip - start_ip), abort_ip) | |
50 | ||
90d9876e MD |
51 | /* |
52 | * Exit points of a rseq critical section consist of all instructions outside | |
53 | * of the critical section where a critical section can branch to. The abort IP | |
54 | * is already part of the __rseq_table and should not be explicitly defined as | |
55 | * an additional exit point. Knowing all exit points is useful to assist | |
56 | * debuggers stepping over the critical section. | |
57 | */ | |
58 | #define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip) \ | |
59 | ".pushsection __rseq_exit_point_array, \"aw\"\n\t" \ | |
60 | ".quad " __rseq_str(start_ip) ", " __rseq_str(exit_ip) "\n\t" \ | |
61 | ".popsection\n\t" | |
62 | ||
784b0012 MD |
63 | #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \ |
64 | RSEQ_INJECT_ASM(1) \ | |
65 | "leaq " __rseq_str(cs_label) "(%%rip), %%rax\n\t" \ | |
66 | "movq %%rax, %[" __rseq_str(rseq_cs) "]\n\t" \ | |
67 | __rseq_str(label) ":\n\t" | |
68 | ||
69 | #define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label) \ | |
70 | RSEQ_INJECT_ASM(2) \ | |
71 | "cmpl %[" __rseq_str(cpu_id) "], %[" __rseq_str(current_cpu_id) "]\n\t" \ | |
72 | "jnz " __rseq_str(label) "\n\t" | |
73 | ||
74 | #define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label) \ | |
75 | ".pushsection __rseq_failure, \"ax\"\n\t" \ | |
76 | /* Disassembler-friendly signature: nopl <sig>(%rip). */\ | |
77 | ".byte 0x0f, 0x1f, 0x05\n\t" \ | |
78 | ".long " __rseq_str(RSEQ_SIG) "\n\t" \ | |
79 | __rseq_str(label) ":\n\t" \ | |
80 | teardown \ | |
81 | "jmp %l[" __rseq_str(abort_label) "]\n\t" \ | |
82 | ".popsection\n\t" | |
83 | ||
84 | #define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label) \ | |
85 | ".pushsection __rseq_failure, \"ax\"\n\t" \ | |
86 | __rseq_str(label) ":\n\t" \ | |
87 | teardown \ | |
88 | "jmp %l[" __rseq_str(cmpfail_label) "]\n\t" \ | |
89 | ".popsection\n\t" | |
90 | ||
91 | static inline __attribute__((always_inline)) | |
92 | int rseq_cmpeqv_storev(intptr_t *v, intptr_t expect, intptr_t newv, int cpu) | |
93 | { | |
94 | RSEQ_INJECT_C(9) | |
95 | ||
96 | __asm__ __volatile__ goto ( | |
97 | RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ | |
90d9876e MD |
98 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) |
99 | #ifdef RSEQ_COMPARE_TWICE | |
100 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) | |
101 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) | |
102 | #endif | |
784b0012 MD |
103 | /* Start rseq by storing table entry pointer into rseq_cs. */ |
104 | RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) | |
105 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) | |
106 | RSEQ_INJECT_ASM(3) | |
107 | "cmpq %[v], %[expect]\n\t" | |
108 | "jnz %l[cmpfail]\n\t" | |
109 | RSEQ_INJECT_ASM(4) | |
110 | #ifdef RSEQ_COMPARE_TWICE | |
111 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1]) | |
112 | "cmpq %[v], %[expect]\n\t" | |
113 | "jnz %l[error2]\n\t" | |
114 | #endif | |
115 | /* final store */ | |
116 | "movq %[newv], %[v]\n\t" | |
117 | "2:\n\t" | |
118 | RSEQ_INJECT_ASM(5) | |
119 | RSEQ_ASM_DEFINE_ABORT(4, "", abort) | |
120 | : /* gcc asm goto does not allow outputs */ | |
121 | : [cpu_id] "r" (cpu), | |
122 | [current_cpu_id] "m" (__rseq_abi.cpu_id), | |
123 | [rseq_cs] "m" (__rseq_abi.rseq_cs), | |
124 | [v] "m" (*v), | |
125 | [expect] "r" (expect), | |
126 | [newv] "r" (newv) | |
127 | : "memory", "cc", "rax" | |
128 | RSEQ_INJECT_CLOBBER | |
129 | : abort, cmpfail | |
130 | #ifdef RSEQ_COMPARE_TWICE | |
131 | , error1, error2 | |
132 | #endif | |
133 | ); | |
134 | return 0; | |
135 | abort: | |
136 | RSEQ_INJECT_FAILED | |
137 | return -1; | |
138 | cmpfail: | |
139 | return 1; | |
140 | #ifdef RSEQ_COMPARE_TWICE | |
141 | error1: | |
142 | rseq_bug("cpu_id comparison failed"); | |
143 | error2: | |
144 | rseq_bug("expected value comparison failed"); | |
145 | #endif | |
146 | } | |
147 | ||
148 | /* | |
149 | * Compare @v against @expectnot. When it does _not_ match, load @v | |
150 | * into @load, and store the content of *@v + voffp into @v. | |
151 | */ | |
152 | static inline __attribute__((always_inline)) | |
153 | int rseq_cmpnev_storeoffp_load(intptr_t *v, intptr_t expectnot, | |
154 | off_t voffp, intptr_t *load, int cpu) | |
155 | { | |
156 | RSEQ_INJECT_C(9) | |
157 | ||
158 | __asm__ __volatile__ goto ( | |
159 | RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ | |
90d9876e MD |
160 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) |
161 | #ifdef RSEQ_COMPARE_TWICE | |
162 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) | |
163 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) | |
164 | #endif | |
784b0012 MD |
165 | /* Start rseq by storing table entry pointer into rseq_cs. */ |
166 | RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) | |
167 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) | |
168 | RSEQ_INJECT_ASM(3) | |
169 | "movq %[v], %%rbx\n\t" | |
170 | "cmpq %%rbx, %[expectnot]\n\t" | |
171 | "je %l[cmpfail]\n\t" | |
172 | RSEQ_INJECT_ASM(4) | |
173 | #ifdef RSEQ_COMPARE_TWICE | |
174 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1]) | |
175 | "movq %[v], %%rbx\n\t" | |
176 | "cmpq %%rbx, %[expectnot]\n\t" | |
177 | "je %l[error2]\n\t" | |
178 | #endif | |
179 | "movq %%rbx, %[load]\n\t" | |
180 | "addq %[voffp], %%rbx\n\t" | |
181 | "movq (%%rbx), %%rbx\n\t" | |
182 | /* final store */ | |
183 | "movq %%rbx, %[v]\n\t" | |
184 | "2:\n\t" | |
185 | RSEQ_INJECT_ASM(5) | |
186 | RSEQ_ASM_DEFINE_ABORT(4, "", abort) | |
187 | : /* gcc asm goto does not allow outputs */ | |
188 | : [cpu_id] "r" (cpu), | |
189 | [current_cpu_id] "m" (__rseq_abi.cpu_id), | |
190 | [rseq_cs] "m" (__rseq_abi.rseq_cs), | |
191 | /* final store input */ | |
192 | [v] "m" (*v), | |
193 | [expectnot] "r" (expectnot), | |
194 | [voffp] "er" (voffp), | |
195 | [load] "m" (*load) | |
196 | : "memory", "cc", "rax", "rbx" | |
197 | RSEQ_INJECT_CLOBBER | |
198 | : abort, cmpfail | |
199 | #ifdef RSEQ_COMPARE_TWICE | |
200 | , error1, error2 | |
201 | #endif | |
202 | ); | |
203 | return 0; | |
204 | abort: | |
205 | RSEQ_INJECT_FAILED | |
206 | return -1; | |
207 | cmpfail: | |
208 | return 1; | |
209 | #ifdef RSEQ_COMPARE_TWICE | |
210 | error1: | |
211 | rseq_bug("cpu_id comparison failed"); | |
212 | error2: | |
213 | rseq_bug("expected value comparison failed"); | |
214 | #endif | |
215 | } | |
216 | ||
217 | static inline __attribute__((always_inline)) | |
218 | int rseq_addv(intptr_t *v, intptr_t count, int cpu) | |
219 | { | |
220 | RSEQ_INJECT_C(9) | |
221 | ||
222 | __asm__ __volatile__ goto ( | |
223 | RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ | |
90d9876e MD |
224 | #ifdef RSEQ_COMPARE_TWICE |
225 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) | |
226 | #endif | |
784b0012 MD |
227 | /* Start rseq by storing table entry pointer into rseq_cs. */ |
228 | RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) | |
229 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) | |
230 | RSEQ_INJECT_ASM(3) | |
231 | #ifdef RSEQ_COMPARE_TWICE | |
232 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1]) | |
233 | #endif | |
234 | /* final store */ | |
235 | "addq %[count], %[v]\n\t" | |
236 | "2:\n\t" | |
237 | RSEQ_INJECT_ASM(4) | |
238 | RSEQ_ASM_DEFINE_ABORT(4, "", abort) | |
239 | : /* gcc asm goto does not allow outputs */ | |
240 | : [cpu_id] "r" (cpu), | |
241 | [current_cpu_id] "m" (__rseq_abi.cpu_id), | |
242 | [rseq_cs] "m" (__rseq_abi.rseq_cs), | |
243 | /* final store input */ | |
244 | [v] "m" (*v), | |
245 | [count] "er" (count) | |
246 | : "memory", "cc", "rax" | |
247 | RSEQ_INJECT_CLOBBER | |
248 | : abort | |
249 | #ifdef RSEQ_COMPARE_TWICE | |
250 | , error1 | |
251 | #endif | |
252 | ); | |
253 | return 0; | |
254 | abort: | |
255 | RSEQ_INJECT_FAILED | |
256 | return -1; | |
257 | #ifdef RSEQ_COMPARE_TWICE | |
258 | error1: | |
259 | rseq_bug("cpu_id comparison failed"); | |
260 | #endif | |
261 | } | |
262 | ||
263 | static inline __attribute__((always_inline)) | |
264 | int rseq_cmpeqv_trystorev_storev(intptr_t *v, intptr_t expect, | |
265 | intptr_t *v2, intptr_t newv2, | |
266 | intptr_t newv, int cpu) | |
267 | { | |
268 | RSEQ_INJECT_C(9) | |
269 | ||
270 | __asm__ __volatile__ goto ( | |
271 | RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ | |
90d9876e MD |
272 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) |
273 | #ifdef RSEQ_COMPARE_TWICE | |
274 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) | |
275 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) | |
276 | #endif | |
784b0012 MD |
277 | /* Start rseq by storing table entry pointer into rseq_cs. */ |
278 | RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) | |
279 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) | |
280 | RSEQ_INJECT_ASM(3) | |
281 | "cmpq %[v], %[expect]\n\t" | |
282 | "jnz %l[cmpfail]\n\t" | |
283 | RSEQ_INJECT_ASM(4) | |
284 | #ifdef RSEQ_COMPARE_TWICE | |
285 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1]) | |
286 | "cmpq %[v], %[expect]\n\t" | |
287 | "jnz %l[error2]\n\t" | |
288 | #endif | |
289 | /* try store */ | |
290 | "movq %[newv2], %[v2]\n\t" | |
291 | RSEQ_INJECT_ASM(5) | |
292 | /* final store */ | |
293 | "movq %[newv], %[v]\n\t" | |
294 | "2:\n\t" | |
295 | RSEQ_INJECT_ASM(6) | |
296 | RSEQ_ASM_DEFINE_ABORT(4, "", abort) | |
297 | : /* gcc asm goto does not allow outputs */ | |
298 | : [cpu_id] "r" (cpu), | |
299 | [current_cpu_id] "m" (__rseq_abi.cpu_id), | |
300 | [rseq_cs] "m" (__rseq_abi.rseq_cs), | |
301 | /* try store input */ | |
302 | [v2] "m" (*v2), | |
303 | [newv2] "r" (newv2), | |
304 | /* final store input */ | |
305 | [v] "m" (*v), | |
306 | [expect] "r" (expect), | |
307 | [newv] "r" (newv) | |
308 | : "memory", "cc", "rax" | |
309 | RSEQ_INJECT_CLOBBER | |
310 | : abort, cmpfail | |
311 | #ifdef RSEQ_COMPARE_TWICE | |
312 | , error1, error2 | |
313 | #endif | |
314 | ); | |
315 | return 0; | |
316 | abort: | |
317 | RSEQ_INJECT_FAILED | |
318 | return -1; | |
319 | cmpfail: | |
320 | return 1; | |
321 | #ifdef RSEQ_COMPARE_TWICE | |
322 | error1: | |
323 | rseq_bug("cpu_id comparison failed"); | |
324 | error2: | |
325 | rseq_bug("expected value comparison failed"); | |
326 | #endif | |
327 | } | |
328 | ||
329 | /* x86-64 is TSO. */ | |
330 | static inline __attribute__((always_inline)) | |
331 | int rseq_cmpeqv_trystorev_storev_release(intptr_t *v, intptr_t expect, | |
332 | intptr_t *v2, intptr_t newv2, | |
333 | intptr_t newv, int cpu) | |
334 | { | |
335 | return rseq_cmpeqv_trystorev_storev(v, expect, v2, newv2, newv, cpu); | |
336 | } | |
337 | ||
338 | static inline __attribute__((always_inline)) | |
339 | int rseq_cmpeqv_cmpeqv_storev(intptr_t *v, intptr_t expect, | |
340 | intptr_t *v2, intptr_t expect2, | |
341 | intptr_t newv, int cpu) | |
342 | { | |
343 | RSEQ_INJECT_C(9) | |
344 | ||
345 | __asm__ __volatile__ goto ( | |
346 | RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ | |
90d9876e MD |
347 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) |
348 | #ifdef RSEQ_COMPARE_TWICE | |
349 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) | |
350 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) | |
351 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error3]) | |
352 | #endif | |
784b0012 MD |
353 | /* Start rseq by storing table entry pointer into rseq_cs. */ |
354 | RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) | |
355 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) | |
356 | RSEQ_INJECT_ASM(3) | |
357 | "cmpq %[v], %[expect]\n\t" | |
358 | "jnz %l[cmpfail]\n\t" | |
359 | RSEQ_INJECT_ASM(4) | |
360 | "cmpq %[v2], %[expect2]\n\t" | |
361 | "jnz %l[cmpfail]\n\t" | |
362 | RSEQ_INJECT_ASM(5) | |
363 | #ifdef RSEQ_COMPARE_TWICE | |
364 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1]) | |
365 | "cmpq %[v], %[expect]\n\t" | |
366 | "jnz %l[error2]\n\t" | |
367 | "cmpq %[v2], %[expect2]\n\t" | |
368 | "jnz %l[error3]\n\t" | |
369 | #endif | |
370 | /* final store */ | |
371 | "movq %[newv], %[v]\n\t" | |
372 | "2:\n\t" | |
373 | RSEQ_INJECT_ASM(6) | |
374 | RSEQ_ASM_DEFINE_ABORT(4, "", abort) | |
375 | : /* gcc asm goto does not allow outputs */ | |
376 | : [cpu_id] "r" (cpu), | |
377 | [current_cpu_id] "m" (__rseq_abi.cpu_id), | |
378 | [rseq_cs] "m" (__rseq_abi.rseq_cs), | |
379 | /* cmp2 input */ | |
380 | [v2] "m" (*v2), | |
381 | [expect2] "r" (expect2), | |
382 | /* final store input */ | |
383 | [v] "m" (*v), | |
384 | [expect] "r" (expect), | |
385 | [newv] "r" (newv) | |
386 | : "memory", "cc", "rax" | |
387 | RSEQ_INJECT_CLOBBER | |
388 | : abort, cmpfail | |
389 | #ifdef RSEQ_COMPARE_TWICE | |
390 | , error1, error2, error3 | |
391 | #endif | |
392 | ); | |
393 | return 0; | |
394 | abort: | |
395 | RSEQ_INJECT_FAILED | |
396 | return -1; | |
397 | cmpfail: | |
398 | return 1; | |
399 | #ifdef RSEQ_COMPARE_TWICE | |
400 | error1: | |
401 | rseq_bug("cpu_id comparison failed"); | |
402 | error2: | |
403 | rseq_bug("1st expected value comparison failed"); | |
404 | error3: | |
405 | rseq_bug("2nd expected value comparison failed"); | |
406 | #endif | |
407 | } | |
408 | ||
409 | static inline __attribute__((always_inline)) | |
410 | int rseq_cmpeqv_trymemcpy_storev(intptr_t *v, intptr_t expect, | |
411 | void *dst, void *src, size_t len, | |
412 | intptr_t newv, int cpu) | |
413 | { | |
414 | uint64_t rseq_scratch[3]; | |
415 | ||
416 | RSEQ_INJECT_C(9) | |
417 | ||
418 | __asm__ __volatile__ goto ( | |
419 | RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ | |
90d9876e MD |
420 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) |
421 | #ifdef RSEQ_COMPARE_TWICE | |
422 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) | |
423 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) | |
424 | #endif | |
784b0012 MD |
425 | "movq %[src], %[rseq_scratch0]\n\t" |
426 | "movq %[dst], %[rseq_scratch1]\n\t" | |
427 | "movq %[len], %[rseq_scratch2]\n\t" | |
428 | /* Start rseq by storing table entry pointer into rseq_cs. */ | |
429 | RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) | |
430 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) | |
431 | RSEQ_INJECT_ASM(3) | |
432 | "cmpq %[v], %[expect]\n\t" | |
433 | "jnz 5f\n\t" | |
434 | RSEQ_INJECT_ASM(4) | |
435 | #ifdef RSEQ_COMPARE_TWICE | |
436 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f) | |
437 | "cmpq %[v], %[expect]\n\t" | |
438 | "jnz 7f\n\t" | |
439 | #endif | |
440 | /* try memcpy */ | |
441 | "test %[len], %[len]\n\t" \ | |
442 | "jz 333f\n\t" \ | |
443 | "222:\n\t" \ | |
444 | "movb (%[src]), %%al\n\t" \ | |
445 | "movb %%al, (%[dst])\n\t" \ | |
446 | "inc %[src]\n\t" \ | |
447 | "inc %[dst]\n\t" \ | |
448 | "dec %[len]\n\t" \ | |
449 | "jnz 222b\n\t" \ | |
450 | "333:\n\t" \ | |
451 | RSEQ_INJECT_ASM(5) | |
452 | /* final store */ | |
453 | "movq %[newv], %[v]\n\t" | |
454 | "2:\n\t" | |
455 | RSEQ_INJECT_ASM(6) | |
456 | /* teardown */ | |
457 | "movq %[rseq_scratch2], %[len]\n\t" | |
458 | "movq %[rseq_scratch1], %[dst]\n\t" | |
459 | "movq %[rseq_scratch0], %[src]\n\t" | |
460 | RSEQ_ASM_DEFINE_ABORT(4, | |
461 | "movq %[rseq_scratch2], %[len]\n\t" | |
462 | "movq %[rseq_scratch1], %[dst]\n\t" | |
463 | "movq %[rseq_scratch0], %[src]\n\t", | |
464 | abort) | |
465 | RSEQ_ASM_DEFINE_CMPFAIL(5, | |
466 | "movq %[rseq_scratch2], %[len]\n\t" | |
467 | "movq %[rseq_scratch1], %[dst]\n\t" | |
468 | "movq %[rseq_scratch0], %[src]\n\t", | |
469 | cmpfail) | |
470 | #ifdef RSEQ_COMPARE_TWICE | |
471 | RSEQ_ASM_DEFINE_CMPFAIL(6, | |
472 | "movq %[rseq_scratch2], %[len]\n\t" | |
473 | "movq %[rseq_scratch1], %[dst]\n\t" | |
474 | "movq %[rseq_scratch0], %[src]\n\t", | |
475 | error1) | |
476 | RSEQ_ASM_DEFINE_CMPFAIL(7, | |
477 | "movq %[rseq_scratch2], %[len]\n\t" | |
478 | "movq %[rseq_scratch1], %[dst]\n\t" | |
479 | "movq %[rseq_scratch0], %[src]\n\t", | |
480 | error2) | |
481 | #endif | |
482 | : /* gcc asm goto does not allow outputs */ | |
483 | : [cpu_id] "r" (cpu), | |
484 | [current_cpu_id] "m" (__rseq_abi.cpu_id), | |
485 | [rseq_cs] "m" (__rseq_abi.rseq_cs), | |
486 | /* final store input */ | |
487 | [v] "m" (*v), | |
488 | [expect] "r" (expect), | |
489 | [newv] "r" (newv), | |
490 | /* try memcpy input */ | |
491 | [dst] "r" (dst), | |
492 | [src] "r" (src), | |
493 | [len] "r" (len), | |
494 | [rseq_scratch0] "m" (rseq_scratch[0]), | |
495 | [rseq_scratch1] "m" (rseq_scratch[1]), | |
496 | [rseq_scratch2] "m" (rseq_scratch[2]) | |
497 | : "memory", "cc", "rax" | |
498 | RSEQ_INJECT_CLOBBER | |
499 | : abort, cmpfail | |
500 | #ifdef RSEQ_COMPARE_TWICE | |
501 | , error1, error2 | |
502 | #endif | |
503 | ); | |
504 | return 0; | |
505 | abort: | |
506 | RSEQ_INJECT_FAILED | |
507 | return -1; | |
508 | cmpfail: | |
509 | return 1; | |
510 | #ifdef RSEQ_COMPARE_TWICE | |
511 | error1: | |
512 | rseq_bug("cpu_id comparison failed"); | |
513 | error2: | |
514 | rseq_bug("expected value comparison failed"); | |
515 | #endif | |
516 | } | |
517 | ||
518 | /* x86-64 is TSO. */ | |
519 | static inline __attribute__((always_inline)) | |
520 | int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v, intptr_t expect, | |
521 | void *dst, void *src, size_t len, | |
522 | intptr_t newv, int cpu) | |
523 | { | |
524 | return rseq_cmpeqv_trymemcpy_storev(v, expect, dst, src, len, | |
525 | newv, cpu); | |
526 | } | |
527 | ||
528 | #endif /* !RSEQ_SKIP_FASTPATH */ | |
529 | ||
530 | #elif __i386__ | |
531 | ||
532 | #define rseq_smp_mb() \ | |
533 | __asm__ __volatile__ ("lock; addl $0,-128(%%esp)" ::: "memory", "cc") | |
534 | #define rseq_smp_rmb() \ | |
535 | __asm__ __volatile__ ("lock; addl $0,-128(%%esp)" ::: "memory", "cc") | |
536 | #define rseq_smp_wmb() \ | |
537 | __asm__ __volatile__ ("lock; addl $0,-128(%%esp)" ::: "memory", "cc") | |
538 | ||
539 | #define rseq_smp_load_acquire(p) \ | |
540 | __extension__ ({ \ | |
541 | __typeof(*p) ____p1 = RSEQ_READ_ONCE(*p); \ | |
542 | rseq_smp_mb(); \ | |
543 | ____p1; \ | |
544 | }) | |
545 | ||
546 | #define rseq_smp_acquire__after_ctrl_dep() rseq_smp_rmb() | |
547 | ||
548 | #define rseq_smp_store_release(p, v) \ | |
549 | do { \ | |
550 | rseq_smp_mb(); \ | |
551 | RSEQ_WRITE_ONCE(*p, v); \ | |
552 | } while (0) | |
553 | ||
554 | #ifdef RSEQ_SKIP_FASTPATH | |
555 | #include "rseq-skip.h" | |
556 | #else /* !RSEQ_SKIP_FASTPATH */ | |
557 | ||
558 | /* | |
559 | * Use eax as scratch register and take memory operands as input to | |
560 | * lessen register pressure. Especially needed when compiling in O0. | |
561 | */ | |
562 | #define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, \ | |
563 | start_ip, post_commit_offset, abort_ip) \ | |
564 | ".pushsection __rseq_table, \"aw\"\n\t" \ | |
565 | ".balign 32\n\t" \ | |
566 | __rseq_str(label) ":\n\t" \ | |
567 | ".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \ | |
568 | ".long " __rseq_str(start_ip) ", 0x0, " __rseq_str(post_commit_offset) ", 0x0, " __rseq_str(abort_ip) ", 0x0\n\t" \ | |
569 | ".popsection\n\t" | |
570 | ||
571 | #define RSEQ_ASM_DEFINE_TABLE(label, start_ip, post_commit_ip, abort_ip) \ | |
572 | __RSEQ_ASM_DEFINE_TABLE(label, 0x0, 0x0, start_ip, \ | |
573 | (post_commit_ip - start_ip), abort_ip) | |
574 | ||
90d9876e MD |
575 | /* |
576 | * Exit points of a rseq critical section consist of all instructions outside | |
577 | * of the critical section where a critical section can branch to. The abort IP | |
578 | * is already part of the __rseq_table and should not be explicitly defined as | |
579 | * an additional exit point. Knowing all exit points is useful to assist | |
580 | * debuggers stepping over the critical section. | |
581 | */ | |
582 | #define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip) \ | |
583 | ".pushsection __rseq_exit_point_array, \"aw\"\n\t" \ | |
584 | ".long " __rseq_str(start_ip) ", 0x0, " __rseq_str(exit_ip) ", 0x0\n\t" \ | |
585 | ".popsection\n\t" | |
586 | ||
784b0012 MD |
587 | #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \ |
588 | RSEQ_INJECT_ASM(1) \ | |
589 | "movl $" __rseq_str(cs_label) ", %[rseq_cs]\n\t" \ | |
590 | __rseq_str(label) ":\n\t" | |
591 | ||
592 | #define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label) \ | |
593 | RSEQ_INJECT_ASM(2) \ | |
594 | "cmpl %[" __rseq_str(cpu_id) "], %[" __rseq_str(current_cpu_id) "]\n\t" \ | |
595 | "jnz " __rseq_str(label) "\n\t" | |
596 | ||
597 | #define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label) \ | |
598 | ".pushsection __rseq_failure, \"ax\"\n\t" \ | |
599 | /* Disassembler-friendly signature: nopl <sig>. */ \ | |
600 | ".byte 0x0f, 0x1f, 0x05\n\t" \ | |
601 | ".long " __rseq_str(RSEQ_SIG) "\n\t" \ | |
602 | __rseq_str(label) ":\n\t" \ | |
603 | teardown \ | |
604 | "jmp %l[" __rseq_str(abort_label) "]\n\t" \ | |
605 | ".popsection\n\t" | |
606 | ||
607 | #define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label) \ | |
608 | ".pushsection __rseq_failure, \"ax\"\n\t" \ | |
609 | __rseq_str(label) ":\n\t" \ | |
610 | teardown \ | |
611 | "jmp %l[" __rseq_str(cmpfail_label) "]\n\t" \ | |
612 | ".popsection\n\t" | |
613 | ||
614 | static inline __attribute__((always_inline)) | |
615 | int rseq_cmpeqv_storev(intptr_t *v, intptr_t expect, intptr_t newv, int cpu) | |
616 | { | |
617 | RSEQ_INJECT_C(9) | |
618 | ||
619 | __asm__ __volatile__ goto ( | |
620 | RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ | |
90d9876e MD |
621 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) |
622 | #ifdef RSEQ_COMPARE_TWICE | |
623 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) | |
624 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) | |
625 | #endif | |
784b0012 MD |
626 | /* Start rseq by storing table entry pointer into rseq_cs. */ |
627 | RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) | |
628 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) | |
629 | RSEQ_INJECT_ASM(3) | |
630 | "cmpl %[v], %[expect]\n\t" | |
631 | "jnz %l[cmpfail]\n\t" | |
632 | RSEQ_INJECT_ASM(4) | |
633 | #ifdef RSEQ_COMPARE_TWICE | |
634 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1]) | |
635 | "cmpl %[v], %[expect]\n\t" | |
636 | "jnz %l[error2]\n\t" | |
637 | #endif | |
638 | /* final store */ | |
639 | "movl %[newv], %[v]\n\t" | |
640 | "2:\n\t" | |
641 | RSEQ_INJECT_ASM(5) | |
642 | RSEQ_ASM_DEFINE_ABORT(4, "", abort) | |
643 | : /* gcc asm goto does not allow outputs */ | |
644 | : [cpu_id] "r" (cpu), | |
645 | [current_cpu_id] "m" (__rseq_abi.cpu_id), | |
646 | [rseq_cs] "m" (__rseq_abi.rseq_cs), | |
647 | [v] "m" (*v), | |
648 | [expect] "r" (expect), | |
649 | [newv] "r" (newv) | |
650 | : "memory", "cc", "eax" | |
651 | RSEQ_INJECT_CLOBBER | |
652 | : abort, cmpfail | |
653 | #ifdef RSEQ_COMPARE_TWICE | |
654 | , error1, error2 | |
655 | #endif | |
656 | ); | |
657 | return 0; | |
658 | abort: | |
659 | RSEQ_INJECT_FAILED | |
660 | return -1; | |
661 | cmpfail: | |
662 | return 1; | |
663 | #ifdef RSEQ_COMPARE_TWICE | |
664 | error1: | |
665 | rseq_bug("cpu_id comparison failed"); | |
666 | error2: | |
667 | rseq_bug("expected value comparison failed"); | |
668 | #endif | |
669 | } | |
670 | ||
671 | /* | |
672 | * Compare @v against @expectnot. When it does _not_ match, load @v | |
673 | * into @load, and store the content of *@v + voffp into @v. | |
674 | */ | |
675 | static inline __attribute__((always_inline)) | |
676 | int rseq_cmpnev_storeoffp_load(intptr_t *v, intptr_t expectnot, | |
677 | off_t voffp, intptr_t *load, int cpu) | |
678 | { | |
679 | RSEQ_INJECT_C(9) | |
680 | ||
681 | __asm__ __volatile__ goto ( | |
682 | RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ | |
90d9876e MD |
683 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) |
684 | #ifdef RSEQ_COMPARE_TWICE | |
685 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) | |
686 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) | |
687 | #endif | |
784b0012 MD |
688 | /* Start rseq by storing table entry pointer into rseq_cs. */ |
689 | RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) | |
690 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) | |
691 | RSEQ_INJECT_ASM(3) | |
692 | "movl %[v], %%ebx\n\t" | |
693 | "cmpl %%ebx, %[expectnot]\n\t" | |
694 | "je %l[cmpfail]\n\t" | |
695 | RSEQ_INJECT_ASM(4) | |
696 | #ifdef RSEQ_COMPARE_TWICE | |
697 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1]) | |
698 | "movl %[v], %%ebx\n\t" | |
699 | "cmpl %%ebx, %[expectnot]\n\t" | |
700 | "je %l[error2]\n\t" | |
701 | #endif | |
702 | "movl %%ebx, %[load]\n\t" | |
703 | "addl %[voffp], %%ebx\n\t" | |
704 | "movl (%%ebx), %%ebx\n\t" | |
705 | /* final store */ | |
706 | "movl %%ebx, %[v]\n\t" | |
707 | "2:\n\t" | |
708 | RSEQ_INJECT_ASM(5) | |
709 | RSEQ_ASM_DEFINE_ABORT(4, "", abort) | |
710 | : /* gcc asm goto does not allow outputs */ | |
711 | : [cpu_id] "r" (cpu), | |
712 | [current_cpu_id] "m" (__rseq_abi.cpu_id), | |
713 | [rseq_cs] "m" (__rseq_abi.rseq_cs), | |
714 | /* final store input */ | |
715 | [v] "m" (*v), | |
716 | [expectnot] "r" (expectnot), | |
717 | [voffp] "ir" (voffp), | |
718 | [load] "m" (*load) | |
719 | : "memory", "cc", "eax", "ebx" | |
720 | RSEQ_INJECT_CLOBBER | |
721 | : abort, cmpfail | |
722 | #ifdef RSEQ_COMPARE_TWICE | |
723 | , error1, error2 | |
724 | #endif | |
725 | ); | |
726 | return 0; | |
727 | abort: | |
728 | RSEQ_INJECT_FAILED | |
729 | return -1; | |
730 | cmpfail: | |
731 | return 1; | |
732 | #ifdef RSEQ_COMPARE_TWICE | |
733 | error1: | |
734 | rseq_bug("cpu_id comparison failed"); | |
735 | error2: | |
736 | rseq_bug("expected value comparison failed"); | |
737 | #endif | |
738 | } | |
739 | ||
740 | static inline __attribute__((always_inline)) | |
741 | int rseq_addv(intptr_t *v, intptr_t count, int cpu) | |
742 | { | |
743 | RSEQ_INJECT_C(9) | |
744 | ||
745 | __asm__ __volatile__ goto ( | |
746 | RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ | |
90d9876e MD |
747 | #ifdef RSEQ_COMPARE_TWICE |
748 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) | |
749 | #endif | |
784b0012 MD |
750 | /* Start rseq by storing table entry pointer into rseq_cs. */ |
751 | RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) | |
752 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) | |
753 | RSEQ_INJECT_ASM(3) | |
754 | #ifdef RSEQ_COMPARE_TWICE | |
755 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1]) | |
756 | #endif | |
757 | /* final store */ | |
758 | "addl %[count], %[v]\n\t" | |
759 | "2:\n\t" | |
760 | RSEQ_INJECT_ASM(4) | |
761 | RSEQ_ASM_DEFINE_ABORT(4, "", abort) | |
762 | : /* gcc asm goto does not allow outputs */ | |
763 | : [cpu_id] "r" (cpu), | |
764 | [current_cpu_id] "m" (__rseq_abi.cpu_id), | |
765 | [rseq_cs] "m" (__rseq_abi.rseq_cs), | |
766 | /* final store input */ | |
767 | [v] "m" (*v), | |
768 | [count] "ir" (count) | |
769 | : "memory", "cc", "eax" | |
770 | RSEQ_INJECT_CLOBBER | |
771 | : abort | |
772 | #ifdef RSEQ_COMPARE_TWICE | |
773 | , error1 | |
774 | #endif | |
775 | ); | |
776 | return 0; | |
777 | abort: | |
778 | RSEQ_INJECT_FAILED | |
779 | return -1; | |
780 | #ifdef RSEQ_COMPARE_TWICE | |
781 | error1: | |
782 | rseq_bug("cpu_id comparison failed"); | |
783 | #endif | |
784 | } | |
785 | ||
786 | static inline __attribute__((always_inline)) | |
787 | int rseq_cmpeqv_trystorev_storev(intptr_t *v, intptr_t expect, | |
788 | intptr_t *v2, intptr_t newv2, | |
789 | intptr_t newv, int cpu) | |
790 | { | |
791 | RSEQ_INJECT_C(9) | |
792 | ||
793 | __asm__ __volatile__ goto ( | |
794 | RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ | |
90d9876e MD |
795 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) |
796 | #ifdef RSEQ_COMPARE_TWICE | |
797 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) | |
798 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) | |
799 | #endif | |
784b0012 MD |
800 | /* Start rseq by storing table entry pointer into rseq_cs. */ |
801 | RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) | |
802 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) | |
803 | RSEQ_INJECT_ASM(3) | |
804 | "cmpl %[v], %[expect]\n\t" | |
805 | "jnz %l[cmpfail]\n\t" | |
806 | RSEQ_INJECT_ASM(4) | |
807 | #ifdef RSEQ_COMPARE_TWICE | |
808 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1]) | |
809 | "cmpl %[v], %[expect]\n\t" | |
810 | "jnz %l[error2]\n\t" | |
811 | #endif | |
812 | /* try store */ | |
813 | "movl %[newv2], %%eax\n\t" | |
814 | "movl %%eax, %[v2]\n\t" | |
815 | RSEQ_INJECT_ASM(5) | |
816 | /* final store */ | |
817 | "movl %[newv], %[v]\n\t" | |
818 | "2:\n\t" | |
819 | RSEQ_INJECT_ASM(6) | |
820 | RSEQ_ASM_DEFINE_ABORT(4, "", abort) | |
821 | : /* gcc asm goto does not allow outputs */ | |
822 | : [cpu_id] "r" (cpu), | |
823 | [current_cpu_id] "m" (__rseq_abi.cpu_id), | |
824 | [rseq_cs] "m" (__rseq_abi.rseq_cs), | |
825 | /* try store input */ | |
826 | [v2] "m" (*v2), | |
827 | [newv2] "m" (newv2), | |
828 | /* final store input */ | |
829 | [v] "m" (*v), | |
830 | [expect] "r" (expect), | |
831 | [newv] "r" (newv) | |
832 | : "memory", "cc", "eax" | |
833 | RSEQ_INJECT_CLOBBER | |
834 | : abort, cmpfail | |
835 | #ifdef RSEQ_COMPARE_TWICE | |
836 | , error1, error2 | |
837 | #endif | |
838 | ); | |
839 | return 0; | |
840 | abort: | |
841 | RSEQ_INJECT_FAILED | |
842 | return -1; | |
843 | cmpfail: | |
844 | return 1; | |
845 | #ifdef RSEQ_COMPARE_TWICE | |
846 | error1: | |
847 | rseq_bug("cpu_id comparison failed"); | |
848 | error2: | |
849 | rseq_bug("expected value comparison failed"); | |
850 | #endif | |
851 | } | |
852 | ||
853 | static inline __attribute__((always_inline)) | |
854 | int rseq_cmpeqv_trystorev_storev_release(intptr_t *v, intptr_t expect, | |
855 | intptr_t *v2, intptr_t newv2, | |
856 | intptr_t newv, int cpu) | |
857 | { | |
858 | RSEQ_INJECT_C(9) | |
859 | ||
860 | __asm__ __volatile__ goto ( | |
861 | RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ | |
90d9876e MD |
862 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) |
863 | #ifdef RSEQ_COMPARE_TWICE | |
864 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) | |
865 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) | |
866 | #endif | |
784b0012 MD |
867 | /* Start rseq by storing table entry pointer into rseq_cs. */ |
868 | RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) | |
869 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) | |
870 | RSEQ_INJECT_ASM(3) | |
871 | "movl %[expect], %%eax\n\t" | |
872 | "cmpl %[v], %%eax\n\t" | |
873 | "jnz %l[cmpfail]\n\t" | |
874 | RSEQ_INJECT_ASM(4) | |
875 | #ifdef RSEQ_COMPARE_TWICE | |
876 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1]) | |
877 | "movl %[expect], %%eax\n\t" | |
878 | "cmpl %[v], %%eax\n\t" | |
879 | "jnz %l[error2]\n\t" | |
880 | #endif | |
881 | /* try store */ | |
882 | "movl %[newv2], %[v2]\n\t" | |
883 | RSEQ_INJECT_ASM(5) | |
884 | "lock; addl $0,-128(%%esp)\n\t" | |
885 | /* final store */ | |
886 | "movl %[newv], %[v]\n\t" | |
887 | "2:\n\t" | |
888 | RSEQ_INJECT_ASM(6) | |
889 | RSEQ_ASM_DEFINE_ABORT(4, "", abort) | |
890 | : /* gcc asm goto does not allow outputs */ | |
891 | : [cpu_id] "r" (cpu), | |
892 | [current_cpu_id] "m" (__rseq_abi.cpu_id), | |
893 | [rseq_cs] "m" (__rseq_abi.rseq_cs), | |
894 | /* try store input */ | |
895 | [v2] "m" (*v2), | |
896 | [newv2] "r" (newv2), | |
897 | /* final store input */ | |
898 | [v] "m" (*v), | |
899 | [expect] "m" (expect), | |
900 | [newv] "r" (newv) | |
901 | : "memory", "cc", "eax" | |
902 | RSEQ_INJECT_CLOBBER | |
903 | : abort, cmpfail | |
904 | #ifdef RSEQ_COMPARE_TWICE | |
905 | , error1, error2 | |
906 | #endif | |
907 | ); | |
908 | return 0; | |
909 | abort: | |
910 | RSEQ_INJECT_FAILED | |
911 | return -1; | |
912 | cmpfail: | |
913 | return 1; | |
914 | #ifdef RSEQ_COMPARE_TWICE | |
915 | error1: | |
916 | rseq_bug("cpu_id comparison failed"); | |
917 | error2: | |
918 | rseq_bug("expected value comparison failed"); | |
919 | #endif | |
920 | ||
921 | } | |
922 | ||
923 | static inline __attribute__((always_inline)) | |
924 | int rseq_cmpeqv_cmpeqv_storev(intptr_t *v, intptr_t expect, | |
925 | intptr_t *v2, intptr_t expect2, | |
926 | intptr_t newv, int cpu) | |
927 | { | |
928 | RSEQ_INJECT_C(9) | |
929 | ||
930 | __asm__ __volatile__ goto ( | |
931 | RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ | |
90d9876e MD |
932 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) |
933 | #ifdef RSEQ_COMPARE_TWICE | |
934 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) | |
935 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) | |
936 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error3]) | |
937 | #endif | |
784b0012 MD |
938 | /* Start rseq by storing table entry pointer into rseq_cs. */ |
939 | RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) | |
940 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) | |
941 | RSEQ_INJECT_ASM(3) | |
942 | "cmpl %[v], %[expect]\n\t" | |
943 | "jnz %l[cmpfail]\n\t" | |
944 | RSEQ_INJECT_ASM(4) | |
945 | "cmpl %[expect2], %[v2]\n\t" | |
946 | "jnz %l[cmpfail]\n\t" | |
947 | RSEQ_INJECT_ASM(5) | |
948 | #ifdef RSEQ_COMPARE_TWICE | |
949 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1]) | |
950 | "cmpl %[v], %[expect]\n\t" | |
951 | "jnz %l[error2]\n\t" | |
952 | "cmpl %[expect2], %[v2]\n\t" | |
953 | "jnz %l[error3]\n\t" | |
954 | #endif | |
955 | "movl %[newv], %%eax\n\t" | |
956 | /* final store */ | |
957 | "movl %%eax, %[v]\n\t" | |
958 | "2:\n\t" | |
959 | RSEQ_INJECT_ASM(6) | |
960 | RSEQ_ASM_DEFINE_ABORT(4, "", abort) | |
961 | : /* gcc asm goto does not allow outputs */ | |
962 | : [cpu_id] "r" (cpu), | |
963 | [current_cpu_id] "m" (__rseq_abi.cpu_id), | |
964 | [rseq_cs] "m" (__rseq_abi.rseq_cs), | |
965 | /* cmp2 input */ | |
966 | [v2] "m" (*v2), | |
967 | [expect2] "r" (expect2), | |
968 | /* final store input */ | |
969 | [v] "m" (*v), | |
970 | [expect] "r" (expect), | |
971 | [newv] "m" (newv) | |
972 | : "memory", "cc", "eax" | |
973 | RSEQ_INJECT_CLOBBER | |
974 | : abort, cmpfail | |
975 | #ifdef RSEQ_COMPARE_TWICE | |
976 | , error1, error2, error3 | |
977 | #endif | |
978 | ); | |
979 | return 0; | |
980 | abort: | |
981 | RSEQ_INJECT_FAILED | |
982 | return -1; | |
983 | cmpfail: | |
984 | return 1; | |
985 | #ifdef RSEQ_COMPARE_TWICE | |
986 | error1: | |
987 | rseq_bug("cpu_id comparison failed"); | |
988 | error2: | |
989 | rseq_bug("1st expected value comparison failed"); | |
990 | error3: | |
991 | rseq_bug("2nd expected value comparison failed"); | |
992 | #endif | |
993 | } | |
994 | ||
995 | /* TODO: implement a faster memcpy. */ | |
996 | static inline __attribute__((always_inline)) | |
997 | int rseq_cmpeqv_trymemcpy_storev(intptr_t *v, intptr_t expect, | |
998 | void *dst, void *src, size_t len, | |
999 | intptr_t newv, int cpu) | |
1000 | { | |
1001 | uint32_t rseq_scratch[3]; | |
1002 | ||
1003 | RSEQ_INJECT_C(9) | |
1004 | ||
1005 | __asm__ __volatile__ goto ( | |
1006 | RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ | |
90d9876e MD |
1007 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) |
1008 | #ifdef RSEQ_COMPARE_TWICE | |
1009 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) | |
1010 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) | |
1011 | #endif | |
784b0012 MD |
1012 | "movl %[src], %[rseq_scratch0]\n\t" |
1013 | "movl %[dst], %[rseq_scratch1]\n\t" | |
1014 | "movl %[len], %[rseq_scratch2]\n\t" | |
1015 | /* Start rseq by storing table entry pointer into rseq_cs. */ | |
1016 | RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) | |
1017 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) | |
1018 | RSEQ_INJECT_ASM(3) | |
1019 | "movl %[expect], %%eax\n\t" | |
1020 | "cmpl %%eax, %[v]\n\t" | |
1021 | "jnz 5f\n\t" | |
1022 | RSEQ_INJECT_ASM(4) | |
1023 | #ifdef RSEQ_COMPARE_TWICE | |
1024 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f) | |
1025 | "movl %[expect], %%eax\n\t" | |
1026 | "cmpl %%eax, %[v]\n\t" | |
1027 | "jnz 7f\n\t" | |
1028 | #endif | |
1029 | /* try memcpy */ | |
1030 | "test %[len], %[len]\n\t" \ | |
1031 | "jz 333f\n\t" \ | |
1032 | "222:\n\t" \ | |
1033 | "movb (%[src]), %%al\n\t" \ | |
1034 | "movb %%al, (%[dst])\n\t" \ | |
1035 | "inc %[src]\n\t" \ | |
1036 | "inc %[dst]\n\t" \ | |
1037 | "dec %[len]\n\t" \ | |
1038 | "jnz 222b\n\t" \ | |
1039 | "333:\n\t" \ | |
1040 | RSEQ_INJECT_ASM(5) | |
1041 | "movl %[newv], %%eax\n\t" | |
1042 | /* final store */ | |
1043 | "movl %%eax, %[v]\n\t" | |
1044 | "2:\n\t" | |
1045 | RSEQ_INJECT_ASM(6) | |
1046 | /* teardown */ | |
1047 | "movl %[rseq_scratch2], %[len]\n\t" | |
1048 | "movl %[rseq_scratch1], %[dst]\n\t" | |
1049 | "movl %[rseq_scratch0], %[src]\n\t" | |
1050 | RSEQ_ASM_DEFINE_ABORT(4, | |
1051 | "movl %[rseq_scratch2], %[len]\n\t" | |
1052 | "movl %[rseq_scratch1], %[dst]\n\t" | |
1053 | "movl %[rseq_scratch0], %[src]\n\t", | |
1054 | abort) | |
1055 | RSEQ_ASM_DEFINE_CMPFAIL(5, | |
1056 | "movl %[rseq_scratch2], %[len]\n\t" | |
1057 | "movl %[rseq_scratch1], %[dst]\n\t" | |
1058 | "movl %[rseq_scratch0], %[src]\n\t", | |
1059 | cmpfail) | |
1060 | #ifdef RSEQ_COMPARE_TWICE | |
1061 | RSEQ_ASM_DEFINE_CMPFAIL(6, | |
1062 | "movl %[rseq_scratch2], %[len]\n\t" | |
1063 | "movl %[rseq_scratch1], %[dst]\n\t" | |
1064 | "movl %[rseq_scratch0], %[src]\n\t", | |
1065 | error1) | |
1066 | RSEQ_ASM_DEFINE_CMPFAIL(7, | |
1067 | "movl %[rseq_scratch2], %[len]\n\t" | |
1068 | "movl %[rseq_scratch1], %[dst]\n\t" | |
1069 | "movl %[rseq_scratch0], %[src]\n\t", | |
1070 | error2) | |
1071 | #endif | |
1072 | : /* gcc asm goto does not allow outputs */ | |
1073 | : [cpu_id] "r" (cpu), | |
1074 | [current_cpu_id] "m" (__rseq_abi.cpu_id), | |
1075 | [rseq_cs] "m" (__rseq_abi.rseq_cs), | |
1076 | /* final store input */ | |
1077 | [v] "m" (*v), | |
1078 | [expect] "m" (expect), | |
1079 | [newv] "m" (newv), | |
1080 | /* try memcpy input */ | |
1081 | [dst] "r" (dst), | |
1082 | [src] "r" (src), | |
1083 | [len] "r" (len), | |
1084 | [rseq_scratch0] "m" (rseq_scratch[0]), | |
1085 | [rseq_scratch1] "m" (rseq_scratch[1]), | |
1086 | [rseq_scratch2] "m" (rseq_scratch[2]) | |
1087 | : "memory", "cc", "eax" | |
1088 | RSEQ_INJECT_CLOBBER | |
1089 | : abort, cmpfail | |
1090 | #ifdef RSEQ_COMPARE_TWICE | |
1091 | , error1, error2 | |
1092 | #endif | |
1093 | ); | |
1094 | return 0; | |
1095 | abort: | |
1096 | RSEQ_INJECT_FAILED | |
1097 | return -1; | |
1098 | cmpfail: | |
1099 | return 1; | |
1100 | #ifdef RSEQ_COMPARE_TWICE | |
1101 | error1: | |
1102 | rseq_bug("cpu_id comparison failed"); | |
1103 | error2: | |
1104 | rseq_bug("expected value comparison failed"); | |
1105 | #endif | |
1106 | } | |
1107 | ||
1108 | /* TODO: implement a faster memcpy. */ | |
1109 | static inline __attribute__((always_inline)) | |
1110 | int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v, intptr_t expect, | |
1111 | void *dst, void *src, size_t len, | |
1112 | intptr_t newv, int cpu) | |
1113 | { | |
1114 | uint32_t rseq_scratch[3]; | |
1115 | ||
1116 | RSEQ_INJECT_C(9) | |
1117 | ||
1118 | __asm__ __volatile__ goto ( | |
1119 | RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */ | |
90d9876e MD |
1120 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail]) |
1121 | #ifdef RSEQ_COMPARE_TWICE | |
1122 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1]) | |
1123 | RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2]) | |
1124 | #endif | |
784b0012 MD |
1125 | "movl %[src], %[rseq_scratch0]\n\t" |
1126 | "movl %[dst], %[rseq_scratch1]\n\t" | |
1127 | "movl %[len], %[rseq_scratch2]\n\t" | |
1128 | /* Start rseq by storing table entry pointer into rseq_cs. */ | |
1129 | RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs) | |
1130 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f) | |
1131 | RSEQ_INJECT_ASM(3) | |
1132 | "movl %[expect], %%eax\n\t" | |
1133 | "cmpl %%eax, %[v]\n\t" | |
1134 | "jnz 5f\n\t" | |
1135 | RSEQ_INJECT_ASM(4) | |
1136 | #ifdef RSEQ_COMPARE_TWICE | |
1137 | RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f) | |
1138 | "movl %[expect], %%eax\n\t" | |
1139 | "cmpl %%eax, %[v]\n\t" | |
1140 | "jnz 7f\n\t" | |
1141 | #endif | |
1142 | /* try memcpy */ | |
1143 | "test %[len], %[len]\n\t" \ | |
1144 | "jz 333f\n\t" \ | |
1145 | "222:\n\t" \ | |
1146 | "movb (%[src]), %%al\n\t" \ | |
1147 | "movb %%al, (%[dst])\n\t" \ | |
1148 | "inc %[src]\n\t" \ | |
1149 | "inc %[dst]\n\t" \ | |
1150 | "dec %[len]\n\t" \ | |
1151 | "jnz 222b\n\t" \ | |
1152 | "333:\n\t" \ | |
1153 | RSEQ_INJECT_ASM(5) | |
1154 | "lock; addl $0,-128(%%esp)\n\t" | |
1155 | "movl %[newv], %%eax\n\t" | |
1156 | /* final store */ | |
1157 | "movl %%eax, %[v]\n\t" | |
1158 | "2:\n\t" | |
1159 | RSEQ_INJECT_ASM(6) | |
1160 | /* teardown */ | |
1161 | "movl %[rseq_scratch2], %[len]\n\t" | |
1162 | "movl %[rseq_scratch1], %[dst]\n\t" | |
1163 | "movl %[rseq_scratch0], %[src]\n\t" | |
1164 | RSEQ_ASM_DEFINE_ABORT(4, | |
1165 | "movl %[rseq_scratch2], %[len]\n\t" | |
1166 | "movl %[rseq_scratch1], %[dst]\n\t" | |
1167 | "movl %[rseq_scratch0], %[src]\n\t", | |
1168 | abort) | |
1169 | RSEQ_ASM_DEFINE_CMPFAIL(5, | |
1170 | "movl %[rseq_scratch2], %[len]\n\t" | |
1171 | "movl %[rseq_scratch1], %[dst]\n\t" | |
1172 | "movl %[rseq_scratch0], %[src]\n\t", | |
1173 | cmpfail) | |
1174 | #ifdef RSEQ_COMPARE_TWICE | |
1175 | RSEQ_ASM_DEFINE_CMPFAIL(6, | |
1176 | "movl %[rseq_scratch2], %[len]\n\t" | |
1177 | "movl %[rseq_scratch1], %[dst]\n\t" | |
1178 | "movl %[rseq_scratch0], %[src]\n\t", | |
1179 | error1) | |
1180 | RSEQ_ASM_DEFINE_CMPFAIL(7, | |
1181 | "movl %[rseq_scratch2], %[len]\n\t" | |
1182 | "movl %[rseq_scratch1], %[dst]\n\t" | |
1183 | "movl %[rseq_scratch0], %[src]\n\t", | |
1184 | error2) | |
1185 | #endif | |
1186 | : /* gcc asm goto does not allow outputs */ | |
1187 | : [cpu_id] "r" (cpu), | |
1188 | [current_cpu_id] "m" (__rseq_abi.cpu_id), | |
1189 | [rseq_cs] "m" (__rseq_abi.rseq_cs), | |
1190 | /* final store input */ | |
1191 | [v] "m" (*v), | |
1192 | [expect] "m" (expect), | |
1193 | [newv] "m" (newv), | |
1194 | /* try memcpy input */ | |
1195 | [dst] "r" (dst), | |
1196 | [src] "r" (src), | |
1197 | [len] "r" (len), | |
1198 | [rseq_scratch0] "m" (rseq_scratch[0]), | |
1199 | [rseq_scratch1] "m" (rseq_scratch[1]), | |
1200 | [rseq_scratch2] "m" (rseq_scratch[2]) | |
1201 | : "memory", "cc", "eax" | |
1202 | RSEQ_INJECT_CLOBBER | |
1203 | : abort, cmpfail | |
1204 | #ifdef RSEQ_COMPARE_TWICE | |
1205 | , error1, error2 | |
1206 | #endif | |
1207 | ); | |
1208 | return 0; | |
1209 | abort: | |
1210 | RSEQ_INJECT_FAILED | |
1211 | return -1; | |
1212 | cmpfail: | |
1213 | return 1; | |
1214 | #ifdef RSEQ_COMPARE_TWICE | |
1215 | error1: | |
1216 | rseq_bug("cpu_id comparison failed"); | |
1217 | error2: | |
1218 | rseq_bug("expected value comparison failed"); | |
1219 | #endif | |
1220 | } | |
1221 | ||
1222 | #endif /* !RSEQ_SKIP_FASTPATH */ | |
1223 | ||
1224 | #endif |