1c482cb11SRichard Henderson /* 2c482cb11SRichard Henderson * Atomic helper templates 3c482cb11SRichard Henderson * Included from tcg-runtime.c and cputlb.c. 4c482cb11SRichard Henderson * 5c482cb11SRichard Henderson * Copyright (c) 2016 Red Hat, Inc 6c482cb11SRichard Henderson * 7c482cb11SRichard Henderson * This library is free software; you can redistribute it and/or 8c482cb11SRichard Henderson * modify it under the terms of the GNU Lesser General Public 9c482cb11SRichard Henderson * License as published by the Free Software Foundation; either 10fb0343d5SThomas Huth * version 2.1 of the License, or (at your option) any later version. 11c482cb11SRichard Henderson * 12c482cb11SRichard Henderson * This library is distributed in the hope that it will be useful, 13c482cb11SRichard Henderson * but WITHOUT ANY WARRANTY; without even the implied warranty of 14c482cb11SRichard Henderson * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15c482cb11SRichard Henderson * Lesser General Public License for more details. 16c482cb11SRichard Henderson * 17c482cb11SRichard Henderson * You should have received a copy of the GNU Lesser General Public 18c482cb11SRichard Henderson * License along with this library; if not, see <http://www.gnu.org/licenses/>. 19c482cb11SRichard Henderson */ 20c482cb11SRichard Henderson 21e6d86bedSEmilio G. Cota #include "qemu/plugin.h" 22d071f4cdSEmilio G. Cota 237ebee43eSRichard Henderson #if DATA_SIZE == 16 247ebee43eSRichard Henderson # define SUFFIX o 257ebee43eSRichard Henderson # define DATA_TYPE Int128 267ebee43eSRichard Henderson # define BSWAP bswap128 27d071f4cdSEmilio G. Cota # define SHIFT 4 287ebee43eSRichard Henderson #elif DATA_SIZE == 8 29c482cb11SRichard Henderson # define SUFFIX q 309ef0c6d6SRichard Henderson # define DATA_TYPE aligned_uint64_t 319ef0c6d6SRichard Henderson # define SDATA_TYPE aligned_int64_t 32c482cb11SRichard Henderson # define BSWAP bswap64 33d071f4cdSEmilio G. Cota # define SHIFT 3 34c482cb11SRichard Henderson #elif DATA_SIZE == 4 35c482cb11SRichard Henderson # define SUFFIX l 36c482cb11SRichard Henderson # define DATA_TYPE uint32_t 375507c2bfSRichard Henderson # define SDATA_TYPE int32_t 38c482cb11SRichard Henderson # define BSWAP bswap32 39d071f4cdSEmilio G. Cota # define SHIFT 2 40c482cb11SRichard Henderson #elif DATA_SIZE == 2 41c482cb11SRichard Henderson # define SUFFIX w 42c482cb11SRichard Henderson # define DATA_TYPE uint16_t 435507c2bfSRichard Henderson # define SDATA_TYPE int16_t 44c482cb11SRichard Henderson # define BSWAP bswap16 45d071f4cdSEmilio G. Cota # define SHIFT 1 46c482cb11SRichard Henderson #elif DATA_SIZE == 1 47c482cb11SRichard Henderson # define SUFFIX b 48c482cb11SRichard Henderson # define DATA_TYPE uint8_t 495507c2bfSRichard Henderson # define SDATA_TYPE int8_t 50c482cb11SRichard Henderson # define BSWAP 51d071f4cdSEmilio G. Cota # define SHIFT 0 52c482cb11SRichard Henderson #else 53c482cb11SRichard Henderson # error unsupported data size 54c482cb11SRichard Henderson #endif 55c482cb11SRichard Henderson 56c482cb11SRichard Henderson #if DATA_SIZE >= 4 57c482cb11SRichard Henderson # define ABI_TYPE DATA_TYPE 58c482cb11SRichard Henderson #else 59c482cb11SRichard Henderson # define ABI_TYPE uint32_t 60c482cb11SRichard Henderson #endif 61c482cb11SRichard Henderson 62c482cb11SRichard Henderson /* Define host-endian atomic operations. Note that END is used within 63c482cb11SRichard Henderson the ATOMIC_NAME macro, and redefined below. */ 64c482cb11SRichard Henderson #if DATA_SIZE == 1 65c482cb11SRichard Henderson # define END 66e03b5686SMarc-André Lureau #elif HOST_BIG_ENDIAN 67c482cb11SRichard Henderson # define END _be 68c482cb11SRichard Henderson #else 69c482cb11SRichard Henderson # define END _le 70c482cb11SRichard Henderson #endif 71c482cb11SRichard Henderson 72c482cb11SRichard Henderson ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, 7348688fafSRichard Henderson ABI_TYPE cmpv, ABI_TYPE newv, 749002ffcbSRichard Henderson MemOpIdx oi, uintptr_t retaddr) 75c482cb11SRichard Henderson { 76*7bedee32SRichard Henderson DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr); 77d071f4cdSEmilio G. Cota DATA_TYPE ret; 78d071f4cdSEmilio G. Cota 79e6cd4bb5SRichard Henderson #if DATA_SIZE == 16 80e6cd4bb5SRichard Henderson ret = atomic16_cmpxchg(haddr, cmpv, newv); 81e6cd4bb5SRichard Henderson #else 82d73415a3SStefan Hajnoczi ret = qatomic_cmpxchg__nocheck(haddr, cmpv, newv); 83e6cd4bb5SRichard Henderson #endif 84ec603b55SRichard Henderson ATOMIC_MMU_CLEANUP; 85c3e83e37SRichard Henderson atomic_trace_rmw_post(env, addr, oi); 86ec603b55SRichard Henderson return ret; 87c482cb11SRichard Henderson } 88c482cb11SRichard Henderson 89ec4a9629SRichard Henderson #if DATA_SIZE < 16 9048688fafSRichard Henderson ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val, 919002ffcbSRichard Henderson MemOpIdx oi, uintptr_t retaddr) 92c482cb11SRichard Henderson { 93*7bedee32SRichard Henderson DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr); 94d071f4cdSEmilio G. Cota DATA_TYPE ret; 95d071f4cdSEmilio G. Cota 96d73415a3SStefan Hajnoczi ret = qatomic_xchg__nocheck(haddr, val); 97ec603b55SRichard Henderson ATOMIC_MMU_CLEANUP; 98c3e83e37SRichard Henderson atomic_trace_rmw_post(env, addr, oi); 99ec603b55SRichard Henderson return ret; 100c482cb11SRichard Henderson } 101c482cb11SRichard Henderson 102c482cb11SRichard Henderson #define GEN_ATOMIC_HELPER(X) \ 103c482cb11SRichard Henderson ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ 1049002ffcbSRichard Henderson ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \ 105c482cb11SRichard Henderson { \ 106*7bedee32SRichard Henderson DATA_TYPE *haddr, ret; \ 107*7bedee32SRichard Henderson haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr); \ 108d73415a3SStefan Hajnoczi ret = qatomic_##X(haddr, val); \ 109ec603b55SRichard Henderson ATOMIC_MMU_CLEANUP; \ 110c3e83e37SRichard Henderson atomic_trace_rmw_post(env, addr, oi); \ 111ec603b55SRichard Henderson return ret; \ 112ec603b55SRichard Henderson } 113c482cb11SRichard Henderson 114c482cb11SRichard Henderson GEN_ATOMIC_HELPER(fetch_add) 115c482cb11SRichard Henderson GEN_ATOMIC_HELPER(fetch_and) 116c482cb11SRichard Henderson GEN_ATOMIC_HELPER(fetch_or) 117c482cb11SRichard Henderson GEN_ATOMIC_HELPER(fetch_xor) 118c482cb11SRichard Henderson GEN_ATOMIC_HELPER(add_fetch) 119c482cb11SRichard Henderson GEN_ATOMIC_HELPER(and_fetch) 120c482cb11SRichard Henderson GEN_ATOMIC_HELPER(or_fetch) 121c482cb11SRichard Henderson GEN_ATOMIC_HELPER(xor_fetch) 122c482cb11SRichard Henderson 123c482cb11SRichard Henderson #undef GEN_ATOMIC_HELPER 1245507c2bfSRichard Henderson 125a754f7f3SRichard Henderson /* 126a754f7f3SRichard Henderson * These helpers are, as a whole, full barriers. Within the helper, 1275507c2bfSRichard Henderson * the leading barrier is explicit and the trailing barrier is within 1285507c2bfSRichard Henderson * cmpxchg primitive. 129d071f4cdSEmilio G. Cota * 130d071f4cdSEmilio G. Cota * Trace this load + RMW loop as a single RMW op. This way, regardless 131d071f4cdSEmilio G. Cota * of CF_PARALLEL's value, we'll trace just a read and a write. 1325507c2bfSRichard Henderson */ 1335507c2bfSRichard Henderson #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \ 1345507c2bfSRichard Henderson ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ 1359002ffcbSRichard Henderson ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \ 1365507c2bfSRichard Henderson { \ 137*7bedee32SRichard Henderson XDATA_TYPE *haddr, cmp, old, new, val = xval; \ 138*7bedee32SRichard Henderson haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr); \ 1395507c2bfSRichard Henderson smp_mb(); \ 140d73415a3SStefan Hajnoczi cmp = qatomic_read__nocheck(haddr); \ 1415507c2bfSRichard Henderson do { \ 1425507c2bfSRichard Henderson old = cmp; new = FN(old, val); \ 143d73415a3SStefan Hajnoczi cmp = qatomic_cmpxchg__nocheck(haddr, old, new); \ 1445507c2bfSRichard Henderson } while (cmp != old); \ 1455507c2bfSRichard Henderson ATOMIC_MMU_CLEANUP; \ 146c3e83e37SRichard Henderson atomic_trace_rmw_post(env, addr, oi); \ 1475507c2bfSRichard Henderson return RET; \ 1485507c2bfSRichard Henderson } 1495507c2bfSRichard Henderson 1505507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old) 1515507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(fetch_umin, MIN, DATA_TYPE, old) 1525507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old) 1535507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(fetch_umax, MAX, DATA_TYPE, old) 1545507c2bfSRichard Henderson 1555507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new) 1565507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new) 1575507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) 1585507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) 1595507c2bfSRichard Henderson 1605507c2bfSRichard Henderson #undef GEN_ATOMIC_HELPER_FN 161ec4a9629SRichard Henderson #endif /* DATA SIZE < 16 */ 1627ebee43eSRichard Henderson 163c482cb11SRichard Henderson #undef END 164c482cb11SRichard Henderson 165c482cb11SRichard Henderson #if DATA_SIZE > 1 166c482cb11SRichard Henderson 167c482cb11SRichard Henderson /* Define reverse-host-endian atomic operations. Note that END is used 168c482cb11SRichard Henderson within the ATOMIC_NAME macro. */ 169e03b5686SMarc-André Lureau #if HOST_BIG_ENDIAN 170c482cb11SRichard Henderson # define END _le 171c482cb11SRichard Henderson #else 172c482cb11SRichard Henderson # define END _be 173c482cb11SRichard Henderson #endif 174c482cb11SRichard Henderson 175c482cb11SRichard Henderson ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, 17648688fafSRichard Henderson ABI_TYPE cmpv, ABI_TYPE newv, 1779002ffcbSRichard Henderson MemOpIdx oi, uintptr_t retaddr) 178c482cb11SRichard Henderson { 179*7bedee32SRichard Henderson DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr); 180d071f4cdSEmilio G. Cota DATA_TYPE ret; 181d071f4cdSEmilio G. Cota 182e6cd4bb5SRichard Henderson #if DATA_SIZE == 16 183e6cd4bb5SRichard Henderson ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv)); 184e6cd4bb5SRichard Henderson #else 185d73415a3SStefan Hajnoczi ret = qatomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); 186e6cd4bb5SRichard Henderson #endif 187ec603b55SRichard Henderson ATOMIC_MMU_CLEANUP; 188c3e83e37SRichard Henderson atomic_trace_rmw_post(env, addr, oi); 189ec603b55SRichard Henderson return BSWAP(ret); 190c482cb11SRichard Henderson } 191c482cb11SRichard Henderson 192ec4a9629SRichard Henderson #if DATA_SIZE < 16 19348688fafSRichard Henderson ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val, 1949002ffcbSRichard Henderson MemOpIdx oi, uintptr_t retaddr) 195c482cb11SRichard Henderson { 196*7bedee32SRichard Henderson DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr); 197d071f4cdSEmilio G. Cota ABI_TYPE ret; 198d071f4cdSEmilio G. Cota 199d73415a3SStefan Hajnoczi ret = qatomic_xchg__nocheck(haddr, BSWAP(val)); 200ec603b55SRichard Henderson ATOMIC_MMU_CLEANUP; 201c3e83e37SRichard Henderson atomic_trace_rmw_post(env, addr, oi); 202ec603b55SRichard Henderson return BSWAP(ret); 203c482cb11SRichard Henderson } 204c482cb11SRichard Henderson 205c482cb11SRichard Henderson #define GEN_ATOMIC_HELPER(X) \ 206c482cb11SRichard Henderson ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ 2079002ffcbSRichard Henderson ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \ 208c482cb11SRichard Henderson { \ 209*7bedee32SRichard Henderson DATA_TYPE *haddr, ret; \ 210*7bedee32SRichard Henderson haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr); \ 211d73415a3SStefan Hajnoczi ret = qatomic_##X(haddr, BSWAP(val)); \ 212ec603b55SRichard Henderson ATOMIC_MMU_CLEANUP; \ 213c3e83e37SRichard Henderson atomic_trace_rmw_post(env, addr, oi); \ 214ec603b55SRichard Henderson return BSWAP(ret); \ 215c482cb11SRichard Henderson } 216c482cb11SRichard Henderson 217c482cb11SRichard Henderson GEN_ATOMIC_HELPER(fetch_and) 218c482cb11SRichard Henderson GEN_ATOMIC_HELPER(fetch_or) 219c482cb11SRichard Henderson GEN_ATOMIC_HELPER(fetch_xor) 220c482cb11SRichard Henderson GEN_ATOMIC_HELPER(and_fetch) 221c482cb11SRichard Henderson GEN_ATOMIC_HELPER(or_fetch) 222c482cb11SRichard Henderson GEN_ATOMIC_HELPER(xor_fetch) 223c482cb11SRichard Henderson 224c482cb11SRichard Henderson #undef GEN_ATOMIC_HELPER 225c482cb11SRichard Henderson 2265507c2bfSRichard Henderson /* These helpers are, as a whole, full barriers. Within the helper, 2275507c2bfSRichard Henderson * the leading barrier is explicit and the trailing barrier is within 2285507c2bfSRichard Henderson * cmpxchg primitive. 229d071f4cdSEmilio G. Cota * 230d071f4cdSEmilio G. Cota * Trace this load + RMW loop as a single RMW op. This way, regardless 231d071f4cdSEmilio G. Cota * of CF_PARALLEL's value, we'll trace just a read and a write. 2325507c2bfSRichard Henderson */ 2335507c2bfSRichard Henderson #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \ 2345507c2bfSRichard Henderson ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ 2359002ffcbSRichard Henderson ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \ 2365507c2bfSRichard Henderson { \ 237*7bedee32SRichard Henderson XDATA_TYPE *haddr, ldo, ldn, old, new, val = xval; \ 238*7bedee32SRichard Henderson haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr); \ 2395507c2bfSRichard Henderson smp_mb(); \ 240d73415a3SStefan Hajnoczi ldn = qatomic_read__nocheck(haddr); \ 2415507c2bfSRichard Henderson do { \ 2425507c2bfSRichard Henderson ldo = ldn; old = BSWAP(ldo); new = FN(old, val); \ 243d73415a3SStefan Hajnoczi ldn = qatomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \ 2445507c2bfSRichard Henderson } while (ldo != ldn); \ 2455507c2bfSRichard Henderson ATOMIC_MMU_CLEANUP; \ 246c3e83e37SRichard Henderson atomic_trace_rmw_post(env, addr, oi); \ 2475507c2bfSRichard Henderson return RET; \ 2485507c2bfSRichard Henderson } 2495507c2bfSRichard Henderson 2505507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old) 2515507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(fetch_umin, MIN, DATA_TYPE, old) 2525507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old) 2535507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(fetch_umax, MAX, DATA_TYPE, old) 2545507c2bfSRichard Henderson 2555507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new) 2565507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new) 2575507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) 2585507c2bfSRichard Henderson GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) 2595507c2bfSRichard Henderson 26058edf9eeSRichard Henderson /* Note that for addition, we need to use a separate cmpxchg loop instead 26158edf9eeSRichard Henderson of bswaps for the reverse-host-endian helpers. */ 26258edf9eeSRichard Henderson #define ADD(X, Y) (X + Y) 26358edf9eeSRichard Henderson GEN_ATOMIC_HELPER_FN(fetch_add, ADD, DATA_TYPE, old) 26458edf9eeSRichard Henderson GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new) 26558edf9eeSRichard Henderson #undef ADD 26658edf9eeSRichard Henderson 2675507c2bfSRichard Henderson #undef GEN_ATOMIC_HELPER_FN 268ec4a9629SRichard Henderson #endif /* DATA_SIZE < 16 */ 269c482cb11SRichard Henderson 270c482cb11SRichard Henderson #undef END 271c482cb11SRichard Henderson #endif /* DATA_SIZE > 1 */ 272c482cb11SRichard Henderson 273c482cb11SRichard Henderson #undef BSWAP 274c482cb11SRichard Henderson #undef ABI_TYPE 275c482cb11SRichard Henderson #undef DATA_TYPE 2765507c2bfSRichard Henderson #undef SDATA_TYPE 277c482cb11SRichard Henderson #undef SUFFIX 278c482cb11SRichard Henderson #undef DATA_SIZE 279d071f4cdSEmilio G. Cota #undef SHIFT 280