| // 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); |
| } |
| } |