Skip to content

Commit

Permalink
bitops.h: Define bit operations on 'uint32_t' arrays
Browse files Browse the repository at this point in the history
Currently bitops.h defines a set of operations that work on
arbitrary-length bit arrays.  However (largely because they
originally came from the Linux kernel) the bit array storage is an
array of 'unsigned long'.  This is OK for the kernel and even for
parts of QEMU where we don't really care about the underlying storage
format, but it is not good for devices, where we often want to expose
the storage to the guest and so need a type that is not
variably-sized between host OSes.

We already have a workaround for this in the GICv3 model:
arm_gicv3_common.h defines equivalents of the bit operations that
work on uint32_t.  It turns out that we should also be using
something similar in hw/intc/loongarch_extioi.c, which currently
casts a pointer to a uint32_t array to 'unsigned long *' in
extio_setirq(), which is both undefined behaviour and not correct on
a big-endian host.

Define equivalents of the set_bit() function family which work
with a uint32_t array.

(Cc stable because we're about to provide a bugfix to
loongarch_extioi which will depend on this commit.)

Cc: [email protected]
Signed-off-by: Peter Maydell <[email protected]>
Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Message-id: [email protected]
  • Loading branch information
pm215 committed Nov 19, 2024
1 parent 0340cb6 commit 3d7680f
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 3 deletions.
8 changes: 8 additions & 0 deletions include/qemu/bitmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@
#define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]

/*
* This is for use with the bit32 versions of set_bit() etc;
* we don't currently support the full range of bitmap operations
* on bitmaps backed by an array of uint32_t.
*/
#define DECLARE_BITMAP32(name, bits) \
uint32_t name[BITS_TO_U32S(bits)]

#define small_nbits(nbits) \
((nbits) <= BITS_PER_LONG)

Expand Down
172 changes: 169 additions & 3 deletions include/qemu/bitops.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,47 @@

#define BITS_PER_BYTE CHAR_BIT
#define BITS_PER_LONG (sizeof (unsigned long) * BITS_PER_BYTE)
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
#define BITS_TO_U32S(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(uint32_t))

#define BIT(nr) (1UL << (nr))
#define BIT_ULL(nr) (1ULL << (nr))
#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))

#define MAKE_64BIT_MASK(shift, length) \
(((~0ULL) >> (64 - (length))) << (shift))

/**
* DOC: Functions operating on arrays of bits
*
* We provide a set of functions which work on arbitrary-length arrays of
* bits. These come in several flavours which vary in what the type of the
* underlying storage for the bits is:
*
* - Bits stored in an array of 'unsigned long': set_bit(), clear_bit(), etc
* - Bits stored in an array of 'uint32_t': set_bit32(), clear_bit32(), etc
*
* Because the 'unsigned long' type has a size which varies between
* host systems, the versions using 'uint32_t' are often preferable.
* This is particularly the case in a device model where there may
* be some guest-visible register view of the bit array.
*
* We do not currently implement uint32_t versions of find_last_bit(),
* find_next_bit(), find_next_zero_bit(), find_first_bit() or
* find_first_zero_bit(), because we haven't yet needed them. If you
* need them you should implement them similarly to the 'unsigned long'
* versions.
*
* You can declare a bitmap to be used with these functions via the
* DECLARE_BITMAP and DECLARE_BITMAP32 macros in bitmap.h.
*/

/**
* DOC: 'unsigned long' bit array APIs
*/

#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)

/**
* set_bit - Set a bit in memory
* @nr: the bit to set
Expand Down Expand Up @@ -224,6 +255,141 @@ static inline unsigned long find_first_zero_bit(const unsigned long *addr,
return find_next_zero_bit(addr, size, 0);
}

/**
* DOC: 'uint32_t' bit array APIs
*/

#define BIT32_MASK(nr) (1UL << ((nr) % 32))
#define BIT32_WORD(nr) ((nr) / 32)

/**
* set_bit32 - Set a bit in memory
* @nr: the bit to set
* @addr: the address to start counting from
*/
static inline void set_bit32(long nr, uint32_t *addr)
{
uint32_t mask = BIT32_MASK(nr);
uint32_t *p = addr + BIT32_WORD(nr);

*p |= mask;
}

/**
* set_bit32_atomic - Set a bit in memory atomically
* @nr: the bit to set
* @addr: the address to start counting from
*/
static inline void set_bit32_atomic(long nr, uint32_t *addr)
{
uint32_t mask = BIT32_MASK(nr);
uint32_t *p = addr + BIT32_WORD(nr);

qatomic_or(p, mask);
}

/**
* clear_bit32 - Clears a bit in memory
* @nr: Bit to clear
* @addr: Address to start counting from
*/
static inline void clear_bit32(long nr, uint32_t *addr)
{
uint32_t mask = BIT32_MASK(nr);
uint32_t *p = addr + BIT32_WORD(nr);

*p &= ~mask;
}

/**
* clear_bit32_atomic - Clears a bit in memory atomically
* @nr: Bit to clear
* @addr: Address to start counting from
*/
static inline void clear_bit32_atomic(long nr, uint32_t *addr)
{
uint32_t mask = BIT32_MASK(nr);
uint32_t *p = addr + BIT32_WORD(nr);

return qatomic_and(p, ~mask);
}

/**
* change_bit32 - Toggle a bit in memory
* @nr: Bit to change
* @addr: Address to start counting from
*/
static inline void change_bit32(long nr, uint32_t *addr)
{
uint32_t mask = BIT32_MASK(nr);
uint32_t *p = addr + BIT32_WORD(nr);

*p ^= mask;
}

/**
* test_and_set_bit32 - Set a bit and return its old value
* @nr: Bit to set
* @addr: Address to count from
*/
static inline int test_and_set_bit32(long nr, uint32_t *addr)
{
uint32_t mask = BIT32_MASK(nr);
uint32_t *p = addr + BIT32_WORD(nr);
uint32_t old = *p;

*p = old | mask;
return (old & mask) != 0;
}

/**
* test_and_clear_bit32 - Clear a bit and return its old value
* @nr: Bit to clear
* @addr: Address to count from
*/
static inline int test_and_clear_bit32(long nr, uint32_t *addr)
{
uint32_t mask = BIT32_MASK(nr);
uint32_t *p = addr + BIT32_WORD(nr);
uint32_t old = *p;

*p = old & ~mask;
return (old & mask) != 0;
}

/**
* test_and_change_bit32 - Change a bit and return its old value
* @nr: Bit to change
* @addr: Address to count from
*/
static inline int test_and_change_bit32(long nr, uint32_t *addr)
{
uint32_t mask = BIT32_MASK(nr);
uint32_t *p = addr + BIT32_WORD(nr);
uint32_t old = *p;

*p = old ^ mask;
return (old & mask) != 0;
}

/**
* test_bit32 - Determine whether a bit is set
* @nr: bit number to test
* @addr: Address to start counting from
*/
static inline int test_bit32(long nr, const uint32_t *addr)
{
return 1U & (addr[BIT32_WORD(nr)] >> (nr & 31));
}

/**
* DOC: Miscellaneous bit operations on single values
*
* These functions are a collection of useful operations
* (rotations, bit extract, bit deposit, etc) on single
* integer values.
*/

/**
* rol8 - rotate an 8-bit value left
* @word: value to rotate
Expand Down

0 comments on commit 3d7680f

Please sign in to comment.