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