mirror of
https://github.com/lkl/linux.git
synced 2025-12-19 16:13:19 +09:00
There's a bug in *_encode_bits() in using ~field_multiplier() for
the check whether or not the constant value fits into the field,
this is wrong and clearly ~field_mask() was intended. This was
triggering for me for both constant and non-constant values.
Additionally, make this case actually into an compile error.
Declaring the extern function that will never exist with just a
warning is pointless as then later we'll just get a link error.
While at it, also fix the indentation in those lines I'm touching.
Finally, as suggested by Andy Shevchenko, add some tests and for
that introduce also u8 helpers. The tests don't compile without
the fix, showing that it's necessary.
Fixes: 00b0c9b826 ("Add primitives for manipulating bitfields both in host- and fixed-endian.")
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
153 lines
4.7 KiB
C
153 lines
4.7 KiB
C
/*
|
|
* Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
|
|
* Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2
|
|
* as published by the Free Software Foundation
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#ifndef _LINUX_BITFIELD_H
|
|
#define _LINUX_BITFIELD_H
|
|
|
|
#include <linux/build_bug.h>
|
|
#include <asm/byteorder.h>
|
|
|
|
/*
|
|
* Bitfield access macros
|
|
*
|
|
* FIELD_{GET,PREP} macros take as first parameter shifted mask
|
|
* from which they extract the base mask and shift amount.
|
|
* Mask must be a compilation time constant.
|
|
*
|
|
* Example:
|
|
*
|
|
* #define REG_FIELD_A GENMASK(6, 0)
|
|
* #define REG_FIELD_B BIT(7)
|
|
* #define REG_FIELD_C GENMASK(15, 8)
|
|
* #define REG_FIELD_D GENMASK(31, 16)
|
|
*
|
|
* Get:
|
|
* a = FIELD_GET(REG_FIELD_A, reg);
|
|
* b = FIELD_GET(REG_FIELD_B, reg);
|
|
*
|
|
* Set:
|
|
* reg = FIELD_PREP(REG_FIELD_A, 1) |
|
|
* FIELD_PREP(REG_FIELD_B, 0) |
|
|
* FIELD_PREP(REG_FIELD_C, c) |
|
|
* FIELD_PREP(REG_FIELD_D, 0x40);
|
|
*
|
|
* Modify:
|
|
* reg &= ~REG_FIELD_C;
|
|
* reg |= FIELD_PREP(REG_FIELD_C, c);
|
|
*/
|
|
|
|
#define __bf_shf(x) (__builtin_ffsll(x) - 1)
|
|
|
|
#define __BF_FIELD_CHECK(_mask, _reg, _val, _pfx) \
|
|
({ \
|
|
BUILD_BUG_ON_MSG(!__builtin_constant_p(_mask), \
|
|
_pfx "mask is not constant"); \
|
|
BUILD_BUG_ON_MSG(!(_mask), _pfx "mask is zero"); \
|
|
BUILD_BUG_ON_MSG(__builtin_constant_p(_val) ? \
|
|
~((_mask) >> __bf_shf(_mask)) & (_val) : 0, \
|
|
_pfx "value too large for the field"); \
|
|
BUILD_BUG_ON_MSG((_mask) > (typeof(_reg))~0ull, \
|
|
_pfx "type of reg too small for mask"); \
|
|
__BUILD_BUG_ON_NOT_POWER_OF_2((_mask) + \
|
|
(1ULL << __bf_shf(_mask))); \
|
|
})
|
|
|
|
/**
|
|
* FIELD_FIT() - check if value fits in the field
|
|
* @_mask: shifted mask defining the field's length and position
|
|
* @_val: value to test against the field
|
|
*
|
|
* Return: true if @_val can fit inside @_mask, false if @_val is too big.
|
|
*/
|
|
#define FIELD_FIT(_mask, _val) \
|
|
({ \
|
|
__BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_FIT: "); \
|
|
!((((typeof(_mask))_val) << __bf_shf(_mask)) & ~(_mask)); \
|
|
})
|
|
|
|
/**
|
|
* FIELD_PREP() - prepare a bitfield element
|
|
* @_mask: shifted mask defining the field's length and position
|
|
* @_val: value to put in the field
|
|
*
|
|
* FIELD_PREP() masks and shifts up the value. The result should
|
|
* be combined with other fields of the bitfield using logical OR.
|
|
*/
|
|
#define FIELD_PREP(_mask, _val) \
|
|
({ \
|
|
__BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
|
|
((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \
|
|
})
|
|
|
|
/**
|
|
* FIELD_GET() - extract a bitfield element
|
|
* @_mask: shifted mask defining the field's length and position
|
|
* @_reg: value of entire bitfield
|
|
*
|
|
* FIELD_GET() extracts the field specified by @_mask from the
|
|
* bitfield passed in as @_reg by masking and shifting it down.
|
|
*/
|
|
#define FIELD_GET(_mask, _reg) \
|
|
({ \
|
|
__BF_FIELD_CHECK(_mask, _reg, 0U, "FIELD_GET: "); \
|
|
(typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
|
|
})
|
|
|
|
extern void __compiletime_error("value doesn't fit into mask")
|
|
__field_overflow(void);
|
|
extern void __compiletime_error("bad bitfield mask")
|
|
__bad_mask(void);
|
|
static __always_inline u64 field_multiplier(u64 field)
|
|
{
|
|
if ((field | (field - 1)) & ((field | (field - 1)) + 1))
|
|
__bad_mask();
|
|
return field & -field;
|
|
}
|
|
static __always_inline u64 field_mask(u64 field)
|
|
{
|
|
return field / field_multiplier(field);
|
|
}
|
|
#define ____MAKE_OP(type,base,to,from) \
|
|
static __always_inline __##type type##_encode_bits(base v, base field) \
|
|
{ \
|
|
if (__builtin_constant_p(v) && (v & ~field_mask(field))) \
|
|
__field_overflow(); \
|
|
return to((v & field_mask(field)) * field_multiplier(field)); \
|
|
} \
|
|
static __always_inline __##type type##_replace_bits(__##type old, \
|
|
base val, base field) \
|
|
{ \
|
|
return (old & ~to(field)) | type##_encode_bits(val, field); \
|
|
} \
|
|
static __always_inline void type##p_replace_bits(__##type *p, \
|
|
base val, base field) \
|
|
{ \
|
|
*p = (*p & ~to(field)) | type##_encode_bits(val, field); \
|
|
} \
|
|
static __always_inline base type##_get_bits(__##type v, base field) \
|
|
{ \
|
|
return (from(v) & field)/field_multiplier(field); \
|
|
}
|
|
#define __MAKE_OP(size) \
|
|
____MAKE_OP(le##size,u##size,cpu_to_le##size,le##size##_to_cpu) \
|
|
____MAKE_OP(be##size,u##size,cpu_to_be##size,be##size##_to_cpu) \
|
|
____MAKE_OP(u##size,u##size,,)
|
|
__MAKE_OP(16)
|
|
__MAKE_OP(32)
|
|
__MAKE_OP(64)
|
|
#undef __MAKE_OP
|
|
#undef ____MAKE_OP
|
|
|
|
#endif
|