xref: /openbmc/qemu/accel/tcg/ldst_atomicity.c.inc (revision 5f88dd43)
1/*
2 * Routines common to user and system emulation of load/store.
3 *
4 *  Copyright (c) 2022 Linaro, Ltd.
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 *
8 * This work is licensed under the terms of the GNU GPL, version 2 or later.
9 * See the COPYING file in the top-level directory.
10 */
11
12#include "host/load-extract-al16-al8.h"
13#include "host/store-insert-al16.h"
14
15#ifdef CONFIG_ATOMIC64
16# define HAVE_al8          true
17#else
18# define HAVE_al8          false
19#endif
20#define HAVE_al8_fast      (ATOMIC_REG_SIZE >= 8)
21
22/**
23 * required_atomicity:
24 *
25 * Return the lg2 bytes of atomicity required by @memop for @p.
26 * If the operation must be split into two operations to be
27 * examined separately for atomicity, return -lg2.
28 */
29static int required_atomicity(CPUArchState *env, uintptr_t p, MemOp memop)
30{
31    MemOp atom = memop & MO_ATOM_MASK;
32    MemOp size = memop & MO_SIZE;
33    MemOp half = size ? size - 1 : 0;
34    unsigned tmp;
35    int atmax;
36
37    switch (atom) {
38    case MO_ATOM_NONE:
39        atmax = MO_8;
40        break;
41
42    case MO_ATOM_IFALIGN_PAIR:
43        size = half;
44        /* fall through */
45
46    case MO_ATOM_IFALIGN:
47        tmp = (1 << size) - 1;
48        atmax = p & tmp ? MO_8 : size;
49        break;
50
51    case MO_ATOM_WITHIN16:
52        tmp = p & 15;
53        atmax = (tmp + (1 << size) <= 16 ? size : MO_8);
54        break;
55
56    case MO_ATOM_WITHIN16_PAIR:
57        tmp = p & 15;
58        if (tmp + (1 << size) <= 16) {
59            atmax = size;
60        } else if (tmp + (1 << half) == 16) {
61            /*
62             * The pair exactly straddles the boundary.
63             * Both halves are naturally aligned and atomic.
64             */
65            atmax = half;
66        } else {
67            /*
68             * One of the pair crosses the boundary, and is non-atomic.
69             * The other of the pair does not cross, and is atomic.
70             */
71            atmax = -half;
72        }
73        break;
74
75    case MO_ATOM_SUBALIGN:
76        /*
77         * Examine the alignment of p to determine if there are subobjects
78         * that must be aligned.  Note that we only really need ctz4() --
79         * any more sigificant bits are discarded by the immediately
80         * following comparison.
81         */
82        tmp = ctz32(p);
83        atmax = MIN(size, tmp);
84        break;
85
86    default:
87        g_assert_not_reached();
88    }
89
90    /*
91     * Here we have the architectural atomicity of the operation.
92     * However, when executing in a serial context, we need no extra
93     * host atomicity in order to avoid racing.  This reduction
94     * avoids looping with cpu_loop_exit_atomic.
95     */
96    if (cpu_in_serial_context(env_cpu(env))) {
97        return MO_8;
98    }
99    return atmax;
100}
101
102/**
103 * load_atomic2:
104 * @pv: host address
105 *
106 * Atomically load 2 aligned bytes from @pv.
107 */
108static inline uint16_t load_atomic2(void *pv)
109{
110    uint16_t *p = __builtin_assume_aligned(pv, 2);
111    return qatomic_read(p);
112}
113
114/**
115 * load_atomic4:
116 * @pv: host address
117 *
118 * Atomically load 4 aligned bytes from @pv.
119 */
120static inline uint32_t load_atomic4(void *pv)
121{
122    uint32_t *p = __builtin_assume_aligned(pv, 4);
123    return qatomic_read(p);
124}
125
126/**
127 * load_atomic8:
128 * @pv: host address
129 *
130 * Atomically load 8 aligned bytes from @pv.
131 */
132static inline uint64_t load_atomic8(void *pv)
133{
134    uint64_t *p = __builtin_assume_aligned(pv, 8);
135
136    qemu_build_assert(HAVE_al8);
137    return qatomic_read__nocheck(p);
138}
139
140/**
141 * load_atomic8_or_exit:
142 * @env: cpu context
143 * @ra: host unwind address
144 * @pv: host address
145 *
146 * Atomically load 8 aligned bytes from @pv.
147 * If this is not possible, longjmp out to restart serially.
148 */
149static uint64_t load_atomic8_or_exit(CPUArchState *env, uintptr_t ra, void *pv)
150{
151    if (HAVE_al8) {
152        return load_atomic8(pv);
153    }
154
155#ifdef CONFIG_USER_ONLY
156    /*
157     * If the page is not writable, then assume the value is immutable
158     * and requires no locking.  This ignores the case of MAP_SHARED with
159     * another process, because the fallback start_exclusive solution
160     * provides no protection across processes.
161     */
162    WITH_MMAP_LOCK_GUARD() {
163        if (!page_check_range(h2g(pv), 8, PAGE_WRITE_ORG)) {
164            uint64_t *p = __builtin_assume_aligned(pv, 8);
165            return *p;
166        }
167    }
168#endif
169
170    /* Ultimate fallback: re-execute in serial context. */
171    cpu_loop_exit_atomic(env_cpu(env), ra);
172}
173
174/**
175 * load_atomic16_or_exit:
176 * @env: cpu context
177 * @ra: host unwind address
178 * @pv: host address
179 *
180 * Atomically load 16 aligned bytes from @pv.
181 * If this is not possible, longjmp out to restart serially.
182 */
183static Int128 load_atomic16_or_exit(CPUArchState *env, uintptr_t ra, void *pv)
184{
185    Int128 *p = __builtin_assume_aligned(pv, 16);
186
187    if (HAVE_ATOMIC128_RO) {
188        return atomic16_read_ro(p);
189    }
190
191    /*
192     * We can only use cmpxchg to emulate a load if the page is writable.
193     * If the page is not writable, then assume the value is immutable
194     * and requires no locking.  This ignores the case of MAP_SHARED with
195     * another process, because the fallback start_exclusive solution
196     * provides no protection across processes.
197     *
198     * In system mode all guest pages are writable.  For user mode,
199     * we must take mmap_lock so that the query remains valid until
200     * the write is complete -- tests/tcg/multiarch/munmap-pthread.c
201     * is an example that can race.
202     */
203    WITH_MMAP_LOCK_GUARD() {
204#ifdef CONFIG_USER_ONLY
205        if (!page_check_range(h2g(p), 16, PAGE_WRITE_ORG)) {
206            return *p;
207        }
208#endif
209        if (HAVE_ATOMIC128_RW) {
210            return atomic16_read_rw(p);
211        }
212    }
213
214    /* Ultimate fallback: re-execute in serial context. */
215    cpu_loop_exit_atomic(env_cpu(env), ra);
216}
217
218/**
219 * load_atom_extract_al4x2:
220 * @pv: host address
221 *
222 * Load 4 bytes from @p, from two sequential atomic 4-byte loads.
223 */
224static uint32_t load_atom_extract_al4x2(void *pv)
225{
226    uintptr_t pi = (uintptr_t)pv;
227    int sh = (pi & 3) * 8;
228    uint32_t a, b;
229
230    pv = (void *)(pi & ~3);
231    a = load_atomic4(pv);
232    b = load_atomic4(pv + 4);
233
234    if (HOST_BIG_ENDIAN) {
235        return (a << sh) | (b >> (-sh & 31));
236    } else {
237        return (a >> sh) | (b << (-sh & 31));
238    }
239}
240
241/**
242 * load_atom_extract_al8x2:
243 * @pv: host address
244 *
245 * Load 8 bytes from @p, from two sequential atomic 8-byte loads.
246 */
247static uint64_t load_atom_extract_al8x2(void *pv)
248{
249    uintptr_t pi = (uintptr_t)pv;
250    int sh = (pi & 7) * 8;
251    uint64_t a, b;
252
253    pv = (void *)(pi & ~7);
254    a = load_atomic8(pv);
255    b = load_atomic8(pv + 8);
256
257    if (HOST_BIG_ENDIAN) {
258        return (a << sh) | (b >> (-sh & 63));
259    } else {
260        return (a >> sh) | (b << (-sh & 63));
261    }
262}
263
264/**
265 * load_atom_extract_al8_or_exit:
266 * @env: cpu context
267 * @ra: host unwind address
268 * @pv: host address
269 * @s: object size in bytes, @s <= 4.
270 *
271 * Atomically load @s bytes from @p, when p % s != 0, and [p, p+s-1] does
272 * not cross an 8-byte boundary.  This means that we can perform an atomic
273 * 8-byte load and extract.
274 * The value is returned in the low bits of a uint32_t.
275 */
276static uint32_t load_atom_extract_al8_or_exit(CPUArchState *env, uintptr_t ra,
277                                              void *pv, int s)
278{
279    uintptr_t pi = (uintptr_t)pv;
280    int o = pi & 7;
281    int shr = (HOST_BIG_ENDIAN ? 8 - s - o : o) * 8;
282
283    pv = (void *)(pi & ~7);
284    return load_atomic8_or_exit(env, ra, pv) >> shr;
285}
286
287/**
288 * load_atom_extract_al16_or_exit:
289 * @env: cpu context
290 * @ra: host unwind address
291 * @p: host address
292 * @s: object size in bytes, @s <= 8.
293 *
294 * Atomically load @s bytes from @p, when p % 16 < 8
295 * and p % 16 + s > 8.  I.e. does not cross a 16-byte
296 * boundary, but *does* cross an 8-byte boundary.
297 * This is the slow version, so we must have eliminated
298 * any faster load_atom_extract_al8_or_exit case.
299 *
300 * If this is not possible, longjmp out to restart serially.
301 */
302static uint64_t load_atom_extract_al16_or_exit(CPUArchState *env, uintptr_t ra,
303                                               void *pv, int s)
304{
305    uintptr_t pi = (uintptr_t)pv;
306    int o = pi & 7;
307    int shr = (HOST_BIG_ENDIAN ? 16 - s - o : o) * 8;
308    Int128 r;
309
310    /*
311     * Note constraints above: p & 8 must be clear.
312     * Provoke SIGBUS if possible otherwise.
313     */
314    pv = (void *)(pi & ~7);
315    r = load_atomic16_or_exit(env, ra, pv);
316
317    r = int128_urshift(r, shr);
318    return int128_getlo(r);
319}
320
321/**
322 * load_atom_4_by_2:
323 * @pv: host address
324 *
325 * Load 4 bytes from @pv, with two 2-byte atomic loads.
326 */
327static inline uint32_t load_atom_4_by_2(void *pv)
328{
329    uint32_t a = load_atomic2(pv);
330    uint32_t b = load_atomic2(pv + 2);
331
332    if (HOST_BIG_ENDIAN) {
333        return (a << 16) | b;
334    } else {
335        return (b << 16) | a;
336    }
337}
338
339/**
340 * load_atom_8_by_2:
341 * @pv: host address
342 *
343 * Load 8 bytes from @pv, with four 2-byte atomic loads.
344 */
345static inline uint64_t load_atom_8_by_2(void *pv)
346{
347    uint32_t a = load_atom_4_by_2(pv);
348    uint32_t b = load_atom_4_by_2(pv + 4);
349
350    if (HOST_BIG_ENDIAN) {
351        return ((uint64_t)a << 32) | b;
352    } else {
353        return ((uint64_t)b << 32) | a;
354    }
355}
356
357/**
358 * load_atom_8_by_4:
359 * @pv: host address
360 *
361 * Load 8 bytes from @pv, with two 4-byte atomic loads.
362 */
363static inline uint64_t load_atom_8_by_4(void *pv)
364{
365    uint32_t a = load_atomic4(pv);
366    uint32_t b = load_atomic4(pv + 4);
367
368    if (HOST_BIG_ENDIAN) {
369        return ((uint64_t)a << 32) | b;
370    } else {
371        return ((uint64_t)b << 32) | a;
372    }
373}
374
375/**
376 * load_atom_8_by_8_or_4:
377 * @pv: host address
378 *
379 * Load 8 bytes from aligned @pv, with at least 4-byte atomicity.
380 */
381static inline uint64_t load_atom_8_by_8_or_4(void *pv)
382{
383    if (HAVE_al8_fast) {
384        return load_atomic8(pv);
385    } else {
386        return load_atom_8_by_4(pv);
387    }
388}
389
390/**
391 * load_atom_2:
392 * @p: host address
393 * @memop: the full memory op
394 *
395 * Load 2 bytes from @p, honoring the atomicity of @memop.
396 */
397static uint16_t load_atom_2(CPUArchState *env, uintptr_t ra,
398                            void *pv, MemOp memop)
399{
400    uintptr_t pi = (uintptr_t)pv;
401    int atmax;
402
403    if (likely((pi & 1) == 0)) {
404        return load_atomic2(pv);
405    }
406    if (HAVE_ATOMIC128_RO) {
407        intptr_t left_in_page = -(pi | TARGET_PAGE_MASK);
408        if (likely(left_in_page > 8)) {
409            return load_atom_extract_al16_or_al8(pv, 2);
410        }
411    }
412
413    atmax = required_atomicity(env, pi, memop);
414    switch (atmax) {
415    case MO_8:
416        return lduw_he_p(pv);
417    case MO_16:
418        /* The only case remaining is MO_ATOM_WITHIN16. */
419        if (!HAVE_al8_fast && (pi & 3) == 1) {
420            /* Big or little endian, we want the middle two bytes. */
421            return load_atomic4(pv - 1) >> 8;
422        }
423        if ((pi & 15) != 7) {
424            return load_atom_extract_al8_or_exit(env, ra, pv, 2);
425        }
426        return load_atom_extract_al16_or_exit(env, ra, pv, 2);
427    default:
428        g_assert_not_reached();
429    }
430}
431
432/**
433 * load_atom_4:
434 * @p: host address
435 * @memop: the full memory op
436 *
437 * Load 4 bytes from @p, honoring the atomicity of @memop.
438 */
439static uint32_t load_atom_4(CPUArchState *env, uintptr_t ra,
440                            void *pv, MemOp memop)
441{
442    uintptr_t pi = (uintptr_t)pv;
443    int atmax;
444
445    if (likely((pi & 3) == 0)) {
446        return load_atomic4(pv);
447    }
448    if (HAVE_ATOMIC128_RO) {
449        intptr_t left_in_page = -(pi | TARGET_PAGE_MASK);
450        if (likely(left_in_page > 8)) {
451            return load_atom_extract_al16_or_al8(pv, 4);
452        }
453    }
454
455    atmax = required_atomicity(env, pi, memop);
456    switch (atmax) {
457    case MO_8:
458    case MO_16:
459    case -MO_16:
460        /*
461         * For MO_ATOM_IFALIGN, this is more atomicity than required,
462         * but it's trivially supported on all hosts, better than 4
463         * individual byte loads (when the host requires alignment),
464         * and overlaps with the MO_ATOM_SUBALIGN case of p % 2 == 0.
465         */
466        return load_atom_extract_al4x2(pv);
467    case MO_32:
468        if (!(pi & 4)) {
469            return load_atom_extract_al8_or_exit(env, ra, pv, 4);
470        }
471        return load_atom_extract_al16_or_exit(env, ra, pv, 4);
472    default:
473        g_assert_not_reached();
474    }
475}
476
477/**
478 * load_atom_8:
479 * @p: host address
480 * @memop: the full memory op
481 *
482 * Load 8 bytes from @p, honoring the atomicity of @memop.
483 */
484static uint64_t load_atom_8(CPUArchState *env, uintptr_t ra,
485                            void *pv, MemOp memop)
486{
487    uintptr_t pi = (uintptr_t)pv;
488    int atmax;
489
490    /*
491     * If the host does not support 8-byte atomics, wait until we have
492     * examined the atomicity parameters below.
493     */
494    if (HAVE_al8 && likely((pi & 7) == 0)) {
495        return load_atomic8(pv);
496    }
497    if (HAVE_ATOMIC128_RO) {
498        return load_atom_extract_al16_or_al8(pv, 8);
499    }
500
501    atmax = required_atomicity(env, pi, memop);
502    if (atmax == MO_64) {
503        if (!HAVE_al8 && (pi & 7) == 0) {
504            load_atomic8_or_exit(env, ra, pv);
505        }
506        return load_atom_extract_al16_or_exit(env, ra, pv, 8);
507    }
508    if (HAVE_al8_fast) {
509        return load_atom_extract_al8x2(pv);
510    }
511    switch (atmax) {
512    case MO_8:
513        return ldq_he_p(pv);
514    case MO_16:
515        return load_atom_8_by_2(pv);
516    case MO_32:
517        return load_atom_8_by_4(pv);
518    case -MO_32:
519        if (HAVE_al8) {
520            return load_atom_extract_al8x2(pv);
521        }
522        cpu_loop_exit_atomic(env_cpu(env), ra);
523    default:
524        g_assert_not_reached();
525    }
526}
527
528/**
529 * load_atom_16:
530 * @p: host address
531 * @memop: the full memory op
532 *
533 * Load 16 bytes from @p, honoring the atomicity of @memop.
534 */
535static Int128 load_atom_16(CPUArchState *env, uintptr_t ra,
536                           void *pv, MemOp memop)
537{
538    uintptr_t pi = (uintptr_t)pv;
539    int atmax;
540    Int128 r;
541    uint64_t a, b;
542
543    /*
544     * If the host does not support 16-byte atomics, wait until we have
545     * examined the atomicity parameters below.
546     */
547    if (HAVE_ATOMIC128_RO && likely((pi & 15) == 0)) {
548        return atomic16_read_ro(pv);
549    }
550
551    atmax = required_atomicity(env, pi, memop);
552    switch (atmax) {
553    case MO_8:
554        memcpy(&r, pv, 16);
555        return r;
556    case MO_16:
557        a = load_atom_8_by_2(pv);
558        b = load_atom_8_by_2(pv + 8);
559        break;
560    case MO_32:
561        a = load_atom_8_by_4(pv);
562        b = load_atom_8_by_4(pv + 8);
563        break;
564    case MO_64:
565        if (!HAVE_al8) {
566            cpu_loop_exit_atomic(env_cpu(env), ra);
567        }
568        a = load_atomic8(pv);
569        b = load_atomic8(pv + 8);
570        break;
571    case -MO_64:
572        if (!HAVE_al8) {
573            cpu_loop_exit_atomic(env_cpu(env), ra);
574        }
575        a = load_atom_extract_al8x2(pv);
576        b = load_atom_extract_al8x2(pv + 8);
577        break;
578    case MO_128:
579        return load_atomic16_or_exit(env, ra, pv);
580    default:
581        g_assert_not_reached();
582    }
583    return int128_make128(HOST_BIG_ENDIAN ? b : a, HOST_BIG_ENDIAN ? a : b);
584}
585
586/**
587 * store_atomic2:
588 * @pv: host address
589 * @val: value to store
590 *
591 * Atomically store 2 aligned bytes to @pv.
592 */
593static inline void store_atomic2(void *pv, uint16_t val)
594{
595    uint16_t *p = __builtin_assume_aligned(pv, 2);
596    qatomic_set(p, val);
597}
598
599/**
600 * store_atomic4:
601 * @pv: host address
602 * @val: value to store
603 *
604 * Atomically store 4 aligned bytes to @pv.
605 */
606static inline void store_atomic4(void *pv, uint32_t val)
607{
608    uint32_t *p = __builtin_assume_aligned(pv, 4);
609    qatomic_set(p, val);
610}
611
612/**
613 * store_atomic8:
614 * @pv: host address
615 * @val: value to store
616 *
617 * Atomically store 8 aligned bytes to @pv.
618 */
619static inline void store_atomic8(void *pv, uint64_t val)
620{
621    uint64_t *p = __builtin_assume_aligned(pv, 8);
622
623    qemu_build_assert(HAVE_al8);
624    qatomic_set__nocheck(p, val);
625}
626
627/**
628 * store_atom_4x2
629 */
630static inline void store_atom_4_by_2(void *pv, uint32_t val)
631{
632    store_atomic2(pv, val >> (HOST_BIG_ENDIAN ? 16 : 0));
633    store_atomic2(pv + 2, val >> (HOST_BIG_ENDIAN ? 0 : 16));
634}
635
636/**
637 * store_atom_8_by_2
638 */
639static inline void store_atom_8_by_2(void *pv, uint64_t val)
640{
641    store_atom_4_by_2(pv, val >> (HOST_BIG_ENDIAN ? 32 : 0));
642    store_atom_4_by_2(pv + 4, val >> (HOST_BIG_ENDIAN ? 0 : 32));
643}
644
645/**
646 * store_atom_8_by_4
647 */
648static inline void store_atom_8_by_4(void *pv, uint64_t val)
649{
650    store_atomic4(pv, val >> (HOST_BIG_ENDIAN ? 32 : 0));
651    store_atomic4(pv + 4, val >> (HOST_BIG_ENDIAN ? 0 : 32));
652}
653
654/**
655 * store_atom_insert_al4:
656 * @p: host address
657 * @val: shifted value to store
658 * @msk: mask for value to store
659 *
660 * Atomically store @val to @p, masked by @msk.
661 */
662static void store_atom_insert_al4(uint32_t *p, uint32_t val, uint32_t msk)
663{
664    uint32_t old, new;
665
666    p = __builtin_assume_aligned(p, 4);
667    old = qatomic_read(p);
668    do {
669        new = (old & ~msk) | val;
670    } while (!__atomic_compare_exchange_n(p, &old, new, true,
671                                          __ATOMIC_RELAXED, __ATOMIC_RELAXED));
672}
673
674/**
675 * store_atom_insert_al8:
676 * @p: host address
677 * @val: shifted value to store
678 * @msk: mask for value to store
679 *
680 * Atomically store @val to @p masked by @msk.
681 */
682static void store_atom_insert_al8(uint64_t *p, uint64_t val, uint64_t msk)
683{
684    uint64_t old, new;
685
686    qemu_build_assert(HAVE_al8);
687    p = __builtin_assume_aligned(p, 8);
688    old = qatomic_read__nocheck(p);
689    do {
690        new = (old & ~msk) | val;
691    } while (!__atomic_compare_exchange_n(p, &old, new, true,
692                                          __ATOMIC_RELAXED, __ATOMIC_RELAXED));
693}
694
695/**
696 * store_bytes_leN:
697 * @pv: host address
698 * @size: number of bytes to store
699 * @val_le: data to store
700 *
701 * Store @size bytes at @p.  The bytes to store are extracted in little-endian order
702 * from @val_le; return the bytes of @val_le beyond @size that have not been stored.
703 */
704static uint64_t store_bytes_leN(void *pv, int size, uint64_t val_le)
705{
706    uint8_t *p = pv;
707    for (int i = 0; i < size; i++, val_le >>= 8) {
708        p[i] = val_le;
709    }
710    return val_le;
711}
712
713/**
714 * store_parts_leN
715 * @pv: host address
716 * @size: number of bytes to store
717 * @val_le: data to store
718 *
719 * As store_bytes_leN, but atomically on each aligned part.
720 */
721G_GNUC_UNUSED
722static uint64_t store_parts_leN(void *pv, int size, uint64_t val_le)
723{
724    do {
725        int n;
726
727        /* Find minimum of alignment and size */
728        switch (((uintptr_t)pv | size) & 7) {
729        case 4:
730            store_atomic4(pv, le32_to_cpu(val_le));
731            val_le >>= 32;
732            n = 4;
733            break;
734        case 2:
735        case 6:
736            store_atomic2(pv, le16_to_cpu(val_le));
737            val_le >>= 16;
738            n = 2;
739            break;
740        default:
741            *(uint8_t *)pv = val_le;
742            val_le >>= 8;
743            n = 1;
744            break;
745        case 0:
746            g_assert_not_reached();
747        }
748        pv += n;
749        size -= n;
750    } while (size != 0);
751
752    return val_le;
753}
754
755/**
756 * store_whole_le4
757 * @pv: host address
758 * @size: number of bytes to store
759 * @val_le: data to store
760 *
761 * As store_bytes_leN, but atomically as a whole.
762 * Four aligned bytes are guaranteed to cover the store.
763 */
764static uint64_t store_whole_le4(void *pv, int size, uint64_t val_le)
765{
766    int sz = size * 8;
767    int o = (uintptr_t)pv & 3;
768    int sh = o * 8;
769    uint32_t m = MAKE_64BIT_MASK(0, sz);
770    uint32_t v;
771
772    if (HOST_BIG_ENDIAN) {
773        v = bswap32(val_le) >> sh;
774        m = bswap32(m) >> sh;
775    } else {
776        v = val_le << sh;
777        m <<= sh;
778    }
779    store_atom_insert_al4(pv - o, v, m);
780    return val_le >> sz;
781}
782
783/**
784 * store_whole_le8
785 * @pv: host address
786 * @size: number of bytes to store
787 * @val_le: data to store
788 *
789 * As store_bytes_leN, but atomically as a whole.
790 * Eight aligned bytes are guaranteed to cover the store.
791 */
792static uint64_t store_whole_le8(void *pv, int size, uint64_t val_le)
793{
794    int sz = size * 8;
795    int o = (uintptr_t)pv & 7;
796    int sh = o * 8;
797    uint64_t m = MAKE_64BIT_MASK(0, sz);
798    uint64_t v;
799
800    qemu_build_assert(HAVE_al8);
801    if (HOST_BIG_ENDIAN) {
802        v = bswap64(val_le) >> sh;
803        m = bswap64(m) >> sh;
804    } else {
805        v = val_le << sh;
806        m <<= sh;
807    }
808    store_atom_insert_al8(pv - o, v, m);
809    return val_le >> sz;
810}
811
812/**
813 * store_whole_le16
814 * @pv: host address
815 * @size: number of bytes to store
816 * @val_le: data to store
817 *
818 * As store_bytes_leN, but atomically as a whole.
819 * 16 aligned bytes are guaranteed to cover the store.
820 */
821static uint64_t store_whole_le16(void *pv, int size, Int128 val_le)
822{
823    int sz = size * 8;
824    int o = (uintptr_t)pv & 15;
825    int sh = o * 8;
826    Int128 m, v;
827
828    qemu_build_assert(HAVE_ATOMIC128_RW);
829
830    /* Like MAKE_64BIT_MASK(0, sz), but larger. */
831    if (sz <= 64) {
832        m = int128_make64(MAKE_64BIT_MASK(0, sz));
833    } else {
834        m = int128_make128(-1, MAKE_64BIT_MASK(0, sz - 64));
835    }
836
837    if (HOST_BIG_ENDIAN) {
838        v = int128_urshift(bswap128(val_le), sh);
839        m = int128_urshift(bswap128(m), sh);
840    } else {
841        v = int128_lshift(val_le, sh);
842        m = int128_lshift(m, sh);
843    }
844    store_atom_insert_al16(pv - o, v, m);
845
846    if (sz <= 64) {
847        return 0;
848    }
849    return int128_gethi(val_le) >> (sz - 64);
850}
851
852/**
853 * store_atom_2:
854 * @p: host address
855 * @val: the value to store
856 * @memop: the full memory op
857 *
858 * Store 2 bytes to @p, honoring the atomicity of @memop.
859 */
860static void store_atom_2(CPUArchState *env, uintptr_t ra,
861                         void *pv, MemOp memop, uint16_t val)
862{
863    uintptr_t pi = (uintptr_t)pv;
864    int atmax;
865
866    if (likely((pi & 1) == 0)) {
867        store_atomic2(pv, val);
868        return;
869    }
870
871    atmax = required_atomicity(env, pi, memop);
872    if (atmax == MO_8) {
873        stw_he_p(pv, val);
874        return;
875    }
876
877    /*
878     * The only case remaining is MO_ATOM_WITHIN16.
879     * Big or little endian, we want the middle two bytes in each test.
880     */
881    if ((pi & 3) == 1) {
882        store_atom_insert_al4(pv - 1, (uint32_t)val << 8, MAKE_64BIT_MASK(8, 16));
883        return;
884    } else if ((pi & 7) == 3) {
885        if (HAVE_al8) {
886            store_atom_insert_al8(pv - 3, (uint64_t)val << 24, MAKE_64BIT_MASK(24, 16));
887            return;
888        }
889    } else if ((pi & 15) == 7) {
890        if (HAVE_ATOMIC128_RW) {
891            Int128 v = int128_lshift(int128_make64(val), 56);
892            Int128 m = int128_lshift(int128_make64(0xffff), 56);
893            store_atom_insert_al16(pv - 7, v, m);
894            return;
895        }
896    } else {
897        g_assert_not_reached();
898    }
899
900    cpu_loop_exit_atomic(env_cpu(env), ra);
901}
902
903/**
904 * store_atom_4:
905 * @p: host address
906 * @val: the value to store
907 * @memop: the full memory op
908 *
909 * Store 4 bytes to @p, honoring the atomicity of @memop.
910 */
911static void store_atom_4(CPUArchState *env, uintptr_t ra,
912                         void *pv, MemOp memop, uint32_t val)
913{
914    uintptr_t pi = (uintptr_t)pv;
915    int atmax;
916
917    if (likely((pi & 3) == 0)) {
918        store_atomic4(pv, val);
919        return;
920    }
921
922    atmax = required_atomicity(env, pi, memop);
923    switch (atmax) {
924    case MO_8:
925        stl_he_p(pv, val);
926        return;
927    case MO_16:
928        store_atom_4_by_2(pv, val);
929        return;
930    case -MO_16:
931        {
932            uint32_t val_le = cpu_to_le32(val);
933            int s2 = pi & 3;
934            int s1 = 4 - s2;
935
936            switch (s2) {
937            case 1:
938                val_le = store_whole_le4(pv, s1, val_le);
939                *(uint8_t *)(pv + 3) = val_le;
940                break;
941            case 3:
942                *(uint8_t *)pv = val_le;
943                store_whole_le4(pv + 1, s2, val_le >> 8);
944                break;
945            case 0: /* aligned */
946            case 2: /* atmax MO_16 */
947            default:
948                g_assert_not_reached();
949            }
950        }
951        return;
952    case MO_32:
953        if ((pi & 7) < 4) {
954            if (HAVE_al8) {
955                store_whole_le8(pv, 4, cpu_to_le32(val));
956                return;
957            }
958        } else {
959            if (HAVE_ATOMIC128_RW) {
960                store_whole_le16(pv, 4, int128_make64(cpu_to_le32(val)));
961                return;
962            }
963        }
964        cpu_loop_exit_atomic(env_cpu(env), ra);
965    default:
966        g_assert_not_reached();
967    }
968}
969
970/**
971 * store_atom_8:
972 * @p: host address
973 * @val: the value to store
974 * @memop: the full memory op
975 *
976 * Store 8 bytes to @p, honoring the atomicity of @memop.
977 */
978static void store_atom_8(CPUArchState *env, uintptr_t ra,
979                         void *pv, MemOp memop, uint64_t val)
980{
981    uintptr_t pi = (uintptr_t)pv;
982    int atmax;
983
984    if (HAVE_al8 && likely((pi & 7) == 0)) {
985        store_atomic8(pv, val);
986        return;
987    }
988
989    atmax = required_atomicity(env, pi, memop);
990    switch (atmax) {
991    case MO_8:
992        stq_he_p(pv, val);
993        return;
994    case MO_16:
995        store_atom_8_by_2(pv, val);
996        return;
997    case MO_32:
998        store_atom_8_by_4(pv, val);
999        return;
1000    case -MO_32:
1001        if (HAVE_al8) {
1002            uint64_t val_le = cpu_to_le64(val);
1003            int s2 = pi & 7;
1004            int s1 = 8 - s2;
1005
1006            switch (s2) {
1007            case 1 ... 3:
1008                val_le = store_whole_le8(pv, s1, val_le);
1009                store_bytes_leN(pv + s1, s2, val_le);
1010                break;
1011            case 5 ... 7:
1012                val_le = store_bytes_leN(pv, s1, val_le);
1013                store_whole_le8(pv + s1, s2, val_le);
1014                break;
1015            case 0: /* aligned */
1016            case 4: /* atmax MO_32 */
1017            default:
1018                g_assert_not_reached();
1019            }
1020            return;
1021        }
1022        break;
1023    case MO_64:
1024        if (HAVE_ATOMIC128_RW) {
1025            store_whole_le16(pv, 8, int128_make64(cpu_to_le64(val)));
1026            return;
1027        }
1028        break;
1029    default:
1030        g_assert_not_reached();
1031    }
1032    cpu_loop_exit_atomic(env_cpu(env), ra);
1033}
1034
1035/**
1036 * store_atom_16:
1037 * @p: host address
1038 * @val: the value to store
1039 * @memop: the full memory op
1040 *
1041 * Store 16 bytes to @p, honoring the atomicity of @memop.
1042 */
1043static void store_atom_16(CPUArchState *env, uintptr_t ra,
1044                          void *pv, MemOp memop, Int128 val)
1045{
1046    uintptr_t pi = (uintptr_t)pv;
1047    uint64_t a, b;
1048    int atmax;
1049
1050    if (HAVE_ATOMIC128_RW && likely((pi & 15) == 0)) {
1051        atomic16_set(pv, val);
1052        return;
1053    }
1054
1055    atmax = required_atomicity(env, pi, memop);
1056
1057    a = HOST_BIG_ENDIAN ? int128_gethi(val) : int128_getlo(val);
1058    b = HOST_BIG_ENDIAN ? int128_getlo(val) : int128_gethi(val);
1059    switch (atmax) {
1060    case MO_8:
1061        memcpy(pv, &val, 16);
1062        return;
1063    case MO_16:
1064        store_atom_8_by_2(pv, a);
1065        store_atom_8_by_2(pv + 8, b);
1066        return;
1067    case MO_32:
1068        store_atom_8_by_4(pv, a);
1069        store_atom_8_by_4(pv + 8, b);
1070        return;
1071    case MO_64:
1072        if (HAVE_al8) {
1073            store_atomic8(pv, a);
1074            store_atomic8(pv + 8, b);
1075            return;
1076        }
1077        break;
1078    case -MO_64:
1079        if (HAVE_ATOMIC128_RW) {
1080            uint64_t val_le;
1081            int s2 = pi & 15;
1082            int s1 = 16 - s2;
1083
1084            if (HOST_BIG_ENDIAN) {
1085                val = bswap128(val);
1086            }
1087            switch (s2) {
1088            case 1 ... 7:
1089                val_le = store_whole_le16(pv, s1, val);
1090                store_bytes_leN(pv + s1, s2, val_le);
1091                break;
1092            case 9 ... 15:
1093                store_bytes_leN(pv, s1, int128_getlo(val));
1094                val = int128_urshift(val, s1 * 8);
1095                store_whole_le16(pv + s1, s2, val);
1096                break;
1097            case 0: /* aligned */
1098            case 8: /* atmax MO_64 */
1099            default:
1100                g_assert_not_reached();
1101            }
1102            return;
1103        }
1104        break;
1105    case MO_128:
1106        if (HAVE_ATOMIC128_RW) {
1107            atomic16_set(pv, val);
1108            return;
1109        }
1110        break;
1111    default:
1112        g_assert_not_reached();
1113    }
1114    cpu_loop_exit_atomic(env_cpu(env), ra);
1115}
1116