The `bitfield!` macro previously generated accessors that returned the
field's value as a `Bounded<$storage, N>` for its raw N-bit width. For
fields whose logical interpretation is a wider value built by widening
the storage type and shifting left (e.g., a 24-bit register field that
stores bits 16..40 of a 40-bit address), callers had to chain
`cast::<TargetType>()` and `shl::<SHIFT, RES>()` (or worse, raw shift
operators) at every read site, and the inverse for writes.

Add a new field declaration shape:

  $hi:$lo $field as Bounded<$target, $res> shl $shift;

The macro generates:

  - A getter `$field(self) -> Bounded<$target, $res>` that extracts the
    raw N-bit field, widens it to $target, and shifts left by $shift.
  - A setter `with_$field(self, value: Bounded<$target, $res>) -> Self`
    that shifts right by $shift, narrows to the storage type, and writes.

Add a KUnit test mirroring nova-core driver's PRAMIN window register
pattern as well which is the usecase for it.

Signed-off-by: Joel Fernandes <[email protected]>
---
 rust/kernel/bitfield.rs | 67 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 67 insertions(+)

diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
index 9ab8dafff36c..1c1fc86441f2 100644
--- a/rust/kernel/bitfield.rs
+++ b/rust/kernel/bitfield.rs
@@ -57,6 +57,8 @@
 //!         hi:lo field_2 => ConvertedType;
 //!         // `field_3` documentation.
 //!         hi:lo field_3 ?=> ConvertedType;
+//!         // `field_4` documentation.
+//!         hi:lo field_4 as Bounded<TargetType, RES> shl SHIFT;
 //!         ...
 //!     }
 //! }
@@ -66,6 +68,8 @@
 //! - `hi:lo`: Bit range (inclusive), where `hi >= lo`.
 //! - `=> Type`: Optional infallible conversion (see 
[below](#infallible-conversion-)).
 //! - `?=> Type`: Optional fallible conversion (see 
[below](#fallible-conversion-)).
+//! - `as Bounded<T, RES> shl SHIFT`: Optional cast-and-shift accessor (see
+//!   [below](#cast-and-shift-accessors-as-bounded-t-res-shl-shift)).
 //! - Documentation strings and attributes are optional.
 //!
 //! # Generated code
@@ -299,6 +303,7 @@ fn from(val: $storage) -> $name {
         $($(#[doc = $doc:expr])* $hi:literal:$lo:literal $field:ident
             $(?=> $try_into_type:ty)?
             $(=> $into_type:ty)?
+            $(as Bounded<$target:ty, $res:literal> shl $shift:literal)?
         ;
         )*
     }
@@ -311,6 +316,7 @@ impl $name {
             @public_field_accessors $(#[doc = $doc])* $vis $name $storage : 
$hi:$lo $field
             $(?=> $try_into_type)?
             $(=> $into_type)?
+            $(as Bounded<$target, $res> shl $shift)?
         );
         )*
         }
@@ -475,6 +481,43 @@ const fn [<__with_ $field>](
         );
     };
 
+    // Public accessors for fields cast to a wider type and left-shifted, 
exposing them as
+    // `Bounded<$target, $res>` where `$res == ($hi + 1 - $lo) + $shift`.
+    (
+        @public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident 
$storage:ty :
+            $hi:literal:$lo:literal $field:ident
+            as Bounded<$target:ty, $res:literal> shl $shift:literal
+    ) => {
+        ::kernel::macros::paste!(
+
+        $(#[doc = $doc])*
+        #[doc = "Returns the value of this field, cast to the target type and 
shifted left."]
+        #[inline(always)]
+        $vis fn $field(self) -> ::kernel::num::Bounded<$target, $res> {
+            $crate::const_assert!($res == ($hi + 1 - $lo) + $shift);
+
+            self.[<__ $field>]()
+                .cast::<$target>()
+                .shl::<$shift, $res>()
+        }
+
+        $(#[doc = $doc])*
+        #[doc = "Sets this field from a target-typed, pre-shifted `Bounded` 
value."]
+        #[inline(always)]
+        $vis fn [<with_ $field>](
+            self,
+            value: ::kernel::num::Bounded<$target, $res>,
+        ) -> Self {
+            $crate::const_assert!($res == ($hi + 1 - $lo) + $shift);
+
+            self.[<__with_ $field>](
+                value.shr::<$shift, { $hi + 1 - $lo }>().cast::<$storage>()
+            )
+        }
+
+        );
+    };
+
     // `Debug` implementation.
     (@debug $name:ident { $($field:ident;)* }) => {
         impl ::kernel::fmt::Debug for $name {
@@ -582,6 +625,15 @@ struct TestStatusRegister(u8) {
         }
     }
 
+    // Mirrors the PRAMIN window register pattern: a 24-bit field in a `u32` 
storage that
+    // represents bits 16..40 of a 40-bit address. The accessor exposes it as 
the full
+    // 40-bit `Bounded<u64, 40>`.
+    bitfield! {
+        struct TestWindowReg(u32) {
+            23:0      window_base as Bounded<u64, 40> shl 16;
+        }
+    }
+
     #[test]
     fn test_single_bits() {
         let mut pte = TestPageTableEntry::zeroed();
@@ -806,4 +858,19 @@ fn test_u8_bitfield() {
         assert_eq!(status4.reserved(), 0xF);
         assert_eq!(status4.full_byte(), 0xFF);
     }
+
+    #[test]
+    fn test_cast_shift_accessor() {
+        // Set a value via the pre-shifted setter and read it back via the 
getter.
+        let addr = Bounded::<u64, 40>::new::<0x12_3456_0000>();
+        let reg = TestWindowReg::zeroed().with_window_base(addr);
+        assert_eq!(reg.window_base().get(), 0x12_3456_0000u64);
+        assert_eq!(u32::from(reg), 0x0012_3456u32);
+
+        // Setting and reading the largest 40-bit aligned value.
+        let max_addr = Bounded::<u64, 40>::new::<0xFF_FFFF_0000>();
+        let reg = TestWindowReg::zeroed().with_window_base(max_addr);
+        assert_eq!(reg.window_base().get(), 0xFF_FFFF_0000u64);
+        assert_eq!(u32::from(reg), 0x00FF_FFFFu32);
+    }
 }
-- 
2.34.1

Reply via email to