mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 17:27:37 -04:00
Add KUnit tests to make sure the macro is working correctly. The unit tests are put behind the new `RUST_BITFIELD_KUNIT_TEST` Kconfig option. Acked-by: Danilo Krummrich <dakr@kernel.org> Reviewed-by: Eliot Courtney <ecourtney@nvidia.com> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com> [acourbot: - Use a consistent test axis where each test focuses on a single thing. - Rename members to generic name including range for readability. - Add test exercising `try_with`. - Add test checking that unallocated bits are left untouched. ] Co-developed-by: Alexandre Courbot <acourbot@nvidia.com> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com> Reviewed-by: Yury Norov <ynorov@nvidia.com> Link: https://patch.msgid.link/20260606-bitfield-v5-2-b92188820914@nvidia.com [ Prefixed test suite name with `rust_` as mentioned. Markdown-formatted a few comments with Markdown. - Miguel ] Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
863 lines
27 KiB
Rust
863 lines
27 KiB
Rust
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
//! Support for defining bitfields as Rust structures.
|
|
//!
|
|
//! The [`bitfield!`](kernel::bitfield!) macro declares integer types that are split into distinct
|
|
//! bit fields of arbitrary length. Each field is typed using [`Bounded`](kernel::num::Bounded) to
|
|
//! ensure values are properly validated and to avoid implicit data loss.
|
|
//!
|
|
//! # Example
|
|
//!
|
|
//! ```rust
|
|
//! use kernel::bitfield;
|
|
//! use kernel::num::Bounded;
|
|
//!
|
|
//! bitfield! {
|
|
//! pub struct Rgb(u16) {
|
|
//! 15:11 blue;
|
|
//! 10:5 green;
|
|
//! 4:0 red;
|
|
//! }
|
|
//! }
|
|
//!
|
|
//! // Valid value for the `blue` field.
|
|
//! let blue = Bounded::<u16, 5>::new::<0x18>();
|
|
//!
|
|
//! // Setters can be chained. Values ranges are checked at compile-time.
|
|
//! let color = Rgb::zeroed()
|
|
//! // Compile-time bounds check of constant value.
|
|
//! .with_const_red::<0x10>()
|
|
//! .with_const_green::<0x1f>()
|
|
//! // A `Bounded` can also be passed.
|
|
//! .with_blue(blue);
|
|
//!
|
|
//! assert_eq!(color.red(), 0x10);
|
|
//! assert_eq!(color.green(), 0x1f);
|
|
//! assert_eq!(color.blue(), 0x18);
|
|
//! assert_eq!(
|
|
//! color.into_raw(),
|
|
//! (0x18 << Rgb::BLUE_SHIFT) + (0x1f << Rgb::GREEN_SHIFT) + 0x10,
|
|
//! );
|
|
//!
|
|
//! // Convert to/from the backing storage type.
|
|
//! let raw: u16 = color.into();
|
|
//! assert_eq!(Rgb::from(raw), color);
|
|
//! ```
|
|
//!
|
|
//! # Syntax
|
|
//!
|
|
//! ```text
|
|
//! bitfield! {
|
|
//! #[attributes]
|
|
//! // Documentation for `Name`.
|
|
//! pub struct Name(storage_type) {
|
|
//! // `field_1` documentation.
|
|
//! hi:lo field_1;
|
|
//! // `field_2` documentation.
|
|
//! hi:lo field_2 => ConvertedType;
|
|
//! // `field_3` documentation.
|
|
//! hi:lo field_3 ?=> ConvertedType;
|
|
//! ...
|
|
//! }
|
|
//! }
|
|
//! ```
|
|
//!
|
|
//! - `storage_type`: The underlying unsigned integer type ([`u8`], [`u16`], [`u32`], [`u64`]).
|
|
//! Signed integer storage types are not supported.
|
|
//! - `hi:lo`: Bit range (inclusive), where `hi >= lo`.
|
|
//! - `=> Type`: Optional infallible conversion (see [below](#infallible-conversion-)).
|
|
//! - `?=> Type`: Optional fallible conversion (see [below](#fallible-conversion-)).
|
|
//! - Documentation strings and attributes are optional.
|
|
//!
|
|
//! # Generated code
|
|
//!
|
|
//! Each field is internally represented as a [`Bounded`] parameterized by its bit width. Field
|
|
//! values can either be set/retrieved directly, or converted from/to another type.
|
|
//!
|
|
//! The use of [`Bounded`] for each field enforces bounds-checking (at build time or runtime) of
|
|
//! every value assigned to a field. This ensures that data is never accidentally truncated.
|
|
//!
|
|
//! The macro generates the bitfield type, [`From`] and [`Into`] implementations for its storage
|
|
//! type, as well as [`Debug`] and [`Zeroable`](pin_init::Zeroable) implementations.
|
|
//!
|
|
//! For each field, it also generates:
|
|
//!
|
|
//! - `field()`: Getter method for the field value.
|
|
//! - `with_field(value)`: Infallible setter; the argument type must fit within the field's width.
|
|
//! - `with_const_field::<VALUE>()`: `const` setter; the value is validated at compile time.
|
|
//! Usually shorter to use than `with_field` for constant values as it doesn't require
|
|
//! constructing a [`Bounded`].
|
|
//! - `try_with_field(value)`: Fallible setter. Returns an error if the value is out of range.
|
|
//! - `FIELD_MASK`, `FIELD_SHIFT`, `FIELD_RANGE`: Constants for manual bit manipulation.
|
|
//!
|
|
//! # Reserved names for field identifiers
|
|
//!
|
|
//! Field identifiers are used to generate methods and associated constants on the bitfield type.
|
|
//! For a field named `field`, the macro may generate methods named `field`, `with_field`,
|
|
//! `with_const_field`, `try_with_field`, `__field` and `__with_field`, as well as constants named
|
|
//! `FIELD_MASK`, `FIELD_SHIFT` and `FIELD_RANGE`.
|
|
//!
|
|
//! Therefore, field identifiers must not use names that would collide with generated items for
|
|
//! any field in the same bitfield. The following prefixes are thus reserved for field identifiers:
|
|
//!
|
|
//! - `with_`
|
|
//! - `const_`
|
|
//! - `try_with_`
|
|
//! - `__`
|
|
//!
|
|
//! The field identifiers `from_raw`, `into_raw`, and `into` are also reserved.
|
|
//!
|
|
//! In addition, field identifiers should follow Rust `snake_case` conventions, since the associated
|
|
//! constants are generated by uppercasing the field name.
|
|
//!
|
|
//! # Implicit conversions
|
|
//!
|
|
//! Types that fit entirely within a field's bit width can be used directly with setters. For
|
|
//! example, [`bool`] works with single-bit fields, and [`u8`] works with 8-bit fields:
|
|
//!
|
|
//! ```rust
|
|
//! use kernel::bitfield;
|
|
//!
|
|
//! bitfield! {
|
|
//! pub struct Flags(u32) {
|
|
//! 15:8 byte_field;
|
|
//! 0:0 flag;
|
|
//! }
|
|
//! }
|
|
//!
|
|
//! let flags = Flags::zeroed()
|
|
//! .with_byte_field(0x42_u8)
|
|
//! .with_flag(true);
|
|
//!
|
|
//! assert_eq!(flags.into_raw(), (0x42 << Flags::BYTE_FIELD_SHIFT) | 1);
|
|
//! ```
|
|
//!
|
|
//! # Runtime bounds checking
|
|
//!
|
|
//! When a value is not known at compile time, use `try_with_field()` to check bounds at runtime:
|
|
//!
|
|
//! ```rust
|
|
//! use kernel::bitfield;
|
|
//!
|
|
//! bitfield! {
|
|
//! pub struct Config(u8) {
|
|
//! 3:0 nibble;
|
|
//! }
|
|
//! }
|
|
//!
|
|
//! fn set_nibble(config: Config, value: u8) -> Result<Config, Error> {
|
|
//! // Returns `EOVERFLOW` if `value > 0xf`.
|
|
//! config.try_with_nibble(value)
|
|
//! }
|
|
//! # Ok::<(), Error>(())
|
|
//! ```
|
|
//!
|
|
//! # Type conversion
|
|
//!
|
|
//! Fields can be automatically converted to/from a custom type using `=>` (infallible) or `?=>`
|
|
//! (fallible). The custom type must implement the appropriate [`From`] or [`TryFrom`] traits with
|
|
//! [`Bounded`].
|
|
//!
|
|
//! ## Infallible conversion (`=>`)
|
|
//!
|
|
//! Use this when all possible bit patterns of a field map to valid values:
|
|
//!
|
|
//! ```rust
|
|
//! use kernel::bitfield;
|
|
//! use kernel::num::Bounded;
|
|
//!
|
|
//! #[derive(Debug, Clone, Copy, PartialEq)]
|
|
//! enum Power {
|
|
//! Off,
|
|
//! On,
|
|
//! }
|
|
//!
|
|
//! impl From<Bounded<u32, 1>> for Power {
|
|
//! fn from(v: Bounded<u32, 1>) -> Self {
|
|
//! match *v {
|
|
//! 0 => Power::Off,
|
|
//! _ => Power::On,
|
|
//! }
|
|
//! }
|
|
//! }
|
|
//!
|
|
//! impl From<Power> for Bounded<u32, 1> {
|
|
//! fn from(p: Power) -> Self {
|
|
//! (p as u32 != 0).into()
|
|
//! }
|
|
//! }
|
|
//!
|
|
//! bitfield! {
|
|
//! pub struct Control(u32) {
|
|
//! 0:0 power => Power;
|
|
//! }
|
|
//! }
|
|
//!
|
|
//! let ctrl = Control::zeroed().with_power(Power::On);
|
|
//! assert_eq!(ctrl.power(), Power::On);
|
|
//! ```
|
|
//!
|
|
//! ## Fallible conversion (`?=>`)
|
|
//!
|
|
//! Use this when some bit patterns of a field are invalid. The getter returns a [`Result`]:
|
|
//!
|
|
//! ```rust
|
|
//! use kernel::bitfield;
|
|
//! use kernel::num::Bounded;
|
|
//!
|
|
//! #[derive(Debug, Clone, Copy, PartialEq)]
|
|
//! enum Mode {
|
|
//! Low = 0,
|
|
//! High = 1,
|
|
//! Auto = 2,
|
|
//! // 3 is invalid
|
|
//! }
|
|
//!
|
|
//! impl TryFrom<Bounded<u32, 2>> for Mode {
|
|
//! type Error = u32;
|
|
//!
|
|
//! fn try_from(v: Bounded<u32, 2>) -> Result<Self, u32> {
|
|
//! match *v {
|
|
//! 0 => Ok(Mode::Low),
|
|
//! 1 => Ok(Mode::High),
|
|
//! 2 => Ok(Mode::Auto),
|
|
//! n => Err(n),
|
|
//! }
|
|
//! }
|
|
//! }
|
|
//!
|
|
//! impl From<Mode> for Bounded<u32, 2> {
|
|
//! fn from(m: Mode) -> Self {
|
|
//! match m {
|
|
//! Mode::Low => Bounded::<u32, _>::new::<0>(),
|
|
//! Mode::High => Bounded::<u32, _>::new::<1>(),
|
|
//! Mode::Auto => Bounded::<u32, _>::new::<2>(),
|
|
//! }
|
|
//! }
|
|
//! }
|
|
//!
|
|
//! bitfield! {
|
|
//! pub struct Config(u32) {
|
|
//! 1:0 mode ?=> Mode;
|
|
//! }
|
|
//! }
|
|
//!
|
|
//! let cfg = Config::zeroed().with_mode(Mode::Auto);
|
|
//! assert_eq!(cfg.mode(), Ok(Mode::Auto));
|
|
//!
|
|
//! // Invalid bit pattern returns an error.
|
|
//! assert_eq!(Config::from(0b11).mode(), Err(3));
|
|
//! ```
|
|
//!
|
|
//! # Bits outside of declared fields
|
|
//!
|
|
//! Bits of the storage type that are not part of any declared field are preserved by the setter
|
|
//! methods, and can only be modified through `from_raw` or the [`From`] implementation from the
|
|
//! storage type.
|
|
//!
|
|
//! ```rust
|
|
//! use kernel::bitfield;
|
|
//!
|
|
//! bitfield! {
|
|
//! pub struct Sparse(u8) {
|
|
//! 7:6 high;
|
|
//! // Bits 5:1 are not covered by any field.
|
|
//! 0:0 low;
|
|
//! }
|
|
//! }
|
|
//!
|
|
//! // Set the gap bits via `from_raw`, then mutate the declared fields.
|
|
//! let val = Sparse::from_raw(0b0010_1010)
|
|
//! .with_const_high::<0b11>()
|
|
//! .with_low(true);
|
|
//!
|
|
//! // Bits 5:1 are unchanged.
|
|
//! assert_eq!(val.into_raw(), 0b1110_1011);
|
|
//! ```
|
|
//!
|
|
//! # Signed field values
|
|
//!
|
|
//! Bitfield storage types are unsigned. Since field getter methods return a [`Bounded`] of the
|
|
//! storage type, fields are also unsigned by default.
|
|
//!
|
|
//! If a field needs to encode a signed value, use a custom conversion type with `=>` or `?=>` to
|
|
//! perform the sign interpretation explicitly.
|
|
//!
|
|
//! [`Bounded`]: kernel::num::Bounded
|
|
|
|
/// Defines a bitfield struct with bounds-checked accessors for individual bit ranges.
|
|
///
|
|
/// See the [`mod@kernel::bitfield`] module for full documentation and examples.
|
|
#[macro_export]
|
|
macro_rules! bitfield {
|
|
// Entry point defining the bitfield struct, its implementations and its field accessors.
|
|
(
|
|
$(#[$attr:meta])* $vis:vis struct $name:ident($storage:ty) { $($fields:tt)* }
|
|
) => {
|
|
$crate::bitfield!(@core
|
|
#[allow(non_camel_case_types)]
|
|
$(#[$attr])* $vis $name $storage
|
|
);
|
|
$crate::bitfield!(@fields $vis $name $storage { $($fields)* });
|
|
};
|
|
|
|
// All rules below are helpers.
|
|
|
|
// Defines the wrapper `$name` type and its conversions from/to the storage type.
|
|
(@core $(#[$attr:meta])* $vis:vis $name:ident $storage:ty) => {
|
|
$(#[$attr])*
|
|
#[repr(transparent)]
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
$vis struct $name {
|
|
inner: $storage,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl $name {
|
|
/// Creates a bitfield from a raw value.
|
|
#[inline(always)]
|
|
$vis const fn from_raw(value: $storage) -> Self {
|
|
Self{ inner: value }
|
|
}
|
|
|
|
/// Turns this bitfield into its raw value.
|
|
///
|
|
/// This is similar to the [`From`] implementation, but is shorter to invoke in
|
|
/// most cases.
|
|
#[inline(always)]
|
|
$vis const fn into_raw(self) -> $storage {
|
|
self.inner
|
|
}
|
|
}
|
|
|
|
// SAFETY: `$storage` is `Zeroable` and `$name` is transparent.
|
|
unsafe impl ::pin_init::Zeroable for $name {}
|
|
|
|
impl ::core::convert::From<$name> for $storage {
|
|
#[inline(always)]
|
|
fn from(val: $name) -> $storage {
|
|
val.into_raw()
|
|
}
|
|
}
|
|
|
|
impl ::core::convert::From<$storage> for $name {
|
|
#[inline(always)]
|
|
fn from(val: $storage) -> $name {
|
|
Self::from_raw(val)
|
|
}
|
|
}
|
|
};
|
|
|
|
// Definitions requiring knowledge of individual fields: private and public field accessors,
|
|
// and `Debug` implementation.
|
|
(@fields $vis:vis $name:ident $storage:ty {
|
|
$($(#[doc = $doc:expr])* $hi:literal:$lo:literal $field:ident
|
|
$(?=> $try_into_type:ty)?
|
|
$(=> $into_type:ty)?
|
|
;
|
|
)*
|
|
}
|
|
) => {
|
|
#[allow(dead_code)]
|
|
impl $name {
|
|
$(
|
|
$crate::bitfield!(@private_field_accessors $vis $name $storage : $hi:$lo $field);
|
|
$crate::bitfield!(
|
|
@public_field_accessors $(#[doc = $doc])* $vis $name $storage : $hi:$lo $field
|
|
$(?=> $try_into_type)?
|
|
$(=> $into_type)?
|
|
);
|
|
)*
|
|
}
|
|
|
|
$crate::bitfield!(@debug $name { $($field;)* });
|
|
};
|
|
|
|
// Private field accessors working with the exact `Bounded` type for the field.
|
|
(
|
|
@private_field_accessors $vis:vis $name:ident $storage:ty : $hi:tt:$lo:tt $field:ident
|
|
) => {
|
|
::kernel::macros::paste!(
|
|
$vis const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
|
|
$vis const [<$field:upper _MASK>]: $storage =
|
|
((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
|
|
$vis const [<$field:upper _SHIFT>]: u32 = $lo;
|
|
);
|
|
|
|
::kernel::macros::paste!(
|
|
#[inline(always)]
|
|
fn [<__ $field>](self) ->
|
|
::kernel::num::Bounded<$storage, { $hi + 1 - $lo }> {
|
|
// Left shift to align the field's MSB with the storage MSB.
|
|
const ALIGN_TOP: u32 = $storage::BITS - ($hi + 1);
|
|
// Right shift to move the top-aligned field to bit 0 of the storage.
|
|
const ALIGN_BOTTOM: u32 = ALIGN_TOP + $lo;
|
|
|
|
// Extract the field using two shifts. `Bounded::shr` produces the correctly-sized
|
|
// output type.
|
|
let val = ::kernel::num::Bounded::<$storage, { $storage::BITS }>::from(
|
|
self.inner << ALIGN_TOP
|
|
);
|
|
val.shr::<ALIGN_BOTTOM, { $hi + 1 - $lo } >()
|
|
}
|
|
|
|
#[inline(always)]
|
|
const fn [<__with_ $field>](
|
|
mut self,
|
|
value: ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>,
|
|
) -> Self
|
|
{
|
|
const MASK: $storage = <$name>::[<$field:upper _MASK>];
|
|
const SHIFT: u32 = <$name>::[<$field:upper _SHIFT>];
|
|
|
|
let value = value.get() << SHIFT;
|
|
self.inner = (self.inner & !MASK) | value;
|
|
|
|
self
|
|
}
|
|
);
|
|
};
|
|
|
|
// Public accessors for fields infallibly (`=>`) converted to a type.
|
|
(
|
|
@public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
|
|
$hi:literal:$lo:literal $field:ident => $into_type:ty
|
|
) => {
|
|
::kernel::macros::paste!(
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Returns the value of this field."]
|
|
#[inline(always)]
|
|
$vis fn $field(self) -> $into_type
|
|
{
|
|
self.[<__ $field>]().into()
|
|
}
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Sets this field to the given `value`."]
|
|
#[inline(always)]
|
|
$vis fn [<with_ $field>](self, value: $into_type) -> Self
|
|
{
|
|
self.[<__with_ $field>](value.into())
|
|
}
|
|
|
|
);
|
|
};
|
|
|
|
// Public accessors for fields fallibly (`?=>`) converted to a type.
|
|
(
|
|
@public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
|
|
$hi:tt:$lo:tt $field:ident ?=> $try_into_type:ty
|
|
) => {
|
|
::kernel::macros::paste!(
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Returns the value of this field."]
|
|
#[inline(always)]
|
|
$vis fn $field(self) ->
|
|
::core::result::Result<
|
|
$try_into_type,
|
|
<$try_into_type as ::core::convert::TryFrom<
|
|
::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>
|
|
>>::Error
|
|
>
|
|
{
|
|
self.[<__ $field>]().try_into()
|
|
}
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Sets this field to the given `value`."]
|
|
#[inline(always)]
|
|
$vis fn [<with_ $field>](self, value: $try_into_type) -> Self
|
|
{
|
|
self.[<__with_ $field>](value.into())
|
|
}
|
|
|
|
);
|
|
};
|
|
|
|
// Public accessors for fields not converted to a type.
|
|
(
|
|
@public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
|
|
$hi:tt:$lo:tt $field:ident
|
|
) => {
|
|
::kernel::macros::paste!(
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Returns the value of this field."]
|
|
#[inline(always)]
|
|
$vis fn $field(self) ->
|
|
::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>
|
|
{
|
|
self.[<__ $field>]()
|
|
}
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Sets this field to the compile-time constant `VALUE`."]
|
|
#[inline(always)]
|
|
$vis const fn [<with_const_ $field>]<const VALUE: $storage>(self) -> Self {
|
|
self.[<__with_ $field>](
|
|
::kernel::num::Bounded::<$storage, { $hi + 1 - $lo }>::new::<VALUE>()
|
|
)
|
|
}
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Sets this field to the given `value`."]
|
|
#[inline(always)]
|
|
$vis fn [<with_ $field>]<T>(
|
|
self,
|
|
value: T,
|
|
) -> Self
|
|
where T: ::core::convert::Into<::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>>,
|
|
{
|
|
self.[<__with_ $field>](value.into())
|
|
}
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Tries to set this field to `value`, returning an error if it is out of range."]
|
|
#[inline(always)]
|
|
$vis fn [<try_with_ $field>]<T>(
|
|
self,
|
|
value: T,
|
|
) -> ::kernel::error::Result<Self>
|
|
where T: ::kernel::num::TryIntoBounded<$storage, { $hi + 1 - $lo }>,
|
|
{
|
|
Ok(
|
|
self.[<__with_ $field>](
|
|
value.try_into_bounded().ok_or(::kernel::error::code::EOVERFLOW)?
|
|
)
|
|
)
|
|
}
|
|
|
|
);
|
|
};
|
|
|
|
// `Debug` implementation.
|
|
(@debug $name:ident { $($field:ident;)* }) => {
|
|
impl ::kernel::fmt::Debug for $name {
|
|
fn fmt(&self, f: &mut ::kernel::fmt::Formatter<'_>) -> ::kernel::fmt::Result {
|
|
f.debug_struct(stringify!($name))
|
|
.field("<raw>", &::kernel::prelude::fmt!("{:#x}", self.inner))
|
|
$(
|
|
.field(stringify!($field), &self.$field())
|
|
)*
|
|
.finish()
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
#[cfg(CONFIG_RUST_BITFIELD_KUNIT_TEST)]
|
|
#[::kernel::macros::kunit_tests(rust_kernel_bitfield)]
|
|
mod tests {
|
|
use core::convert::TryFrom;
|
|
|
|
use pin_init::Zeroable;
|
|
|
|
use kernel::num::Bounded;
|
|
|
|
// Enum types for testing `=>` and `?=>` conversions.
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
enum MemoryType {
|
|
Unmapped = 0,
|
|
Normal = 1,
|
|
Device = 2,
|
|
Reserved = 3,
|
|
}
|
|
|
|
impl TryFrom<Bounded<u64, 4>> for MemoryType {
|
|
type Error = u64;
|
|
fn try_from(value: Bounded<u64, 4>) -> Result<Self, Self::Error> {
|
|
match value.get() {
|
|
0 => Ok(MemoryType::Unmapped),
|
|
1 => Ok(MemoryType::Normal),
|
|
2 => Ok(MemoryType::Device),
|
|
3 => Ok(MemoryType::Reserved),
|
|
_ => Err(value.get()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<MemoryType> for Bounded<u64, 4> {
|
|
fn from(mt: MemoryType) -> Bounded<u64, 4> {
|
|
Bounded::from_expr(mt as u64)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
enum Priority {
|
|
Low = 0,
|
|
Medium = 1,
|
|
High = 2,
|
|
Critical = 3,
|
|
}
|
|
|
|
impl From<Bounded<u16, 2>> for Priority {
|
|
fn from(value: Bounded<u16, 2>) -> Self {
|
|
match value & 0x3 {
|
|
0 => Priority::Low,
|
|
1 => Priority::Medium,
|
|
2 => Priority::High,
|
|
_ => Priority::Critical,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Priority> for Bounded<u16, 2> {
|
|
fn from(p: Priority) -> Bounded<u16, 2> {
|
|
Bounded::from_expr(p as u16)
|
|
}
|
|
}
|
|
|
|
bitfield! {
|
|
struct TestU64(u64) {
|
|
63:63 field_63;
|
|
61:52 field_61_52;
|
|
51:16 field_51_16;
|
|
15:12 field_15_12 ?=> MemoryType;
|
|
11:9 field_11_9;
|
|
1:1 field_1;
|
|
0:0 field_0;
|
|
}
|
|
}
|
|
|
|
bitfield! {
|
|
struct TestU16(u16) {
|
|
15:8 field_15_8;
|
|
7:4 field_7_4; // Partial overlap with `field_5_4`.
|
|
5:4 field_5_4 => Priority;
|
|
3:1 field_3_1;
|
|
0:0 field_0;
|
|
}
|
|
}
|
|
|
|
bitfield! {
|
|
struct TestU8(u8) {
|
|
7:0 field_7_0; // Full byte overlap.
|
|
7:4 field_7_4;
|
|
3:2 field_3_2;
|
|
1:1 field_1;
|
|
0:0 field_0;
|
|
}
|
|
}
|
|
|
|
// Single and multi-bit fields basic access.
|
|
#[test]
|
|
fn test_basic_access() {
|
|
// `TestU64`.
|
|
let mut val = TestU64::zeroed();
|
|
assert_eq!(val.into_raw(), 0x0);
|
|
|
|
val = val.with_field_0(true);
|
|
assert!(val.field_0().into_bool());
|
|
assert_eq!(val.into_raw(), 0x1);
|
|
|
|
val = val.with_field_1(true);
|
|
assert!(val.field_1().into_bool());
|
|
val = val.with_field_1(false);
|
|
assert!(!val.field_1().into_bool());
|
|
assert_eq!(val.into_raw(), 0x1);
|
|
|
|
val = val.with_const_field_11_9::<0x5>();
|
|
assert_eq!(val.field_11_9(), 0x5);
|
|
assert_eq!(val.into_raw(), 0xA01);
|
|
|
|
val = val.with_const_field_51_16::<0x123456>();
|
|
assert_eq!(val.field_51_16(), 0x123456);
|
|
assert_eq!(val.into_raw(), 0x0012_3456_0A01);
|
|
|
|
const MAX_FIELD_51_16: u64 = ::kernel::bits::genmask_u64(0..=35);
|
|
val = val.with_const_field_51_16::<{ MAX_FIELD_51_16 }>();
|
|
assert_eq!(val.field_51_16(), MAX_FIELD_51_16);
|
|
|
|
val = val.with_const_field_61_52::<0x3FF>();
|
|
assert_eq!(val.field_61_52(), 0x3FF);
|
|
|
|
val = val.with_field_63(true);
|
|
assert!(val.field_63().into_bool());
|
|
|
|
// `TestU16`.
|
|
let mut val = TestU16::zeroed();
|
|
assert_eq!(val.into_raw(), 0x0);
|
|
|
|
val = val.with_field_0(true);
|
|
assert!(val.field_0().into_bool());
|
|
assert_eq!(val.into_raw(), 0x1);
|
|
|
|
val = val.with_const_field_3_1::<0x5>();
|
|
assert_eq!(val.field_3_1(), 0x5);
|
|
assert_eq!(val.into_raw(), 0xB);
|
|
|
|
val = val.with_const_field_7_4::<0xA>();
|
|
assert_eq!(val.field_7_4(), 0xA);
|
|
assert_eq!(val.into_raw(), 0xAB);
|
|
|
|
val = val.with_const_field_15_8::<0x42>();
|
|
assert_eq!(val.field_15_8(), 0x42);
|
|
assert_eq!(val.into_raw(), 0x42AB);
|
|
|
|
// `TestU8`.
|
|
let mut val = TestU8::zeroed();
|
|
assert_eq!(val.into_raw(), 0x0);
|
|
|
|
val = val.with_field_0(true);
|
|
assert!(val.field_0().into_bool());
|
|
assert_eq!(val.into_raw(), 0x1);
|
|
|
|
val = val.with_field_1(true);
|
|
assert!(val.field_1().into_bool());
|
|
assert_eq!(val.into_raw(), 0x3);
|
|
|
|
val = val.with_const_field_3_2::<0x3>();
|
|
assert_eq!(val.field_3_2(), 0x3);
|
|
assert_eq!(val.into_raw(), 0xF);
|
|
|
|
val = val.with_const_field_7_4::<0xA>();
|
|
assert_eq!(val.field_7_4(), 0xA);
|
|
assert_eq!(val.into_raw(), 0xAF);
|
|
}
|
|
|
|
// `=>` infallible conversion.
|
|
#[test]
|
|
fn test_infallible_conversion() {
|
|
let mut val = TestU16::zeroed();
|
|
|
|
val = val.with_field_5_4(Priority::Low);
|
|
assert_eq!(val.field_5_4(), Priority::Low);
|
|
assert_eq!(val.into_raw() & 0x30, 0x00);
|
|
|
|
val = val.with_field_5_4(Priority::Medium);
|
|
assert_eq!(val.field_5_4(), Priority::Medium);
|
|
assert_eq!(val.into_raw() & 0x30, 0x10);
|
|
|
|
val = val.with_field_5_4(Priority::High);
|
|
assert_eq!(val.field_5_4(), Priority::High);
|
|
assert_eq!(val.into_raw() & 0x30, 0x20);
|
|
|
|
val = val.with_field_5_4(Priority::Critical);
|
|
assert_eq!(val.field_5_4(), Priority::Critical);
|
|
assert_eq!(val.into_raw() & 0x30, 0x30);
|
|
}
|
|
|
|
// `?=>` fallible conversion.
|
|
#[test]
|
|
fn test_fallible_conversion() {
|
|
let mut val = TestU64::zeroed();
|
|
|
|
val = val.with_field_15_12(MemoryType::Unmapped);
|
|
assert_eq!(val.field_15_12(), Ok(MemoryType::Unmapped));
|
|
val = val.with_field_15_12(MemoryType::Normal);
|
|
assert_eq!(val.field_15_12(), Ok(MemoryType::Normal));
|
|
val = val.with_field_15_12(MemoryType::Device);
|
|
assert_eq!(val.field_15_12(), Ok(MemoryType::Device));
|
|
val = val.with_field_15_12(MemoryType::Reserved);
|
|
assert_eq!(val.field_15_12(), Ok(MemoryType::Reserved));
|
|
|
|
// `field_15_12` is 4 bits wide (0-15); `MemoryType` only covers 0-3, so 4-15 return `Err`.
|
|
let raw = (val.into_raw() & !::kernel::bits::genmask_u64(12..=15)) | (0x7 << 12);
|
|
assert_eq!(TestU64::from_raw(raw).field_15_12(), Err(0x7));
|
|
}
|
|
|
|
// Test that setting an overlapping field affects the overlapped one as expected.
|
|
#[test]
|
|
fn test_overlapping_fields() {
|
|
let mut val = TestU16::zeroed();
|
|
|
|
val = val.with_field_5_4(Priority::High); // High == 2 == 0b10.
|
|
assert_eq!(val.field_5_4(), Priority::High);
|
|
assert_eq!(val.field_7_4(), 0x2); // Bits 7:6 == 0, bits 5:4 == 0b10.
|
|
|
|
val = val.with_const_field_7_4::<0xF>();
|
|
assert_eq!(val.field_7_4(), 0xF);
|
|
assert_eq!(val.field_5_4(), Priority::Critical); // Bits 5:4 == 0b11.
|
|
|
|
// `field_7_0` should encompass all other fields.
|
|
let mut val = TestU8::zeroed()
|
|
.with_field_0(true)
|
|
.with_field_1(true)
|
|
.with_const_field_3_2::<0x3>()
|
|
.with_const_field_7_4::<0xA>();
|
|
assert_eq!(val.into_raw(), 0xAF);
|
|
|
|
val = val.with_field_7_0(0x55);
|
|
assert_eq!(val.field_7_0(), 0x55);
|
|
assert!(val.field_0().into_bool());
|
|
assert!(!val.field_1().into_bool());
|
|
assert_eq!(val.field_3_2(), 0x1);
|
|
assert_eq!(val.field_7_4(), 0x5);
|
|
}
|
|
|
|
// Checks that bits not mapped to any field are left untouched.
|
|
#[test]
|
|
fn test_unallocated_bits() {
|
|
let gap_bits = (1u64 << 62) | 0x1FC;
|
|
|
|
let set_all_fields = |val: TestU64| {
|
|
val.with_field_63(true)
|
|
.with_const_field_61_52::<0x155>()
|
|
.with_const_field_51_16::<0x123456>()
|
|
.with_field_15_12(MemoryType::Device)
|
|
.with_const_field_11_9::<0x5>()
|
|
.with_field_1(true)
|
|
.with_field_0(true)
|
|
};
|
|
|
|
// Gap bits to 0.
|
|
let val = set_all_fields(TestU64::from_raw(0));
|
|
assert_eq!(val.into_raw() & gap_bits, 0);
|
|
|
|
// Gap bits to 1.
|
|
let val = set_all_fields(TestU64::from_raw(gap_bits));
|
|
assert_eq!(val.into_raw() & gap_bits, gap_bits);
|
|
}
|
|
|
|
#[test]
|
|
fn test_try_with() {
|
|
let val = TestU64::zeroed().try_with_field_51_16(0x123456).unwrap();
|
|
assert_eq!(val.field_51_16(), 0x123456);
|
|
|
|
let err = TestU64::zeroed().try_with_field_51_16(u64::MAX);
|
|
assert_eq!(err, Err(::kernel::error::code::EOVERFLOW));
|
|
|
|
let val = TestU64::zeroed()
|
|
.try_with_field_51_16(0xABCDEF)
|
|
.and_then(|p| p.try_with_field_0(1))
|
|
.unwrap();
|
|
assert_eq!(val.field_51_16(), 0xABCDEF);
|
|
assert!(val.field_0().into_bool());
|
|
}
|
|
|
|
// `from_raw`/`into_raw` and `From`/`Into` round-trips.
|
|
#[test]
|
|
fn test_raw() {
|
|
let raw: u64 = 0xBFF0_0000_3123_3E03;
|
|
let val = TestU64::from_raw(raw);
|
|
assert_eq!(u64::from(val), raw);
|
|
assert!(val.field_0().into_bool());
|
|
assert!(val.field_1().into_bool());
|
|
assert_eq!(val.field_11_9(), 0x7);
|
|
assert_eq!(val.field_51_16(), 0x3123);
|
|
assert_eq!(val.field_15_12(), Ok(MemoryType::Reserved));
|
|
assert_eq!(val.field_61_52(), 0x3FF);
|
|
assert!(val.field_63().into_bool());
|
|
|
|
let raw: u16 = 0x42AB;
|
|
let val = TestU16::from_raw(raw);
|
|
assert_eq!(u16::from(val), raw);
|
|
assert!(val.field_0().into_bool());
|
|
assert_eq!(val.field_3_1(), 0x5);
|
|
assert_eq!(val.field_7_4(), 0xA);
|
|
assert_eq!(val.field_15_8(), 0x42);
|
|
|
|
let raw: u8 = 0xAF;
|
|
let val = TestU8::from_raw(raw);
|
|
assert_eq!(u8::from(val), raw);
|
|
assert!(val.field_0().into_bool());
|
|
assert!(val.field_1().into_bool());
|
|
assert_eq!(val.field_3_2(), 0x3);
|
|
assert_eq!(val.field_7_4(), 0xA);
|
|
assert_eq!(val.field_7_0(), 0xAF);
|
|
}
|
|
}
|