blob: b699bf76886db18805be1e379736d8ea0b95a264 [file] [log] [blame]
/*
* Constants for memory operations
*
* Authors:
* Richard Henderson <rth@twiddle.net>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#ifndef MEMOP_H
#define MEMOP_H
#include "qemu/host-utils.h"
typedef enum MemOp {
MO_8 = 0,
MO_16 = 1,
MO_32 = 2,
MO_64 = 3,
MO_128 = 4,
MO_256 = 5,
MO_512 = 6,
MO_1024 = 7,
MO_SIZE = 0x07, /* Mask for the above. */
MO_SIGN = 0x08, /* Sign-extended, otherwise zero-extended. */
MO_BSWAP = 0x10, /* Host reverse endian. */
#if HOST_BIG_ENDIAN
MO_LE = MO_BSWAP,
MO_BE = 0,
#else
MO_LE = 0,
MO_BE = MO_BSWAP,
#endif
#ifdef COMPILING_PER_TARGET
#if TARGET_BIG_ENDIAN
MO_TE = MO_BE,
#else
MO_TE = MO_LE,
#endif
#endif
/*
* MO_UNALN accesses are never checked for alignment.
* MO_ALIGN accesses will result in a call to the CPU's
* do_unaligned_access hook if the guest address is not aligned.
*
* Some architectures (e.g. ARMv8) need the address which is aligned
* to a size more than the size of the memory access.
* Some architectures (e.g. SPARCv9) need an address which is aligned,
* but less strictly than the natural alignment.
*
* MO_ALIGN supposes the alignment size is the size of a memory access.
*
* There are three options:
* - unaligned access permitted (MO_UNALN).
* - an alignment to the size of an access (MO_ALIGN);
* - an alignment to a specified size, which may be more or less than
* the access size (MO_ALIGN_x where 'x' is a size in bytes);
*/
MO_ASHIFT = 5,
MO_AMASK = 0x7 << MO_ASHIFT,
MO_UNALN = 0,
MO_ALIGN_2 = 1 << MO_ASHIFT,
MO_ALIGN_4 = 2 << MO_ASHIFT,
MO_ALIGN_8 = 3 << MO_ASHIFT,
MO_ALIGN_16 = 4 << MO_ASHIFT,
MO_ALIGN_32 = 5 << MO_ASHIFT,
MO_ALIGN_64 = 6 << MO_ASHIFT,
MO_ALIGN = MO_AMASK,
/*
* MO_ATOM_* describes the atomicity requirements of the operation:
* MO_ATOM_IFALIGN: the operation must be single-copy atomic if it
* is aligned; if unaligned there is no atomicity.
* MO_ATOM_IFALIGN_PAIR: the entire operation may be considered to
* be a pair of half-sized operations which are packed together
* for convenience, with single-copy atomicity on each half if
* the half is aligned.
* This is the atomicity e.g. of Arm pre-FEAT_LSE2 LDP.
* MO_ATOM_WITHIN16: the operation is single-copy atomic, even if it
* is unaligned, so long as it does not cross a 16-byte boundary;
* if it crosses a 16-byte boundary there is no atomicity.
* This is the atomicity e.g. of Arm FEAT_LSE2 LDR.
* MO_ATOM_WITHIN16_PAIR: the entire operation is single-copy atomic,
* if it happens to be within a 16-byte boundary, otherwise it
* devolves to a pair of half-sized MO_ATOM_WITHIN16 operations.
* Depending on alignment, one or both will be single-copy atomic.
* This is the atomicity e.g. of Arm FEAT_LSE2 LDP.
* MO_ATOM_SUBALIGN: the operation is single-copy atomic by parts
* by the alignment. E.g. if the address is 0 mod 4, then each
* 4-byte subobject is single-copy atomic.
* This is the atomicity e.g. of IBM Power.
* MO_ATOM_NONE: the operation has no atomicity requirements.
*
* Note the default (i.e. 0) value is single-copy atomic to the
* size of the operation, if aligned. This retains the behaviour
* from before this field was introduced.
*/
MO_ATOM_SHIFT = 8,
MO_ATOM_IFALIGN = 0 << MO_ATOM_SHIFT,
MO_ATOM_IFALIGN_PAIR = 1 << MO_ATOM_SHIFT,
MO_ATOM_WITHIN16 = 2 << MO_ATOM_SHIFT,
MO_ATOM_WITHIN16_PAIR = 3 << MO_ATOM_SHIFT,
MO_ATOM_SUBALIGN = 4 << MO_ATOM_SHIFT,
MO_ATOM_NONE = 5 << MO_ATOM_SHIFT,
MO_ATOM_MASK = 7 << MO_ATOM_SHIFT,
/* Combinations of the above, for ease of use. */
MO_UB = MO_8,
MO_UW = MO_16,
MO_UL = MO_32,
MO_UQ = MO_64,
MO_UO = MO_128,
MO_SB = MO_SIGN | MO_8,
MO_SW = MO_SIGN | MO_16,
MO_SL = MO_SIGN | MO_32,
MO_SQ = MO_SIGN | MO_64,
MO_SO = MO_SIGN | MO_128,
MO_LEUW = MO_LE | MO_UW,
MO_LEUL = MO_LE | MO_UL,
MO_LEUQ = MO_LE | MO_UQ,
MO_LESW = MO_LE | MO_SW,
MO_LESL = MO_LE | MO_SL,
MO_LESQ = MO_LE | MO_SQ,
MO_BEUW = MO_BE | MO_UW,
MO_BEUL = MO_BE | MO_UL,
MO_BEUQ = MO_BE | MO_UQ,
MO_BESW = MO_BE | MO_SW,
MO_BESL = MO_BE | MO_SL,
MO_BESQ = MO_BE | MO_SQ,
#ifdef COMPILING_PER_TARGET
MO_TEUW = MO_TE | MO_UW,
MO_TEUL = MO_TE | MO_UL,
MO_TEUQ = MO_TE | MO_UQ,
MO_TEUO = MO_TE | MO_UO,
MO_TESW = MO_TE | MO_SW,
MO_TESL = MO_TE | MO_SL,
MO_TESQ = MO_TE | MO_SQ,
#endif
MO_SSIZE = MO_SIZE | MO_SIGN,
} MemOp;
/* MemOp to size in bytes. */
static inline unsigned memop_size(MemOp op)
{
return 1 << (op & MO_SIZE);
}
/* Size in bytes to MemOp. */
static inline MemOp size_memop(unsigned size)
{
#ifdef CONFIG_DEBUG_TCG
/* Power of 2 up to 8. */
assert((size & (size - 1)) == 0 && size >= 1 && size <= 8);
#endif
return (MemOp)ctz32(size);
}
/* Big endianness from MemOp. */
static inline bool memop_big_endian(MemOp op)
{
return (op & MO_BSWAP) == MO_BE;
}
/**
* memop_alignment_bits:
* @memop: MemOp value
*
* Extract the alignment size from the memop.
*/
static inline unsigned memop_alignment_bits(MemOp memop)
{
unsigned a = memop & MO_AMASK;
if (a == MO_UNALN) {
/* No alignment required. */
a = 0;
} else if (a == MO_ALIGN) {
/* A natural alignment requirement. */
a = memop & MO_SIZE;
} else {
/* A specific alignment requirement. */
a = a >> MO_ASHIFT;
}
return a;
}
/*
* memop_atomicity_bits:
* @memop: MemOp value
*
* Extract the atomicity size from the memop.
*/
static inline unsigned memop_atomicity_bits(MemOp memop)
{
unsigned size = memop & MO_SIZE;
switch (memop & MO_ATOM_MASK) {
case MO_ATOM_NONE:
size = MO_8;
break;
case MO_ATOM_IFALIGN_PAIR:
case MO_ATOM_WITHIN16_PAIR:
size = size ? size - 1 : 0;
break;
default:
break;
}
return size;
}
#endif