]>
Commit | Line | Data |
---|---|---|
e6cd4bb5 RH |
1 | /* |
2 | * Simple interface for 128-bit atomic operations. | |
3 | * | |
4 | * Copyright (C) 2018 Linaro, Ltd. | |
5 | * | |
6 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
7 | * See the COPYING file in the top-level directory. | |
8 | * | |
9 | * See docs/devel/atomics.txt for discussion about the guarantees each | |
10 | * atomic primitive is meant to provide. | |
11 | */ | |
12 | ||
13 | #ifndef QEMU_ATOMIC128_H | |
14 | #define QEMU_ATOMIC128_H | |
15 | ||
16 | /* | |
17 | * GCC is a house divided about supporting large atomic operations. | |
18 | * | |
19 | * For hosts that only have large compare-and-swap, a legalistic reading | |
20 | * of the C++ standard means that one cannot implement __atomic_read on | |
21 | * read-only memory, and thus all atomic operations must synchronize | |
22 | * through libatomic. | |
23 | * | |
24 | * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80878 | |
25 | * | |
26 | * This interpretation is not especially helpful for QEMU. | |
27 | * For softmmu, all RAM is always read/write from the hypervisor. | |
28 | * For user-only, if the guest doesn't implement such an __atomic_read | |
29 | * then the host need not worry about it either. | |
30 | * | |
31 | * Moreover, using libatomic is not an option, because its interface is | |
32 | * built for std::atomic<T>, and requires that *all* accesses to such an | |
33 | * object go through the library. In our case we do not have an object | |
34 | * in the C/C++ sense, but a view of memory as seen by the guest. | |
35 | * The guest may issue a large atomic operation and then access those | |
36 | * pieces using word-sized accesses. From the hypervisor, we have no | |
37 | * way to connect those two actions. | |
38 | * | |
39 | * Therefore, special case each platform. | |
40 | */ | |
41 | ||
42 | #if defined(CONFIG_ATOMIC128) | |
43 | static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) | |
44 | { | |
45 | return atomic_cmpxchg__nocheck(ptr, cmp, new); | |
46 | } | |
47 | # define HAVE_CMPXCHG128 1 | |
48 | #elif defined(CONFIG_CMPXCHG128) | |
49 | static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) | |
50 | { | |
51 | return __sync_val_compare_and_swap_16(ptr, cmp, new); | |
52 | } | |
53 | # define HAVE_CMPXCHG128 1 | |
54 | #elif defined(__aarch64__) | |
55 | /* Through gcc 8, aarch64 has no support for 128-bit at all. */ | |
56 | static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) | |
57 | { | |
58 | uint64_t cmpl = int128_getlo(cmp), cmph = int128_gethi(cmp); | |
59 | uint64_t newl = int128_getlo(new), newh = int128_gethi(new); | |
60 | uint64_t oldl, oldh; | |
61 | uint32_t tmp; | |
62 | ||
63 | asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t" | |
64 | "cmp %[oldl], %[cmpl]\n\t" | |
65 | "ccmp %[oldh], %[cmph], #0, eq\n\t" | |
66 | "b.ne 1f\n\t" | |
67 | "stlxp %w[tmp], %[newl], %[newh], %[mem]\n\t" | |
68 | "cbnz %w[tmp], 0b\n" | |
69 | "1:" | |
70 | : [mem] "+m"(*ptr), [tmp] "=&r"(tmp), | |
7400d693 | 71 | [oldl] "=&r"(oldl), [oldh] "=&r"(oldh) |
e6cd4bb5 RH |
72 | : [cmpl] "r"(cmpl), [cmph] "r"(cmph), |
73 | [newl] "r"(newl), [newh] "r"(newh) | |
74 | : "memory", "cc"); | |
75 | ||
76 | return int128_make128(oldl, oldh); | |
77 | } | |
78 | # define HAVE_CMPXCHG128 1 | |
79 | #else | |
80 | /* Fallback definition that must be optimized away, or error. */ | |
81 | Int128 QEMU_ERROR("unsupported atomic") | |
82 | atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new); | |
83 | # define HAVE_CMPXCHG128 0 | |
84 | #endif /* Some definition for HAVE_CMPXCHG128 */ | |
85 | ||
86 | ||
87 | #if defined(CONFIG_ATOMIC128) | |
88 | static inline Int128 atomic16_read(Int128 *ptr) | |
89 | { | |
90 | return atomic_read__nocheck(ptr); | |
91 | } | |
92 | ||
93 | static inline void atomic16_set(Int128 *ptr, Int128 val) | |
94 | { | |
95 | atomic_set__nocheck(ptr, val); | |
96 | } | |
97 | ||
98 | # define HAVE_ATOMIC128 1 | |
99 | #elif !defined(CONFIG_USER_ONLY) && defined(__aarch64__) | |
100 | /* We can do better than cmpxchg for AArch64. */ | |
101 | static inline Int128 atomic16_read(Int128 *ptr) | |
102 | { | |
103 | uint64_t l, h; | |
104 | uint32_t tmp; | |
105 | ||
106 | /* The load must be paired with the store to guarantee not tearing. */ | |
107 | asm("0: ldxp %[l], %[h], %[mem]\n\t" | |
108 | "stxp %w[tmp], %[l], %[h], %[mem]\n\t" | |
109 | "cbnz %w[tmp], 0b" | |
110 | : [mem] "+m"(*ptr), [tmp] "=r"(tmp), [l] "=r"(l), [h] "=r"(h)); | |
111 | ||
112 | return int128_make128(l, h); | |
113 | } | |
114 | ||
115 | static inline void atomic16_set(Int128 *ptr, Int128 val) | |
116 | { | |
117 | uint64_t l = int128_getlo(val), h = int128_gethi(val); | |
118 | uint64_t t1, t2; | |
119 | ||
120 | /* Load into temporaries to acquire the exclusive access lock. */ | |
121 | asm("0: ldxp %[t1], %[t2], %[mem]\n\t" | |
122 | "stxp %w[t1], %[l], %[h], %[mem]\n\t" | |
123 | "cbnz %w[t1], 0b" | |
124 | : [mem] "+m"(*ptr), [t1] "=&r"(t1), [t2] "=&r"(t2) | |
125 | : [l] "r"(l), [h] "r"(h)); | |
126 | } | |
127 | ||
128 | # define HAVE_ATOMIC128 1 | |
129 | #elif !defined(CONFIG_USER_ONLY) && HAVE_CMPXCHG128 | |
130 | static inline Int128 atomic16_read(Int128 *ptr) | |
131 | { | |
132 | /* Maybe replace 0 with 0, returning the old value. */ | |
133 | return atomic16_cmpxchg(ptr, 0, 0); | |
134 | } | |
135 | ||
136 | static inline void atomic16_set(Int128 *ptr, Int128 val) | |
137 | { | |
138 | Int128 old = *ptr, cmp; | |
139 | do { | |
140 | cmp = old; | |
141 | old = atomic16_cmpxchg(ptr, cmp, val); | |
142 | } while (old != cmp); | |
143 | } | |
144 | ||
145 | # define HAVE_ATOMIC128 1 | |
146 | #else | |
147 | /* Fallback definitions that must be optimized away, or error. */ | |
148 | Int128 QEMU_ERROR("unsupported atomic") atomic16_read(Int128 *ptr); | |
149 | void QEMU_ERROR("unsupported atomic") atomic16_set(Int128 *ptr, Int128 val); | |
150 | # define HAVE_ATOMIC128 0 | |
151 | #endif /* Some definition for HAVE_ATOMIC128 */ | |
152 | ||
153 | #endif /* QEMU_ATOMIC128_H */ |