mirror of
https://github.com/yuzu-emu/unicorn
synced 2024-11-24 11:28:11 +00:00
4956effd11
Provide helpers to convert bitmaps to little endian format. It can be used when we want to send one bitmap via network to some other hosts. One thing to mention is that, these helpers only solve the problem of endianess, but it does not solve the problem of different word size on machines (the bitmaps managing same count of bits may contains different size when malloced). So we need to take care of the size alignment issue on the callers for now. Backports commit d7788151a0807d5d2d410e3f8944d8c8a651f8d2 from qemu
198 lines
4.8 KiB
C
198 lines
4.8 KiB
C
/*
|
|
* Bitmap Module
|
|
*
|
|
* Stolen from linux/src/lib/bitmap.c
|
|
*
|
|
* Copyright (C) 2010 Corentin Chary
|
|
*
|
|
* This source code is licensed under the GNU General Public License,
|
|
* Version 2.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/bitops.h"
|
|
#include "qemu/bitmap.h"
|
|
#include "qemu/atomic.h"
|
|
|
|
#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) % BITS_PER_LONG))
|
|
|
|
void bitmap_set(unsigned long *map, long start, long nr)
|
|
{
|
|
unsigned long *p = map + BIT_WORD(start);
|
|
const long size = start + nr;
|
|
int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG);
|
|
unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start);
|
|
|
|
while (nr - bits_to_set >= 0) {
|
|
*p |= mask_to_set;
|
|
nr -= bits_to_set;
|
|
bits_to_set = BITS_PER_LONG;
|
|
mask_to_set = ~0UL;
|
|
p++;
|
|
}
|
|
if (nr) {
|
|
mask_to_set &= BITMAP_LAST_WORD_MASK(size);
|
|
*p |= mask_to_set;
|
|
}
|
|
}
|
|
|
|
void bitmap_set_atomic(unsigned long *map, long start, long nr)
|
|
{
|
|
unsigned long *p = map + BIT_WORD(start);
|
|
const long size = start + nr;
|
|
int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG);
|
|
unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start);
|
|
|
|
/* First word */
|
|
if (nr - bits_to_set > 0) {
|
|
atomic_or(p, mask_to_set);
|
|
nr -= bits_to_set;
|
|
bits_to_set = BITS_PER_LONG;
|
|
mask_to_set = ~0UL;
|
|
p++;
|
|
}
|
|
|
|
/* Full words */
|
|
if (bits_to_set == BITS_PER_LONG) {
|
|
while (nr >= BITS_PER_LONG) {
|
|
*p = ~0UL;
|
|
nr -= BITS_PER_LONG;
|
|
p++;
|
|
}
|
|
}
|
|
|
|
/* Last word */
|
|
if (nr) {
|
|
mask_to_set &= BITMAP_LAST_WORD_MASK(size);
|
|
atomic_or(p, mask_to_set);
|
|
} else {
|
|
/* If we avoided the full barrier in atomic_or(), issue a
|
|
* barrier to account for the assignments in the while loop.
|
|
*/
|
|
smp_mb();
|
|
}
|
|
}
|
|
|
|
void bitmap_clear(unsigned long *map, long start, long nr)
|
|
{
|
|
unsigned long *p = map + BIT_WORD(start);
|
|
const long size = start + nr;
|
|
int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG);
|
|
unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start);
|
|
|
|
while (nr - bits_to_clear >= 0) {
|
|
*p &= ~mask_to_clear;
|
|
nr -= bits_to_clear;
|
|
bits_to_clear = BITS_PER_LONG;
|
|
mask_to_clear = ~0UL;
|
|
p++;
|
|
}
|
|
if (nr) {
|
|
mask_to_clear &= BITMAP_LAST_WORD_MASK(size);
|
|
*p &= ~mask_to_clear;
|
|
}
|
|
}
|
|
|
|
bool bitmap_test_and_clear_atomic(unsigned long *map, long start, long nr)
|
|
{
|
|
unsigned long *p = map + BIT_WORD(start);
|
|
const long size = start + nr;
|
|
int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG);
|
|
unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start);
|
|
unsigned long dirty = 0;
|
|
unsigned long old_bits;
|
|
|
|
/* First word */
|
|
if (nr - bits_to_clear > 0) {
|
|
old_bits = atomic_fetch_and(p, ~mask_to_clear);
|
|
dirty |= old_bits & mask_to_clear;
|
|
nr -= bits_to_clear;
|
|
bits_to_clear = BITS_PER_LONG;
|
|
mask_to_clear = ~0UL;
|
|
p++;
|
|
}
|
|
|
|
/* Full words */
|
|
if (bits_to_clear == BITS_PER_LONG) {
|
|
while (nr >= BITS_PER_LONG) {
|
|
if (*p) {
|
|
old_bits = atomic_xchg(p, 0);
|
|
dirty |= old_bits;
|
|
}
|
|
nr -= BITS_PER_LONG;
|
|
p++;
|
|
}
|
|
}
|
|
|
|
/* Last word */
|
|
if (nr) {
|
|
mask_to_clear &= BITMAP_LAST_WORD_MASK(size);
|
|
old_bits = atomic_fetch_and(p, ~mask_to_clear);
|
|
dirty |= old_bits & mask_to_clear;
|
|
} else {
|
|
if (!dirty) {
|
|
smp_mb();
|
|
}
|
|
}
|
|
|
|
return dirty != 0;
|
|
}
|
|
|
|
void bitmap_copy_and_clear_atomic(unsigned long *dst, unsigned long *src,
|
|
long nr)
|
|
{
|
|
while (nr > 0) {
|
|
*dst = atomic_xchg(src, 0);
|
|
dst++;
|
|
src++;
|
|
nr -= BITS_PER_LONG;
|
|
}
|
|
}
|
|
|
|
long slow_bitmap_count_one(const unsigned long *bitmap, long nbits)
|
|
{
|
|
long k, lim = nbits / BITS_PER_LONG, result = 0;
|
|
|
|
for (k = 0; k < lim; k++) {
|
|
result += ctpopl(bitmap[k]);
|
|
}
|
|
|
|
if (nbits % BITS_PER_LONG) {
|
|
result += ctpopl(bitmap[k] & BITMAP_LAST_WORD_MASK(nbits));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void bitmap_to_from_le(unsigned long *dst,
|
|
const unsigned long *src, long nbits)
|
|
{
|
|
long len = BITS_TO_LONGS(nbits);
|
|
|
|
#ifdef HOST_WORDS_BIGENDIAN
|
|
long index;
|
|
|
|
for (index = 0; index < len; index++) {
|
|
# if HOST_LONG_BITS == 64
|
|
dst[index] = bswap64(src[index]);
|
|
# else
|
|
dst[index] = bswap32(src[index]);
|
|
# endif
|
|
}
|
|
#else
|
|
memcpy(dst, src, len * sizeof(unsigned long));
|
|
#endif
|
|
}
|
|
|
|
void bitmap_from_le(unsigned long *dst, const unsigned long *src,
|
|
long nbits)
|
|
{
|
|
bitmap_to_from_le(dst, src, nbits);
|
|
}
|
|
|
|
void bitmap_to_le(unsigned long *dst, const unsigned long *src,
|
|
long nbits)
|
|
{
|
|
bitmap_to_from_le(dst, src, nbits);
|
|
}
|
|
|