]>
Commit | Line | Data |
---|---|---|
41aac0de | 1 | /* 64bit Linux-specific atomic operations for ARM EABI. |
a945c346 | 2 | Copyright (C) 2008-2024 Free Software Foundation, Inc. |
41aac0de DAG |
3 | Based on linux-atomic.c |
4 | ||
5 | 64 bit additions david.gilbert@linaro.org | |
6 | ||
7 | This file is part of GCC. | |
8 | ||
9 | GCC is free software; you can redistribute it and/or modify it under | |
10 | the terms of the GNU General Public License as published by the Free | |
11 | Software Foundation; either version 3, or (at your option) any later | |
12 | version. | |
13 | ||
14 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
15 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
17 | for more details. | |
18 | ||
19 | Under Section 7 of GPL version 3, you are granted additional | |
20 | permissions described in the GCC Runtime Library Exception, version | |
21 | 3.1, as published by the Free Software Foundation. | |
22 | ||
23 | You should have received a copy of the GNU General Public License and | |
24 | a copy of the GCC Runtime Library Exception along with this program; | |
25 | see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
26 | <http://www.gnu.org/licenses/>. */ | |
27 | ||
28 | /* 64bit helper functions for atomic operations; the compiler will | |
29 | call these when the code is compiled for a CPU without ldrexd/strexd. | |
30 | (If the CPU had those then the compiler inlines the operation). | |
31 | ||
32 | These helpers require a kernel helper that's only present on newer | |
33 | kernels; we check for that in an init section and bail out rather | |
34 | unceremoneously. */ | |
35 | ||
78cc43a2 | 36 | extern int write (int fd, const void *buf, unsigned int count); |
41aac0de DAG |
37 | extern void abort (void); |
38 | ||
39 | /* Kernel helper for compare-and-exchange. */ | |
40 | typedef int (__kernel_cmpxchg64_t) (const long long* oldval, | |
41 | const long long* newval, | |
42 | long long *ptr); | |
43 | #define __kernel_cmpxchg64 (*(__kernel_cmpxchg64_t *) 0xffff0f60) | |
44 | ||
45 | /* Kernel helper page version number. */ | |
46 | #define __kernel_helper_version (*(unsigned int *)0xffff0ffc) | |
47 | ||
48 | /* Check that the kernel has a new enough version at load. */ | |
49 | static void __check_for_sync8_kernelhelper (void) | |
50 | { | |
51 | if (__kernel_helper_version < 5) | |
52 | { | |
53 | const char err[] = "A newer kernel is required to run this binary. " | |
54 | "(__kernel_cmpxchg64 helper)\n"; | |
55 | /* At this point we need a way to crash with some information | |
56 | for the user - I'm not sure I can rely on much else being | |
57 | available at this point, so do the same as generic-morestack.c | |
58 | write () and abort (). */ | |
78cc43a2 | 59 | write (2 /* stderr. */, err, sizeof (err)); |
41aac0de DAG |
60 | abort (); |
61 | } | |
62 | }; | |
63 | ||
64 | static void (*__sync8_kernelhelper_inithook[]) (void) | |
65 | __attribute__ ((used, section (".init_array"))) = { | |
66 | &__check_for_sync8_kernelhelper | |
67 | }; | |
68 | ||
69 | #define HIDDEN __attribute__ ((visibility ("hidden"))) | |
70 | ||
71 | #define FETCH_AND_OP_WORD64(OP, PFX_OP, INF_OP) \ | |
72 | long long HIDDEN \ | |
73 | __sync_fetch_and_##OP##_8 (long long *ptr, long long val) \ | |
74 | { \ | |
75 | int failure; \ | |
76 | long long tmp,tmp2; \ | |
77 | \ | |
78 | do { \ | |
79 | tmp = *ptr; \ | |
80 | tmp2 = PFX_OP (tmp INF_OP val); \ | |
81 | failure = __kernel_cmpxchg64 (&tmp, &tmp2, ptr); \ | |
82 | } while (failure != 0); \ | |
83 | \ | |
84 | return tmp; \ | |
85 | } | |
86 | ||
87 | FETCH_AND_OP_WORD64 (add, , +) | |
88 | FETCH_AND_OP_WORD64 (sub, , -) | |
89 | FETCH_AND_OP_WORD64 (or, , |) | |
90 | FETCH_AND_OP_WORD64 (and, , &) | |
91 | FETCH_AND_OP_WORD64 (xor, , ^) | |
92 | FETCH_AND_OP_WORD64 (nand, ~, &) | |
93 | ||
94 | #define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH | |
95 | #define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH | |
96 | ||
97 | /* Implement both __sync_<op>_and_fetch and __sync_fetch_and_<op> for | |
98 | subword-sized quantities. */ | |
99 | ||
100 | #define OP_AND_FETCH_WORD64(OP, PFX_OP, INF_OP) \ | |
101 | long long HIDDEN \ | |
102 | __sync_##OP##_and_fetch_8 (long long *ptr, long long val) \ | |
103 | { \ | |
104 | int failure; \ | |
105 | long long tmp,tmp2; \ | |
106 | \ | |
107 | do { \ | |
108 | tmp = *ptr; \ | |
109 | tmp2 = PFX_OP (tmp INF_OP val); \ | |
110 | failure = __kernel_cmpxchg64 (&tmp, &tmp2, ptr); \ | |
111 | } while (failure != 0); \ | |
112 | \ | |
113 | return tmp2; \ | |
114 | } | |
115 | ||
116 | OP_AND_FETCH_WORD64 (add, , +) | |
117 | OP_AND_FETCH_WORD64 (sub, , -) | |
118 | OP_AND_FETCH_WORD64 (or, , |) | |
119 | OP_AND_FETCH_WORD64 (and, , &) | |
120 | OP_AND_FETCH_WORD64 (xor, , ^) | |
121 | OP_AND_FETCH_WORD64 (nand, ~, &) | |
122 | ||
123 | long long HIDDEN | |
124 | __sync_val_compare_and_swap_8 (long long *ptr, long long oldval, | |
125 | long long newval) | |
126 | { | |
127 | int failure; | |
128 | long long actual_oldval; | |
129 | ||
130 | while (1) | |
131 | { | |
132 | actual_oldval = *ptr; | |
133 | ||
134 | if (__builtin_expect (oldval != actual_oldval, 0)) | |
135 | return actual_oldval; | |
136 | ||
137 | failure = __kernel_cmpxchg64 (&actual_oldval, &newval, ptr); | |
138 | ||
139 | if (__builtin_expect (!failure, 1)) | |
140 | return oldval; | |
141 | } | |
142 | } | |
143 | ||
144 | typedef unsigned char bool; | |
145 | ||
146 | bool HIDDEN | |
147 | __sync_bool_compare_and_swap_8 (long long *ptr, long long oldval, | |
148 | long long newval) | |
149 | { | |
150 | int failure = __kernel_cmpxchg64 (&oldval, &newval, ptr); | |
151 | return (failure == 0); | |
152 | } | |
153 | ||
154 | long long HIDDEN | |
155 | __sync_lock_test_and_set_8 (long long *ptr, long long val) | |
156 | { | |
157 | int failure; | |
158 | long long oldval; | |
159 | ||
160 | do { | |
161 | oldval = *ptr; | |
162 | failure = __kernel_cmpxchg64 (&oldval, &val, ptr); | |
163 | } while (failure != 0); | |
164 | ||
165 | return oldval; | |
166 | } |