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 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 #if DATA_SIZE == 16 22 # define SUFFIX o 23 # define DATA_TYPE Int128 24 # define BSWAP bswap128 25 #elif DATA_SIZE == 8 26 # define SUFFIX q 27 # define DATA_TYPE uint64_t 28 # define BSWAP bswap64 29 #elif DATA_SIZE == 4 30 # define SUFFIX l 31 # define DATA_TYPE uint32_t 32 # define BSWAP bswap32 33 #elif DATA_SIZE == 2 34 # define SUFFIX w 35 # define DATA_TYPE uint16_t 36 # define BSWAP bswap16 37 #elif DATA_SIZE == 1 38 # define SUFFIX b 39 # define DATA_TYPE uint8_t 40 # define BSWAP 41 #else 42 # error unsupported data size 43 #endif 44 45 #if DATA_SIZE >= 4 46 # define ABI_TYPE DATA_TYPE 47 #else 48 # define ABI_TYPE uint32_t 49 #endif 50 51 /* Define host-endian atomic operations. Note that END is used within 52 the ATOMIC_NAME macro, and redefined below. */ 53 #if DATA_SIZE == 1 54 # define END 55 #elif defined(HOST_WORDS_BIGENDIAN) 56 # define END _be 57 #else 58 # define END _le 59 #endif 60 61 ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, 62 ABI_TYPE cmpv, ABI_TYPE newv EXTRA_ARGS) 63 { 64 DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; 65 return atomic_cmpxchg__nocheck(haddr, cmpv, newv); 66 } 67 68 #if DATA_SIZE >= 16 69 ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) 70 { 71 DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; 72 __atomic_load(haddr, &val, __ATOMIC_RELAXED); 73 return val; 74 } 75 76 void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, 77 ABI_TYPE val EXTRA_ARGS) 78 { 79 DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; 80 __atomic_store(haddr, &val, __ATOMIC_RELAXED); 81 } 82 #else 83 ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, 84 ABI_TYPE val EXTRA_ARGS) 85 { 86 DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; 87 return atomic_xchg__nocheck(haddr, val); 88 } 89 90 #define GEN_ATOMIC_HELPER(X) \ 91 ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ 92 ABI_TYPE val EXTRA_ARGS) \ 93 { \ 94 DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ 95 return atomic_##X(haddr, val); \ 96 } \ 97 98 GEN_ATOMIC_HELPER(fetch_add) 99 GEN_ATOMIC_HELPER(fetch_and) 100 GEN_ATOMIC_HELPER(fetch_or) 101 GEN_ATOMIC_HELPER(fetch_xor) 102 GEN_ATOMIC_HELPER(add_fetch) 103 GEN_ATOMIC_HELPER(and_fetch) 104 GEN_ATOMIC_HELPER(or_fetch) 105 GEN_ATOMIC_HELPER(xor_fetch) 106 107 #undef GEN_ATOMIC_HELPER 108 #endif /* DATA SIZE >= 16 */ 109 110 #undef END 111 112 #if DATA_SIZE > 1 113 114 /* Define reverse-host-endian atomic operations. Note that END is used 115 within the ATOMIC_NAME macro. */ 116 #ifdef HOST_WORDS_BIGENDIAN 117 # define END _le 118 #else 119 # define END _be 120 #endif 121 122 ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, 123 ABI_TYPE cmpv, ABI_TYPE newv EXTRA_ARGS) 124 { 125 DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; 126 return BSWAP(atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv))); 127 } 128 129 #if DATA_SIZE >= 16 130 ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) 131 { 132 DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; 133 __atomic_load(haddr, &val, __ATOMIC_RELAXED); 134 return BSWAP(val); 135 } 136 137 void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, 138 ABI_TYPE val EXTRA_ARGS) 139 { 140 DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; 141 val = BSWAP(val); 142 __atomic_store(haddr, &val, __ATOMIC_RELAXED); 143 } 144 #else 145 ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, 146 ABI_TYPE val EXTRA_ARGS) 147 { 148 DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; 149 return BSWAP(atomic_xchg__nocheck(haddr, BSWAP(val))); 150 } 151 152 #define GEN_ATOMIC_HELPER(X) \ 153 ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ 154 ABI_TYPE val EXTRA_ARGS) \ 155 { \ 156 DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ 157 return BSWAP(atomic_##X(haddr, BSWAP(val))); \ 158 } 159 160 GEN_ATOMIC_HELPER(fetch_and) 161 GEN_ATOMIC_HELPER(fetch_or) 162 GEN_ATOMIC_HELPER(fetch_xor) 163 GEN_ATOMIC_HELPER(and_fetch) 164 GEN_ATOMIC_HELPER(or_fetch) 165 GEN_ATOMIC_HELPER(xor_fetch) 166 167 #undef GEN_ATOMIC_HELPER 168 169 /* Note that for addition, we need to use a separate cmpxchg loop instead 170 of bswaps for the reverse-host-endian helpers. */ 171 ABI_TYPE ATOMIC_NAME(fetch_add)(CPUArchState *env, target_ulong addr, 172 ABI_TYPE val EXTRA_ARGS) 173 { 174 DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; 175 DATA_TYPE ldo, ldn, ret, sto; 176 177 ldo = atomic_read__nocheck(haddr); 178 while (1) { 179 ret = BSWAP(ldo); 180 sto = BSWAP(ret + val); 181 ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto); 182 if (ldn == ldo) { 183 return ret; 184 } 185 ldo = ldn; 186 } 187 } 188 189 ABI_TYPE ATOMIC_NAME(add_fetch)(CPUArchState *env, target_ulong addr, 190 ABI_TYPE val EXTRA_ARGS) 191 { 192 DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; 193 DATA_TYPE ldo, ldn, ret, sto; 194 195 ldo = atomic_read__nocheck(haddr); 196 while (1) { 197 ret = BSWAP(ldo) + val; 198 sto = BSWAP(ret); 199 ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto); 200 if (ldn == ldo) { 201 return ret; 202 } 203 ldo = ldn; 204 } 205 } 206 #endif /* DATA_SIZE >= 16 */ 207 208 #undef END 209 #endif /* DATA_SIZE > 1 */ 210 211 #undef BSWAP 212 #undef ABI_TYPE 213 #undef DATA_TYPE 214 #undef SUFFIX 215 #undef DATA_SIZE 216