master
h 405 lines 14.8 KB
Raw
1 /*
2 * Atomic helper templates
3 * Included from tcg-runtime.c and cputlb.c.
4 *
5 * Copyright (c) 2016 Red Hat, Inc
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "qemu/plugin.h"
22
23 #if DATA_SIZE == 16
24 # define SUFFIX o
25 # define DATA_TYPE Int128
26 # define BSWAP bswap128
27 # define SHIFT 4
28 #elif DATA_SIZE == 8
29 # define SUFFIX q
30 # define DATA_TYPE uint64_t
31 # define SDATA_TYPE int64_t
32 # define BSWAP bswap64
33 # define SHIFT 3
34 #elif DATA_SIZE == 4
35 # define SUFFIX l
36 # define DATA_TYPE uint32_t
37 # define SDATA_TYPE int32_t
38 # define BSWAP bswap32
39 # define SHIFT 2
40 #elif DATA_SIZE == 2
41 # define SUFFIX w
42 # define DATA_TYPE uint16_t
43 # define SDATA_TYPE int16_t
44 # define BSWAP bswap16
45 # define SHIFT 1
46 #elif DATA_SIZE == 1
47 # define SUFFIX b
48 # define DATA_TYPE uint8_t
49 # define SDATA_TYPE int8_t
50 # define BSWAP
51 # define SHIFT 0
52 #else
53 # error unsupported data size
54 #endif
55
56 #if DATA_SIZE == 16
57 # define VALUE_LOW(val) int128_getlo(val)
58 # define VALUE_HIGH(val) int128_gethi(val)
59 #else
60 # define VALUE_LOW(val) val
61 # define VALUE_HIGH(val) 0
62 #endif
63
64 #if DATA_SIZE >= 4
65 # define ABI_TYPE DATA_TYPE
66 #else
67 # define ABI_TYPE uint32_t
68 #endif
69
70 /* Define host-endian atomic operations. Note that END is used within
71 the ATOMIC_NAME macro, and redefined below. */
72 #if DATA_SIZE == 1
73 # define END
74 #elif HOST_BIG_ENDIAN
75 # define END _be
76 #else
77 # define END _le
78 #endif
79
80 ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr,
81 ABI_TYPE cmpv, ABI_TYPE newv,
82 MemOpIdx oi, uintptr_t retaddr)
83 {
84 DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
85 DATA_SIZE, retaddr);
86 DATA_TYPE ret;
87
88 #if DATA_SIZE == 16
89 ret = atomic16_cmpxchg(haddr, cmpv, newv);
90 #else
91 ret = qatomic_cmpxchg__nocheck(haddr, cmpv, newv);
92 #endif
93 ATOMIC_MMU_CLEANUP;
94 atomic_trace_rmw_post(env, addr,
95 VALUE_LOW(ret),
96 VALUE_HIGH(ret),
97 VALUE_LOW(newv),
98 VALUE_HIGH(newv),
99 oi);
100 return ret;
101 }
102
103 ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
104 MemOpIdx oi, uintptr_t retaddr)
105 {
106 DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
107 DATA_SIZE, retaddr);
108 DATA_TYPE ret;
109
110 #if DATA_SIZE == 16
111 ret = atomic16_xchg(haddr, val);
112 #else
113 ret = qatomic_xchg__nocheck(haddr, val);
114 #endif
115 ATOMIC_MMU_CLEANUP;
116 atomic_trace_rmw_post(env, addr,
117 VALUE_LOW(ret),
118 VALUE_HIGH(ret),
119 VALUE_LOW(val),
120 VALUE_HIGH(val),
121 oi);
122 return ret;
123 }
124
125 #if DATA_SIZE == 16
126 ABI_TYPE ATOMIC_NAME(fetch_and)(CPUArchState *env, vaddr addr, ABI_TYPE val,
127 MemOpIdx oi, uintptr_t retaddr)
128 {
129 DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
130 DATA_SIZE, retaddr);
131 DATA_TYPE ret = atomic16_fetch_and(haddr, val);
132 ATOMIC_MMU_CLEANUP;
133 atomic_trace_rmw_post(env, addr,
134 VALUE_LOW(ret),
135 VALUE_HIGH(ret),
136 VALUE_LOW(val),
137 VALUE_HIGH(val),
138 oi);
139 return ret;
140 }
141
142 ABI_TYPE ATOMIC_NAME(fetch_or)(CPUArchState *env, vaddr addr, ABI_TYPE val,
143 MemOpIdx oi, uintptr_t retaddr)
144 {
145 DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
146 DATA_SIZE, retaddr);
147 DATA_TYPE ret = atomic16_fetch_or(haddr, val);
148 ATOMIC_MMU_CLEANUP;
149 atomic_trace_rmw_post(env, addr,
150 VALUE_LOW(ret),
151 VALUE_HIGH(ret),
152 VALUE_LOW(val),
153 VALUE_HIGH(val),
154 oi);
155 return ret;
156 }
157 #else
158 #define GEN_ATOMIC_HELPER(X) \
159 ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
160 ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
161 { \
162 DATA_TYPE *haddr, ret; \
163 haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, DATA_SIZE, retaddr); \
164 ret = qatomic_##X(haddr, val); \
165 ATOMIC_MMU_CLEANUP; \
166 atomic_trace_rmw_post(env, addr, \
167 VALUE_LOW(ret), \
168 VALUE_HIGH(ret), \
169 VALUE_LOW(val), \
170 VALUE_HIGH(val), \
171 oi); \
172 return ret; \
173 }
174
175 GEN_ATOMIC_HELPER(fetch_add)
176 GEN_ATOMIC_HELPER(fetch_and)
177 GEN_ATOMIC_HELPER(fetch_or)
178 GEN_ATOMIC_HELPER(fetch_xor)
179 GEN_ATOMIC_HELPER(add_fetch)
180 GEN_ATOMIC_HELPER(and_fetch)
181 GEN_ATOMIC_HELPER(or_fetch)
182 GEN_ATOMIC_HELPER(xor_fetch)
183
184 #undef GEN_ATOMIC_HELPER
185
186 /*
187 * These helpers are, as a whole, full barriers. Within the helper,
188 * the leading barrier is explicit and the trailing barrier is within
189 * cmpxchg primitive.
190 *
191 * Trace this load + RMW loop as a single RMW op. This way, regardless
192 * of CF_PARALLEL's value, we'll trace just a read and a write.
193 */
194 #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
195 ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
196 ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \
197 { \
198 XDATA_TYPE *haddr, cmp, old, new, val = xval; \
199 haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, DATA_SIZE, retaddr); \
200 smp_mb(); \
201 cmp = qatomic_read__nocheck(haddr); \
202 do { \
203 old = cmp; new = FN(old, val); \
204 cmp = qatomic_cmpxchg__nocheck(haddr, old, new); \
205 } while (cmp != old); \
206 ATOMIC_MMU_CLEANUP; \
207 atomic_trace_rmw_post(env, addr, \
208 VALUE_LOW(old), \
209 VALUE_HIGH(old), \
210 VALUE_LOW(xval), \
211 VALUE_HIGH(xval), \
212 oi); \
213 return RET; \
214 }
215
216 GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old)
217 GEN_ATOMIC_HELPER_FN(fetch_umin, MIN, DATA_TYPE, old)
218 GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old)
219 GEN_ATOMIC_HELPER_FN(fetch_umax, MAX, DATA_TYPE, old)
220
221 GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new)
222 GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new)
223 GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
224 GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
225
226 #undef GEN_ATOMIC_HELPER_FN
227 #endif /* DATA SIZE == 16 */
228
229 #undef END
230
231 #if DATA_SIZE > 1
232
233 /* Define reverse-host-endian atomic operations. Note that END is used
234 within the ATOMIC_NAME macro. */
235 #if HOST_BIG_ENDIAN
236 # define END _le
237 #else
238 # define END _be
239 #endif
240
241 ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr,
242 ABI_TYPE cmpv, ABI_TYPE newv,
243 MemOpIdx oi, uintptr_t retaddr)
244 {
245 DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
246 DATA_SIZE, retaddr);
247 DATA_TYPE ret;
248
249 #if DATA_SIZE == 16
250 ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv));
251 #else
252 ret = qatomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
253 #endif
254 ATOMIC_MMU_CLEANUP;
255 atomic_trace_rmw_post(env, addr,
256 VALUE_LOW(ret),
257 VALUE_HIGH(ret),
258 VALUE_LOW(newv),
259 VALUE_HIGH(newv),
260 oi);
261 return BSWAP(ret);
262 }
263
264 ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
265 MemOpIdx oi, uintptr_t retaddr)
266 {
267 DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
268 DATA_SIZE, retaddr);
269 ABI_TYPE ret;
270
271 #if DATA_SIZE == 16
272 ret = atomic16_xchg(haddr, BSWAP(val));
273 #else
274 ret = qatomic_xchg__nocheck(haddr, BSWAP(val));
275 #endif
276 ATOMIC_MMU_CLEANUP;
277 atomic_trace_rmw_post(env, addr,
278 VALUE_LOW(ret),
279 VALUE_HIGH(ret),
280 VALUE_LOW(val),
281 VALUE_HIGH(val),
282 oi);
283 return BSWAP(ret);
284 }
285
286 #if DATA_SIZE == 16
287 ABI_TYPE ATOMIC_NAME(fetch_and)(CPUArchState *env, vaddr addr, ABI_TYPE val,
288 MemOpIdx oi, uintptr_t retaddr)
289 {
290 DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
291 DATA_SIZE, retaddr);
292 DATA_TYPE ret = atomic16_fetch_and(haddr, BSWAP(val));
293 ATOMIC_MMU_CLEANUP;
294 atomic_trace_rmw_post(env, addr,
295 VALUE_LOW(ret),
296 VALUE_HIGH(ret),
297 VALUE_LOW(val),
298 VALUE_HIGH(val),
299 oi);
300 return BSWAP(ret);
301 }
302
303 ABI_TYPE ATOMIC_NAME(fetch_or)(CPUArchState *env, vaddr addr, ABI_TYPE val,
304 MemOpIdx oi, uintptr_t retaddr)
305 {
306 DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
307 DATA_SIZE, retaddr);
308 DATA_TYPE ret = atomic16_fetch_or(haddr, BSWAP(val));
309 ATOMIC_MMU_CLEANUP;
310 atomic_trace_rmw_post(env, addr,
311 VALUE_LOW(ret),
312 VALUE_HIGH(ret),
313 VALUE_LOW(val),
314 VALUE_HIGH(val),
315 oi);
316 return BSWAP(ret);
317 }
318 #else
319 #define GEN_ATOMIC_HELPER(X) \
320 ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
321 ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
322 { \
323 DATA_TYPE *haddr, ret; \
324 haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, DATA_SIZE, retaddr); \
325 ret = qatomic_##X(haddr, BSWAP(val)); \
326 ATOMIC_MMU_CLEANUP; \
327 atomic_trace_rmw_post(env, addr, \
328 VALUE_LOW(ret), \
329 VALUE_HIGH(ret), \
330 VALUE_LOW(val), \
331 VALUE_HIGH(val), \
332 oi); \
333 return BSWAP(ret); \
334 }
335
336 GEN_ATOMIC_HELPER(fetch_and)
337 GEN_ATOMIC_HELPER(fetch_or)
338 GEN_ATOMIC_HELPER(fetch_xor)
339 GEN_ATOMIC_HELPER(and_fetch)
340 GEN_ATOMIC_HELPER(or_fetch)
341 GEN_ATOMIC_HELPER(xor_fetch)
342
343 #undef GEN_ATOMIC_HELPER
344
345 /* These helpers are, as a whole, full barriers. Within the helper,
346 * the leading barrier is explicit and the trailing barrier is within
347 * cmpxchg primitive.
348 *
349 * Trace this load + RMW loop as a single RMW op. This way, regardless
350 * of CF_PARALLEL's value, we'll trace just a read and a write.
351 */
352 #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
353 ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
354 ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \
355 { \
356 XDATA_TYPE *haddr, ldo, ldn, old, new, val = xval; \
357 haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, DATA_SIZE, retaddr); \
358 smp_mb(); \
359 ldn = qatomic_read__nocheck(haddr); \
360 do { \
361 ldo = ldn; old = BSWAP(ldo); new = FN(old, val); \
362 ldn = qatomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \
363 } while (ldo != ldn); \
364 ATOMIC_MMU_CLEANUP; \
365 atomic_trace_rmw_post(env, addr, \
366 VALUE_LOW(old), \
367 VALUE_HIGH(old), \
368 VALUE_LOW(xval), \
369 VALUE_HIGH(xval), \
370 oi); \
371 return RET; \
372 }
373
374 GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old)
375 GEN_ATOMIC_HELPER_FN(fetch_umin, MIN, DATA_TYPE, old)
376 GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old)
377 GEN_ATOMIC_HELPER_FN(fetch_umax, MAX, DATA_TYPE, old)
378
379 GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new)
380 GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new)
381 GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
382 GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
383
384 /* Note that for addition, we need to use a separate cmpxchg loop instead
385 of bswaps for the reverse-host-endian helpers. */
386 #define ADD(X, Y) (X + Y)
387 GEN_ATOMIC_HELPER_FN(fetch_add, ADD, DATA_TYPE, old)
388 GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
389 #undef ADD
390
391 #undef GEN_ATOMIC_HELPER_FN
392 #endif /* DATA_SIZE == 16 */
393
394 #undef END
395 #endif /* DATA_SIZE > 1 */
396
397 #undef BSWAP
398 #undef ABI_TYPE
399 #undef DATA_TYPE
400 #undef SDATA_TYPE
401 #undef SUFFIX
402 #undef DATA_SIZE
403 #undef SHIFT
404 #undef VALUE_LOW
405 #undef VALUE_HIGH