| // 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 { | |
| #[inline] | |
| 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); | |
| } | |
| } |