* [PATCH 0/6] rust: add `bitfield!` and `register!` macros
@ 2026-01-20 6:17 Alexandre Courbot
2026-01-20 6:17 ` [PATCH 1/6] rust: num: add `shr` and `shl` methods to `Bounded` Alexandre Courbot
` (6 more replies)
0 siblings, 7 replies; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-20 6:17 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Yury Norov
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, Daniel Almeida, Dirk Behme,
Steven Price, rust-for-linux, linux-kernel, Alexandre Courbot
Add an improved version of nova-core's `bitfield!` and `register!`
macros to the `kernel` crate for all drivers to use.
This is not a direct move from `nova-core`, but rather a new
introduction to facilitate code review and introduce features that are
missing in the nova-core versions. Differences notably include:
- Use of `Bounded` to prevent any data truncation when manipulating
bitfields,
- Extended documentation,
- Doccomments now build and run,
- `register!` supports visibility and different storage sizes.
These updates basically turn a register into a bitfield extended with
some controlled I/O. This is reflected in the syntax of the macros,
which are mostly identical save for the additional `@` parameter of
`register!` and the fact that the `struct` keyword is omitted in the
latter, i.e:
bitfield! {
pub struct Foo(u32) {
...
}
}
vs
register! {
pub FOO(u32) @ 0x00000100 {
...
}
}
The use of `struct` in `register!` looks superfluous to me, but we can
of course add it if consistency between the two macros is deemed more
important.
The first commit adds `shr` and `shl` methods to `Bounded`. These were
suggested by Alice during LPC as a way to avoid the use of the
controversial `Bounded::from_expr` in both the bitfield macro and the
Nova code. Second commit adds another convenience method to obtain a
`bool` from single-bit `Bounded`s.
Patches 3-5 add the `bitfield!` and `register!` macros.
The last patch illustrates how these macros are used by converting
nova-core to them, and removing the local implementation. This patch is
to be merged one cycle after the other patches.
Previous work to extract the macros was done in the partially-merged
[1]. The current series can be considered a reboot, with the inclusion
of the KUNIT tests from the previous effort.
This patchset is based on `rust-fixes`, but should apply equally well on
`rust-next`.
[1] https://lore.kernel.org/all/20251003154748.1687160-1-joelagnelf@nvidia.com/
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
Alexandre Courbot (5):
rust: num: add `shr` and `shl` methods to `Bounded`
rust: num: add `as_bool` method to `Bounded<_, 1>`
rust: add `bitfield!` macro
rust: io: add `register!` macro
[FOR REFERENCE] gpu: nova-core: use the kernel `register!` and `bitfield!` macros
Joel Fernandes (1):
rust: bitfield: Add KUNIT tests for bitfield
drivers/gpu/nova-core/bitfield.rs | 330 ---------
drivers/gpu/nova-core/falcon.rs | 127 ++--
drivers/gpu/nova-core/falcon/gsp.rs | 10 +-
drivers/gpu/nova-core/falcon/hal/ga102.rs | 5 +-
drivers/gpu/nova-core/falcon/sec2.rs | 13 +-
drivers/gpu/nova-core/fb/hal/ga100.rs | 9 +-
drivers/gpu/nova-core/gpu.rs | 24 +-
drivers/gpu/nova-core/gsp/cmdq.rs | 2 +-
drivers/gpu/nova-core/gsp/fw.rs | 5 +-
drivers/gpu/nova-core/nova_core.rs | 3 -
drivers/gpu/nova-core/regs.rs | 265 +++----
rust/kernel/bitfield.rs | 821 +++++++++++++++++++++
rust/kernel/io.rs | 1 +
.../regs/macros.rs => rust/kernel/io/register.rs | 561 +++++++++-----
rust/kernel/lib.rs | 1 +
rust/kernel/num/bounded.rs | 61 ++
16 files changed, 1490 insertions(+), 748 deletions(-)
---
base-commit: 2af6ad09fc7dfe9b3610100983cccf16998bf34d
change-id: 20260117-register-ccaba1d21713
Best regards,
--
Alexandre Courbot <acourbot@nvidia.com>
^ permalink raw reply [flat|nested] 56+ messages in thread
* [PATCH 1/6] rust: num: add `shr` and `shl` methods to `Bounded`
2026-01-20 6:17 [PATCH 0/6] rust: add `bitfield!` and `register!` macros Alexandre Courbot
@ 2026-01-20 6:17 ` Alexandre Courbot
2026-01-20 8:44 ` Alice Ryhl
` (2 more replies)
2026-01-20 6:17 ` [PATCH 2/6] rust: num: add `as_bool` method to `Bounded<_, 1>` Alexandre Courbot
` (5 subsequent siblings)
6 siblings, 3 replies; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-20 6:17 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Yury Norov
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, Daniel Almeida, Dirk Behme,
Steven Price, rust-for-linux, linux-kernel, Alexandre Courbot
Shifting a `Bounded` left or right changes the number of bits required
to represent the value. Add methods that perform the shift and return a
`Bounded` with the appropriately adjusted bit width.
These methods are particularly useful for bitfield extraction.
Suggested-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
rust/kernel/num/bounded.rs | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs
index 5ef8361cf5d5..6e3f4a7a5262 100644
--- a/rust/kernel/num/bounded.rs
+++ b/rust/kernel/num/bounded.rs
@@ -475,6 +475,46 @@ pub fn cast<U>(self) -> Bounded<U, N>
// `N` bits, and with the same signedness.
unsafe { Bounded::__new(value) }
}
+
+ /// Right-shifts `self` by `SHIFT` and returns the result as a `Bounded<_, { N - SHIFT }>`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::num::Bounded;
+ ///
+ /// let v = Bounded::<u32, 16>::new::<0xff00>();
+ /// let v_shifted: Bounded::<u32, 8> = v.shr::<8, _>();
+ ///
+ /// assert_eq!(v_shifted.get(), 0xff);
+ /// ```
+ pub fn shr<const SHIFT: u32, const RES: u32>(self) -> Bounded<T, RES> {
+ const { assert!(RES == N - SHIFT) }
+
+ // SAFETY: we shift the value right by `SHIFT`, reducing the number of bits needed to
+ // represent the shifted value by as much, and just asserted that `RES == N - SHIFT`.
+ unsafe { Bounded::__new(self.0 >> SHIFT) }
+ }
+
+ /// Left-shifts `self` by `SHIFT` and returns the result as a `Bounded<_, { N + SHIFT }>`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::num::Bounded;
+ ///
+ /// let v = Bounded::<u32, 8>::new::<0xff>();
+ /// let v_shifted: Bounded::<u32, 16> = v.shl::<8, _>();
+ ///
+ /// assert_eq!(v_shifted.get(), 0xff00);
+ /// ```
+ pub fn shl<const SHIFT: u32, const RES: u32>(self) -> Bounded<T, RES> {
+ const { assert!(RES == N + SHIFT) }
+
+ // SAFETY: we shift the value left by `SHIFT`, augmenting the number of bits needed to
+ // represent the shifted value by as much, and just asserted that `RES == N + SHIFT`.
+ unsafe { Bounded::__new(self.0 << SHIFT) }
+ }
}
impl<T, const N: u32> Deref for Bounded<T, N>
--
2.52.0
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH 2/6] rust: num: add `as_bool` method to `Bounded<_, 1>`
2026-01-20 6:17 [PATCH 0/6] rust: add `bitfield!` and `register!` macros Alexandre Courbot
2026-01-20 6:17 ` [PATCH 1/6] rust: num: add `shr` and `shl` methods to `Bounded` Alexandre Courbot
@ 2026-01-20 6:17 ` Alexandre Courbot
2026-01-20 8:45 ` Alice Ryhl
2026-01-20 6:17 ` [PATCH 3/6] rust: add `bitfield!` macro Alexandre Courbot
` (4 subsequent siblings)
6 siblings, 1 reply; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-20 6:17 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Yury Norov
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, Daniel Almeida, Dirk Behme,
Steven Price, rust-for-linux, linux-kernel, Alexandre Courbot
Single-bit numbers are typically treated as booleans. There is an
`Into<bool>` implementation for those, but invoking it from contexts
that lack type expectations is not always convenient.
Add an `as_bool` method as a simpler shortcut.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
rust/kernel/num/bounded.rs | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs
index 6e3f4a7a5262..803ed0889ddd 100644
--- a/rust/kernel/num/bounded.rs
+++ b/rust/kernel/num/bounded.rs
@@ -1101,3 +1101,24 @@ fn from(value: bool) -> Self {
unsafe { Self::__new(T::from(value)) }
}
}
+
+impl<T> Bounded<T, 1>
+where
+ T: Integer + Zeroable,
+{
+ /// Returns the value of this `Bounded` as a `bool`.
+ ///
+ /// This is a shorter way of writing `bool::from(self)`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::num::Bounded;
+ ///
+ /// assert_eq!(Bounded::<u8, 1>::new::<0>().as_bool(), false);
+ /// assert_eq!(Bounded::<u8, 1>::new::<1>().as_bool(), true);
+ /// ```
+ pub fn as_bool(self) -> bool {
+ self.into()
+ }
+}
--
2.52.0
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH 3/6] rust: add `bitfield!` macro
2026-01-20 6:17 [PATCH 0/6] rust: add `bitfield!` and `register!` macros Alexandre Courbot
2026-01-20 6:17 ` [PATCH 1/6] rust: num: add `shr` and `shl` methods to `Bounded` Alexandre Courbot
2026-01-20 6:17 ` [PATCH 2/6] rust: num: add `as_bool` method to `Bounded<_, 1>` Alexandre Courbot
@ 2026-01-20 6:17 ` Alexandre Courbot
2026-01-20 11:45 ` Dirk Behme
2026-01-21 9:16 ` Yury Norov
2026-01-20 6:17 ` [PATCH 4/6] rust: bitfield: Add KUNIT tests for bitfield Alexandre Courbot
` (3 subsequent siblings)
6 siblings, 2 replies; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-20 6:17 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Yury Norov
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, Daniel Almeida, Dirk Behme,
Steven Price, rust-for-linux, linux-kernel, Alexandre Courbot
Add a macro for defining bitfield structs with bounds-checked accessors.
Each field is represented as a `Bounded` of the appropriate bit width,
ensuring field values are never silently truncated.
Fields can optionally be converted to/from custom types, either fallibly
or infallibly.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
rust/kernel/bitfield.rs | 503 ++++++++++++++++++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 1 +
2 files changed, 504 insertions(+)
diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
new file mode 100644
index 000000000000..2926ab802227
--- /dev/null
+++ b/rust/kernel/bitfield.rs
@@ -0,0 +1,503 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Support for defining bitfields as Rust structures.
+
+/// Defines a bitfield struct with bounds-checked accessors for individual bit ranges.
+///
+/// # Example
+///
+/// ```rust
+/// use kernel::bitfield;
+/// use kernel::num::Bounded;
+///
+/// bitfield! {
+/// pub struct Rgb(u16) {
+/// 15:11 blue;
+/// 10:5 green;
+/// 4:0 red;
+/// }
+/// }
+///
+/// // Setters can be chained. Bounded::new::<N>() does compile-time bounds checking.
+/// let color = Rgb::default()
+/// .set_red(Bounded::<u16, _>::new::<0x10>())
+/// .set_green(Bounded::<u16, _>::new::<0x1f>())
+/// .set_blue(Bounded::<u16, _>::new::<0x18>());
+///
+/// assert_eq!(color.red(), 0x10);
+/// assert_eq!(color.green(), 0x1f);
+/// assert_eq!(color.blue(), 0x18);
+/// assert_eq!(
+/// color.as_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]
+/// pub struct Name(storage_type), "Struct documentation." {
+/// hi:lo field_1, "Field documentation.";
+/// hi:lo field_2 => ConvertedType, "Field documentation.";
+/// hi:lo field_3 ?=> ConvertedType, "Field documentation.";
+/// ...
+/// }
+/// }
+/// ```
+///
+/// - `storage_type`: The underlying integer type (`u8`, `u16`, `u32`, `u64`).
+/// - `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, and [`Default`] and [`Debug`] implementations.
+///
+/// For each field, it also generates:
+/// - `field()` - getter returning a [`Bounded`] (or converted type) for the field,
+/// - `set_field(value)` - setter with compile-time bounds checking,
+/// - `try_set_field(value)` - setter with runtime bounds checking (for fields without type
+/// conversion),
+/// - `FIELD_MASK`, `FIELD_SHIFT`, `FIELD_RANGE` - constants for manual bit manipulation.
+///
+/// # 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::default()
+/// .set_byte_field(0x42_u8)
+/// .set_flag(true);
+///
+/// assert_eq!(flags.as_raw(), (0x42 << Flags::BYTE_FIELD_SHIFT) | 1);
+/// ```
+///
+/// # Runtime bounds checking
+///
+/// When a value is not known at compile time, use `try_set_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_set_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 when all bit patterns map to valid values:
+///
+/// ```rust
+/// use kernel::bitfield;
+/// use kernel::num::Bounded;
+///
+/// #[derive(Debug, Clone, Copy, Default, PartialEq)]
+/// enum Power {
+/// #[default]
+/// 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::default().set_power(Power::On);
+/// assert_eq!(ctrl.power(), Power::On);
+/// ```
+///
+/// ## Fallible conversion (`?=>`)
+///
+/// Use when some bit patterns are invalid. The getter returns a [`Result`]:
+///
+/// ```rust
+/// use kernel::bitfield;
+/// use kernel::num::Bounded;
+///
+/// #[derive(Debug, Clone, Copy, Default, PartialEq)]
+/// enum Mode {
+/// #[default]
+/// 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::default().set_mode(Mode::Auto);
+/// assert_eq!(cfg.mode(), Ok(Mode::Auto));
+///
+/// // Invalid bit pattern returns an error.
+/// assert_eq!(Config::from(0b11).mode(), Err(3));
+/// ```
+///
+/// [`Bounded`]: kernel::num::Bounded
+#[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)
+ $(, $comment:literal)? { $($fields:tt)* }
+ ) => {
+ ::kernel::bitfield!(@core $(#[$attr])* $vis $name $storage $(, $comment)?);
+ ::kernel::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 $(, $comment:literal)?) => {
+ $(
+ #[doc=$comment]
+ )?
+ $(#[$attr])*
+ #[repr(transparent)]
+ #[derive(Clone, Copy, PartialEq, Eq)]
+ $vis struct $name($storage);
+
+ #[allow(dead_code)]
+ impl $name {
+ /// Returns the raw value of this bitfield.
+ ///
+ /// This is similar to the [`From`] implementation, but is shorter to invoke in
+ /// most cases.
+ $vis fn as_raw(self) -> $storage {
+ self.0
+ }
+ }
+
+ impl ::core::convert::From<$name> for $storage {
+ fn from(val: $name) -> $storage {
+ val.0
+ }
+ }
+
+ impl ::core::convert::From<$storage> for $name {
+ fn from(val: $storage) -> $name {
+ Self(val)
+ }
+ }
+ };
+
+ // Definitions requiring knowledge of individual fields: private and public field accessors,
+ // and `Debug` and `Default` implementations.
+ (@fields $vis:vis $name:ident $storage:ty {
+ $($hi:tt:$lo:tt $field:ident
+ $(?=> $try_into_type:ty)?
+ $(=> $into_type:ty)?
+ $(, $comment:literal)?
+ ;
+ )*
+ }
+ ) => {
+ #[allow(dead_code)]
+ impl $name {
+ $(
+ ::kernel::bitfield!(@private_field_accessors $vis $name $storage : $hi:$lo $field);
+ ::kernel::bitfield!(@public_field_accessors $vis $name $storage : $hi:$lo $field
+ $(?=> $try_into_type)?
+ $(=> $into_type)?
+ $(, $comment)?
+ );
+ )*
+ }
+
+ ::kernel::bitfield!(@debug $name { $($field;)* });
+ ::kernel::bitfield!(@default $name { $($field;)* });
+ };
+
+ // Private field accessors working with the correct `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!(
+ 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.0 << ALIGN_TOP
+ );
+ val.shr::<ALIGN_BOTTOM, _>()
+ }
+
+ fn [<__set_ $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.0 = (self.0 & !MASK) | value;
+
+ self
+ }
+ );
+ };
+
+ // Public accessors for fields infallibly (`=>`) converted to a type.
+ (
+ @public_field_accessors $vis:vis $name:ident $storage:ty : $hi:tt:$lo:tt $field:ident
+ => $into_type:ty $(, $comment:literal)?
+ ) => {
+ ::kernel::macros::paste!(
+
+ $(
+ #[doc="Returns the value of this field:"]
+ #[doc=$comment]
+ )?
+ #[inline(always)]
+ $vis fn $field(self) -> $into_type
+ {
+ self.[<__ $field>]().into()
+ }
+
+ $(
+ #[doc="Sets the value of this field:"]
+ #[doc=$comment]
+ )?
+ #[inline(always)]
+ $vis fn [<set_ $field>](self, value: $into_type) -> Self
+ {
+ self.[<__set_ $field>](value.into())
+ }
+
+ /// Private method, for use in the [`Default`] implementation.
+ fn [<$field _default>]() -> $into_type {
+ Default::default()
+ }
+
+ );
+ };
+
+ // Public accessors for fields fallibly (`?=>`) converted to a type.
+ (
+ @public_field_accessors $vis:vis $name:ident $storage:ty : $hi:tt:$lo:tt $field:ident
+ ?=> $try_into_type:ty $(, $comment:literal)?
+ ) => {
+ ::kernel::macros::paste!(
+
+ $(
+ #[doc="Returns the value of this field:"]
+ #[doc=$comment]
+ )?
+ #[inline(always)]
+ $vis fn $field(self) ->
+ Result<
+ $try_into_type,
+ <$try_into_type as ::core::convert::TryFrom<
+ ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>
+ >>::Error
+ >
+ {
+ self.[<__ $field>]().try_into()
+ }
+
+ $(
+ #[doc="Sets the value of this field:"]
+ #[doc=$comment]
+ )?
+ #[inline(always)]
+ $vis fn [<set_ $field>](self, value: $try_into_type) -> Self
+ {
+ self.[<__set_ $field>](value.into())
+ }
+
+ /// Private method, for use in the [`Default`] implementation.
+ fn [<$field _default>]() -> $try_into_type {
+ Default::default()
+ }
+
+ );
+ };
+
+ // Public accessors for fields not converted to a type.
+ (
+ @public_field_accessors $vis:vis $name:ident $storage:ty : $hi:tt:$lo:tt $field:ident
+ $(, $comment:literal)?
+ ) => {
+ ::kernel::macros::paste!(
+
+ $(
+ #[doc="Returns the value of this field:"]
+ #[doc=$comment]
+ )?
+ #[inline(always)]
+ $vis fn $field(self) ->
+ ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>
+ {
+ self.[<__ $field>]()
+ }
+
+ $(
+ #[doc="Sets the value of this field:"]
+ #[doc=$comment]
+ )?
+ #[inline(always)]
+ $vis fn [<set_ $field>]<T>(
+ self,
+ value: T,
+ ) -> Self
+ where T: Into<::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>>,
+ {
+ self.[<__set_ $field>](value.into())
+ }
+
+ $(
+ #[doc="Attempts to set the value of this field:"]
+ #[doc=$comment]
+ )?
+ #[inline(always)]
+ $vis fn [<try_set_ $field>]<T>(
+ self,
+ value: T,
+ ) -> ::kernel::error::Result<Self>
+ where T: ::kernel::num::TryIntoBounded<$storage, { $hi + 1 - $lo }>,
+ {
+ Ok(
+ self.[<__set_ $field>](
+ value.try_into_bounded().ok_or(::kernel::error::code::EOVERFLOW)?
+ )
+ )
+ }
+
+ /// Private method, for use in the [`Default`] implementation.
+ fn [<$field _default>]() -> ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }> {
+ Default::default()
+ }
+
+ );
+ };
+
+ // `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.0))
+ $(
+ .field(stringify!($field), &self.$field())
+ )*
+ .finish()
+ }
+ }
+ };
+
+ // `Default` implementation.
+ (@default $name:ident { $($field:ident;)* }) => {
+ /// Returns a value for the bitfield where all fields are set to their default value.
+ impl ::core::default::Default for $name {
+ fn default() -> Self {
+ #[allow(unused_mut)]
+ let mut value = Self(Default::default());
+
+ ::kernel::macros::paste!(
+ $(
+ value = value.[<set_ $field>](Self::[<$field _default>]());
+ )*
+ );
+
+ value
+ }
+ }
+ };
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index f812cf120042..66198e69d1f5 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -68,6 +68,7 @@
pub mod alloc;
#[cfg(CONFIG_AUXILIARY_BUS)]
pub mod auxiliary;
+pub mod bitfield;
pub mod bitmap;
pub mod bits;
#[cfg(CONFIG_BLOCK)]
--
2.52.0
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH 4/6] rust: bitfield: Add KUNIT tests for bitfield
2026-01-20 6:17 [PATCH 0/6] rust: add `bitfield!` and `register!` macros Alexandre Courbot
` (2 preceding siblings ...)
2026-01-20 6:17 ` [PATCH 3/6] rust: add `bitfield!` macro Alexandre Courbot
@ 2026-01-20 6:17 ` Alexandre Courbot
2026-01-20 6:17 ` [PATCH 5/6] rust: io: add `register!` macro Alexandre Courbot
` (2 subsequent siblings)
6 siblings, 0 replies; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-20 6:17 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Yury Norov
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, Daniel Almeida, Dirk Behme,
Steven Price, rust-for-linux, linux-kernel, Alexandre Courbot
From: Joel Fernandes <joelagnelf@nvidia.com>
Add KUNIT tests to make sure the macro is working correctly.
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
[acourbot: update code to latest bitfield! macro.]
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
rust/kernel/bitfield.rs | 318 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 318 insertions(+)
diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
index 2926ab802227..a1505816a590 100644
--- a/rust/kernel/bitfield.rs
+++ b/rust/kernel/bitfield.rs
@@ -501,3 +501,321 @@ fn default() -> Self {
}
};
}
+
+#[::kernel::macros::kunit_tests(kernel_bitfield)]
+mod tests {
+ use core::convert::TryFrom;
+
+ use kernel::num::Bounded;
+
+ // Enum types for testing => and ?=> conversions
+ #[derive(Debug, Default, Clone, Copy, PartialEq)]
+ enum MemoryType {
+ #[default]
+ 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, Default, Clone, Copy, PartialEq)]
+ enum Priority {
+ #[default]
+ 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 TestPageTableEntry(u64) {
+ 0:0 present;
+ 1:1 writable;
+ 11:9 available;
+ 15:12 mem_type ?=> MemoryType;
+ 51:16 pfn;
+ 61:52 available2;
+ }
+ }
+
+ bitfield! {
+ struct TestControlRegister(u16) {
+ 0:0 enable;
+ 3:1 mode;
+ 5:4 priority => Priority;
+ 7:4 priority_nibble;
+ 15:8 channel;
+ }
+ }
+
+ bitfield! {
+ struct TestStatusRegister(u8) {
+ 0:0 ready;
+ 1:1 error;
+ 3:2 state;
+ 7:4 reserved;
+ 7:0 full_byte; // For entire register
+ }
+ }
+
+ #[test]
+ fn test_single_bits() {
+ let mut pte = TestPageTableEntry::default();
+
+ assert!(!pte.present().as_bool());
+ assert!(!pte.writable().as_bool());
+ assert_eq!(u64::from(pte), 0x0);
+
+ pte = pte.set_present(true);
+ assert!(pte.present().as_bool());
+ assert_eq!(u64::from(pte), 0x1);
+
+ pte = pte.set_writable(true);
+ assert!(pte.writable().as_bool());
+ assert_eq!(u64::from(pte), 0x3);
+
+ pte = pte.set_writable(false);
+ assert!(!pte.writable().as_bool());
+ assert_eq!(u64::from(pte), 0x1);
+
+ assert_eq!(pte.available(), 0);
+ pte = pte.set_available(Bounded::<u64, 3>::new::<0x5>());
+ assert_eq!(pte.available(), 0x5);
+ assert_eq!(u64::from(pte), 0xA01);
+ }
+
+ #[test]
+ fn test_range_fields() {
+ let mut pte = TestPageTableEntry::default();
+ assert_eq!(u64::from(pte), 0x0);
+
+ pte = pte.set_pfn(Bounded::<u64, 36>::new::<0x123456>());
+ assert_eq!(pte.pfn(), 0x123456);
+ assert_eq!(u64::from(pte), 0x1234560000);
+
+ pte = pte.set_available(Bounded::<u64, 3>::new::<0x7>());
+ assert_eq!(pte.available(), 0x7);
+ assert_eq!(u64::from(pte), 0x1234560E00);
+
+ pte = pte.set_available2(Bounded::<u64, 10>::new::<0x3FF>());
+ assert_eq!(pte.available2(), 0x3FF);
+ assert_eq!(u64::from(pte), 0x3FF0_0012_3456_0E00u64);
+
+ // Test TryFrom with ?=> for MemoryType
+ pte = pte.set_mem_type(MemoryType::Device);
+ assert_eq!(pte.mem_type(), Ok(MemoryType::Device));
+ assert_eq!(u64::from(pte), 0x3FF0_0012_3456_2E00u64);
+
+ pte = pte.set_mem_type(MemoryType::Normal);
+ assert_eq!(pte.mem_type(), Ok(MemoryType::Normal));
+ assert_eq!(u64::from(pte), 0x3FF0_0012_3456_1E00u64);
+
+ // Test all valid values for mem_type
+ pte = pte.set_mem_type(MemoryType::Reserved);
+ assert_eq!(pte.mem_type(), Ok(MemoryType::Reserved));
+ assert_eq!(u64::from(pte), 0x3FF0_0012_3456_3E00u64);
+
+ // Test failure case using mem_type field which has 4 bits (0-15)
+ // MemoryType only handles 0-3, so values 4-15 should return Err
+ let mut raw = pte.into();
+ // Set bits 15:12 to 7 (invalid for MemoryType)
+ raw = (raw & !::kernel::bits::genmask_u64(12..=15)) | (0x7 << 12);
+ let invalid_pte = TestPageTableEntry(raw);
+ // Should return Err with the invalid value
+ assert_eq!(invalid_pte.mem_type(), Err(0x7));
+
+ // Test a valid value after testing invalid to ensure both cases work
+ // Set bits 15:12 to 2 (valid: Device)
+ raw = (raw & !::kernel::bits::genmask_u64(12..=15)) | (0x2 << 12);
+ let valid_pte = TestPageTableEntry(raw);
+ assert_eq!(valid_pte.mem_type(), Ok(MemoryType::Device));
+
+ const MAX_PFN: u64 = ::kernel::bits::genmask_u64(0..=35);
+ pte = pte.set_pfn(Bounded::<u64, 36>::new::<{ MAX_PFN }>());
+ assert_eq!(pte.pfn(), MAX_PFN);
+ }
+
+ #[test]
+ fn test_builder_pattern() {
+ let pte = TestPageTableEntry::default()
+ .set_present(true)
+ .set_writable(true)
+ .set_available(Bounded::<u64, 3>::new::<0x7>())
+ .set_pfn(Bounded::<u64, 36>::new::<0xABCDEF>())
+ .set_mem_type(MemoryType::Reserved)
+ .set_available2(Bounded::<u64, 10>::new::<0x3FF>());
+
+ assert!(pte.present().as_bool());
+ assert!(pte.writable().as_bool());
+ assert_eq!(pte.available(), 0x7);
+ assert_eq!(pte.pfn(), 0xABCDEF);
+ assert_eq!(pte.mem_type(), Ok(MemoryType::Reserved));
+ assert_eq!(pte.available2(), 0x3FF);
+ }
+
+ #[test]
+ fn test_raw_operations() {
+ let raw_value = 0x3FF0000031233E03u64;
+
+ let pte = TestPageTableEntry(raw_value);
+ assert_eq!(u64::from(pte), raw_value);
+
+ assert!(pte.present().as_bool());
+ assert!(pte.writable().as_bool());
+ assert_eq!(pte.available(), 0x7);
+ assert_eq!(pte.pfn(), 0x3123);
+ assert_eq!(pte.mem_type(), Ok(MemoryType::Reserved));
+ assert_eq!(pte.available2(), 0x3FF);
+
+ // Test using direct constructor syntax TestStruct(value)
+ let pte2 = TestPageTableEntry(raw_value);
+ assert_eq!(u64::from(pte2), raw_value);
+ }
+
+ #[test]
+ fn test_u16_bitfield() {
+ let mut ctrl = TestControlRegister::default();
+
+ assert!(!ctrl.enable().as_bool());
+ assert_eq!(ctrl.mode(), 0);
+ assert_eq!(ctrl.priority(), Priority::Low);
+ assert_eq!(ctrl.priority_nibble(), 0);
+ assert_eq!(ctrl.channel(), 0);
+
+ ctrl = ctrl.set_enable(true);
+ assert!(ctrl.enable().as_bool());
+
+ ctrl = ctrl.set_mode(Bounded::<u16, 3>::new::<0x5>());
+ assert_eq!(ctrl.mode(), 0x5);
+
+ // Test From conversion with =>
+ ctrl = ctrl.set_priority(Priority::High);
+ assert_eq!(ctrl.priority(), Priority::High);
+ assert_eq!(ctrl.priority_nibble(), 0x2); // High = 2 in bits 5:4
+
+ ctrl = ctrl.set_channel(0xAB);
+ assert_eq!(ctrl.channel(), 0xAB);
+
+ // Test overlapping fields
+ ctrl = ctrl.set_priority_nibble(Bounded::<u16, 4>::new::<0xF>());
+ assert_eq!(ctrl.priority_nibble(), 0xF);
+ assert_eq!(ctrl.priority(), Priority::Critical); // bits 5:4 = 0x3
+
+ let ctrl2 = TestControlRegister::default()
+ .set_enable(true)
+ .set_mode(Bounded::<u16, 3>::new::<0x3>())
+ .set_priority(Priority::Medium)
+ .set_channel(0x42);
+
+ assert!(ctrl2.enable().as_bool());
+ assert_eq!(ctrl2.mode(), 0x3);
+ assert_eq!(ctrl2.priority(), Priority::Medium);
+ assert_eq!(ctrl2.channel(), 0x42);
+
+ let raw_value: u16 = 0x4217;
+ let ctrl3 = TestControlRegister(raw_value);
+ assert_eq!(u16::from(ctrl3), raw_value);
+ assert!(ctrl3.enable().as_bool());
+ assert_eq!(ctrl3.priority(), Priority::Medium);
+ assert_eq!(ctrl3.priority_nibble(), 0x1);
+ assert_eq!(ctrl3.channel(), 0x42);
+ }
+
+ #[test]
+ fn test_u8_bitfield() {
+ let mut status = TestStatusRegister::default();
+
+ assert!(!status.ready().as_bool());
+ assert!(!status.error().as_bool());
+ assert_eq!(status.state(), 0);
+ assert_eq!(status.reserved(), 0);
+ assert_eq!(status.full_byte(), 0);
+
+ status = status.set_ready(true);
+ assert!(status.ready().as_bool());
+ assert_eq!(status.full_byte(), 0x01);
+
+ status = status.set_error(true);
+ assert!(status.error().as_bool());
+ assert_eq!(status.full_byte(), 0x03);
+
+ status = status.set_state(Bounded::<u8, 2>::new::<0x3>());
+ assert_eq!(status.state(), 0x3);
+ assert_eq!(status.full_byte(), 0x0F);
+
+ status = status.set_reserved(Bounded::<u8, 4>::new::<0xA>());
+ assert_eq!(status.reserved(), 0xA);
+ assert_eq!(status.full_byte(), 0xAF);
+
+ // Test overlapping field
+ status = status.set_full_byte(0x55);
+ assert_eq!(status.full_byte(), 0x55);
+ assert!(status.ready().as_bool());
+ assert!(!status.error().as_bool());
+ assert_eq!(status.state(), 0x1);
+ assert_eq!(status.reserved(), 0x5);
+
+ let status2 = TestStatusRegister::default()
+ .set_ready(true)
+ .set_state(Bounded::<u8, 2>::new::<0x2>())
+ .set_reserved(Bounded::<u8, 4>::new::<0x5>());
+
+ assert!(status2.ready().as_bool());
+ assert!(!status2.error().as_bool());
+ assert_eq!(status2.state(), 0x2);
+ assert_eq!(status2.reserved(), 0x5);
+ assert_eq!(status2.full_byte(), 0x59);
+
+ let raw_value: u8 = 0x59;
+ let status3 = TestStatusRegister(raw_value);
+ assert_eq!(u8::from(status3), raw_value);
+ assert!(status3.ready().as_bool());
+ assert!(!status3.error().as_bool());
+ assert_eq!(status3.state(), 0x2);
+ assert_eq!(status3.reserved(), 0x5);
+ assert_eq!(status3.full_byte(), 0x59);
+
+ let status4 = TestStatusRegister(0xFF);
+ assert!(status4.ready().as_bool());
+ assert!(status4.error().as_bool());
+ assert_eq!(status4.state(), 0x3);
+ assert_eq!(status4.reserved(), 0xF);
+ assert_eq!(status4.full_byte(), 0xFF);
+ }
+}
--
2.52.0
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH 5/6] rust: io: add `register!` macro
2026-01-20 6:17 [PATCH 0/6] rust: add `bitfield!` and `register!` macros Alexandre Courbot
` (3 preceding siblings ...)
2026-01-20 6:17 ` [PATCH 4/6] rust: bitfield: Add KUNIT tests for bitfield Alexandre Courbot
@ 2026-01-20 6:17 ` Alexandre Courbot
2026-01-20 6:17 ` [PATCH FOR REFERENCE 6/6] gpu: nova-core: use the kernel `register!` and `bitfield!` macros Alexandre Courbot
2026-01-20 13:14 ` [PATCH 0/6] rust: add `bitfield!` and `register!` macros Miguel Ojeda
6 siblings, 0 replies; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-20 6:17 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Yury Norov
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, Daniel Almeida, Dirk Behme,
Steven Price, rust-for-linux, linux-kernel, Alexandre Courbot
Add a macro for defining hardware register types with I/O accessors.
Registers are essentially bitfields with an address and I/O accessors.
The address of registers can be direct, relative, or indexed, supporting
most of the patterns in which registers are arranged.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
rust/kernel/io.rs | 1 +
rust/kernel/io/register.rs | 926 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 927 insertions(+)
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index 98e8b84e68d1..19b34a88b66c 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -11,6 +11,7 @@
pub mod mem;
pub mod poll;
+pub mod register;
pub mod resource;
pub use resource::Resource;
diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs
new file mode 100644
index 000000000000..f28dda146679
--- /dev/null
+++ b/rust/kernel/io/register.rs
@@ -0,0 +1,926 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! A macro to define register layout and accessors.
+//!
+//! A single register typically includes several fields, which are accessed through a combination
+//! of bit-shift and mask operations that introduce a class of potential mistakes, notably because
+//! not all possible field values are necessarily valid.
+//!
+//! The [`register!`] macro in this module provides an intuitive and readable syntax for defining a
+//! dedicated type for each register. Each such type comes with its own field accessors that can
+//! return an error if a field's value is invalid. Please look at the [`bitfield!`] macro for the
+//! complete syntax of fields definitions.
+//!
+//! [`register!`]: kernel::register!
+//! [`bitfield!`]: kernel::bitfield!
+
+/// Trait providing a base address to be added to the offset of a relative register to obtain
+/// its actual offset.
+///
+/// The `T` generic argument is used to distinguish which base to use, in case a type provides
+/// several bases. It is given to the `register!` macro to restrict the use of the register to
+/// implementors of this particular variant.
+pub trait RegisterBase<T> {
+ /// Base address to which register offsets are added.
+ const BASE: usize;
+}
+
+/// Trait providing I/O read/write operations for register storage types.
+///
+/// This trait is implemented for all integer types on which I/O can be performed, allowing the
+/// `register!` macro to generate appropriate I/O accessor methods based on the register's storage
+/// type.
+pub trait RegisterIo: Sized {
+ /// Read a value from the given offset in the I/O region.
+ fn read<const SIZE: usize, T>(io: &T, offset: usize) -> Self
+ where
+ T: core::ops::Deref<Target = crate::io::Io<SIZE>>;
+
+ /// Write a value to the given offset in the I/O region.
+ fn write<const SIZE: usize, T>(self, io: &T, offset: usize)
+ where
+ T: core::ops::Deref<Target = crate::io::Io<SIZE>>;
+}
+
+impl RegisterIo for u8 {
+ #[inline(always)]
+ fn read<const SIZE: usize, T>(io: &T, offset: usize) -> Self
+ where
+ T: core::ops::Deref<Target = crate::io::Io<SIZE>>,
+ {
+ io.read8(offset)
+ }
+
+ #[inline(always)]
+ fn write<const SIZE: usize, T>(self, io: &T, offset: usize)
+ where
+ T: core::ops::Deref<Target = crate::io::Io<SIZE>>,
+ {
+ io.write8(self, offset)
+ }
+}
+
+impl RegisterIo for u16 {
+ #[inline(always)]
+ fn read<const SIZE: usize, T>(io: &T, offset: usize) -> Self
+ where
+ T: core::ops::Deref<Target = crate::io::Io<SIZE>>,
+ {
+ io.read16(offset)
+ }
+
+ #[inline(always)]
+ fn write<const SIZE: usize, T>(self, io: &T, offset: usize)
+ where
+ T: core::ops::Deref<Target = crate::io::Io<SIZE>>,
+ {
+ io.write16(self, offset)
+ }
+}
+
+impl RegisterIo for u32 {
+ #[inline(always)]
+ fn read<const SIZE: usize, T>(io: &T, offset: usize) -> Self
+ where
+ T: core::ops::Deref<Target = crate::io::Io<SIZE>>,
+ {
+ io.read32(offset)
+ }
+
+ #[inline(always)]
+ fn write<const SIZE: usize, T>(self, io: &T, offset: usize)
+ where
+ T: core::ops::Deref<Target = crate::io::Io<SIZE>>,
+ {
+ io.write32(self, offset)
+ }
+}
+
+#[cfg(CONFIG_64BIT)]
+impl RegisterIo for u64 {
+ #[inline(always)]
+ fn read<const SIZE: usize, T>(io: &T, offset: usize) -> Self
+ where
+ T: core::ops::Deref<Target = crate::io::Io<SIZE>>,
+ {
+ io.read64(offset)
+ }
+
+ #[inline(always)]
+ fn write<const SIZE: usize, T>(self, io: &T, offset: usize)
+ where
+ T: core::ops::Deref<Target = crate::io::Io<SIZE>>,
+ {
+ io.write64(self, offset)
+ }
+}
+
+/// Defines a dedicated type for a register with an absolute offset, including getter and setter
+/// methods for its fields and methods to read and write it from an `Io` region.
+///
+/// A register is essentially a [`bitfield!`] with I/O capabilities. The syntax of the `register!`
+/// macro reflects that fact, being essentially identical to that of [`bitfield!`] with the
+/// addition of addressing information after the `@` token.
+///
+/// Example:
+///
+/// ```
+/// use kernel::register;
+///
+/// register!(pub BOOT_0(u32) @ 0x00000100, "Basic revision information about the chip" {
+/// 7:4 major_revision, "Major revision of the chip";
+/// 3:0 minor_revision, "Minor revision of the chip";
+/// });
+/// ```
+///
+/// This defines a `BOOT_0` type which can be read or written from offset `0x100` of an `Io`
+/// region. For instance, `minor_revision` is made of the 4 least significant bits of the
+/// register. Each field can be accessed and modified using accessor
+/// methods:
+///
+/// ```no_run
+/// use kernel::register;
+/// use kernel::num::Bounded;
+///
+/// # register!(pub BOOT_0(u32) @ 0x00000100, "Basic revision information about the chip" {
+/// # 7:4 major_revision, "Major revision of the chip";
+/// # 3:0 minor_revision, "Minor revision of the chip";
+/// # });
+/// # fn test(bar: &kernel::io::Io) {
+/// // Read from the register's defined offset (0x100).
+/// let boot0 = BOOT_0::read(&bar);
+/// pr_info!("chip revision: {}.{}", boot0.major_revision().get(), boot0.minor_revision().get());
+///
+/// // Update some fields and write the value back.
+/// boot0
+/// .set_major_revision(Bounded::<u32, _>::new::<3>())
+/// .set_minor_revision(Bounded::<u32, _>::new::<10>())
+/// .write(&bar);
+///
+/// // Or, just read and update the register in a single step:
+/// BOOT_0::update(&bar, |r| r
+/// .set_major_revision(Bounded::<u32, _>::new::<3>())
+/// .set_minor_revision(Bounded::<u32, _>::new::<10>())
+/// );
+/// # }
+/// ```
+///
+/// The documentation strings are optional. If present, they will be added to the type's
+/// definition, or the field getter and setter methods they are attached to.
+///
+/// Attributes can be applied to the generated struct. The `#[allow(non_camel_case_types)]`
+/// attribute is automatically added since register names typically use SCREAMING_CASE:
+///
+/// ```
+/// use kernel::register;
+///
+/// register! {
+/// pub STATUS(u32) @ 0x00000000, "Status register" {
+/// 0:0 ready, "Device ready flag";
+/// }
+/// }
+/// ```
+///
+/// It is also possible to create an alias register by using the `=> ALIAS` syntax. This is useful
+/// for cases where a register's interpretation depends on the context:
+///
+/// ```
+/// use kernel::register;
+///
+/// register!(pub SCRATCH(u32) @ 0x00000200, "Scratch register" {
+/// 31:0 value, "Raw value";
+/// });
+///
+/// register!(pub SCRATCH_BOOT_STATUS(u32) => SCRATCH, "Boot status of the firmware" {
+/// 0:0 completed, "Whether the firmware has completed booting";
+/// });
+/// ```
+///
+/// In this example, `SCRATCH_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while also
+/// providing its own `completed` field.
+///
+/// ## Relative registers
+///
+/// A register can be defined as being accessible from a fixed offset of a provided base. For
+/// instance, imagine the following I/O space:
+///
+/// ```text
+/// +-----------------------------+
+/// | ... |
+/// | |
+/// 0x100--->+------------CPU0-------------+
+/// | |
+/// 0x110--->+-----------------------------+
+/// | CPU_CTL |
+/// +-----------------------------+
+/// | ... |
+/// | |
+/// | |
+/// 0x200--->+------------CPU1-------------+
+/// | |
+/// 0x210--->+-----------------------------+
+/// | CPU_CTL |
+/// +-----------------------------+
+/// | ... |
+/// +-----------------------------+
+/// ```
+///
+/// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O
+/// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define
+/// them twice and would prefer a way to select which one to use from a single definition
+///
+/// This can be done using the `Base[Offset]` syntax when specifying the register's address.
+///
+/// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the
+/// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for
+/// this register needs to implement `RegisterBase<Base>`. Here is the above example translated
+/// into code:
+///
+/// ```no_run
+/// use kernel::register;
+/// use kernel::io::register::RegisterBase;
+///
+/// // Type used to identify the base.
+/// pub struct CpuCtlBase;
+///
+/// // ZST describing `CPU0`.
+/// struct Cpu0;
+/// impl RegisterBase<CpuCtlBase> for Cpu0 {
+/// const BASE: usize = 0x100;
+/// }
+/// // Singleton of `CPU0` used to identify it.
+/// const CPU0: Cpu0 = Cpu0;
+///
+/// // ZST describing `CPU1`.
+/// struct Cpu1;
+/// impl RegisterBase<CpuCtlBase> for Cpu1 {
+/// const BASE: usize = 0x200;
+/// }
+/// // Singleton of `CPU1` used to identify it.
+/// const CPU1: Cpu1 = Cpu1;
+///
+/// # fn test(bar: &kernel::io::Io) {
+/// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`.
+/// register!(pub CPU_CTL(u32) @ CpuCtlBase[0x10], "CPU core control" {
+/// 0:0 start, "Start the CPU core";
+/// });
+///
+/// // The `read`, `write` and `update` methods of relative registers take an extra `base` argument
+/// // that is used to resolve its final address by adding its `BASE` to the offset of the
+/// // register.
+///
+/// // Start `CPU0`.
+/// CPU_CTL::update(&bar, &CPU0, |r| r.set_start(true));
+///
+/// // Start `CPU1`.
+/// CPU_CTL::update(&bar, &CPU1, |r| r.set_start(true));
+///
+/// // Aliases can also be defined for relative register.
+/// register!(pub CPU_CTL_ALIAS(u32) => CpuCtlBase[CPU_CTL], "Alias to CPU core control" {
+/// 1:1 alias_start, "Start the aliased CPU core";
+/// });
+///
+/// // Start the aliased `CPU0`.
+/// CPU_CTL_ALIAS::update(&bar, &CPU0, |r| r.set_alias_start(true));
+/// # }
+/// ```
+///
+/// ## Arrays of registers
+///
+/// Some I/O areas contain consecutive values that can be interpreted in the same way. These areas
+/// can be defined as an array of identical registers, allowing them to be accessed by index with
+/// compile-time or runtime bound checking. Simply define their address as `Address[Size]`, and add
+/// an `idx` parameter to their `read`, `write` and `update` methods:
+///
+/// ```no_run
+/// use kernel::register;
+///
+/// # fn no_run(bar: &kernel::io::Io) -> Result<(), Error> {
+/// # fn get_scratch_idx() -> usize {
+/// # 0x15
+/// # }
+/// // Array of 64 consecutive registers with the same layout starting at offset `0x80`.
+/// register!(pub SCRATCH(u32) @ 0x00000080[64], "Scratch registers" {
+/// 31:0 value;
+/// });
+///
+/// // Read scratch register 0, i.e. I/O address `0x80`.
+/// let scratch_0 = SCRATCH::read(&bar, 0).value();
+/// // Read scratch register 15, i.e. I/O address `0x80 + (15 * 4)`.
+/// let scratch_15 = SCRATCH::read(&bar, 15).value();
+///
+/// // This is out of bounds and won't build.
+/// // let scratch_128 = SCRATCH::read(&bar, 128).value();
+///
+/// // Runtime-obtained array index.
+/// let scratch_idx = get_scratch_idx();
+/// // Access on a runtime index returns an error if it is out-of-bounds.
+/// let some_scratch = SCRATCH::try_read(&bar, scratch_idx)?.value();
+///
+/// // Alias to a particular register in an array.
+/// // Here `SCRATCH[8]` is used to convey the firmware exit code.
+/// register!(pub FIRMWARE_STATUS(u32) => SCRATCH[8], "Firmware exit status code" {
+/// 7:0 status;
+/// });
+///
+/// let status = FIRMWARE_STATUS::read(&bar).status();
+///
+/// // Non-contiguous register arrays can be defined by adding a stride parameter.
+/// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the
+/// // registers of the two declarations below are interleaved.
+/// register!(pub SCRATCH_INTERLEAVED_0(u32) @ 0x000000c0[16 ; 8], "Scratch registers bank 0" {
+/// 31:0 value;
+/// });
+/// register!(pub SCRATCH_INTERLEAVED_1(u32) @ 0x000000c4[16 ; 8], "Scratch registers bank 1" {
+/// 31:0 value;
+/// });
+/// # Ok(())
+/// # }
+/// ```
+///
+/// ## Relative arrays of registers
+///
+/// Combining the two features described in the sections above, arrays of registers accessible from
+/// a base can also be defined:
+///
+/// ```no_run
+/// use kernel::register;
+/// use kernel::io::register::RegisterBase;
+///
+/// # fn no_run(bar: &kernel::io::Io) -> Result<(), Error> {
+/// # fn get_scratch_idx() -> usize {
+/// # 0x15
+/// # }
+/// // Type used as parameter of `RegisterBase` to specify the base.
+/// pub struct CpuCtlBase;
+///
+/// // ZST describing `CPU0`.
+/// struct Cpu0;
+/// impl RegisterBase<CpuCtlBase> for Cpu0 {
+/// const BASE: usize = 0x100;
+/// }
+/// // Singleton of `CPU0` used to identify it.
+/// const CPU0: Cpu0 = Cpu0;
+///
+/// // ZST describing `CPU1`.
+/// struct Cpu1;
+/// impl RegisterBase<CpuCtlBase> for Cpu1 {
+/// const BASE: usize = 0x200;
+/// }
+/// // Singleton of `CPU1` used to identify it.
+/// const CPU1: Cpu1 = Cpu1;
+///
+/// // 64 per-cpu scratch registers, arranged as a contiguous array.
+/// register!(pub CPU_SCRATCH(u32) @ CpuCtlBase[0x00000080[64]], "Per-CPU scratch registers" {
+/// 31:0 value;
+/// });
+///
+/// let cpu0_scratch_0 = CPU_SCRATCH::read(&bar, &Cpu0, 0).value();
+/// let cpu1_scratch_15 = CPU_SCRATCH::read(&bar, &Cpu1, 15).value();
+///
+/// // This won't build.
+/// // let cpu0_scratch_128 = CPU_SCRATCH::read(&bar, &Cpu0, 128).value();
+///
+/// // Runtime-obtained array index.
+/// let scratch_idx = get_scratch_idx();
+/// // Access on a runtime value returns an error if it is out-of-bounds.
+/// let cpu0_some_scratch = CPU_SCRATCH::try_read(&bar, &Cpu0, scratch_idx)?.value();
+///
+/// // `SCRATCH[8]` is used to convey the firmware exit code.
+/// register!(pub CPU_FIRMWARE_STATUS(u32) => CpuCtlBase[CPU_SCRATCH[8]],
+/// "Per-CPU firmware exit status code" {
+/// 7:0 status;
+/// });
+///
+/// let cpu0_status = CPU_FIRMWARE_STATUS::read(&bar, &Cpu0).status();
+///
+/// // Non-contiguous register arrays can be defined by adding a stride parameter.
+/// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the
+/// // registers of the two declarations below are interleaved.
+/// register!(pub CPU_SCRATCH_INTERLEAVED_0(u32) @ CpuCtlBase[0x00000d00[16 ; 8]],
+/// "Scratch registers bank 0" {
+/// 31:0 value;
+/// });
+/// register!(pub CPU_SCRATCH_INTERLEAVED_1(u32) @ CpuCtlBase[0x00000d04[16 ; 8]],
+/// "Scratch registers bank 1" {
+/// 31:0 value;
+/// });
+/// # Ok(())
+/// # }
+/// ```
+/// [`bitfield!`]: kernel::bitfield!
+#[macro_export]
+macro_rules! register {
+ // Creates a register at a fixed offset of the MMIO space.
+ (
+ $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $offset:literal
+ $(, $comment:literal)? { $($fields:tt)* }
+ ) => {
+ ::kernel::register!(
+ @bitfield $(#[$attr])* $vis struct $name($storage) $(, $comment)? { $($fields)* }
+ );
+ ::kernel::register!(@io_fixed $name($storage) @ $offset);
+ };
+
+ // Creates an alias register of fixed offset register `alias` with its own fields.
+ (
+ $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident
+ $(, $comment:literal)? { $($fields:tt)* }
+ ) => {
+ ::kernel::register!(
+ @bitfield $(#[$attr])* $vis struct $name($storage) $(, $comment)? { $($fields)* }
+ );
+ ::kernel::register!(@io_fixed $name($storage) @ $alias::OFFSET);
+ };
+
+ // Creates a register at a relative offset from a base address provider.
+ (
+ $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $base:ty [ $offset:literal ]
+ $(, $comment:literal)? { $($fields:tt)* }
+ ) => {
+ ::kernel::register!(
+ @bitfield $(#[$attr])* $vis struct $name($storage) $(, $comment)? { $($fields)* }
+ );
+ ::kernel::register!(@io_relative $name($storage) @ $base [ $offset ]);
+ };
+
+ // Creates an alias register of relative offset register `alias` with its own fields.
+ (
+ $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $base:ty [ $alias:ident ]
+ $(, $comment:literal)? { $($fields:tt)* }
+ ) => {
+ ::kernel::register!(
+ @bitfield $(#[$attr])* $vis struct $name($storage) $(, $comment)? { $($fields)* }
+ );
+ ::kernel::register!(@io_relative $name($storage) @ $base [ $alias::OFFSET ]);
+ };
+
+ // Creates an array of registers at a fixed offset of the MMIO space.
+ (
+ $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
+ @ $offset:literal [ $size:expr ; $stride:expr ]
+ $(, $comment:literal)? { $($fields:tt)* }
+ ) => {
+ static_assert!(::core::mem::size_of::<$storage>() <= $stride);
+
+ ::kernel::register!(
+ @bitfield $(#[$attr])* $vis struct $name($storage) $(, $comment)? { $($fields)* }
+ );
+ ::kernel::register!(@io_array $name($storage) @ $offset [ $size ; $stride ]);
+ };
+
+ // Shortcut for contiguous array of registers (stride == size of element).
+ (
+ $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $offset:literal [ $size:expr ]
+ $(, $comment:literal)? { $($fields:tt)* }
+ ) => {
+ ::kernel::register!(
+ $(#[$attr])* $vis $name($storage)
+ @ $offset [ $size ; ::core::mem::size_of::<$storage>() ]
+ $(, $comment)? { $($fields)* }
+ );
+ };
+
+ // Creates an array of registers at a relative offset from a base address provider.
+ (
+ $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
+ @ $base:ty [ $offset:literal [ $size:expr ; $stride:expr ] ]
+ $(, $comment:literal)? { $($fields:tt)* }
+ ) => {
+ static_assert!(::core::mem::size_of::<$storage>() <= $stride);
+
+ ::kernel::register!(
+ @bitfield $(#[$attr])* $vis struct $name($storage) $(, $comment)? { $($fields)* }
+ );
+ ::kernel::register!(
+ @io_relative_array $name($storage) @ $base [ $offset [ $size ; $stride ] ]
+ );
+ };
+
+ // Shortcut for contiguous array of relative registers (stride == size of element).
+ (
+ $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
+ @ $base:ty [ $offset:literal [ $size:expr ] ]
+ $(, $comment:literal)? { $($fields:tt)* }
+ ) => {
+ ::kernel::register!(
+ $(#[$attr])* $vis $name($storage)
+ @ $base [ $offset [ $size ; ::core::mem::size_of::<$storage>() ] ]
+ $(, $comment)? { $($fields)* }
+ );
+ };
+
+ // Creates an alias of register `idx` of relative array of registers `alias` with its own
+ // fields.
+ (
+ $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
+ => $base:ty [ $alias:ident [ $idx:expr ] ]
+ $(, $comment:literal)? { $($fields:tt)* }
+ ) => {
+ static_assert!($idx < $alias::SIZE);
+
+ ::kernel::register!(
+ @bitfield $(#[$attr])* $vis struct $name($storage) $(, $comment)? { $($fields)* }
+ );
+ ::kernel::register!(
+ @io_relative $name($storage) @ $base [ $alias::OFFSET + $idx * $alias::STRIDE ]
+ );
+ };
+
+ // Creates an alias of register `idx` of array of registers `alias` with its own fields.
+ // This rule belongs to the (non-relative) register arrays set, but needs to be put last
+ // to avoid it being interpreted in place of the relative register array alias rule.
+ (
+ $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident [ $idx:expr ]
+ $(, $comment:literal)? { $($fields:tt)* }
+ ) => {
+ static_assert!($idx < $alias::SIZE);
+
+ ::kernel::register!(
+ @bitfield $(#[$attr])* $vis struct $name($storage) $(, $comment)? { $($fields)* }
+ );
+ ::kernel::register!(@io_fixed $name($storage) @ $alias::OFFSET + $idx * $alias::STRIDE);
+ };
+
+ // All rules below are helpers.
+
+ // Generates the bitfield for the register.
+ //
+ // `#[allow(non_camel_case_types)]` is added since register names typically use SCREAMING_CASE.
+ (
+ @bitfield $(#[$attr:meta])* $vis:vis struct $name:ident($storage:ty)
+ $(, $comment:literal)? { $($fields:tt)* }
+ ) => {
+ ::kernel::bitfield!(
+ #[allow(non_camel_case_types)]
+ $(#[$attr])* $vis struct $name($storage) $(, $comment)? { $($fields)* }
+ );
+ };
+
+ // Generates the IO accessors for a fixed offset register.
+ (@io_fixed $name:ident ($storage:ty) @ $offset:expr) => {
+ #[allow(dead_code)]
+ impl $name {
+ pub const OFFSET: usize = $offset;
+
+ /// Read the register from its address in `io`.
+ #[inline(always)]
+ pub fn read<const SIZE: usize, T>(io: &T) -> Self where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ {
+ Self(<$storage as $crate::io::register::RegisterIo>::read(io, $offset))
+ }
+
+ /// Write the value contained in `self` to the register address in `io`.
+ #[inline(always)]
+ pub fn write<const SIZE: usize, T>(self, io: &T) where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ {
+ <$storage as $crate::io::register::RegisterIo>::write(self.0, io, $offset)
+ }
+
+ /// Read the register from its address in `io` and run `f` on its value to obtain a new
+ /// value to write back.
+ ///
+ /// Note that this operation is not atomic. In concurrent contexts, external
+ /// synchronization may be required to prevent race conditions.
+ #[inline(always)]
+ pub fn update<const SIZE: usize, T, F>(
+ io: &T,
+ f: F,
+ ) where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ F: ::core::ops::FnOnce(Self) -> Self,
+ {
+ let reg = f(Self::read(io));
+ reg.write(io);
+ }
+ }
+ };
+
+ // Generates the IO accessors for a relative offset register.
+ (@io_relative $name:ident ($storage:ty) @ $base:ty [ $offset:expr ]) => {
+ #[allow(dead_code)]
+ impl $name {
+ pub const OFFSET: usize = $offset;
+
+ /// Read the register from `io`, using the base address provided by `base` and adding
+ /// the register's offset to it.
+ #[inline(always)]
+ pub fn read<const SIZE: usize, T, B>(
+ io: &T,
+ #[allow(unused_variables)]
+ base: &B,
+ ) -> Self where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ B: $crate::io::register::RegisterBase<$base>,
+ {
+ let offset = <B as $crate::io::register::RegisterBase<$base>>::BASE + $name::OFFSET;
+
+ Self(<$storage as $crate::io::register::RegisterIo>::read(io, offset))
+ }
+
+ /// Write the value contained in `self` to `io`, using the base address provided by
+ /// `base` and adding the register's offset to it.
+ #[inline(always)]
+ pub fn write<const SIZE: usize, T, B>(
+ self,
+ io: &T,
+ #[allow(unused_variables)]
+ base: &B,
+ ) where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ B: $crate::io::register::RegisterBase<$base>,
+ {
+ let offset = <B as $crate::io::register::RegisterBase<$base>>::BASE + $name::OFFSET;
+
+ <$storage as $crate::io::register::RegisterIo>::write(self.0, io, offset)
+ }
+
+ /// Read the register from `io`, using the base address provided by `base` and adding
+ /// the register's offset to it, then run `f` on its value to obtain a new value to
+ /// write back.
+ ///
+ /// Note that this operation is not atomic. In concurrent contexts, external
+ /// synchronization may be required to prevent race conditions.
+ #[inline(always)]
+ pub fn update<const SIZE: usize, T, B, F>(
+ io: &T,
+ base: &B,
+ f: F,
+ ) where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ B: $crate::io::register::RegisterBase<$base>,
+ F: ::core::ops::FnOnce(Self) -> Self,
+ {
+ let reg = f(Self::read(io, base));
+ reg.write(io, base);
+ }
+ }
+ };
+
+ // Generates the IO accessors for an array of registers.
+ (@io_array $name:ident ($storage:ty) @ $offset:literal [ $size:expr ; $stride:expr ]) => {
+ #[allow(dead_code)]
+ impl $name {
+ pub const OFFSET: usize = $offset;
+ pub const SIZE: usize = $size;
+ pub const STRIDE: usize = $stride;
+
+ /// Read the array register at index `idx` from its address in `io`.
+ #[inline(always)]
+ pub fn read<const SIZE: usize, T>(
+ io: &T,
+ idx: usize,
+ ) -> Self where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ {
+ build_assert!(idx < Self::SIZE);
+
+ let offset = Self::OFFSET + (idx * Self::STRIDE);
+
+ Self(<$storage as $crate::io::register::RegisterIo>::read(io, offset))
+ }
+
+ /// Write the value contained in `self` to the array register with index `idx` in `io`.
+ #[inline(always)]
+ pub fn write<const SIZE: usize, T>(
+ self,
+ io: &T,
+ idx: usize
+ ) where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ {
+ build_assert!(idx < Self::SIZE);
+
+ let offset = Self::OFFSET + (idx * Self::STRIDE);
+
+ <$storage as $crate::io::register::RegisterIo>::write(self.0, io, offset)
+ }
+
+ /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
+ /// new value to write back.
+ ///
+ /// Note that this operation is not atomic. In concurrent contexts, external
+ /// synchronization may be required to prevent race conditions.
+ #[inline(always)]
+ pub fn update<const SIZE: usize, T, F>(
+ io: &T,
+ idx: usize,
+ f: F,
+ ) where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ F: ::core::ops::FnOnce(Self) -> Self,
+ {
+ let reg = f(Self::read(io, idx));
+ reg.write(io, idx);
+ }
+
+ /// Read the array register at index `idx` from its address in `io`.
+ ///
+ /// The validity of `idx` is checked at run-time, and `EINVAL` is returned if the
+ /// access was out-of-bounds.
+ #[inline(always)]
+ pub fn try_read<const SIZE: usize, T>(
+ io: &T,
+ idx: usize,
+ ) -> ::kernel::error::Result<Self> where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ {
+ if idx < Self::SIZE {
+ Ok(Self::read(io, idx))
+ } else {
+ Err(::kernel::error::code::EINVAL)
+ }
+ }
+
+ /// Write the value contained in `self` to the array register with index `idx` in `io`.
+ ///
+ /// The validity of `idx` is checked at run-time, and `EINVAL` is returned if the
+ /// access was out-of-bounds.
+ #[inline(always)]
+ pub fn try_write<const SIZE: usize, T>(
+ self,
+ io: &T,
+ idx: usize,
+ ) -> ::kernel::error::Result where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ {
+ if idx < Self::SIZE {
+ Ok(self.write(io, idx))
+ } else {
+ Err(::kernel::error::code::EINVAL)
+ }
+ }
+
+ /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
+ /// new value to write back.
+ ///
+ /// The validity of `idx` is checked at run-time, and `EINVAL` is returned if the
+ /// access was out-of-bounds.
+ ///
+ /// Note that this operation is not atomic. In concurrent contexts, external
+ /// synchronization may be required to prevent race conditions.
+ #[inline(always)]
+ pub fn try_update<const SIZE: usize, T, F>(
+ io: &T,
+ idx: usize,
+ f: F,
+ ) -> ::kernel::error::Result where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ F: ::core::ops::FnOnce(Self) -> Self,
+ {
+ if idx < Self::SIZE {
+ Ok(Self::update(io, idx, f))
+ } else {
+ Err(::kernel::error::code::EINVAL)
+ }
+ }
+ }
+ };
+
+ // Generates the IO accessors for an array of relative registers.
+ (
+ @io_relative_array $name:ident ($storage:ty) @ $base:ty
+ [ $offset:literal [ $size:expr ; $stride:expr ] ]
+ ) => {
+ #[allow(dead_code)]
+ impl $name {
+ pub const OFFSET: usize = $offset;
+ pub const SIZE: usize = $size;
+ pub const STRIDE: usize = $stride;
+
+ /// Read the array register at index `idx` from `io`, using the base address provided
+ /// by `base` and adding the register's offset to it.
+ #[inline(always)]
+ pub fn read<const SIZE: usize, T, B>(
+ io: &T,
+ #[allow(unused_variables)]
+ base: &B,
+ idx: usize,
+ ) -> Self where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ B: $crate::io::register::RegisterBase<$base>,
+ {
+ build_assert!(idx < Self::SIZE);
+
+ let offset = <B as $crate::io::register::RegisterBase<$base>>::BASE +
+ Self::OFFSET + (idx * Self::STRIDE);
+
+ Self(<$storage as $crate::io::register::RegisterIo>::read(io, offset))
+ }
+
+ /// Write the value contained in `self` to `io`, using the base address provided by
+ /// `base` and adding the offset of array register `idx` to it.
+ #[inline(always)]
+ pub fn write<const SIZE: usize, T, B>(
+ self,
+ io: &T,
+ #[allow(unused_variables)]
+ base: &B,
+ idx: usize
+ ) where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ B: $crate::io::register::RegisterBase<$base>,
+ {
+ build_assert!(idx < Self::SIZE);
+
+ let offset = <B as $crate::io::register::RegisterBase<$base>>::BASE +
+ Self::OFFSET + (idx * Self::STRIDE);
+
+ <$storage as $crate::io::register::RegisterIo>::write(self.0, io, offset)
+ }
+
+ /// Read the array register at index `idx` from `io`, using the base address provided
+ /// by `base` and adding the register's offset to it, then run `f` on its value to
+ /// obtain a new value to write back.
+ ///
+ /// Note that this operation is not atomic. In concurrent contexts, external
+ /// synchronization may be required to prevent race conditions.
+ #[inline(always)]
+ pub fn update<const SIZE: usize, T, B, F>(
+ io: &T,
+ base: &B,
+ idx: usize,
+ f: F,
+ ) where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ B: $crate::io::register::RegisterBase<$base>,
+ F: ::core::ops::FnOnce(Self) -> Self,
+ {
+ let reg = f(Self::read(io, base, idx));
+ reg.write(io, base, idx);
+ }
+
+ /// Read the array register at index `idx` from `io`, using the base address provided
+ /// by `base` and adding the register's offset to it.
+ ///
+ /// The validity of `idx` is checked at run-time, and `EINVAL` is returned if the
+ /// access was out-of-bounds.
+ #[inline(always)]
+ pub fn try_read<const SIZE: usize, T, B>(
+ io: &T,
+ base: &B,
+ idx: usize,
+ ) -> ::kernel::error::Result<Self> where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ B: $crate::io::register::RegisterBase<$base>,
+ {
+ if idx < Self::SIZE {
+ Ok(Self::read(io, base, idx))
+ } else {
+ Err(::kernel::error::code::EINVAL)
+ }
+ }
+
+ /// Write the value contained in `self` to `io`, using the base address provided by
+ /// `base` and adding the offset of array register `idx` to it.
+ ///
+ /// The validity of `idx` is checked at run-time, and `EINVAL` is returned if the
+ /// access was out-of-bounds.
+ #[inline(always)]
+ pub fn try_write<const SIZE: usize, T, B>(
+ self,
+ io: &T,
+ base: &B,
+ idx: usize,
+ ) -> ::kernel::error::Result where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ B: $crate::io::register::RegisterBase<$base>,
+ {
+ if idx < Self::SIZE {
+ Ok(self.write(io, base, idx))
+ } else {
+ Err(::kernel::error::code::EINVAL)
+ }
+ }
+
+ /// Read the array register at index `idx` from `io`, using the base address provided
+ /// by `base` and adding the register's offset to it, then run `f` on its value to
+ /// obtain a new value to write back.
+ ///
+ /// The validity of `idx` is checked at run-time, and `EINVAL` is returned if the
+ /// access was out-of-bounds.
+ ///
+ /// Note that this operation is not atomic. In concurrent contexts, external
+ /// synchronization may be required to prevent race conditions.
+ #[inline(always)]
+ pub fn try_update<const SIZE: usize, T, B, F>(
+ io: &T,
+ base: &B,
+ idx: usize,
+ f: F,
+ ) -> ::kernel::error::Result where
+ T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
+ B: $crate::io::register::RegisterBase<$base>,
+ F: ::core::ops::FnOnce(Self) -> Self,
+ {
+ if idx < Self::SIZE {
+ Ok(Self::update(io, base, idx, f))
+ } else {
+ Err(::kernel::error::code::EINVAL)
+ }
+ }
+ }
+ };
+}
--
2.52.0
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH FOR REFERENCE 6/6] gpu: nova-core: use the kernel `register!` and `bitfield!` macros
2026-01-20 6:17 [PATCH 0/6] rust: add `bitfield!` and `register!` macros Alexandre Courbot
` (4 preceding siblings ...)
2026-01-20 6:17 ` [PATCH 5/6] rust: io: add `register!` macro Alexandre Courbot
@ 2026-01-20 6:17 ` Alexandre Courbot
2026-01-20 13:14 ` [PATCH 0/6] rust: add `bitfield!` and `register!` macros Miguel Ojeda
6 siblings, 0 replies; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-20 6:17 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Yury Norov
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, Daniel Almeida, Dirk Behme,
Steven Price, rust-for-linux, linux-kernel, Alexandre Courbot
Replace the nova-core internal `bitfield` and `register` macros by the
ones defined in the `kernel` crate and remove our own private
implementations.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/bitfield.rs | 330 --------------
drivers/gpu/nova-core/falcon.rs | 127 +++---
drivers/gpu/nova-core/falcon/gsp.rs | 10 +-
drivers/gpu/nova-core/falcon/hal/ga102.rs | 5 +-
drivers/gpu/nova-core/falcon/sec2.rs | 13 +-
drivers/gpu/nova-core/fb/hal/ga100.rs | 9 +-
drivers/gpu/nova-core/gpu.rs | 24 +-
drivers/gpu/nova-core/gsp/cmdq.rs | 2 +-
drivers/gpu/nova-core/gsp/fw.rs | 5 +-
drivers/gpu/nova-core/nova_core.rs | 3 -
drivers/gpu/nova-core/regs.rs | 265 +++++------
drivers/gpu/nova-core/regs/macros.rs | 721 ------------------------------
12 files changed, 223 insertions(+), 1291 deletions(-)
diff --git a/drivers/gpu/nova-core/bitfield.rs b/drivers/gpu/nova-core/bitfield.rs
deleted file mode 100644
index 16e143658c51..000000000000
--- a/drivers/gpu/nova-core/bitfield.rs
+++ /dev/null
@@ -1,330 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-//! Bitfield library for Rust structures
-//!
-//! Support for defining bitfields in Rust structures. Also used by the [`register!`] macro.
-
-/// Defines a struct with accessors to access bits within an inner unsigned integer.
-///
-/// # Syntax
-///
-/// ```rust
-/// use nova_core::bitfield;
-///
-/// #[derive(Debug, Clone, Copy, Default)]
-/// enum Mode {
-/// #[default]
-/// Low = 0,
-/// High = 1,
-/// Auto = 2,
-/// }
-///
-/// impl TryFrom<u8> for Mode {
-/// type Error = u8;
-/// fn try_from(value: u8) -> Result<Self, Self::Error> {
-/// match value {
-/// 0 => Ok(Mode::Low),
-/// 1 => Ok(Mode::High),
-/// 2 => Ok(Mode::Auto),
-/// _ => Err(value),
-/// }
-/// }
-/// }
-///
-/// impl From<Mode> for u8 {
-/// fn from(mode: Mode) -> u8 {
-/// mode as u8
-/// }
-/// }
-///
-/// #[derive(Debug, Clone, Copy, Default)]
-/// enum State {
-/// #[default]
-/// Inactive = 0,
-/// Active = 1,
-/// }
-///
-/// impl From<bool> for State {
-/// fn from(value: bool) -> Self {
-/// if value { State::Active } else { State::Inactive }
-/// }
-/// }
-///
-/// impl From<State> for bool {
-/// fn from(state: State) -> bool {
-/// match state {
-/// State::Inactive => false,
-/// State::Active => true,
-/// }
-/// }
-/// }
-///
-/// bitfield! {
-/// pub struct ControlReg(u32) {
-/// 7:7 state as bool => State;
-/// 3:0 mode as u8 ?=> Mode;
-/// }
-/// }
-/// ```
-///
-/// This generates a struct with:
-/// - Field accessors: `mode()`, `state()`, etc.
-/// - Field setters: `set_mode()`, `set_state()`, etc. (supports chaining with builder pattern).
-/// Note that the compiler will error out if the size of the setter's arg exceeds the
-/// struct's storage size.
-/// - Debug and Default implementations.
-///
-/// Note: Field accessors and setters inherit the same visibility as the struct itself.
-/// In the example above, both `mode()` and `set_mode()` methods will be `pub`.
-///
-/// Fields are defined as follows:
-///
-/// - `as <type>` simply returns the field value casted to <type>, typically `u32`, `u16`, `u8` or
-/// `bool`. Note that `bool` fields must have a range of 1 bit.
-/// - `as <type> => <into_type>` calls `<into_type>`'s `From::<<type>>` implementation and returns
-/// the result.
-/// - `as <type> ?=> <try_into_type>` calls `<try_into_type>`'s `TryFrom::<<type>>` implementation
-/// and returns the result. This is useful with fields for which not all values are valid.
-macro_rules! bitfield {
- // Main entry point - defines the bitfield struct with fields
- ($vis:vis struct $name:ident($storage:ty) $(, $comment:literal)? { $($fields:tt)* }) => {
- bitfield!(@core $vis $name $storage $(, $comment)? { $($fields)* });
- };
-
- // All rules below are helpers.
-
- // Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`,
- // `Default`, and conversion to the value type) and field accessor methods.
- (@core $vis:vis $name:ident $storage:ty $(, $comment:literal)? { $($fields:tt)* }) => {
- $(
- #[doc=$comment]
- )?
- #[repr(transparent)]
- #[derive(Clone, Copy)]
- $vis struct $name($storage);
-
- impl ::core::convert::From<$name> for $storage {
- fn from(val: $name) -> $storage {
- val.0
- }
- }
-
- bitfield!(@fields_dispatcher $vis $name $storage { $($fields)* });
- };
-
- // Captures the fields and passes them to all the implementers that require field information.
- //
- // Used to simplify the matching rules for implementers, so they don't need to match the entire
- // complex fields rule even though they only make use of part of it.
- (@fields_dispatcher $vis:vis $name:ident $storage:ty {
- $($hi:tt:$lo:tt $field:ident as $type:tt
- $(?=> $try_into_type:ty)?
- $(=> $into_type:ty)?
- $(, $comment:literal)?
- ;
- )*
- }
- ) => {
- bitfield!(@field_accessors $vis $name $storage {
- $(
- $hi:$lo $field as $type
- $(?=> $try_into_type)?
- $(=> $into_type)?
- $(, $comment)?
- ;
- )*
- });
- bitfield!(@debug $name { $($field;)* });
- bitfield!(@default $name { $($field;)* });
- };
-
- // Defines all the field getter/setter methods for `$name`.
- (
- @field_accessors $vis:vis $name:ident $storage:ty {
- $($hi:tt:$lo:tt $field:ident as $type:tt
- $(?=> $try_into_type:ty)?
- $(=> $into_type:ty)?
- $(, $comment:literal)?
- ;
- )*
- }
- ) => {
- $(
- bitfield!(@check_field_bounds $hi:$lo $field as $type);
- )*
-
- #[allow(dead_code)]
- impl $name {
- $(
- bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field as $type
- $(?=> $try_into_type)?
- $(=> $into_type)?
- $(, $comment)?
- ;
- );
- )*
- }
- };
-
- // Boolean fields must have `$hi == $lo`.
- (@check_field_bounds $hi:tt:$lo:tt $field:ident as bool) => {
- #[allow(clippy::eq_op)]
- const _: () = {
- ::kernel::build_assert!(
- $hi == $lo,
- concat!("boolean field `", stringify!($field), "` covers more than one bit")
- );
- };
- };
-
- // Non-boolean fields must have `$hi >= $lo`.
- (@check_field_bounds $hi:tt:$lo:tt $field:ident as $type:tt) => {
- #[allow(clippy::eq_op)]
- const _: () = {
- ::kernel::build_assert!(
- $hi >= $lo,
- concat!("field `", stringify!($field), "`'s MSB is smaller than its LSB")
- );
- };
- };
-
- // Catches fields defined as `bool` and convert them into a boolean value.
- (
- @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as bool
- => $into_type:ty $(, $comment:literal)?;
- ) => {
- bitfield!(
- @leaf_accessor $vis $name $storage, $hi:$lo $field
- { |f| <$into_type>::from(f != 0) }
- bool $into_type => $into_type $(, $comment)?;
- );
- };
-
- // Shortcut for fields defined as `bool` without the `=>` syntax.
- (
- @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as bool
- $(, $comment:literal)?;
- ) => {
- bitfield!(
- @field_accessor $vis $name $storage, $hi:$lo $field as bool => bool $(, $comment)?;
- );
- };
-
- // Catches the `?=>` syntax for non-boolean fields.
- (
- @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as $type:tt
- ?=> $try_into_type:ty $(, $comment:literal)?;
- ) => {
- bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field
- { |f| <$try_into_type>::try_from(f as $type) } $type $try_into_type =>
- ::core::result::Result<
- $try_into_type,
- <$try_into_type as ::core::convert::TryFrom<$type>>::Error
- >
- $(, $comment)?;);
- };
-
- // Catches the `=>` syntax for non-boolean fields.
- (
- @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as $type:tt
- => $into_type:ty $(, $comment:literal)?;
- ) => {
- bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field
- { |f| <$into_type>::from(f as $type) } $type $into_type => $into_type $(, $comment)?;);
- };
-
- // Shortcut for non-boolean fields defined without the `=>` or `?=>` syntax.
- (
- @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as $type:tt
- $(, $comment:literal)?;
- ) => {
- bitfield!(
- @field_accessor $vis $name $storage, $hi:$lo $field as $type => $type $(, $comment)?;
- );
- };
-
- // Generates the accessor methods for a single field.
- (
- @leaf_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident
- { $process:expr } $prim_type:tt $to_type:ty => $res_type:ty $(, $comment:literal)?;
- ) => {
- ::kernel::macros::paste!(
- const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
- const [<$field:upper _MASK>]: $storage = {
- // Generate mask for shifting
- match ::core::mem::size_of::<$storage>() {
- 1 => ::kernel::bits::genmask_u8($lo..=$hi) as $storage,
- 2 => ::kernel::bits::genmask_u16($lo..=$hi) as $storage,
- 4 => ::kernel::bits::genmask_u32($lo..=$hi) as $storage,
- 8 => ::kernel::bits::genmask_u64($lo..=$hi) as $storage,
- _ => ::kernel::build_error!("Unsupported storage type size")
- }
- };
- const [<$field:upper _SHIFT>]: u32 = $lo;
- );
-
- $(
- #[doc="Returns the value of this field:"]
- #[doc=$comment]
- )?
- #[inline(always)]
- $vis fn $field(self) -> $res_type {
- ::kernel::macros::paste!(
- const MASK: $storage = $name::[<$field:upper _MASK>];
- const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
- );
- let field = ((self.0 & MASK) >> SHIFT);
-
- $process(field)
- }
-
- ::kernel::macros::paste!(
- $(
- #[doc="Sets the value of this field:"]
- #[doc=$comment]
- )?
- #[inline(always)]
- $vis fn [<set_ $field>](mut self, value: $to_type) -> Self {
- const MASK: $storage = $name::[<$field:upper _MASK>];
- const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
- let value = ($storage::from($prim_type::from(value)) << SHIFT) & MASK;
- self.0 = (self.0 & !MASK) | value;
-
- self
- }
- );
- };
-
- // Generates the `Debug` implementation for `$name`.
- (@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.0))
- $(
- .field(stringify!($field), &self.$field())
- )*
- .finish()
- }
- }
- };
-
- // Generates the `Default` implementation for `$name`.
- (@default $name:ident { $($field:ident;)* }) => {
- /// Returns a value for the bitfield where all fields are set to their default value.
- impl ::core::default::Default for $name {
- fn default() -> Self {
- #[allow(unused_mut)]
- let mut value = Self(Default::default());
-
- ::kernel::macros::paste!(
- $(
- value.[<set_ $field>](Default::default());
- )*
- );
-
- value
- }
- }
- };
-}
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 82c661aef594..2d0c4cbd9e67 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -9,12 +9,16 @@
use kernel::{
device,
dma::DmaAddress,
- io::poll::read_poll_timeout,
+ io::{
+ poll::read_poll_timeout,
+ register::RegisterBase, //
+ },
+ num::Bounded,
prelude::*,
sync::aref::ARef,
time::{
- delay::fsleep,
- Delta, //
+ delay::fsleep, //
+ Delta,
},
};
@@ -27,7 +31,6 @@
IntoSafeCast, //
},
regs,
- regs::macros::RegisterBase, //
};
pub(crate) mod gsp;
@@ -35,11 +38,12 @@
pub(crate) mod sec2;
// TODO[FPRI]: Replace with `ToPrimitive`.
-macro_rules! impl_from_enum_to_u8 {
- ($enum_type:ty) => {
- impl From<$enum_type> for u8 {
+// TODO: macro that defines the struct and impls, like for Chipset.
+macro_rules! impl_from_enum_to_bounded {
+ ($enum_type:ty, $length:literal) => {
+ impl From<$enum_type> for Bounded<u32, $length> {
fn from(value: $enum_type) -> Self {
- value as u8
+ Bounded::from_expr(value as u32)
}
}
};
@@ -47,7 +51,6 @@ fn from(value: $enum_type) -> Self {
/// Revision number of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`]
/// register.
-#[repr(u8)]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum FalconCoreRev {
#[default]
@@ -59,16 +62,16 @@ pub(crate) enum FalconCoreRev {
Rev6 = 6,
Rev7 = 7,
}
-impl_from_enum_to_u8!(FalconCoreRev);
+impl_from_enum_to_bounded!(FalconCoreRev, 4);
// TODO[FPRI]: replace with `FromPrimitive`.
-impl TryFrom<u8> for FalconCoreRev {
+impl TryFrom<Bounded<u32, 4>> for FalconCoreRev {
type Error = Error;
- fn try_from(value: u8) -> Result<Self> {
+ fn try_from(value: Bounded<u32, 4>) -> Result<Self> {
use FalconCoreRev::*;
- let rev = match value {
+ let rev = match value.get() {
1 => Rev1,
2 => Rev2,
3 => Rev3,
@@ -85,7 +88,6 @@ fn try_from(value: u8) -> Result<Self> {
/// Revision subversion number of a falcon core, used in the
/// [`crate::regs::NV_PFALCON_FALCON_HWCFG1`] register.
-#[repr(u8)]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum FalconCoreRevSubversion {
#[default]
@@ -94,30 +96,27 @@ pub(crate) enum FalconCoreRevSubversion {
Subversion2 = 2,
Subversion3 = 3,
}
-impl_from_enum_to_u8!(FalconCoreRevSubversion);
+impl_from_enum_to_bounded!(FalconCoreRevSubversion, 2);
// TODO[FPRI]: replace with `FromPrimitive`.
-impl TryFrom<u8> for FalconCoreRevSubversion {
- type Error = Error;
-
- fn try_from(value: u8) -> Result<Self> {
+impl From<Bounded<u32, 2>> for FalconCoreRevSubversion {
+ fn from(value: Bounded<u32, 2>) -> Self {
use FalconCoreRevSubversion::*;
- let sub_version = match value & 0b11 {
+ match value.get() {
0 => Subversion0,
1 => Subversion1,
2 => Subversion2,
3 => Subversion3,
- _ => return Err(EINVAL),
- };
-
- Ok(sub_version)
+ // SAFETY: `value` comes from a 2-bit `Bounded`, and we just checked all possible
+ // values.
+ _ => unsafe { core::hint::unreachable_unchecked() },
+ }
}
}
/// Security model of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`]
/// register.
-#[repr(u8)]
#[derive(Debug, Default, Copy, Clone)]
/// Security mode of the Falcon microprocessor.
///
@@ -138,16 +137,16 @@ pub(crate) enum FalconSecurityModel {
/// Also known as High-Secure, Privilege Level 3 or PL3.
Heavy = 3,
}
-impl_from_enum_to_u8!(FalconSecurityModel);
+impl_from_enum_to_bounded!(FalconSecurityModel, 2);
// TODO[FPRI]: replace with `FromPrimitive`.
-impl TryFrom<u8> for FalconSecurityModel {
+impl TryFrom<Bounded<u32, 2>> for FalconSecurityModel {
type Error = Error;
- fn try_from(value: u8) -> Result<Self> {
+ fn try_from(value: Bounded<u32, 2>) -> Result<Self> {
use FalconSecurityModel::*;
- let sec_model = match value {
+ let sec_model = match value.get() {
0 => None,
2 => Light,
3 => Heavy,
@@ -160,24 +159,23 @@ fn try_from(value: u8) -> Result<Self> {
/// Signing algorithm for a given firmware, used in the [`crate::regs::NV_PFALCON2_FALCON_MOD_SEL`]
/// register. It is passed to the Falcon Boot ROM (BROM) as a parameter.
-#[repr(u8)]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
pub(crate) enum FalconModSelAlgo {
/// AES.
- #[expect(dead_code)]
Aes = 0,
/// RSA3K.
#[default]
Rsa3k = 1,
}
-impl_from_enum_to_u8!(FalconModSelAlgo);
+impl_from_enum_to_bounded!(FalconModSelAlgo, 8);
// TODO[FPRI]: replace with `FromPrimitive`.
-impl TryFrom<u8> for FalconModSelAlgo {
+impl TryFrom<Bounded<u32, 8>> for FalconModSelAlgo {
type Error = Error;
- fn try_from(value: u8) -> Result<Self> {
- match value {
+ fn try_from(value: Bounded<u32, 8>) -> Result<Self> {
+ match value.get() {
+ 0 => Ok(FalconModSelAlgo::Aes),
1 => Ok(FalconModSelAlgo::Rsa3k),
_ => Err(EINVAL),
}
@@ -185,21 +183,20 @@ fn try_from(value: u8) -> Result<Self> {
}
/// Valid values for the `size` field of the [`crate::regs::NV_PFALCON_FALCON_DMATRFCMD`] register.
-#[repr(u8)]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
pub(crate) enum DmaTrfCmdSize {
/// 256 bytes transfer.
#[default]
Size256B = 0x6,
}
-impl_from_enum_to_u8!(DmaTrfCmdSize);
+impl_from_enum_to_bounded!(DmaTrfCmdSize, 3);
// TODO[FPRI]: replace with `FromPrimitive`.
-impl TryFrom<u8> for DmaTrfCmdSize {
+impl TryFrom<Bounded<u32, 3>> for DmaTrfCmdSize {
type Error = Error;
- fn try_from(value: u8) -> Result<Self> {
- match value {
+ fn try_from(value: Bounded<u32, 3>) -> Result<Self> {
+ match value.get() {
0x6 => Ok(Self::Size256B),
_ => Err(EINVAL),
}
@@ -215,25 +212,17 @@ pub(crate) enum PeregrineCoreSelect {
/// RISC-V core is active.
Riscv = 1,
}
+impl_from_enum_to_bounded!(PeregrineCoreSelect, 1);
-impl From<bool> for PeregrineCoreSelect {
- fn from(value: bool) -> Self {
- match value {
+impl From<Bounded<u32, 1>> for PeregrineCoreSelect {
+ fn from(value: Bounded<u32, 1>) -> Self {
+ match bool::from(value) {
false => PeregrineCoreSelect::Falcon,
true => PeregrineCoreSelect::Riscv,
}
}
}
-impl From<PeregrineCoreSelect> for bool {
- fn from(value: PeregrineCoreSelect) -> Self {
- match value {
- PeregrineCoreSelect::Falcon => false,
- PeregrineCoreSelect::Riscv => true,
- }
- }
-}
-
/// Different types of memory present in a falcon core.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum FalconMem {
@@ -257,14 +246,14 @@ pub(crate) enum FalconFbifTarget {
/// Non-coherent system memory (System DRAM).
NoncoherentSysmem = 2,
}
-impl_from_enum_to_u8!(FalconFbifTarget);
+impl_from_enum_to_bounded!(FalconFbifTarget, 2);
// TODO[FPRI]: replace with `FromPrimitive`.
-impl TryFrom<u8> for FalconFbifTarget {
+impl TryFrom<Bounded<u32, 2>> for FalconFbifTarget {
type Error = Error;
- fn try_from(value: u8) -> Result<Self> {
- let res = match value {
+ fn try_from(value: Bounded<u32, 2>) -> Result<Self> {
+ let res = match value.get() {
0 => Self::LocalFb,
1 => Self::CoherentSysmem,
2 => Self::NoncoherentSysmem,
@@ -284,26 +273,18 @@ pub(crate) enum FalconFbifMemType {
/// Physical memory addresses.
Physical = 1,
}
+impl_from_enum_to_bounded!(FalconFbifMemType, 1);
/// Conversion from a single-bit register field.
-impl From<bool> for FalconFbifMemType {
- fn from(value: bool) -> Self {
- match value {
+impl From<Bounded<u32, 1>> for FalconFbifMemType {
+ fn from(value: Bounded<u32, 1>) -> Self {
+ match bool::from(value) {
false => Self::Virtual,
true => Self::Physical,
}
}
}
-impl From<FalconFbifMemType> for bool {
- fn from(value: FalconFbifMemType) -> Self {
- match value {
- FalconFbifMemType::Virtual => false,
- FalconFbifMemType::Physical => true,
- }
- }
-}
-
/// Type used to represent the `PFALCON` registers address base for a given falcon engine.
pub(crate) struct PFalconBase(());
@@ -432,7 +413,7 @@ pub(crate) fn reset(&self, bar: &Bar0) -> Result {
self.reset_wait_mem_scrubbing(bar)?;
regs::NV_PFALCON_FALCON_RM::default()
- .set_value(regs::NV_PMC_BOOT_0::read(bar).into())
+ .set_value(regs::NV_PMC_BOOT_0::read(bar).as_raw())
.write(bar, &E::ID);
Ok(())
@@ -501,20 +482,18 @@ fn dma_wr<F: FalconFirmware<Target = E>>(
.set_base((dma_start >> 8) as u32)
.write(bar, &E::ID);
regs::NV_PFALCON_FALCON_DMATRFBASE1::default()
- // CAST: `as u16` is used on purpose since the remaining bits are guaranteed to fit
- // within a `u16`.
- .set_base((dma_start >> 40) as u16)
+ .try_set_base(dma_start >> 40)?
.write(bar, &E::ID);
let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default()
.set_size(DmaTrfCmdSize::Size256B)
.set_imem(target_mem == FalconMem::Imem)
- .set_sec(if sec { 1 } else { 0 });
+ .set_sec(sec);
for pos in (0..num_transfers).map(|i| i * DMA_LEN) {
// Perform a transfer of size `DMA_LEN`.
regs::NV_PFALCON_FALCON_DMATRFMOFFS::default()
- .set_offs(load_offsets.dst_start + pos)
+ .try_set_offs(load_offsets.dst_start + pos)?
.write(bar, &E::ID);
regs::NV_PFALCON_FALCON_DMATRFFBOFFS::default()
.set_offs(src_start + pos)
diff --git a/drivers/gpu/nova-core/falcon/gsp.rs b/drivers/gpu/nova-core/falcon/gsp.rs
index 67edef3636c1..dcdf3962ab0d 100644
--- a/drivers/gpu/nova-core/falcon/gsp.rs
+++ b/drivers/gpu/nova-core/falcon/gsp.rs
@@ -1,7 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
use kernel::{
- io::poll::read_poll_timeout,
+ io::{
+ poll::read_poll_timeout,
+ register::RegisterBase, //
+ },
prelude::*,
time::Delta, //
};
@@ -14,10 +17,7 @@
PFalcon2Base,
PFalconBase, //
},
- regs::{
- self,
- macros::RegisterBase, //
- },
+ regs,
};
/// Type specifying the `Gsp` falcon engine. Cannot be instantiated.
diff --git a/drivers/gpu/nova-core/falcon/hal/ga102.rs b/drivers/gpu/nova-core/falcon/hal/ga102.rs
index 69a7a95cac16..72afbd9101cf 100644
--- a/drivers/gpu/nova-core/falcon/hal/ga102.rs
+++ b/drivers/gpu/nova-core/falcon/hal/ga102.rs
@@ -59,7 +59,7 @@ fn signature_reg_fuse_version_ga102(
// `ucode_idx` is guaranteed to be in the range [0..15], making the `read` calls provable valid
// at build-time.
- let reg_fuse_version = if engine_id_mask & 0x0001 != 0 {
+ let reg_fuse_version: u16 = if engine_id_mask & 0x0001 != 0 {
regs::NV_FUSE_OPT_FPF_SEC2_UCODE1_VERSION::read(bar, ucode_idx).data()
} else if engine_id_mask & 0x0004 != 0 {
regs::NV_FUSE_OPT_FPF_NVDEC_UCODE1_VERSION::read(bar, ucode_idx).data()
@@ -68,7 +68,8 @@ fn signature_reg_fuse_version_ga102(
} else {
dev_err!(dev, "unexpected engine_id_mask {:#x}", engine_id_mask);
return Err(EINVAL);
- };
+ }
+ .into();
// TODO[NUMM]: replace with `last_set_bit` once it lands.
Ok(u16::BITS - reg_fuse_version.leading_zeros())
diff --git a/drivers/gpu/nova-core/falcon/sec2.rs b/drivers/gpu/nova-core/falcon/sec2.rs
index b57d362e576a..5d836e2d17dd 100644
--- a/drivers/gpu/nova-core/falcon/sec2.rs
+++ b/drivers/gpu/nova-core/falcon/sec2.rs
@@ -1,12 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
-use crate::{
- falcon::{
- FalconEngine,
- PFalcon2Base,
- PFalconBase, //
- },
- regs::macros::RegisterBase,
+use kernel::io::register::RegisterBase;
+
+use crate::falcon::{
+ FalconEngine,
+ PFalcon2Base,
+ PFalconBase, //
};
/// Type specifying the `Sec2` falcon engine. Cannot be instantiated.
diff --git a/drivers/gpu/nova-core/fb/hal/ga100.rs b/drivers/gpu/nova-core/fb/hal/ga100.rs
index e0acc41aa7cd..acf46ad0dba1 100644
--- a/drivers/gpu/nova-core/fb/hal/ga100.rs
+++ b/drivers/gpu/nova-core/fb/hal/ga100.rs
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
-use kernel::prelude::*;
+use kernel::{
+ num::Bounded,
+ prelude::*, //
+};
use crate::{
driver::Bar0,
@@ -20,9 +23,7 @@ pub(super) fn read_sysmem_flush_page_ga100(bar: &Bar0) -> u64 {
pub(super) fn write_sysmem_flush_page_ga100(bar: &Bar0, addr: u64) {
regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI::default()
- // CAST: `as u32` is used on purpose since the remaining bits are guaranteed to fit within
- // a `u32`.
- .set_adr_63_40((addr >> FLUSH_SYSMEM_ADDR_SHIFT_HI) as u32)
+ .set_adr_63_40(Bounded::from_expr(addr >> FLUSH_SYSMEM_ADDR_SHIFT_HI).cast())
.write(bar);
regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::default()
// CAST: `as u32` is used on purpose since we want to strip the upper bits that have been
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 629c9d2dc994..556b2454b5a6 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -4,6 +4,7 @@
device,
devres::Devres,
fmt,
+ num::Bounded,
pci,
prelude::*,
sync::Arc, //
@@ -135,11 +136,11 @@ pub(crate) enum Architecture {
Ada = 0x19,
}
-impl TryFrom<u8> for Architecture {
+impl TryFrom<Bounded<u32, 6>> for Architecture {
type Error = Error;
- fn try_from(value: u8) -> Result<Self> {
- match value {
+ fn try_from(value: Bounded<u32, 6>) -> Result<Self> {
+ match u8::from(value) {
0x16 => Ok(Self::Turing),
0x17 => Ok(Self::Ampere),
0x19 => Ok(Self::Ada),
@@ -148,23 +149,26 @@ fn try_from(value: u8) -> Result<Self> {
}
}
-impl From<Architecture> for u8 {
+impl From<Architecture> for Bounded<u32, 6> {
fn from(value: Architecture) -> Self {
- // CAST: `Architecture` is `repr(u8)`, so this cast is always lossless.
- value as u8
+ match value {
+ Architecture::Turing => Bounded::<u8, 6>::new::<0x16>().cast(),
+ Architecture::Ampere => Bounded::<u8, 6>::new::<0x17>().cast(),
+ Architecture::Ada => Bounded::<u8, 6>::new::<0x19>().cast(),
+ }
}
}
pub(crate) struct Revision {
- major: u8,
- minor: u8,
+ major: Bounded<u8, 4>,
+ minor: Bounded<u8, 4>,
}
impl From<regs::NV_PMC_BOOT_42> for Revision {
fn from(boot0: regs::NV_PMC_BOOT_42) -> Self {
Self {
- major: boot0.major_revision(),
- minor: boot0.minor_revision(),
+ major: boot0.major_revision().cast(),
+ minor: boot0.minor_revision().cast(),
}
}
}
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 6f946d14868a..baa36139017b 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -476,7 +476,7 @@ fn calculate_checksum<T: Iterator<Item = u8>>(it: T) -> u32 {
/// Notifies the GSP that we have updated the command queue pointers.
fn notify_gsp(bar: &Bar0) {
regs::NV_PGSP_QUEUE_HEAD::default()
- .set_address(0)
+ .set_address(0u32)
.write(bar);
}
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index abffd6beec65..91beeaca0e9e 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -9,6 +9,7 @@
use core::ops::Range;
use kernel::{
+ bitfield,
dma::CoherentAllocation,
fmt,
prelude::*,
@@ -762,8 +763,8 @@ unsafe impl AsBytes for MsgqRxHeader {}
bitfield! {
struct MsgHeaderVersion(u32) {
- 31:24 major as u8;
- 23:16 minor as u8;
+ 31:24 major;
+ 23:16 minor;
}
}
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index b98a1c03f13d..92dd38e21c2c 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -2,9 +2,6 @@
//! Nova Core GPU Driver
-#[macro_use]
-mod bitfield;
-
mod dma;
mod driver;
mod falcon;
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 82cc6c0790e5..794401122f06 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -1,12 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-// Required to retain the original register names used by OpenRM, which are all capital snake case
-// but are mapped to types.
-#![allow(non_camel_case_types)]
-
-#[macro_use]
-pub(crate) mod macros;
-
use kernel::prelude::*;
use crate::{
@@ -29,20 +22,28 @@
num::FromSafeCast,
};
+// All nova-core registers are 32-bit and `pub(crate)`. Wrap the `register!` macro to avoid
+// repeating this information for every register.
+macro_rules! nv_reg {
+ ($(#[$attr:meta])* $name:ident $($tail:tt)*) => {
+ ::kernel::register!($(#[$attr])* pub(crate) $name(u32) $($tail)*);
+ };
+}
+
// PMC
-register!(NV_PMC_BOOT_0 @ 0x00000000, "Basic revision information about the GPU" {
- 3:0 minor_revision as u8, "Minor revision of the chip";
- 7:4 major_revision as u8, "Major revision of the chip";
- 8:8 architecture_1 as u8, "MSB of the architecture";
- 23:20 implementation as u8, "Implementation version of the architecture";
- 28:24 architecture_0 as u8, "Lower bits of the architecture";
+nv_reg!(NV_PMC_BOOT_0 @ 0x00000000, "Basic revision information about the GPU" {
+ 3:0 minor_revision, "Minor revision of the chip";
+ 7:4 major_revision, "Major revision of the chip";
+ 8:8 architecture_1, "MSB of the architecture";
+ 23:20 implementation, "Implementation version of the architecture";
+ 28:24 architecture_0, "Lower bits of the architecture";
});
impl NV_PMC_BOOT_0 {
pub(crate) fn is_older_than_fermi(self) -> bool {
// From https://github.com/NVIDIA/open-gpu-doc/tree/master/manuals :
- const NV_PMC_BOOT_0_ARCHITECTURE_GF100: u8 = 0xc;
+ const NV_PMC_BOOT_0_ARCHITECTURE_GF100: u32 = 0xc;
// Older chips left arch1 zeroed out. That, combined with an arch0 value that is less than
// GF100, means "older than Fermi".
@@ -50,11 +51,11 @@ pub(crate) fn is_older_than_fermi(self) -> bool {
}
}
-register!(NV_PMC_BOOT_42 @ 0x00000a00, "Extended architecture information" {
- 15:12 minor_revision as u8, "Minor revision of the chip";
- 19:16 major_revision as u8, "Major revision of the chip";
- 23:20 implementation as u8, "Implementation version of the architecture";
- 29:24 architecture as u8 ?=> Architecture, "Architecture value";
+nv_reg!(NV_PMC_BOOT_42 @ 0x00000a00, "Extended architecture information" {
+ 15:12 minor_revision, "Minor revision of the chip";
+ 19:16 major_revision, "Major revision of the chip";
+ 23:20 implementation, "Implementation version of the architecture";
+ 29:24 architecture ?=> Architecture, "Architecture value";
});
impl NV_PMC_BOOT_42 {
@@ -89,11 +90,11 @@ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result {
// PBUS
-register!(NV_PBUS_SW_SCRATCH @ 0x00001400[64] {});
+nv_reg!(NV_PBUS_SW_SCRATCH @ 0x00001400[64] {});
-register!(NV_PBUS_SW_SCRATCH_0E_FRTS_ERR => NV_PBUS_SW_SCRATCH[0xe],
+nv_reg!(NV_PBUS_SW_SCRATCH_0E_FRTS_ERR => NV_PBUS_SW_SCRATCH[0xe],
"scratch register 0xe used as FRTS firmware error code" {
- 31:16 frts_err_code as u16;
+ 31:16 frts_err_code;
});
// PFB
@@ -101,22 +102,22 @@ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result {
// The following two registers together hold the physical system memory address that is used by the
// GPU to perform sysmembar operations (see `fb::SysmemFlush`).
-register!(NV_PFB_NISO_FLUSH_SYSMEM_ADDR @ 0x00100c10 {
- 31:0 adr_39_08 as u32;
+nv_reg!(NV_PFB_NISO_FLUSH_SYSMEM_ADDR @ 0x00100c10 {
+ 31:0 adr_39_08;
});
-register!(NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI @ 0x00100c40 {
- 23:0 adr_63_40 as u32;
+nv_reg!(NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI @ 0x00100c40 {
+ 23:0 adr_63_40;
});
-register!(NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE @ 0x00100ce0 {
- 3:0 lower_scale as u8;
- 9:4 lower_mag as u8;
- 30:30 ecc_mode_enabled as bool;
+nv_reg!(NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE @ 0x00100ce0 {
+ 3:0 lower_scale;
+ 9:4 lower_mag;
+ 30:30 ecc_mode_enabled => bool;
});
-register!(NV_PGSP_QUEUE_HEAD @ 0x00110c00 {
- 31:0 address as u32;
+nv_reg!(NV_PGSP_QUEUE_HEAD @ 0x00110c00 {
+ 31:0 address;
});
impl NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE {
@@ -134,8 +135,8 @@ pub(crate) fn usable_fb_size(self) -> u64 {
}
}
-register!(NV_PFB_PRI_MMU_WPR2_ADDR_LO@0x001fa824 {
- 31:4 lo_val as u32, "Bits 12..40 of the lower (inclusive) bound of the WPR2 region";
+nv_reg!(NV_PFB_PRI_MMU_WPR2_ADDR_LO@0x001fa824 {
+ 31:4 lo_val, "Bits 12..40 of the lower (inclusive) bound of the WPR2 region";
});
impl NV_PFB_PRI_MMU_WPR2_ADDR_LO {
@@ -145,8 +146,8 @@ pub(crate) fn lower_bound(self) -> u64 {
}
}
-register!(NV_PFB_PRI_MMU_WPR2_ADDR_HI@0x001fa828 {
- 31:4 hi_val as u32, "Bits 12..40 of the higher (exclusive) bound of the WPR2 region";
+nv_reg!(NV_PFB_PRI_MMU_WPR2_ADDR_HI@0x001fa828 {
+ 31:4 hi_val, "Bits 12..40 of the higher (exclusive) bound of the WPR2 region";
});
impl NV_PFB_PRI_MMU_WPR2_ADDR_HI {
@@ -169,25 +170,25 @@ pub(crate) fn higher_bound(self) -> u64 {
// Boot Sequence Interface (BSI) register used to determine
// if GSP reload/resume has completed during the boot process.
-register!(NV_PGC6_BSI_SECURE_SCRATCH_14 @ 0x001180f8 {
- 26:26 boot_stage_3_handoff as bool;
+nv_reg!(NV_PGC6_BSI_SECURE_SCRATCH_14 @ 0x001180f8 {
+ 26:26 boot_stage_3_handoff => bool;
});
// Privilege level mask register. It dictates whether the host CPU has privilege to access the
// `PGC6_AON_SECURE_SCRATCH_GROUP_05` register (which it needs to read GFW_BOOT).
-register!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK @ 0x00118128,
+nv_reg!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK @ 0x00118128,
"Privilege level mask register" {
- 0:0 read_protection_level0 as bool, "Set after FWSEC lowers its protection level";
+ 0:0 read_protection_level0 => bool, "Set after FWSEC lowers its protection level";
});
// OpenRM defines this as a register array, but doesn't specify its size and only uses its first
// element. Be conservative until we know the actual size or need to use more registers.
-register!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_05 @ 0x00118234[1] {});
+nv_reg!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_05 @ 0x00118234[1] {});
-register!(
+nv_reg!(
NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT => NV_PGC6_AON_SECURE_SCRATCH_GROUP_05[0],
"Scratch group 05 register 0 used as GFW boot progress indicator" {
- 7:0 progress as u8, "Progress of GFW boot (0xff means completed)";
+ 7:0 progress, "Progress of GFW boot (0xff means completed)";
}
);
@@ -198,14 +199,14 @@ pub(crate) fn completed(self) -> bool {
}
}
-register!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_42 @ 0x001183a4 {
- 31:0 value as u32;
+nv_reg!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_42 @ 0x001183a4 {
+ 31:0 value;
});
-register!(
+nv_reg!(
NV_USABLE_FB_SIZE_IN_MB => NV_PGC6_AON_SECURE_SCRATCH_GROUP_42,
"Scratch group 42 register used as framebuffer size" {
- 31:0 value as u32, "Usable framebuffer size, in megabytes";
+ 31:0 value, "Usable framebuffer size, in megabytes";
}
);
@@ -218,9 +219,9 @@ pub(crate) fn usable_fb_size(self) -> u64 {
// PDISP
-register!(NV_PDISP_VGA_WORKSPACE_BASE @ 0x00625f04 {
- 3:3 status_valid as bool, "Set if the `addr` field is valid";
- 31:8 addr as u32, "VGA workspace base address divided by 0x10000";
+nv_reg!(NV_PDISP_VGA_WORKSPACE_BASE @ 0x00625f04 {
+ 3:3 status_valid => bool, "Set if the `addr` field is valid";
+ 31:8 addr, "VGA workspace base address divided by 0x10000";
});
impl NV_PDISP_VGA_WORKSPACE_BASE {
@@ -238,47 +239,47 @@ pub(crate) fn vga_workspace_addr(self) -> Option<u64> {
pub(crate) const NV_FUSE_OPT_FPF_SIZE: usize = 16;
-register!(NV_FUSE_OPT_FPF_NVDEC_UCODE1_VERSION @ 0x00824100[NV_FUSE_OPT_FPF_SIZE] {
- 15:0 data as u16;
+nv_reg!(NV_FUSE_OPT_FPF_NVDEC_UCODE1_VERSION @ 0x00824100[NV_FUSE_OPT_FPF_SIZE] {
+ 15:0 data;
});
-register!(NV_FUSE_OPT_FPF_SEC2_UCODE1_VERSION @ 0x00824140[NV_FUSE_OPT_FPF_SIZE] {
- 15:0 data as u16;
+nv_reg!(NV_FUSE_OPT_FPF_SEC2_UCODE1_VERSION @ 0x00824140[NV_FUSE_OPT_FPF_SIZE] {
+ 15:0 data;
});
-register!(NV_FUSE_OPT_FPF_GSP_UCODE1_VERSION @ 0x008241c0[NV_FUSE_OPT_FPF_SIZE] {
- 15:0 data as u16;
+nv_reg!(NV_FUSE_OPT_FPF_GSP_UCODE1_VERSION @ 0x008241c0[NV_FUSE_OPT_FPF_SIZE] {
+ 15:0 data;
});
// PFALCON
-register!(NV_PFALCON_FALCON_IRQSCLR @ PFalconBase[0x00000004] {
- 4:4 halt as bool;
- 6:6 swgen0 as bool;
+nv_reg!(NV_PFALCON_FALCON_IRQSCLR @ PFalconBase[0x00000004] {
+ 4:4 halt => bool;
+ 6:6 swgen0 => bool;
});
-register!(NV_PFALCON_FALCON_MAILBOX0 @ PFalconBase[0x00000040] {
- 31:0 value as u32;
+nv_reg!(NV_PFALCON_FALCON_MAILBOX0 @ PFalconBase[0x00000040] {
+ 31:0 value => u32;
});
-register!(NV_PFALCON_FALCON_MAILBOX1 @ PFalconBase[0x00000044] {
- 31:0 value as u32;
+nv_reg!(NV_PFALCON_FALCON_MAILBOX1 @ PFalconBase[0x00000044] {
+ 31:0 value => u32;
});
// Used to store version information about the firmware running
// on the Falcon processor.
-register!(NV_PFALCON_FALCON_OS @ PFalconBase[0x00000080] {
- 31:0 value as u32;
+nv_reg!(NV_PFALCON_FALCON_OS @ PFalconBase[0x00000080] {
+ 31:0 value => u32;
});
-register!(NV_PFALCON_FALCON_RM @ PFalconBase[0x00000084] {
- 31:0 value as u32;
+nv_reg!(NV_PFALCON_FALCON_RM @ PFalconBase[0x00000084] {
+ 31:0 value => u32;
});
-register!(NV_PFALCON_FALCON_HWCFG2 @ PFalconBase[0x000000f4] {
- 10:10 riscv as bool;
- 12:12 mem_scrubbing as bool, "Set to 0 after memory scrubbing is completed";
- 31:31 reset_ready as bool, "Signal indicating that reset is completed (GA102+)";
+nv_reg!(NV_PFALCON_FALCON_HWCFG2 @ PFalconBase[0x000000f4] {
+ 10:10 riscv => bool;
+ 12:12 mem_scrubbing => bool, "Set to 0 after memory scrubbing is completed";
+ 31:31 reset_ready => bool, "Signal indicating that reset is completed (GA102+)";
});
impl NV_PFALCON_FALCON_HWCFG2 {
@@ -288,107 +289,107 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
}
}
-register!(NV_PFALCON_FALCON_CPUCTL @ PFalconBase[0x00000100] {
- 1:1 startcpu as bool;
- 4:4 halted as bool;
- 6:6 alias_en as bool;
+nv_reg!(NV_PFALCON_FALCON_CPUCTL @ PFalconBase[0x00000100] {
+ 1:1 startcpu => bool;
+ 4:4 halted => bool;
+ 6:6 alias_en => bool;
});
-register!(NV_PFALCON_FALCON_BOOTVEC @ PFalconBase[0x00000104] {
- 31:0 value as u32;
+nv_reg!(NV_PFALCON_FALCON_BOOTVEC @ PFalconBase[0x00000104] {
+ 31:0 value => u32;
});
-register!(NV_PFALCON_FALCON_DMACTL @ PFalconBase[0x0000010c] {
- 0:0 require_ctx as bool;
- 1:1 dmem_scrubbing as bool;
- 2:2 imem_scrubbing as bool;
- 6:3 dmaq_num as u8;
- 7:7 secure_stat as bool;
+nv_reg!(NV_PFALCON_FALCON_DMACTL @ PFalconBase[0x0000010c] {
+ 0:0 require_ctx => bool;
+ 1:1 dmem_scrubbing => bool;
+ 2:2 imem_scrubbing => bool;
+ 6:3 dmaq_num;
+ 7:7 secure_stat => bool;
});
-register!(NV_PFALCON_FALCON_DMATRFBASE @ PFalconBase[0x00000110] {
- 31:0 base as u32;
+nv_reg!(NV_PFALCON_FALCON_DMATRFBASE @ PFalconBase[0x00000110] {
+ 31:0 base => u32;
});
-register!(NV_PFALCON_FALCON_DMATRFMOFFS @ PFalconBase[0x00000114] {
- 23:0 offs as u32;
+nv_reg!(NV_PFALCON_FALCON_DMATRFMOFFS @ PFalconBase[0x00000114] {
+ 23:0 offs;
});
-register!(NV_PFALCON_FALCON_DMATRFCMD @ PFalconBase[0x00000118] {
- 0:0 full as bool;
- 1:1 idle as bool;
- 3:2 sec as u8;
- 4:4 imem as bool;
- 5:5 is_write as bool;
- 10:8 size as u8 ?=> DmaTrfCmdSize;
- 14:12 ctxdma as u8;
- 16:16 set_dmtag as u8;
+nv_reg!(NV_PFALCON_FALCON_DMATRFCMD @ PFalconBase[0x00000118] {
+ 0:0 full => bool;
+ 1:1 idle => bool;
+ 3:2 sec;
+ 4:4 imem => bool;
+ 5:5 is_write => bool;
+ 10:8 size ?=> DmaTrfCmdSize;
+ 14:12 ctxdma;
+ 16:16 set_dmtag;
});
-register!(NV_PFALCON_FALCON_DMATRFFBOFFS @ PFalconBase[0x0000011c] {
- 31:0 offs as u32;
+nv_reg!(NV_PFALCON_FALCON_DMATRFFBOFFS @ PFalconBase[0x0000011c] {
+ 31:0 offs => u32;
});
-register!(NV_PFALCON_FALCON_DMATRFBASE1 @ PFalconBase[0x00000128] {
- 8:0 base as u16;
+nv_reg!(NV_PFALCON_FALCON_DMATRFBASE1 @ PFalconBase[0x00000128] {
+ 8:0 base;
});
-register!(NV_PFALCON_FALCON_HWCFG1 @ PFalconBase[0x0000012c] {
- 3:0 core_rev as u8 ?=> FalconCoreRev, "Core revision";
- 5:4 security_model as u8 ?=> FalconSecurityModel, "Security model";
- 7:6 core_rev_subversion as u8 ?=> FalconCoreRevSubversion, "Core revision subversion";
+nv_reg!(NV_PFALCON_FALCON_HWCFG1 @ PFalconBase[0x0000012c] {
+ 3:0 core_rev ?=> FalconCoreRev, "Core revision";
+ 5:4 security_model ?=> FalconSecurityModel, "Security model";
+ 7:6 core_rev_subversion => FalconCoreRevSubversion, "Core revision subversion";
});
-register!(NV_PFALCON_FALCON_CPUCTL_ALIAS @ PFalconBase[0x00000130] {
- 1:1 startcpu as bool;
+nv_reg!(NV_PFALCON_FALCON_CPUCTL_ALIAS @ PFalconBase[0x00000130] {
+ 1:1 startcpu => bool;
});
// Actually known as `NV_PSEC_FALCON_ENGINE` and `NV_PGSP_FALCON_ENGINE` depending on the falcon
// instance.
-register!(NV_PFALCON_FALCON_ENGINE @ PFalconBase[0x000003c0] {
- 0:0 reset as bool;
+nv_reg!(NV_PFALCON_FALCON_ENGINE @ PFalconBase[0x000003c0] {
+ 0:0 reset => bool;
});
-register!(NV_PFALCON_FBIF_TRANSCFG @ PFalconBase[0x00000600[8]] {
- 1:0 target as u8 ?=> FalconFbifTarget;
- 2:2 mem_type as bool => FalconFbifMemType;
+nv_reg!(NV_PFALCON_FBIF_TRANSCFG @ PFalconBase[0x00000600[8]] {
+ 1:0 target ?=> FalconFbifTarget;
+ 2:2 mem_type => FalconFbifMemType;
});
-register!(NV_PFALCON_FBIF_CTL @ PFalconBase[0x00000624] {
- 7:7 allow_phys_no_ctx as bool;
+nv_reg!(NV_PFALCON_FBIF_CTL @ PFalconBase[0x00000624] {
+ 7:7 allow_phys_no_ctx => bool;
});
/* PFALCON2 */
-register!(NV_PFALCON2_FALCON_MOD_SEL @ PFalcon2Base[0x00000180] {
- 7:0 algo as u8 ?=> FalconModSelAlgo;
+nv_reg!(NV_PFALCON2_FALCON_MOD_SEL @ PFalcon2Base[0x00000180] {
+ 7:0 algo ?=> FalconModSelAlgo;
});
-register!(NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID @ PFalcon2Base[0x00000198] {
- 7:0 ucode_id as u8;
+nv_reg!(NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID @ PFalcon2Base[0x00000198] {
+ 7:0 ucode_id => u8;
});
-register!(NV_PFALCON2_FALCON_BROM_ENGIDMASK @ PFalcon2Base[0x0000019c] {
- 31:0 value as u32;
+nv_reg!(NV_PFALCON2_FALCON_BROM_ENGIDMASK @ PFalcon2Base[0x0000019c] {
+ 31:0 value => u32;
});
// OpenRM defines this as a register array, but doesn't specify its size and only uses its first
// element. Be conservative until we know the actual size or need to use more registers.
-register!(NV_PFALCON2_FALCON_BROM_PARAADDR @ PFalcon2Base[0x00000210[1]] {
- 31:0 value as u32;
+nv_reg!(NV_PFALCON2_FALCON_BROM_PARAADDR @ PFalcon2Base[0x00000210[1]] {
+ 31:0 value => u32;
});
// PRISCV
-register!(NV_PRISCV_RISCV_CPUCTL @ PFalcon2Base[0x00000388] {
- 0:0 halted as bool;
- 7:7 active_stat as bool;
+nv_reg!(NV_PRISCV_RISCV_CPUCTL @ PFalcon2Base[0x00000388] {
+ 0:0 halted => bool;
+ 7:7 active_stat => bool;
});
-register!(NV_PRISCV_RISCV_BCR_CTRL @ PFalcon2Base[0x00000668] {
- 0:0 valid as bool;
- 4:4 core_select as bool => PeregrineCoreSelect;
- 8:8 br_fetch as bool;
+nv_reg!(NV_PRISCV_RISCV_BCR_CTRL @ PFalconBase[0x00001668] {
+ 0:0 valid => bool;
+ 4:4 core_select => PeregrineCoreSelect;
+ 8:8 br_fetch => bool;
});
// The modules below provide registers that are not identical on all supported chips. They should
@@ -397,15 +398,15 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
pub(crate) mod gm107 {
// FUSE
- register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00021c04 {
- 0:0 display_disabled as bool;
+ nv_reg!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00021c04 {
+ 0:0 display_disabled => bool;
});
}
pub(crate) mod ga100 {
// FUSE
- register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00820c04 {
- 0:0 display_disabled as bool;
+ nv_reg!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00820c04 {
+ 0:0 display_disabled => bool;
});
}
diff --git a/drivers/gpu/nova-core/regs/macros.rs b/drivers/gpu/nova-core/regs/macros.rs
deleted file mode 100644
index fd1a815fa57d..000000000000
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ /dev/null
@@ -1,721 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-//! `register!` macro to define register layout and accessors.
-//!
-//! A single register typically includes several fields, which are accessed through a combination
-//! of bit-shift and mask operations that introduce a class of potential mistakes, notably because
-//! not all possible field values are necessarily valid.
-//!
-//! The `register!` macro in this module provides an intuitive and readable syntax for defining a
-//! dedicated type for each register. Each such type comes with its own field accessors that can
-//! return an error if a field's value is invalid. Please look at the [`bitfield`] macro for the
-//! complete syntax of fields definitions.
-
-/// Trait providing a base address to be added to the offset of a relative register to obtain
-/// its actual offset.
-///
-/// The `T` generic argument is used to distinguish which base to use, in case a type provides
-/// several bases. It is given to the `register!` macro to restrict the use of the register to
-/// implementors of this particular variant.
-pub(crate) trait RegisterBase<T> {
- const BASE: usize;
-}
-
-/// Defines a dedicated type for a register with an absolute offset, including getter and setter
-/// methods for its fields and methods to read and write it from an `Io` region.
-///
-/// Example:
-///
-/// ```no_run
-/// register!(BOOT_0 @ 0x00000100, "Basic revision information about the GPU" {
-/// 3:0 minor_revision as u8, "Minor revision of the chip";
-/// 7:4 major_revision as u8, "Major revision of the chip";
-/// 28:20 chipset as u32 ?=> Chipset, "Chipset model";
-/// });
-/// ```
-///
-/// This defines a `BOOT_0` type which can be read or written from offset `0x100` of an `Io`
-/// region. It is composed of 3 fields, for instance `minor_revision` is made of the 4 least
-/// significant bits of the register. Each field can be accessed and modified using accessor
-/// methods:
-///
-/// ```no_run
-/// // Read from the register's defined offset (0x100).
-/// let boot0 = BOOT_0::read(&bar);
-/// pr_info!("chip revision: {}.{}", boot0.major_revision(), boot0.minor_revision());
-///
-/// // `Chipset::try_from` is called with the value of the `chipset` field and returns an
-/// // error if it is invalid.
-/// let chipset = boot0.chipset()?;
-///
-/// // Update some fields and write the value back.
-/// boot0.set_major_revision(3).set_minor_revision(10).write(&bar);
-///
-/// // Or, just read and update the register in a single step:
-/// BOOT_0::update(&bar, |r| r.set_major_revision(3).set_minor_revision(10));
-/// ```
-///
-/// The documentation strings are optional. If present, they will be added to the type's
-/// definition, or the field getter and setter methods they are attached to.
-///
-/// It is also possible to create a alias register by using the `=> ALIAS` syntax. This is useful
-/// for cases where a register's interpretation depends on the context:
-///
-/// ```no_run
-/// register!(SCRATCH @ 0x00000200, "Scratch register" {
-/// 31:0 value as u32, "Raw value";
-/// });
-///
-/// register!(SCRATCH_BOOT_STATUS => SCRATCH, "Boot status of the firmware" {
-/// 0:0 completed as bool, "Whether the firmware has completed booting";
-/// });
-/// ```
-///
-/// In this example, `SCRATCH_0_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while also
-/// providing its own `completed` field.
-///
-/// ## Relative registers
-///
-/// A register can be defined as being accessible from a fixed offset of a provided base. For
-/// instance, imagine the following I/O space:
-///
-/// ```text
-/// +-----------------------------+
-/// | ... |
-/// | |
-/// 0x100--->+------------CPU0-------------+
-/// | |
-/// 0x110--->+-----------------------------+
-/// | CPU_CTL |
-/// +-----------------------------+
-/// | ... |
-/// | |
-/// | |
-/// 0x200--->+------------CPU1-------------+
-/// | |
-/// 0x210--->+-----------------------------+
-/// | CPU_CTL |
-/// +-----------------------------+
-/// | ... |
-/// +-----------------------------+
-/// ```
-///
-/// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O
-/// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define
-/// them twice and would prefer a way to select which one to use from a single definition
-///
-/// This can be done using the `Base[Offset]` syntax when specifying the register's address.
-///
-/// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the
-/// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for
-/// this register needs to implement `RegisterBase<Base>`. Here is the above example translated
-/// into code:
-///
-/// ```no_run
-/// // Type used to identify the base.
-/// pub(crate) struct CpuCtlBase;
-///
-/// // ZST describing `CPU0`.
-/// struct Cpu0;
-/// impl RegisterBase<CpuCtlBase> for Cpu0 {
-/// const BASE: usize = 0x100;
-/// }
-/// // Singleton of `CPU0` used to identify it.
-/// const CPU0: Cpu0 = Cpu0;
-///
-/// // ZST describing `CPU1`.
-/// struct Cpu1;
-/// impl RegisterBase<CpuCtlBase> for Cpu1 {
-/// const BASE: usize = 0x200;
-/// }
-/// // Singleton of `CPU1` used to identify it.
-/// const CPU1: Cpu1 = Cpu1;
-///
-/// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`.
-/// register!(CPU_CTL @ CpuCtlBase[0x10], "CPU core control" {
-/// 0:0 start as bool, "Start the CPU core";
-/// });
-///
-/// // The `read`, `write` and `update` methods of relative registers take an extra `base` argument
-/// // that is used to resolve its final address by adding its `BASE` to the offset of the
-/// // register.
-///
-/// // Start `CPU0`.
-/// CPU_CTL::update(bar, &CPU0, |r| r.set_start(true));
-///
-/// // Start `CPU1`.
-/// CPU_CTL::update(bar, &CPU1, |r| r.set_start(true));
-///
-/// // Aliases can also be defined for relative register.
-/// register!(CPU_CTL_ALIAS => CpuCtlBase[CPU_CTL], "Alias to CPU core control" {
-/// 1:1 alias_start as bool, "Start the aliased CPU core";
-/// });
-///
-/// // Start the aliased `CPU0`.
-/// CPU_CTL_ALIAS::update(bar, &CPU0, |r| r.set_alias_start(true));
-/// ```
-///
-/// ## Arrays of registers
-///
-/// Some I/O areas contain consecutive values that can be interpreted in the same way. These areas
-/// can be defined as an array of identical registers, allowing them to be accessed by index with
-/// compile-time or runtime bound checking. Simply define their address as `Address[Size]`, and add
-/// an `idx` parameter to their `read`, `write` and `update` methods:
-///
-/// ```no_run
-/// # fn no_run() -> Result<(), Error> {
-/// # fn get_scratch_idx() -> usize {
-/// # 0x15
-/// # }
-/// // Array of 64 consecutive registers with the same layout starting at offset `0x80`.
-/// register!(SCRATCH @ 0x00000080[64], "Scratch registers" {
-/// 31:0 value as u32;
-/// });
-///
-/// // Read scratch register 0, i.e. I/O address `0x80`.
-/// let scratch_0 = SCRATCH::read(bar, 0).value();
-/// // Read scratch register 15, i.e. I/O address `0x80 + (15 * 4)`.
-/// let scratch_15 = SCRATCH::read(bar, 15).value();
-///
-/// // This is out of bounds and won't build.
-/// // let scratch_128 = SCRATCH::read(bar, 128).value();
-///
-/// // Runtime-obtained array index.
-/// let scratch_idx = get_scratch_idx();
-/// // Access on a runtime index returns an error if it is out-of-bounds.
-/// let some_scratch = SCRATCH::try_read(bar, scratch_idx)?.value();
-///
-/// // Alias to a particular register in an array.
-/// // Here `SCRATCH[8]` is used to convey the firmware exit code.
-/// register!(FIRMWARE_STATUS => SCRATCH[8], "Firmware exit status code" {
-/// 7:0 status as u8;
-/// });
-///
-/// let status = FIRMWARE_STATUS::read(bar).status();
-///
-/// // Non-contiguous register arrays can be defined by adding a stride parameter.
-/// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the
-/// // registers of the two declarations below are interleaved.
-/// register!(SCRATCH_INTERLEAVED_0 @ 0x000000c0[16 ; 8], "Scratch registers bank 0" {
-/// 31:0 value as u32;
-/// });
-/// register!(SCRATCH_INTERLEAVED_1 @ 0x000000c4[16 ; 8], "Scratch registers bank 1" {
-/// 31:0 value as u32;
-/// });
-/// # Ok(())
-/// # }
-/// ```
-///
-/// ## Relative arrays of registers
-///
-/// Combining the two features described in the sections above, arrays of registers accessible from
-/// a base can also be defined:
-///
-/// ```no_run
-/// # fn no_run() -> Result<(), Error> {
-/// # fn get_scratch_idx() -> usize {
-/// # 0x15
-/// # }
-/// // Type used as parameter of `RegisterBase` to specify the base.
-/// pub(crate) struct CpuCtlBase;
-///
-/// // ZST describing `CPU0`.
-/// struct Cpu0;
-/// impl RegisterBase<CpuCtlBase> for Cpu0 {
-/// const BASE: usize = 0x100;
-/// }
-/// // Singleton of `CPU0` used to identify it.
-/// const CPU0: Cpu0 = Cpu0;
-///
-/// // ZST describing `CPU1`.
-/// struct Cpu1;
-/// impl RegisterBase<CpuCtlBase> for Cpu1 {
-/// const BASE: usize = 0x200;
-/// }
-/// // Singleton of `CPU1` used to identify it.
-/// const CPU1: Cpu1 = Cpu1;
-///
-/// // 64 per-cpu scratch registers, arranged as an contiguous array.
-/// register!(CPU_SCRATCH @ CpuCtlBase[0x00000080[64]], "Per-CPU scratch registers" {
-/// 31:0 value as u32;
-/// });
-///
-/// let cpu0_scratch_0 = CPU_SCRATCH::read(bar, &Cpu0, 0).value();
-/// let cpu1_scratch_15 = CPU_SCRATCH::read(bar, &Cpu1, 15).value();
-///
-/// // This won't build.
-/// // let cpu0_scratch_128 = CPU_SCRATCH::read(bar, &Cpu0, 128).value();
-///
-/// // Runtime-obtained array index.
-/// let scratch_idx = get_scratch_idx();
-/// // Access on a runtime value returns an error if it is out-of-bounds.
-/// let cpu0_some_scratch = CPU_SCRATCH::try_read(bar, &Cpu0, scratch_idx)?.value();
-///
-/// // `SCRATCH[8]` is used to convey the firmware exit code.
-/// register!(CPU_FIRMWARE_STATUS => CpuCtlBase[CPU_SCRATCH[8]],
-/// "Per-CPU firmware exit status code" {
-/// 7:0 status as u8;
-/// });
-///
-/// let cpu0_status = CPU_FIRMWARE_STATUS::read(bar, &Cpu0).status();
-///
-/// // Non-contiguous register arrays can be defined by adding a stride parameter.
-/// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the
-/// // registers of the two declarations below are interleaved.
-/// register!(CPU_SCRATCH_INTERLEAVED_0 @ CpuCtlBase[0x00000d00[16 ; 8]],
-/// "Scratch registers bank 0" {
-/// 31:0 value as u32;
-/// });
-/// register!(CPU_SCRATCH_INTERLEAVED_1 @ CpuCtlBase[0x00000d04[16 ; 8]],
-/// "Scratch registers bank 1" {
-/// 31:0 value as u32;
-/// });
-/// # Ok(())
-/// # }
-/// ```
-macro_rules! register {
- // Creates a register at a fixed offset of the MMIO space.
- ($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } ) => {
- bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
- register!(@io_fixed $name @ $offset);
- };
-
- // Creates an alias register of fixed offset register `alias` with its own fields.
- ($name:ident => $alias:ident $(, $comment:literal)? { $($fields:tt)* } ) => {
- bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
- register!(@io_fixed $name @ $alias::OFFSET);
- };
-
- // Creates a register at a relative offset from a base address provider.
- ($name:ident @ $base:ty [ $offset:literal ] $(, $comment:literal)? { $($fields:tt)* } ) => {
- bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
- register!(@io_relative $name @ $base [ $offset ]);
- };
-
- // Creates an alias register of relative offset register `alias` with its own fields.
- ($name:ident => $base:ty [ $alias:ident ] $(, $comment:literal)? { $($fields:tt)* }) => {
- bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
- register!(@io_relative $name @ $base [ $alias::OFFSET ]);
- };
-
- // Creates an array of registers at a fixed offset of the MMIO space.
- (
- $name:ident @ $offset:literal [ $size:expr ; $stride:expr ] $(, $comment:literal)? {
- $($fields:tt)*
- }
- ) => {
- static_assert!(::core::mem::size_of::<u32>() <= $stride);
- bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
- register!(@io_array $name @ $offset [ $size ; $stride ]);
- };
-
- // Shortcut for contiguous array of registers (stride == size of element).
- (
- $name:ident @ $offset:literal [ $size:expr ] $(, $comment:literal)? {
- $($fields:tt)*
- }
- ) => {
- register!($name @ $offset [ $size ; ::core::mem::size_of::<u32>() ] $(, $comment)? {
- $($fields)*
- } );
- };
-
- // Creates an array of registers at a relative offset from a base address provider.
- (
- $name:ident @ $base:ty [ $offset:literal [ $size:expr ; $stride:expr ] ]
- $(, $comment:literal)? { $($fields:tt)* }
- ) => {
- static_assert!(::core::mem::size_of::<u32>() <= $stride);
- bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
- register!(@io_relative_array $name @ $base [ $offset [ $size ; $stride ] ]);
- };
-
- // Shortcut for contiguous array of relative registers (stride == size of element).
- (
- $name:ident @ $base:ty [ $offset:literal [ $size:expr ] ] $(, $comment:literal)? {
- $($fields:tt)*
- }
- ) => {
- register!($name @ $base [ $offset [ $size ; ::core::mem::size_of::<u32>() ] ]
- $(, $comment)? { $($fields)* } );
- };
-
- // Creates an alias of register `idx` of relative array of registers `alias` with its own
- // fields.
- (
- $name:ident => $base:ty [ $alias:ident [ $idx:expr ] ] $(, $comment:literal)? {
- $($fields:tt)*
- }
- ) => {
- static_assert!($idx < $alias::SIZE);
- bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
- register!(@io_relative $name @ $base [ $alias::OFFSET + $idx * $alias::STRIDE ] );
- };
-
- // Creates an alias of register `idx` of array of registers `alias` with its own fields.
- // This rule belongs to the (non-relative) register arrays set, but needs to be put last
- // to avoid it being interpreted in place of the relative register array alias rule.
- ($name:ident => $alias:ident [ $idx:expr ] $(, $comment:literal)? { $($fields:tt)* }) => {
- static_assert!($idx < $alias::SIZE);
- bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
- register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE );
- };
-
- // Generates the IO accessors for a fixed offset register.
- (@io_fixed $name:ident @ $offset:expr) => {
- #[allow(dead_code)]
- impl $name {
- pub(crate) const OFFSET: usize = $offset;
-
- /// Read the register from its address in `io`.
- #[inline(always)]
- pub(crate) fn read<const SIZE: usize, T>(io: &T) -> Self where
- T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- {
- Self(io.read32($offset))
- }
-
- /// Write the value contained in `self` to the register address in `io`.
- #[inline(always)]
- pub(crate) fn write<const SIZE: usize, T>(self, io: &T) where
- T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- {
- io.write32(self.0, $offset)
- }
-
- /// Read the register from its address in `io` and run `f` on its value to obtain a new
- /// value to write back.
- #[inline(always)]
- pub(crate) fn update<const SIZE: usize, T, F>(
- io: &T,
- f: F,
- ) where
- T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- F: ::core::ops::FnOnce(Self) -> Self,
- {
- let reg = f(Self::read(io));
- reg.write(io);
- }
- }
- };
-
- // Generates the IO accessors for a relative offset register.
- (@io_relative $name:ident @ $base:ty [ $offset:expr ]) => {
- #[allow(dead_code)]
- impl $name {
- pub(crate) const OFFSET: usize = $offset;
-
- /// Read the register from `io`, using the base address provided by `base` and adding
- /// the register's offset to it.
- #[inline(always)]
- pub(crate) fn read<const SIZE: usize, T, B>(
- io: &T,
- #[allow(unused_variables)]
- base: &B,
- ) -> Self where
- T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- B: crate::regs::macros::RegisterBase<$base>,
- {
- const OFFSET: usize = $name::OFFSET;
-
- let value = io.read32(
- <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
- );
-
- Self(value)
- }
-
- /// Write the value contained in `self` to `io`, using the base address provided by
- /// `base` and adding the register's offset to it.
- #[inline(always)]
- pub(crate) fn write<const SIZE: usize, T, B>(
- self,
- io: &T,
- #[allow(unused_variables)]
- base: &B,
- ) where
- T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- B: crate::regs::macros::RegisterBase<$base>,
- {
- const OFFSET: usize = $name::OFFSET;
-
- io.write32(
- self.0,
- <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
- );
- }
-
- /// Read the register from `io`, using the base address provided by `base` and adding
- /// the register's offset to it, then run `f` on its value to obtain a new value to
- /// write back.
- #[inline(always)]
- pub(crate) fn update<const SIZE: usize, T, B, F>(
- io: &T,
- base: &B,
- f: F,
- ) where
- T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- B: crate::regs::macros::RegisterBase<$base>,
- F: ::core::ops::FnOnce(Self) -> Self,
- {
- let reg = f(Self::read(io, base));
- reg.write(io, base);
- }
- }
- };
-
- // Generates the IO accessors for an array of registers.
- (@io_array $name:ident @ $offset:literal [ $size:expr ; $stride:expr ]) => {
- #[allow(dead_code)]
- impl $name {
- pub(crate) const OFFSET: usize = $offset;
- pub(crate) const SIZE: usize = $size;
- pub(crate) const STRIDE: usize = $stride;
-
- /// Read the array register at index `idx` from its address in `io`.
- #[inline(always)]
- pub(crate) fn read<const SIZE: usize, T>(
- io: &T,
- idx: usize,
- ) -> Self where
- T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- {
- build_assert!(idx < Self::SIZE);
-
- let offset = Self::OFFSET + (idx * Self::STRIDE);
- let value = io.read32(offset);
-
- Self(value)
- }
-
- /// Write the value contained in `self` to the array register with index `idx` in `io`.
- #[inline(always)]
- pub(crate) fn write<const SIZE: usize, T>(
- self,
- io: &T,
- idx: usize
- ) where
- T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- {
- build_assert!(idx < Self::SIZE);
-
- let offset = Self::OFFSET + (idx * Self::STRIDE);
-
- io.write32(self.0, offset);
- }
-
- /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
- /// new value to write back.
- #[inline(always)]
- pub(crate) fn update<const SIZE: usize, T, F>(
- io: &T,
- idx: usize,
- f: F,
- ) where
- T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- F: ::core::ops::FnOnce(Self) -> Self,
- {
- let reg = f(Self::read(io, idx));
- reg.write(io, idx);
- }
-
- /// Read the array register at index `idx` from its address in `io`.
- ///
- /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
- /// access was out-of-bounds.
- #[inline(always)]
- pub(crate) fn try_read<const SIZE: usize, T>(
- io: &T,
- idx: usize,
- ) -> ::kernel::error::Result<Self> where
- T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- {
- if idx < Self::SIZE {
- Ok(Self::read(io, idx))
- } else {
- Err(EINVAL)
- }
- }
-
- /// Write the value contained in `self` to the array register with index `idx` in `io`.
- ///
- /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
- /// access was out-of-bounds.
- #[inline(always)]
- pub(crate) fn try_write<const SIZE: usize, T>(
- self,
- io: &T,
- idx: usize,
- ) -> ::kernel::error::Result where
- T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- {
- if idx < Self::SIZE {
- Ok(self.write(io, idx))
- } else {
- Err(EINVAL)
- }
- }
-
- /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
- /// new value to write back.
- ///
- /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
- /// access was out-of-bounds.
- #[inline(always)]
- pub(crate) fn try_update<const SIZE: usize, T, F>(
- io: &T,
- idx: usize,
- f: F,
- ) -> ::kernel::error::Result where
- T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- F: ::core::ops::FnOnce(Self) -> Self,
- {
- if idx < Self::SIZE {
- Ok(Self::update(io, idx, f))
- } else {
- Err(EINVAL)
- }
- }
- }
- };
-
- // Generates the IO accessors for an array of relative registers.
- (
- @io_relative_array $name:ident @ $base:ty
- [ $offset:literal [ $size:expr ; $stride:expr ] ]
- ) => {
- #[allow(dead_code)]
- impl $name {
- pub(crate) const OFFSET: usize = $offset;
- pub(crate) const SIZE: usize = $size;
- pub(crate) const STRIDE: usize = $stride;
-
- /// Read the array register at index `idx` from `io`, using the base address provided
- /// by `base` and adding the register's offset to it.
- #[inline(always)]
- pub(crate) fn read<const SIZE: usize, T, B>(
- io: &T,
- #[allow(unused_variables)]
- base: &B,
- idx: usize,
- ) -> Self where
- T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- B: crate::regs::macros::RegisterBase<$base>,
- {
- build_assert!(idx < Self::SIZE);
-
- let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +
- Self::OFFSET + (idx * Self::STRIDE);
- let value = io.read32(offset);
-
- Self(value)
- }
-
- /// Write the value contained in `self` to `io`, using the base address provided by
- /// `base` and adding the offset of array register `idx` to it.
- #[inline(always)]
- pub(crate) fn write<const SIZE: usize, T, B>(
- self,
- io: &T,
- #[allow(unused_variables)]
- base: &B,
- idx: usize
- ) where
- T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- B: crate::regs::macros::RegisterBase<$base>,
- {
- build_assert!(idx < Self::SIZE);
-
- let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +
- Self::OFFSET + (idx * Self::STRIDE);
-
- io.write32(self.0, offset);
- }
-
- /// Read the array register at index `idx` from `io`, using the base address provided
- /// by `base` and adding the register's offset to it, then run `f` on its value to
- /// obtain a new value to write back.
- #[inline(always)]
- pub(crate) fn update<const SIZE: usize, T, B, F>(
- io: &T,
- base: &B,
- idx: usize,
- f: F,
- ) where
- T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- B: crate::regs::macros::RegisterBase<$base>,
- F: ::core::ops::FnOnce(Self) -> Self,
- {
- let reg = f(Self::read(io, base, idx));
- reg.write(io, base, idx);
- }
-
- /// Read the array register at index `idx` from `io`, using the base address provided
- /// by `base` and adding the register's offset to it.
- ///
- /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
- /// access was out-of-bounds.
- #[inline(always)]
- pub(crate) fn try_read<const SIZE: usize, T, B>(
- io: &T,
- base: &B,
- idx: usize,
- ) -> ::kernel::error::Result<Self> where
- T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- B: crate::regs::macros::RegisterBase<$base>,
- {
- if idx < Self::SIZE {
- Ok(Self::read(io, base, idx))
- } else {
- Err(EINVAL)
- }
- }
-
- /// Write the value contained in `self` to `io`, using the base address provided by
- /// `base` and adding the offset of array register `idx` to it.
- ///
- /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
- /// access was out-of-bounds.
- #[inline(always)]
- pub(crate) fn try_write<const SIZE: usize, T, B>(
- self,
- io: &T,
- base: &B,
- idx: usize,
- ) -> ::kernel::error::Result where
- T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- B: crate::regs::macros::RegisterBase<$base>,
- {
- if idx < Self::SIZE {
- Ok(self.write(io, base, idx))
- } else {
- Err(EINVAL)
- }
- }
-
- /// Read the array register at index `idx` from `io`, using the base address provided
- /// by `base` and adding the register's offset to it, then run `f` on its value to
- /// obtain a new value to write back.
- ///
- /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
- /// access was out-of-bounds.
- #[inline(always)]
- pub(crate) fn try_update<const SIZE: usize, T, B, F>(
- io: &T,
- base: &B,
- idx: usize,
- f: F,
- ) -> ::kernel::error::Result where
- T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- B: crate::regs::macros::RegisterBase<$base>,
- F: ::core::ops::FnOnce(Self) -> Self,
- {
- if idx < Self::SIZE {
- Ok(Self::update(io, base, idx, f))
- } else {
- Err(EINVAL)
- }
- }
- }
- };
-}
--
2.52.0
^ permalink raw reply related [flat|nested] 56+ messages in thread
* Re: [PATCH 1/6] rust: num: add `shr` and `shl` methods to `Bounded`
2026-01-20 6:17 ` [PATCH 1/6] rust: num: add `shr` and `shl` methods to `Bounded` Alexandre Courbot
@ 2026-01-20 8:44 ` Alice Ryhl
2026-01-20 12:53 ` Alexandre Courbot
2026-01-20 16:12 ` kernel test robot
2026-01-21 8:15 ` Yury Norov
2 siblings, 1 reply; 56+ messages in thread
From: Alice Ryhl @ 2026-01-20 8:44 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Yury Norov, John Hubbard, Alistair Popple, Joel Fernandes,
Timur Tabi, Edwin Peer, Eliot Courtney, Daniel Almeida,
Dirk Behme, Steven Price, rust-for-linux, linux-kernel
On Tue, Jan 20, 2026 at 03:17:54PM +0900, Alexandre Courbot wrote:
> Shifting a `Bounded` left or right changes the number of bits required
> to represent the value. Add methods that perform the shift and return a
> `Bounded` with the appropriately adjusted bit width.
>
> These methods are particularly useful for bitfield extraction.
>
> Suggested-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> + /// Right-shifts `self` by `SHIFT` and returns the result as a `Bounded<_, { N - SHIFT }>`.
> + ///
> + /// # Examples
> + ///
> + /// ```
> + /// use kernel::num::Bounded;
> + ///
> + /// let v = Bounded::<u32, 16>::new::<0xff00>();
> + /// let v_shifted: Bounded::<u32, 8> = v.shr::<8, _>();
> + ///
> + /// assert_eq!(v_shifted.get(), 0xff);
> + /// ```
> + pub fn shr<const SHIFT: u32, const RES: u32>(self) -> Bounded<T, RES> {
> + const { assert!(RES == N - SHIFT) }
In principle this could be
const { assert!(RES >= N - SHIFT) }
Alice
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 2/6] rust: num: add `as_bool` method to `Bounded<_, 1>`
2026-01-20 6:17 ` [PATCH 2/6] rust: num: add `as_bool` method to `Bounded<_, 1>` Alexandre Courbot
@ 2026-01-20 8:45 ` Alice Ryhl
0 siblings, 0 replies; 56+ messages in thread
From: Alice Ryhl @ 2026-01-20 8:45 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Yury Norov, John Hubbard, Alistair Popple, Joel Fernandes,
Timur Tabi, Edwin Peer, Eliot Courtney, Daniel Almeida,
Dirk Behme, Steven Price, rust-for-linux, linux-kernel
On Tue, Jan 20, 2026 at 03:17:55PM +0900, Alexandre Courbot wrote:
> Single-bit numbers are typically treated as booleans. There is an
> `Into<bool>` implementation for those, but invoking it from contexts
> that lack type expectations is not always convenient.
>
> Add an `as_bool` method as a simpler shortcut.
>
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-20 6:17 ` [PATCH 3/6] rust: add `bitfield!` macro Alexandre Courbot
@ 2026-01-20 11:45 ` Dirk Behme
2026-01-20 12:37 ` Miguel Ojeda
2026-01-20 12:51 ` Alexandre Courbot
2026-01-21 9:16 ` Yury Norov
1 sibling, 2 replies; 56+ messages in thread
From: Dirk Behme @ 2026-01-20 11:45 UTC (permalink / raw)
To: Alexandre Courbot, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov
Cc: John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, Daniel Almeida, Steven Price,
rust-for-linux, linux-kernel
Hi Alexandre,
On 20/01/2026 07:17, Alexandre Courbot wrote:
> Add a macro for defining bitfield structs with bounds-checked accessors.
>
> Each field is represented as a `Bounded` of the appropriate bit width,
> ensuring field values are never silently truncated.
>
> Fields can optionally be converted to/from custom types, either fallibly
> or infallibly.
>
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> ---
> rust/kernel/bitfield.rs | 503 ++++++++++++++++++++++++++++++++++++++++++++++++
> rust/kernel/lib.rs | 1 +
> 2 files changed, 504 insertions(+)
>
> diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
> new file mode 100644
> index 000000000000..2926ab802227
> --- /dev/null
> +++ b/rust/kernel/bitfield.rs
> @@ -0,0 +1,503 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Support for defining bitfields as Rust structures.
> +
> +/// Defines a bitfield struct with bounds-checked accessors for individual bit ranges.
> +///
> +/// # Example
> +///
> +/// ```rust
> +/// use kernel::bitfield;
> +/// use kernel::num::Bounded;
> +///
> +/// bitfield! {
> +/// pub struct Rgb(u16) {
> +/// 15:11 blue;
> +/// 10:5 green;
> +/// 4:0 red;
> +/// }
> +/// }
> +///
> +/// // Setters can be chained. Bounded::new::<N>() does compile-time bounds checking.
> +/// let color = Rgb::default()
> +/// .set_red(Bounded::<u16, _>::new::<0x10>())
> +/// .set_green(Bounded::<u16, _>::new::<0x1f>())
> +/// .set_blue(Bounded::<u16, _>::new::<0x18>());
> +///
> +/// assert_eq!(color.red(), 0x10);
> +/// assert_eq!(color.green(), 0x1f);
> +/// assert_eq!(color.blue(), 0x18);
> +/// assert_eq!(
> +/// color.as_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]
> +/// pub struct Name(storage_type), "Struct documentation." {
> +/// hi:lo field_1, "Field documentation.";
> +/// hi:lo field_2 => ConvertedType, "Field documentation.";
> +/// hi:lo field_3 ?=> ConvertedType, "Field documentation.";
> +/// ...
> +/// }
> +/// }
> +/// ```
> +///
> +/// - `storage_type`: The underlying integer type (`u8`, `u16`, `u32`, `u64`).
> +/// - `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, and [`Default`] and [`Debug`] implementations.
> +///
> +/// For each field, it also generates:
> +/// - `field()` - getter returning a [`Bounded`] (or converted type) for the field,
> +/// - `set_field(value)` - setter with compile-time bounds checking,
> +/// - `try_set_field(value)` - setter with runtime bounds checking (for fields without type
> +/// conversion),
> +/// - `FIELD_MASK`, `FIELD_SHIFT`, `FIELD_RANGE` - constants for manual bit manipulation.
> +///
> +/// # 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::default()
> +/// .set_byte_field(0x42_u8)
> +/// .set_flag(true);
> +///
> +/// assert_eq!(flags.as_raw(), (0x42 << Flags::BYTE_FIELD_SHIFT) | 1);
> +/// ```
> +///
> +/// # Runtime bounds checking
> +///
> +/// When a value is not known at compile time, use `try_set_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_set_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 when all bit patterns map to valid values:
> +///
> +/// ```rust
> +/// use kernel::bitfield;
> +/// use kernel::num::Bounded;
> +///
> +/// #[derive(Debug, Clone, Copy, Default, PartialEq)]
> +/// enum Power {
> +/// #[default]
> +/// 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::default().set_power(Power::On);
> +/// assert_eq!(ctrl.power(), Power::On);
> +/// ```
> +///
> +/// ## Fallible conversion (`?=>`)
> +///
> +/// Use when some bit patterns are invalid. The getter returns a [`Result`]:
> +///
> +/// ```rust
> +/// use kernel::bitfield;
> +/// use kernel::num::Bounded;
> +///
> +/// #[derive(Debug, Clone, Copy, Default, PartialEq)]
> +/// enum Mode {
> +/// #[default]
> +/// 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::default().set_mode(Mode::Auto);
> +/// assert_eq!(cfg.mode(), Ok(Mode::Auto));
> +///
> +/// // Invalid bit pattern returns an error.
> +/// assert_eq!(Config::from(0b11).mode(), Err(3));
> +/// ```
> +///
> +/// [`Bounded`]: kernel::num::Bounded
> +#[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)
> + $(, $comment:literal)? { $($fields:tt)* }
> + ) => {
> + ::kernel::bitfield!(@core $(#[$attr])* $vis $name $storage $(, $comment)?);
> + ::kernel::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 $(, $comment:literal)?) => {
> + $(
> + #[doc=$comment]
> + )?
> + $(#[$attr])*
> + #[repr(transparent)]
> + #[derive(Clone, Copy, PartialEq, Eq)]
> + $vis struct $name($storage);
> +
> + #[allow(dead_code)]
> + impl $name {
> + /// Returns the raw value of this bitfield.
> + ///
> + /// This is similar to the [`From`] implementation, but is shorter to invoke in
> + /// most cases.
> + $vis fn as_raw(self) -> $storage {
> + self.0
> + }
> + }
> +
> + impl ::core::convert::From<$name> for $storage {
> + fn from(val: $name) -> $storage {
> + val.0
> + }
> + }
> +
> + impl ::core::convert::From<$storage> for $name {
> + fn from(val: $storage) -> $name {
> + Self(val)
> + }
> + }
> + };
> +
> + // Definitions requiring knowledge of individual fields: private and public field accessors,
> + // and `Debug` and `Default` implementations.
> + (@fields $vis:vis $name:ident $storage:ty {
> + $($hi:tt:$lo:tt $field:ident
> + $(?=> $try_into_type:ty)?
> + $(=> $into_type:ty)?
> + $(, $comment:literal)?
> + ;
> + )*
> + }
> + ) => {
> + #[allow(dead_code)]
> + impl $name {
> + $(
> + ::kernel::bitfield!(@private_field_accessors $vis $name $storage : $hi:$lo $field);
> + ::kernel::bitfield!(@public_field_accessors $vis $name $storage : $hi:$lo $field
> + $(?=> $try_into_type)?
> + $(=> $into_type)?
> + $(, $comment)?
> + );
> + )*
> + }
> +
> + ::kernel::bitfield!(@debug $name { $($field;)* });
> + ::kernel::bitfield!(@default $name { $($field;)* });
> + };
> +
> + // Private field accessors working with the correct `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!(
> + 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.0 << ALIGN_TOP
> + );
> + val.shr::<ALIGN_BOTTOM, _>()
What have I missed?
error[E0747]: type provided when a constant was expected
--> rust/kernel/bitfield.rs:318:37
|
318 | val.shr::<ALIGN_BOTTOM, _>()
| ^
...
566 | / bitfield! {
567 | | struct TestPageTableEntry(u64) {
568 | | 0:0 present;
569 | | 1:1 writable;
... |
574 | | }
575 | | }
| |_____- in this macro invocation
|
= help: const arguments cannot yet be inferred with `_`
= note: this error originates in the macro `::kernel::bitfield`
which comes from the expansion of the macro `bitfield` (in Nightly
builds, run with -Z macro-backtrace for more info)
help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
--> rust/kernel/lib.rs:59:1
|
59 + #![feature(generic_arg_infer)]
$ rustc --version
rustc 1.81.0 (eeb90cda1 2024-09-04)
Thanks
Dirk
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-20 11:45 ` Dirk Behme
@ 2026-01-20 12:37 ` Miguel Ojeda
2026-01-20 12:47 ` Dirk Behme
2026-01-20 12:51 ` Alexandre Courbot
1 sibling, 1 reply; 56+ messages in thread
From: Miguel Ojeda @ 2026-01-20 12:37 UTC (permalink / raw)
To: Dirk Behme
Cc: Alexandre Courbot, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Joel Fernandes, Timur Tabi, Edwin Peer,
Eliot Courtney, Daniel Almeida, Steven Price, rust-for-linux,
linux-kernel
On Tue, Jan 20, 2026 at 12:45 PM Dirk Behme <dirk.behme@de.bosch.com> wrote:
>
> What have I missed?
I think the patch is missing enabling the (already stable by now)
feature. If you add it at the top of `rust/kernel/lib.rs`, does it
work for you?
Thanks for catching that!
Cheers,
Miguel
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-20 12:37 ` Miguel Ojeda
@ 2026-01-20 12:47 ` Dirk Behme
2026-01-20 13:08 ` Miguel Ojeda
0 siblings, 1 reply; 56+ messages in thread
From: Dirk Behme @ 2026-01-20 12:47 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Alexandre Courbot, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Joel Fernandes, Timur Tabi, Edwin Peer,
Eliot Courtney, Daniel Almeida, Steven Price, rust-for-linux,
linux-kernel
On 20/01/2026 13:37, Miguel Ojeda wrote:
> On Tue, Jan 20, 2026 at 12:45 PM Dirk Behme <dirk.behme@de.bosch.com> wrote:
>>
>> What have I missed?
>
> I think the patch is missing enabling the (already stable by now)
> feature. If you add it at the top of `rust/kernel/lib.rs`, does it
> work for you?
Yes :)
And I think we need the same for the doctests where it fails as well then:
error[E0747]: type provided when a constant was expected
--> rust/doctests_kernel_generated.rs:2758:1
|
2758 | / bitfield! {
2759 | | pub struct Config(u8) {
2760 | | 3:0 nibble;
2761 | | }
2762 | | }
| |_^
|
= help: const arguments cannot yet be inferred with `_`
= help: add `#![feature(generic_arg_infer)]` to the crate
attributes to enable
= note: this error originates in the macro `::kernel::bitfield`
which comes from the expansion of the macro `bitfield` (in Nightly
builds, run with -Z macro-backtrace for more info)
error[E0747]: type provided when a constant was expected
--> rust/doctests_kernel_generated.rs:2846:1
|
2846 | / bitfield! {
2847 | | pub struct Control(u32) {
2848 | | 0:0 power => Power;
2849 | | }
2850 | | }
| |_^
|
= help: const arguments cannot yet be inferred with `_`
= help: add `#![feature(generic_arg_infer)]` to the crate
attributes to enable
= note: this error originates in the macro `::kernel::bitfield`
which comes from the expansion of the macro `bitfield` (in Nightly
builds, run with -Z macro-backtrace for more info)
error[E0747]: type provided when a constant was expected
--> rust/doctests_kernel_generated.rs:2934:41
|
2934 | Mode::Low => Bounded::<u32, _>::new::<0>(),
| ^
|
= help: const arguments cannot yet be inferred with `_`
= help: add `#![feature(generic_arg_infer)]` to the crate
attributes to enable
...
Thanks!
Dirk
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-20 11:45 ` Dirk Behme
2026-01-20 12:37 ` Miguel Ojeda
@ 2026-01-20 12:51 ` Alexandre Courbot
1 sibling, 0 replies; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-20 12:51 UTC (permalink / raw)
To: Dirk Behme
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Yury Norov, John Hubbard, Alistair Popple,
Joel Fernandes, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Steven Price, rust-for-linux, linux-kernel
Hi Dirk,
On Tue Jan 20, 2026 at 8:45 PM JST, Dirk Behme wrote:
> Hi Alexandre,
>
> On 20/01/2026 07:17, Alexandre Courbot wrote:
>> Add a macro for defining bitfield structs with bounds-checked accessors.
>>
>> Each field is represented as a `Bounded` of the appropriate bit width,
>> ensuring field values are never silently truncated.
>>
>> Fields can optionally be converted to/from custom types, either fallibly
>> or infallibly.
>>
>> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
>> ---
>> rust/kernel/bitfield.rs | 503 ++++++++++++++++++++++++++++++++++++++++++++++++
>> rust/kernel/lib.rs | 1 +
>> 2 files changed, 504 insertions(+)
>>
>> diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
>> new file mode 100644
>> index 000000000000..2926ab802227
>> --- /dev/null
>> +++ b/rust/kernel/bitfield.rs
>> @@ -0,0 +1,503 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +
>> +//! Support for defining bitfields as Rust structures.
>> +
>> +/// Defines a bitfield struct with bounds-checked accessors for individual bit ranges.
>> +///
>> +/// # Example
>> +///
>> +/// ```rust
>> +/// use kernel::bitfield;
>> +/// use kernel::num::Bounded;
>> +///
>> +/// bitfield! {
>> +/// pub struct Rgb(u16) {
>> +/// 15:11 blue;
>> +/// 10:5 green;
>> +/// 4:0 red;
>> +/// }
>> +/// }
>> +///
>> +/// // Setters can be chained. Bounded::new::<N>() does compile-time bounds checking.
>> +/// let color = Rgb::default()
>> +/// .set_red(Bounded::<u16, _>::new::<0x10>())
>> +/// .set_green(Bounded::<u16, _>::new::<0x1f>())
>> +/// .set_blue(Bounded::<u16, _>::new::<0x18>());
>> +///
>> +/// assert_eq!(color.red(), 0x10);
>> +/// assert_eq!(color.green(), 0x1f);
>> +/// assert_eq!(color.blue(), 0x18);
>> +/// assert_eq!(
>> +/// color.as_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]
>> +/// pub struct Name(storage_type), "Struct documentation." {
>> +/// hi:lo field_1, "Field documentation.";
>> +/// hi:lo field_2 => ConvertedType, "Field documentation.";
>> +/// hi:lo field_3 ?=> ConvertedType, "Field documentation.";
>> +/// ...
>> +/// }
>> +/// }
>> +/// ```
>> +///
>> +/// - `storage_type`: The underlying integer type (`u8`, `u16`, `u32`, `u64`).
>> +/// - `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, and [`Default`] and [`Debug`] implementations.
>> +///
>> +/// For each field, it also generates:
>> +/// - `field()` - getter returning a [`Bounded`] (or converted type) for the field,
>> +/// - `set_field(value)` - setter with compile-time bounds checking,
>> +/// - `try_set_field(value)` - setter with runtime bounds checking (for fields without type
>> +/// conversion),
>> +/// - `FIELD_MASK`, `FIELD_SHIFT`, `FIELD_RANGE` - constants for manual bit manipulation.
>> +///
>> +/// # 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::default()
>> +/// .set_byte_field(0x42_u8)
>> +/// .set_flag(true);
>> +///
>> +/// assert_eq!(flags.as_raw(), (0x42 << Flags::BYTE_FIELD_SHIFT) | 1);
>> +/// ```
>> +///
>> +/// # Runtime bounds checking
>> +///
>> +/// When a value is not known at compile time, use `try_set_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_set_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 when all bit patterns map to valid values:
>> +///
>> +/// ```rust
>> +/// use kernel::bitfield;
>> +/// use kernel::num::Bounded;
>> +///
>> +/// #[derive(Debug, Clone, Copy, Default, PartialEq)]
>> +/// enum Power {
>> +/// #[default]
>> +/// 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::default().set_power(Power::On);
>> +/// assert_eq!(ctrl.power(), Power::On);
>> +/// ```
>> +///
>> +/// ## Fallible conversion (`?=>`)
>> +///
>> +/// Use when some bit patterns are invalid. The getter returns a [`Result`]:
>> +///
>> +/// ```rust
>> +/// use kernel::bitfield;
>> +/// use kernel::num::Bounded;
>> +///
>> +/// #[derive(Debug, Clone, Copy, Default, PartialEq)]
>> +/// enum Mode {
>> +/// #[default]
>> +/// 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::default().set_mode(Mode::Auto);
>> +/// assert_eq!(cfg.mode(), Ok(Mode::Auto));
>> +///
>> +/// // Invalid bit pattern returns an error.
>> +/// assert_eq!(Config::from(0b11).mode(), Err(3));
>> +/// ```
>> +///
>> +/// [`Bounded`]: kernel::num::Bounded
>> +#[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)
>> + $(, $comment:literal)? { $($fields:tt)* }
>> + ) => {
>> + ::kernel::bitfield!(@core $(#[$attr])* $vis $name $storage $(, $comment)?);
>> + ::kernel::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 $(, $comment:literal)?) => {
>> + $(
>> + #[doc=$comment]
>> + )?
>> + $(#[$attr])*
>> + #[repr(transparent)]
>> + #[derive(Clone, Copy, PartialEq, Eq)]
>> + $vis struct $name($storage);
>> +
>> + #[allow(dead_code)]
>> + impl $name {
>> + /// Returns the raw value of this bitfield.
>> + ///
>> + /// This is similar to the [`From`] implementation, but is shorter to invoke in
>> + /// most cases.
>> + $vis fn as_raw(self) -> $storage {
>> + self.0
>> + }
>> + }
>> +
>> + impl ::core::convert::From<$name> for $storage {
>> + fn from(val: $name) -> $storage {
>> + val.0
>> + }
>> + }
>> +
>> + impl ::core::convert::From<$storage> for $name {
>> + fn from(val: $storage) -> $name {
>> + Self(val)
>> + }
>> + }
>> + };
>> +
>> + // Definitions requiring knowledge of individual fields: private and public field accessors,
>> + // and `Debug` and `Default` implementations.
>> + (@fields $vis:vis $name:ident $storage:ty {
>> + $($hi:tt:$lo:tt $field:ident
>> + $(?=> $try_into_type:ty)?
>> + $(=> $into_type:ty)?
>> + $(, $comment:literal)?
>> + ;
>> + )*
>> + }
>> + ) => {
>> + #[allow(dead_code)]
>> + impl $name {
>> + $(
>> + ::kernel::bitfield!(@private_field_accessors $vis $name $storage : $hi:$lo $field);
>> + ::kernel::bitfield!(@public_field_accessors $vis $name $storage : $hi:$lo $field
>> + $(?=> $try_into_type)?
>> + $(=> $into_type)?
>> + $(, $comment)?
>> + );
>> + )*
>> + }
>> +
>> + ::kernel::bitfield!(@debug $name { $($field;)* });
>> + ::kernel::bitfield!(@default $name { $($field;)* });
>> + };
>> +
>> + // Private field accessors working with the correct `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!(
>> + 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.0 << ALIGN_TOP
>> + );
>> + val.shr::<ALIGN_BOTTOM, _>()
>
> What have I missed?
>
> error[E0747]: type provided when a constant was expected
> --> rust/kernel/bitfield.rs:318:37
> |
> 318 | val.shr::<ALIGN_BOTTOM, _>()
> | ^
> ...
> 566 | / bitfield! {
> 567 | | struct TestPageTableEntry(u64) {
> 568 | | 0:0 present;
> 569 | | 1:1 writable;
> ... |
> 574 | | }
> 575 | | }
> | |_____- in this macro invocation
> |
> = help: const arguments cannot yet be inferred with `_`
> = note: this error originates in the macro `::kernel::bitfield`
> which comes from the expansion of the macro `bitfield` (in Nightly
> builds, run with -Z macro-backtrace for more info)
> help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
> --> rust/kernel/lib.rs:59:1
> |
> 59 + #![feature(generic_arg_infer)]
>
>
> $ rustc --version
> rustc 1.81.0 (eeb90cda1 2024-09-04)
Ah, I'm the one who missed something - this code builds fine with rustc
1.92, but on older versions the second generic parameter of `shr` cannot
be inferred and we need to specify the expected number of bits
explicitly. I will fix that in v2 as we need to support 1.78 anyway.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 1/6] rust: num: add `shr` and `shl` methods to `Bounded`
2026-01-20 8:44 ` Alice Ryhl
@ 2026-01-20 12:53 ` Alexandre Courbot
0 siblings, 0 replies; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-20 12:53 UTC (permalink / raw)
To: Alice Ryhl
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Yury Norov, John Hubbard, Alistair Popple, Joel Fernandes,
Timur Tabi, Edwin Peer, Eliot Courtney, Daniel Almeida,
Dirk Behme, Steven Price, rust-for-linux, linux-kernel
On Tue Jan 20, 2026 at 5:44 PM JST, Alice Ryhl wrote:
> On Tue, Jan 20, 2026 at 03:17:54PM +0900, Alexandre Courbot wrote:
>> Shifting a `Bounded` left or right changes the number of bits required
>> to represent the value. Add methods that perform the shift and return a
>> `Bounded` with the appropriately adjusted bit width.
>>
>> These methods are particularly useful for bitfield extraction.
>>
>> Suggested-by: Alice Ryhl <aliceryhl@google.com>
>> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
>
>> + /// Right-shifts `self` by `SHIFT` and returns the result as a `Bounded<_, { N - SHIFT }>`.
>> + ///
>> + /// # Examples
>> + ///
>> + /// ```
>> + /// use kernel::num::Bounded;
>> + ///
>> + /// let v = Bounded::<u32, 16>::new::<0xff00>();
>> + /// let v_shifted: Bounded::<u32, 8> = v.shr::<8, _>();
>> + ///
>> + /// assert_eq!(v_shifted.get(), 0xff);
>> + /// ```
>> + pub fn shr<const SHIFT: u32, const RES: u32>(self) -> Bounded<T, RES> {
>> + const { assert!(RES == N - SHIFT) }
>
> In principle this could be
>
> const { assert!(RES >= N - SHIFT) }
Oh, that's right and that's also more flexible - thanks, will do that for v2.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-20 12:47 ` Dirk Behme
@ 2026-01-20 13:08 ` Miguel Ojeda
2026-01-20 13:20 ` Alexandre Courbot
0 siblings, 1 reply; 56+ messages in thread
From: Miguel Ojeda @ 2026-01-20 13:08 UTC (permalink / raw)
To: Dirk Behme
Cc: Alexandre Courbot, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Joel Fernandes, Timur Tabi, Edwin Peer,
Eliot Courtney, Daniel Almeida, Steven Price, rust-for-linux,
linux-kernel
On Tue, Jan 20, 2026 at 1:47 PM Dirk Behme <dirk.behme@de.bosch.com> wrote:
>
> And I think we need the same for the doctests where it fails as well then:
Yeah, since it is a macro meant for other crates, if we want to use
the feature, then we should have it in the set of Rust allowed
features for all kernel code.
But I see Alexandre has already replied and IIUC he plans to provide
it explicitly instead?
I wonder if we could just use it instead. Hmm... I see there was a PR
1.86 that significantly reworked the implementation, and Debian has
1.85 only, so perhaps it is a good idea to conservatively avoid the
feature, even if we may not hit any differences in practice.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 0/6] rust: add `bitfield!` and `register!` macros
2026-01-20 6:17 [PATCH 0/6] rust: add `bitfield!` and `register!` macros Alexandre Courbot
` (5 preceding siblings ...)
2026-01-20 6:17 ` [PATCH FOR REFERENCE 6/6] gpu: nova-core: use the kernel `register!` and `bitfield!` macros Alexandre Courbot
@ 2026-01-20 13:14 ` Miguel Ojeda
2026-01-20 13:38 ` Danilo Krummrich
6 siblings, 1 reply; 56+ messages in thread
From: Miguel Ojeda @ 2026-01-20 13:14 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Yury Norov, John Hubbard, Alistair Popple,
Joel Fernandes, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Tue, Jan 20, 2026 at 7:23 AM Alexandre Courbot <acourbot@nvidia.com> wrote:
>
> rust: num: add `shr` and `shl` methods to `Bounded`
> rust: num: add `as_bool` method to `Bounded<_, 1>`
I could apply these two this cycle if that would help, since they are
straightforward and you say in the cover letter that you also use it
in Nova code -- does that mean outside the macro? (I don't see it in
linux-next, but perhaps I am blind...)
Thanks!
Cheers,
Miguel
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-20 13:08 ` Miguel Ojeda
@ 2026-01-20 13:20 ` Alexandre Courbot
2026-01-20 21:02 ` Miguel Ojeda
0 siblings, 1 reply; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-20 13:20 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Dirk Behme, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Joel Fernandes, Timur Tabi, Edwin Peer,
Eliot Courtney, Daniel Almeida, Steven Price, rust-for-linux,
linux-kernel
On Tue Jan 20, 2026 at 10:08 PM JST, Miguel Ojeda wrote:
> On Tue, Jan 20, 2026 at 1:47 PM Dirk Behme <dirk.behme@de.bosch.com> wrote:
>>
>> And I think we need the same for the doctests where it fails as well then:
>
> Yeah, since it is a macro meant for other crates, if we want to use
> the feature, then we should have it in the set of Rust allowed
> features for all kernel code.
>
> But I see Alexandre has already replied and IIUC he plans to provide
> it explicitly instead?
>
> I wonder if we could just use it instead. Hmm... I see there was a PR
> 1.86 that significantly reworked the implementation, and Debian has
> 1.85 only, so perhaps it is a good idea to conservatively avoid the
> feature, even if we may not hit any differences in practice.
We can avoid the feature altogether, but it is of course more convenient
if we can infer these generic parameters when possible. But if you
prefer we eschew that, no big deal. One can even argue the code is more
readable that way.
I haven't found how to enable it for doctests anyway - maybe this is not
currently doable?
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 0/6] rust: add `bitfield!` and `register!` macros
2026-01-20 13:14 ` [PATCH 0/6] rust: add `bitfield!` and `register!` macros Miguel Ojeda
@ 2026-01-20 13:38 ` Danilo Krummrich
2026-01-20 13:50 ` Miguel Ojeda
0 siblings, 1 reply; 56+ messages in thread
From: Danilo Krummrich @ 2026-01-20 13:38 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Alexandre Courbot, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Yury Norov, John Hubbard, Alistair Popple,
Joel Fernandes, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Tue Jan 20, 2026 at 2:14 PM CET, Miguel Ojeda wrote:
> On Tue, Jan 20, 2026 at 7:23 AM Alexandre Courbot <acourbot@nvidia.com> wrote:
>>
>> rust: num: add `shr` and `shl` methods to `Bounded`
>> rust: num: add `as_bool` method to `Bounded<_, 1>`
>
> I could apply these two this cycle if that would help, since they are
> straightforward and you say in the cover letter that you also use it
> in Nova code -- does that mean outside the macro? (I don't see it in
> linux-next, but perhaps I am blind...)
I discussed with Alex and Alice that it would be good to land the register!()
macro this cycle (having some local to register.rs bitfield support), so drivers
can start using it next cycle.
Subsequently, we can follow up on the discussions for global bitfield support.
For this purpose, it would be nice if I could pick up those two prerequisites as
part of this.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 0/6] rust: add `bitfield!` and `register!` macros
2026-01-20 13:38 ` Danilo Krummrich
@ 2026-01-20 13:50 ` Miguel Ojeda
2026-01-20 14:18 ` Danilo Krummrich
0 siblings, 1 reply; 56+ messages in thread
From: Miguel Ojeda @ 2026-01-20 13:50 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Alexandre Courbot, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Yury Norov, John Hubbard, Alistair Popple,
Joel Fernandes, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Tue, Jan 20, 2026 at 2:38 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> I discussed with Alex and Alice that it would be good to land the register!()
> macro this cycle (having some local to register.rs bitfield support), so drivers
> can start using it next cycle.
By "local to register.rs bitfield support", do you mean something else
than this series?
To be honest, I think this is all quite late for the cycle.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 0/6] rust: add `bitfield!` and `register!` macros
2026-01-20 13:50 ` Miguel Ojeda
@ 2026-01-20 14:18 ` Danilo Krummrich
2026-01-20 14:57 ` Miguel Ojeda
0 siblings, 1 reply; 56+ messages in thread
From: Danilo Krummrich @ 2026-01-20 14:18 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Alexandre Courbot, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Yury Norov, John Hubbard, Alistair Popple,
Joel Fernandes, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Tue Jan 20, 2026 at 2:50 PM CET, Miguel Ojeda wrote:
> On Tue, Jan 20, 2026 at 2:38 PM Danilo Krummrich <dakr@kernel.org> wrote:
>>
>> I discussed with Alex and Alice that it would be good to land the register!()
>> macro this cycle (having some local to register.rs bitfield support), so drivers
>> can start using it next cycle.
>
> By "local to register.rs bitfield support", do you mean something else
> than this series?
Correct, this series came from a misunderstanding, I think Alex plans to follow
up on it tomorrow.
> To be honest, I think this is all quite late for the cycle.
I think there has been consensus on the register!() macro, so it seems possible.
But as always, if it works out, great -- if not, that's okay too.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 0/6] rust: add `bitfield!` and `register!` macros
2026-01-20 14:18 ` Danilo Krummrich
@ 2026-01-20 14:57 ` Miguel Ojeda
2026-01-20 15:27 ` Danilo Krummrich
0 siblings, 1 reply; 56+ messages in thread
From: Miguel Ojeda @ 2026-01-20 14:57 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Alexandre Courbot, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Yury Norov, John Hubbard, Alistair Popple,
Joel Fernandes, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Tue, Jan 20, 2026 at 3:18 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> Correct, this series came from a misunderstanding, I think Alex plans to follow
> up on it tomorrow.
I see, thanks.
> I think there has been consensus on the register!() macro, so it seems possible.
> But as always, if it works out, great -- if not, that's okay too.
I want to make things easy for everyone, and I know there is pressure
to deliver etc., but yeah... I am supposed to also push back when
things get stretched a bit too much... :)
Having said that, depending on what the "local bitfield!" entails,
i.e. how much of a hack/workaround/extra work it is, it may be best to
avoid it and go directly for `bitfield!`.
But for that to happen, we would need Linus to really do the -rc8, and
very fast agreements and reviews on it.
It seems to me the easiest is that I give you a branch/tag for you
(and others that want it) to merge with the `bitfield!` one next
cycle. That would avoid the workaround too and Alexandre having to
come up with another series etc.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 0/6] rust: add `bitfield!` and `register!` macros
2026-01-20 14:57 ` Miguel Ojeda
@ 2026-01-20 15:27 ` Danilo Krummrich
2026-01-20 15:48 ` Miguel Ojeda
0 siblings, 1 reply; 56+ messages in thread
From: Danilo Krummrich @ 2026-01-20 15:27 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Alexandre Courbot, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Yury Norov, John Hubbard, Alistair Popple,
Joel Fernandes, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Tue Jan 20, 2026 at 3:57 PM CET, Miguel Ojeda wrote:
> I want to make things easy for everyone, and I know there is pressure
> to deliver etc., but yeah... I am supposed to also push back when
> things get stretched a bit too much... :)
I think it would be good if drivers other than nova-core would finally be able
to use the register!() macro, rather than having to go with raw I/O operations.
> Having said that, depending on what the "local bitfield!" entails,
> i.e. how much of a hack/workaround/extra work it is, it may be best to
> avoid it and go directly for `bitfield!`.
I think it's just about pasting the current bitfield work into register.rs, such
that it can only be used from there. In a follow up we can work out how it
should turn out finally and have to patches: 1. introduce bitfield.rs and 2.
move register!() to use the code from bitfield.rs.
I also proposed that at the end of last cycle [1] because I think the two things
are mostly orthogonal and we don't need to stall register!() on the generic
bitfield work.
[1] https://lore.kernel.org/all/DDC49ZIRX79X.2Q4KW0UY7WUF3@kernel.org/
> But for that to happen, we would need Linus to really do the -rc8, and
> very fast agreements and reviews on it.
The register!() macro code is worked on by Alex for about 10 month in nova-core,
the first RFC for general I/O infrastructure is from March 2025 and the current
series has been discussed on the list for about two cycles.
Given the above, that doesn't seem too unrealistic, let's see.
> It seems to me the easiest is that I give you a branch/tag for you
> (and others that want it) to merge with the `bitfield!` one next
> cycle. That would avoid the workaround too and Alexandre having to
> come up with another series etc.
You mean a tag with the bitfield!() code? Yeah, that would help if we don't make
it this cycle.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 0/6] rust: add `bitfield!` and `register!` macros
2026-01-20 15:27 ` Danilo Krummrich
@ 2026-01-20 15:48 ` Miguel Ojeda
2026-01-20 20:01 ` Danilo Krummrich
0 siblings, 1 reply; 56+ messages in thread
From: Miguel Ojeda @ 2026-01-20 15:48 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Alexandre Courbot, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Yury Norov, John Hubbard, Alistair Popple,
Joel Fernandes, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Tue, Jan 20, 2026 at 4:27 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> I think it's just about pasting the current bitfield work into register.rs, such
> that it can only be used from there. In a follow up we can work out how it
> should turn out finally and have to patches: 1. introduce bitfield.rs and 2.
> move register!() to use the code from bitfield.rs.
Hmm... I am generally not a fan of putting local code. I know you did
it for the DRM `register!` itself, which is fine given the constraints
you have, but here perhaps we could put it in the right file but as
private/hidden as possible just to be used by the other file?
i.e. that way we can debate improvements on top of what we have,
rather than having a move or new code and a drop of the old one?
I mention this since you say "pasting", i.e. I guess it is still
smaller, but it sounds like you would essentially use it more or less
as-is?
> The register!() macro code is worked on by Alex for about 10 month in nova-core,
> the first RFC for general I/O infrastructure is from March 2025 and the current
> series has been discussed on the list for about two cycles.
I meant the `bitfield!` one, i.e. landing the patches as they are here
if we get very fast agreement etc. etc. I can see that possibility,
especially if private/hidden.
> You mean a tag with the bitfield!() code? Yeah, that would help if we don't make
> it this cycle.
Yeah.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 1/6] rust: num: add `shr` and `shl` methods to `Bounded`
2026-01-20 6:17 ` [PATCH 1/6] rust: num: add `shr` and `shl` methods to `Bounded` Alexandre Courbot
2026-01-20 8:44 ` Alice Ryhl
@ 2026-01-20 16:12 ` kernel test robot
2026-01-21 8:15 ` Yury Norov
2 siblings, 0 replies; 56+ messages in thread
From: kernel test robot @ 2026-01-20 16:12 UTC (permalink / raw)
To: Alexandre Courbot, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov
Cc: llvm, oe-kbuild-all, John Hubbard, Alistair Popple,
Joel Fernandes, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel, Alexandre Courbot
Hi Alexandre,
kernel test robot noticed the following build errors:
[auto build test ERROR on 2af6ad09fc7dfe9b3610100983cccf16998bf34d]
url: https://github.com/intel-lab-lkp/linux/commits/Alexandre-Courbot/rust-num-add-shr-and-shl-methods-to-Bounded/20260120-143042
base: 2af6ad09fc7dfe9b3610100983cccf16998bf34d
patch link: https://lore.kernel.org/r/20260120-register-v1-1-723a1743b557%40nvidia.com
patch subject: [PATCH 1/6] rust: num: add `shr` and `shl` methods to `Bounded`
config: x86_64-rhel-9.4-rust (https://download.01.org/0day-ci/archive/20260120/202601202328.hnTsogt9-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
rustc: rustc 1.88.0 (6b00bc388 2025-06-23)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260120/202601202328.hnTsogt9-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601202328.hnTsogt9-lkp@intel.com/
All errors (new ones prefixed by >>):
>> error[E0658]: const arguments cannot yet be inferred with `_`
--> rust/doctests_kernel_generated.rs:10572:47
|
10572 | let v_shifted: Bounded::<u32, 8> = v.shr::<8, _>();
| ^
|
= note: see issue #85077 <https://github.com/rust-lang/rust/issues/85077> for more information
= help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
= note: this compiler was built on 2025-06-23; consider upgrading it if it is out of date
--
>> error[E0658]: const arguments cannot yet be inferred with `_`
--> rust/doctests_kernel_generated.rs:10630:48
|
10630 | let v_shifted: Bounded::<u32, 16> = v.shl::<8, _>();
| ^
|
= note: see issue #85077 <https://github.com/rust-lang/rust/issues/85077> for more information
= help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
= note: this compiler was built on 2025-06-23; consider upgrading it if it is out of date
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 0/6] rust: add `bitfield!` and `register!` macros
2026-01-20 15:48 ` Miguel Ojeda
@ 2026-01-20 20:01 ` Danilo Krummrich
2026-01-20 20:31 ` Miguel Ojeda
0 siblings, 1 reply; 56+ messages in thread
From: Danilo Krummrich @ 2026-01-20 20:01 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Alexandre Courbot, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Yury Norov, John Hubbard, Alistair Popple,
Joel Fernandes, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Tue Jan 20, 2026 at 4:48 PM CET, Miguel Ojeda wrote:
> On Tue, Jan 20, 2026 at 4:27 PM Danilo Krummrich <dakr@kernel.org> wrote:
>>
>> I think it's just about pasting the current bitfield work into register.rs, such
>> that it can only be used from there. In a follow up we can work out how it
>> should turn out finally and have to patches: 1. introduce bitfield.rs and 2.
>> move register!() to use the code from bitfield.rs.
>
> Hmm... I am generally not a fan of putting local code. I know you did
> it for the DRM `register!` itself, which is fine given the constraints
> you have, but here perhaps we could put it in the right file but as
> private/hidden as possible just to be used by the other file?
>
> i.e. that way we can debate improvements on top of what we have,
> rather than having a move or new code and a drop of the old one?
I'm fine either way; the reason I proposed it the way I did was that I think it
sets the least precedence for how bitfields should turn out eventually.
> I mention this since you say "pasting", i.e. I guess it is still
> smaller, but it sounds like you would essentially use it more or less
> as-is?
I left this to Alex, he knows best what makes sense.
>> The register!() macro code is worked on by Alex for about 10 month in nova-core,
>> the first RFC for general I/O infrastructure is from March 2025 and the current
>> series has been discussed on the list for about two cycles.
>
> I meant the `bitfield!` one, i.e. landing the patches as they are here
> if we get very fast agreement etc. etc. I can see that possibility,
> especially if private/hidden.
I think it's rather unlikely to land bitfields this cycle. I think Yury
explicitly requested more discussion on bitfields and also encouraged the "two
stage" approach [1] moving register!() first and then extract bitfields
subsequently.
[1] https://lore.kernel.org/lkml/aOaIIV8KDA0GjF6E@yury/
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 0/6] rust: add `bitfield!` and `register!` macros
2026-01-20 20:01 ` Danilo Krummrich
@ 2026-01-20 20:31 ` Miguel Ojeda
2026-01-21 5:57 ` Yury Norov
0 siblings, 1 reply; 56+ messages in thread
From: Miguel Ojeda @ 2026-01-20 20:31 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot, Yury Norov
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
John Hubbard, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, Daniel Almeida, Dirk Behme,
Steven Price, rust-for-linux, linux-kernel
On Tue, Jan 20, 2026 at 9:01 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> I think it's rather unlikely to land bitfields this cycle. I think Yury
> explicitly requested more discussion on bitfields and also encouraged the "two
> stage" approach [1] moving register!() first and then extract bitfields
> subsequently.
Does Yury want to maintain it? From his messages back then I am not
sure if he does or not (he suggested "some non-rust person from a
related kernel subsystem", not sure if that meant himself in this case
too).
At least, he suggested adding a `MAINTAINERS` entry, which I agreed it
was a good idea (by the way, this patch series should add it, or at
least the eventual one that adds `bitfield!`).
In any case, it has been a while since those discussions. It would be
nice to know what people think nowadays about the macro here etc.
But yeah, from my side, no rush. And if Alexandre wants to maintain
the new file and he wants a tree, I am happy to set it one more up.
Thanks!
Cheers,
Miguel
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-20 13:20 ` Alexandre Courbot
@ 2026-01-20 21:02 ` Miguel Ojeda
0 siblings, 0 replies; 56+ messages in thread
From: Miguel Ojeda @ 2026-01-20 21:02 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Dirk Behme, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Joel Fernandes, Timur Tabi, Edwin Peer,
Eliot Courtney, Daniel Almeida, Steven Price, rust-for-linux,
linux-kernel
On Tue, Jan 20, 2026 at 2:20 PM Alexandre Courbot <acourbot@nvidia.com> wrote:
>
> We can avoid the feature altogether, but it is of course more convenient
> if we can infer these generic parameters when possible. But if you
> prefer we eschew that, no big deal. One can even argue the code is more
> readable that way.
As long as it builds (infers) properly with the minimum version, it
seems fine to me to use.
> I haven't found how to enable it for doctests anyway - maybe this is not
> currently doable?
"World-available" features are enabled for the entire kernel,
including doctests (this is what I meant in my reply to Dirk).
We list them in the `rust_allowed_features` line in `scripts/Makefile.build`.
Thanks!
Cheers,
Miguel
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 0/6] rust: add `bitfield!` and `register!` macros
2026-01-20 20:31 ` Miguel Ojeda
@ 2026-01-21 5:57 ` Yury Norov
2026-01-21 6:55 ` Alexandre Courbot
2026-01-26 14:03 ` Joel Fernandes
0 siblings, 2 replies; 56+ messages in thread
From: Yury Norov @ 2026-01-21 5:57 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Danilo Krummrich, Alexandre Courbot, Yury Norov, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, John Hubbard,
Alistair Popple, Joel Fernandes, Timur Tabi, Edwin Peer,
Eliot Courtney, Daniel Almeida, Dirk Behme, Steven Price,
rust-for-linux, linux-kernel
On Tue, Jan 20, 2026 at 09:31:14PM +0100, Miguel Ojeda wrote:
> On Tue, Jan 20, 2026 at 9:01 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > I think it's rather unlikely to land bitfields this cycle. I think Yury
> > explicitly requested more discussion on bitfields and also encouraged the "two
> > stage" approach [1] moving register!() first and then extract bitfields
> > subsequently.
>
> Does Yury want to maintain it? From his messages back then I am not
> sure if he does or not (he suggested "some non-rust person from a
> related kernel subsystem", not sure if that meant himself in this case
> too).
I can become a reviewer or maintainer, assuming Alex will become the first
maintainer as the main developer. I can also move it with my branch if you
prefer, guys. In this case it's better to make me a maintainer.
> At least, he suggested adding a `MAINTAINERS` entry, which I agreed it
> was a good idea (by the way, this patch series should add it, or at
> least the eventual one that adds `bitfield!`).
Yes it is. Alexandre, can you please add a maintenance section(s) for
all new files?
> In any case, it has been a while since those discussions. It would be
> nice to know what people think nowadays about the macro here etc.
>
> But yeah, from my side, no rush. And if Alexandre wants to maintain
> the new file and he wants a tree, I am happy to set it one more up.
>
> Thanks!
>
> Cheers,
> Miguel
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 0/6] rust: add `bitfield!` and `register!` macros
2026-01-21 5:57 ` Yury Norov
@ 2026-01-21 6:55 ` Alexandre Courbot
2026-01-26 14:03 ` Joel Fernandes
1 sibling, 0 replies; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-21 6:55 UTC (permalink / raw)
To: Yury Norov
Cc: Miguel Ojeda, Danilo Krummrich, Yury Norov, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, John Hubbard,
Alistair Popple, Joel Fernandes, Timur Tabi, Edwin Peer,
Eliot Courtney, Daniel Almeida, Dirk Behme, Steven Price,
rust-for-linux, linux-kernel
On Wed Jan 21, 2026 at 2:57 PM JST, Yury Norov wrote:
> On Tue, Jan 20, 2026 at 09:31:14PM +0100, Miguel Ojeda wrote:
>> On Tue, Jan 20, 2026 at 9:01 PM Danilo Krummrich <dakr@kernel.org> wrote:
>> >
>> > I think it's rather unlikely to land bitfields this cycle. I think Yury
>> > explicitly requested more discussion on bitfields and also encouraged the "two
>> > stage" approach [1] moving register!() first and then extract bitfields
>> > subsequently.
>>
>> Does Yury want to maintain it? From his messages back then I am not
>> sure if he does or not (he suggested "some non-rust person from a
>> related kernel subsystem", not sure if that meant himself in this case
>> too).
>
> I can become a reviewer or maintainer, assuming Alex will become the first
> maintainer as the main developer. I can also move it with my branch if you
> prefer, guys. In this case it's better to make me a maintainer.
>
>> At least, he suggested adding a `MAINTAINERS` entry, which I agreed it
>> was a good idea (by the way, this patch series should add it, or at
>> least the eventual one that adds `bitfield!`).
>
> Yes it is. Alexandre, can you please add a maintenance section(s) for
> all new files?
It looks like we are heading towards introducing `bitfield!` a bit
later (v2 will only have `register!`). I will add a MAINTAINERS entry
once we re-introduce `bitfield!`.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 1/6] rust: num: add `shr` and `shl` methods to `Bounded`
2026-01-20 6:17 ` [PATCH 1/6] rust: num: add `shr` and `shl` methods to `Bounded` Alexandre Courbot
2026-01-20 8:44 ` Alice Ryhl
2026-01-20 16:12 ` kernel test robot
@ 2026-01-21 8:15 ` Yury Norov
2026-01-21 10:10 ` Alice Ryhl
2 siblings, 1 reply; 56+ messages in thread
From: Yury Norov @ 2026-01-21 8:15 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Yury Norov, John Hubbard, Alistair Popple,
Joel Fernandes, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Tue, Jan 20, 2026 at 03:17:54PM +0900, Alexandre Courbot wrote:
> Shifting a `Bounded` left or right changes the number of bits required
> to represent the value. Add methods that perform the shift and return a
> `Bounded` with the appropriately adjusted bit width.
>
> These methods are particularly useful for bitfield extraction.
>
> Suggested-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> ---
> rust/kernel/num/bounded.rs | 40 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 40 insertions(+)
>
> diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs
> index 5ef8361cf5d5..6e3f4a7a5262 100644
> --- a/rust/kernel/num/bounded.rs
> +++ b/rust/kernel/num/bounded.rs
> @@ -475,6 +475,46 @@ pub fn cast<U>(self) -> Bounded<U, N>
> // `N` bits, and with the same signedness.
> unsafe { Bounded::__new(value) }
> }
> +
> + /// Right-shifts `self` by `SHIFT` and returns the result as a `Bounded<_, { N - SHIFT }>`.
> + ///
> + /// # Examples
> + ///
> + /// ```
> + /// use kernel::num::Bounded;
> + ///
> + /// let v = Bounded::<u32, 16>::new::<0xff00>();
> + /// let v_shifted: Bounded::<u32, 8> = v.shr::<8, _>();
This syntax is really confusing. Does it work for runtime shifts?
Is there any chance to make it just v.shr(8)?
> + ///
> + /// assert_eq!(v_shifted.get(), 0xff);
> + /// ```
> + pub fn shr<const SHIFT: u32, const RES: u32>(self) -> Bounded<T, RES> {
> + const { assert!(RES == N - SHIFT) }
> +
> + // SAFETY: we shift the value right by `SHIFT`, reducing the number of bits needed to
> + // represent the shifted value by as much, and just asserted that `RES == N - SHIFT`.
> + unsafe { Bounded::__new(self.0 >> SHIFT) }
Assuming you relax it to RES >= N - SHIFT, as suggested by Alice,
you'd also check for SHIFT < N.
> + }
> +
> + /// Left-shifts `self` by `SHIFT` and returns the result as a `Bounded<_, { N + SHIFT }>`.
> + ///
> + /// # Examples
> + ///
> + /// ```
> + /// use kernel::num::Bounded;
> + ///
> + /// let v = Bounded::<u32, 8>::new::<0xff>();
> + /// let v_shifted: Bounded::<u32, 16> = v.shl::<8, _>();
> + ///
> + /// assert_eq!(v_shifted.get(), 0xff00);
> + /// ```
> + pub fn shl<const SHIFT: u32, const RES: u32>(self) -> Bounded<T, RES> {
> + const { assert!(RES == N + SHIFT) }
> +
> + // SAFETY: we shift the value left by `SHIFT`, augmenting the number of bits needed to
> + // represent the shifted value by as much, and just asserted that `RES == N + SHIFT`.
> + unsafe { Bounded::__new(self.0 << SHIFT) }
> + }
So, it protects most significant bits when shifting left, but doesn't
protect least significant bits when shifting right.
It also makes impossible to left-shift Bounded::<u32, 32> at all, or
shift Bounded::<u32, 31> for 2 or more bits. This doesn't look nice.
At this layer, there's seemingly nothing wrong to loose bits during
regular shift, just like non-bounded integers do. (Lets consider them
naturally bounded.) At higher layers, people may add any extra checks
as desired.
Even more, you mention you're going to use .shl and .shr for bitfield
extraction, which means you want to loose some bits intentionally.
Let's design shifts like this. Plain .shl() and .shr() will operate on
bounded integers just like '<<' and '>>' operate on non-bounded ones,
i.e. they may loose bits and the result has the same bit capacity. (But
shifting over the capacity should be be forbidden as undef).
If I want to adjust the capacity, I just do it explicitly:
let x = Bounded::<u32, 12>::new::<0x123>();
let a = x.shl(4); // 12-bit 0x230
let b = x.extend::<12+4>().shl(4) // 16-bit 0x1230
let c = x.shr(4); // 12-bit 0x012
let d = if x & 0xf { None } else { x.shr(4).try_shrink::<12-4>() }
For b and d you can invent handy helpers, of course, and for a and c
you can add 'safe' versions that will check shifted-out parts for
emptiness at runtime, in case you need it.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-20 6:17 ` [PATCH 3/6] rust: add `bitfield!` macro Alexandre Courbot
2026-01-20 11:45 ` Dirk Behme
@ 2026-01-21 9:16 ` Yury Norov
2026-01-26 13:35 ` Alexandre Courbot
1 sibling, 1 reply; 56+ messages in thread
From: Yury Norov @ 2026-01-21 9:16 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Yury Norov, John Hubbard, Alistair Popple,
Joel Fernandes, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Tue, Jan 20, 2026 at 03:17:56PM +0900, Alexandre Courbot wrote:
> Add a macro for defining bitfield structs with bounds-checked accessors.
>
> Each field is represented as a `Bounded` of the appropriate bit width,
> ensuring field values are never silently truncated.
>
> Fields can optionally be converted to/from custom types, either fallibly
> or infallibly.
>
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> ---
> rust/kernel/bitfield.rs | 503 ++++++++++++++++++++++++++++++++++++++++++++++++
> rust/kernel/lib.rs | 1 +
> 2 files changed, 504 insertions(+)
>
> diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
> new file mode 100644
> index 000000000000..2926ab802227
> --- /dev/null
> +++ b/rust/kernel/bitfield.rs
> @@ -0,0 +1,503 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Support for defining bitfields as Rust structures.
> +
> +/// Defines a bitfield struct with bounds-checked accessors for individual bit ranges.
> +///
> +/// # Example
> +///
> +/// ```rust
> +/// use kernel::bitfield;
> +/// use kernel::num::Bounded;
> +///
> +/// bitfield! {
> +/// pub struct Rgb(u16) {
> +/// 15:11 blue;
> +/// 10:5 green;
> +/// 4:0 red;
> +/// }
> +/// }
> +///
> +/// // Setters can be chained. Bounded::new::<N>() does compile-time bounds checking.
> +/// let color = Rgb::default()
> +/// .set_red(Bounded::<u16, _>::new::<0x10>())
> +/// .set_green(Bounded::<u16, _>::new::<0x1f>())
> +/// .set_blue(Bounded::<u16, _>::new::<0x18>());
Is there a way to just say:
let color = Rgb::default().
.set_red(0x10)
.set_green(0x1f)
.set_blue(0x18)
I think it should be the default style. Later in the patch you say:
Each field is internally represented as a [`Bounded`]
So, let's keep implementation decoupled from an interface?
> +///
> +/// assert_eq!(color.red(), 0x10);
> +/// assert_eq!(color.green(), 0x1f);
> +/// assert_eq!(color.blue(), 0x18);
> +/// assert_eq!(
> +/// color.as_raw(),
> +/// (0x18 << Rgb::BLUE_SHIFT) + (0x1f << Rgb::GREEN_SHIFT) + 0x10,
> +/// );
What about:
bitfield! {
pub struct Rgb(u16) {
15:11 blue;
10:5 Blue;
4:0 BLUE;
}
}
What Rgb::BLUE_SHIFT would mean in this case? Maybe Rgb::SHIFT(blue)?
> +///
> +/// // Convert to/from the backing storage type.
> +/// let raw: u16 = color.into();
What about:
bitfield! {
pub struct Rgb(u16) {
15:11 blue;
10:5 set_blue;
4:0 into;
}
}
What color.set_blue() and color.into() would mean? Even if they work,
I think, to stay on safe side there should be a more conventional set
of accessors: color.get(into), color.set(set_blue, 0xff) and son on.
Thanks,
Yury
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 1/6] rust: num: add `shr` and `shl` methods to `Bounded`
2026-01-21 8:15 ` Yury Norov
@ 2026-01-21 10:10 ` Alice Ryhl
0 siblings, 0 replies; 56+ messages in thread
From: Alice Ryhl @ 2026-01-21 10:10 UTC (permalink / raw)
To: Yury Norov
Cc: Alexandre Courbot, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Joel Fernandes, Timur Tabi, Edwin Peer,
Eliot Courtney, Daniel Almeida, Dirk Behme, Steven Price,
rust-for-linux, linux-kernel
On Wed, Jan 21, 2026 at 03:15:49AM -0500, Yury Norov wrote:
> On Tue, Jan 20, 2026 at 03:17:54PM +0900, Alexandre Courbot wrote:
> > Shifting a `Bounded` left or right changes the number of bits required
> > to represent the value. Add methods that perform the shift and return a
> > `Bounded` with the appropriately adjusted bit width.
> >
> > These methods are particularly useful for bitfield extraction.
> >
> > Suggested-by: Alice Ryhl <aliceryhl@google.com>
> > Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> > ---
> > rust/kernel/num/bounded.rs | 40 ++++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 40 insertions(+)
> >
> > diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs
> > index 5ef8361cf5d5..6e3f4a7a5262 100644
> > --- a/rust/kernel/num/bounded.rs
> > +++ b/rust/kernel/num/bounded.rs
> > @@ -475,6 +475,46 @@ pub fn cast<U>(self) -> Bounded<U, N>
> > // `N` bits, and with the same signedness.
> > unsafe { Bounded::__new(value) }
> > }
> > +
> > + /// Right-shifts `self` by `SHIFT` and returns the result as a `Bounded<_, { N - SHIFT }>`.
> > + ///
> > + /// # Examples
> > + ///
> > + /// ```
> > + /// use kernel::num::Bounded;
> > + ///
> > + /// let v = Bounded::<u32, 16>::new::<0xff00>();
> > + /// let v_shifted: Bounded::<u32, 8> = v.shr::<8, _>();
>
> This syntax is really confusing. Does it work for runtime shifts?
> Is there any chance to make it just v.shr(8)?
The ::<NUM> syntax is how you pass a build-time constant value in Rust.
The syntax 'v.shr(8)' implies that 8 is a runtime value, and not a
build-time constant. I agree it's ugly, but that's how it is.
For runtime shifts, we can make it 'v.shr(8)' or even 'v >> 8' using
operator overloading, but see my further reply below:
> > + }
> > +
> > + /// Left-shifts `self` by `SHIFT` and returns the result as a `Bounded<_, { N + SHIFT }>`.
> > + ///
> > + /// # Examples
> > + ///
> > + /// ```
> > + /// use kernel::num::Bounded;
> > + ///
> > + /// let v = Bounded::<u32, 8>::new::<0xff>();
> > + /// let v_shifted: Bounded::<u32, 16> = v.shl::<8, _>();
> > + ///
> > + /// assert_eq!(v_shifted.get(), 0xff00);
> > + /// ```
> > + pub fn shl<const SHIFT: u32, const RES: u32>(self) -> Bounded<T, RES> {
> > + const { assert!(RES == N + SHIFT) }
> > +
> > + // SAFETY: we shift the value left by `SHIFT`, augmenting the number of bits needed to
> > + // represent the shifted value by as much, and just asserted that `RES == N + SHIFT`.
> > + unsafe { Bounded::__new(self.0 << SHIFT) }
> > + }
>
> So, it protects most significant bits when shifting left, but doesn't
> protect least significant bits when shifting right.
>
> It also makes impossible to left-shift Bounded::<u32, 32> at all, or
> shift Bounded::<u32, 31> for 2 or more bits. This doesn't look nice.
>
> At this layer, there's seemingly nothing wrong to loose bits during
> regular shift, just like non-bounded integers do. (Lets consider them
> naturally bounded.) At higher layers, people may add any extra checks
> as desired.
>
> Even more, you mention you're going to use .shl and .shr for bitfield
> extraction, which means you want to loose some bits intentionally.
>
> Let's design shifts like this. Plain .shl() and .shr() will operate on
> bounded integers just like '<<' and '>>' operate on non-bounded ones,
> i.e. they may loose bits and the result has the same bit capacity. (But
> shifting over the capacity should be be forbidden as undef).
>
> If I want to adjust the capacity, I just do it explicitly:
>
> let x = Bounded::<u32, 12>::new::<0x123>();
> let a = x.shl(4); // 12-bit 0x230
> let b = x.extend::<12+4>().shl(4) // 16-bit 0x1230
> let c = x.shr(4); // 12-bit 0x012
> let d = if x & 0xf { None } else { x.shr(4).try_shrink::<12-4>() }
>
> For b and d you can invent handy helpers, of course, and for a and c
> you can add 'safe' versions that will check shifted-out parts for
> emptiness at runtime, in case you need it.
This 'shr' method came up from a discussion that Alexandre and I had at
plumbers. The entire point of them is that when you shift right by a
compile-time constant amount, you can change the bit capacity in the
type, and doing exactly that lets us remove BUILD_BUG_ON calls that Nova
has right now. (Right now Nova does a shift-right by a compile-time
constant, then uses BUILD_BUG_ON to assert that the resulting *runtime
value* has the desired target bit-capacity.)
So if we want a shift right method that doesn't change the bit-capacity,
we can do that. It can even use the ordinary '>>' operator. But the
purpose of this patch is precisely to support shifts that:
1. Shift by a compile-time constant amount.
2. Change the bit-capacity accordingly.
All that to say ... support for left-shifting Bounded::<u32, 32> and
similar certainly makes sense as something we could support, but that's
not what this patch is trying to achieve. Ultimately, the end-goal of
this patch is to allow us to get rid of BUILD_BUG_ON() calls operating
on runtime values, while still performing the same check that the
bit-capacity is as expected at build-time - just using types instead of
compiler optimizations to check it.
Alice
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-21 9:16 ` Yury Norov
@ 2026-01-26 13:35 ` Alexandre Courbot
2026-01-27 2:55 ` Yury Norov
0 siblings, 1 reply; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-26 13:35 UTC (permalink / raw)
To: Yury Norov
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Yury Norov, John Hubbard, Alistair Popple,
Joel Fernandes, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Wed Jan 21, 2026 at 6:16 PM JST, Yury Norov wrote:
> On Tue, Jan 20, 2026 at 03:17:56PM +0900, Alexandre Courbot wrote:
>> Add a macro for defining bitfield structs with bounds-checked accessors.
>>
>> Each field is represented as a `Bounded` of the appropriate bit width,
>> ensuring field values are never silently truncated.
>>
>> Fields can optionally be converted to/from custom types, either fallibly
>> or infallibly.
>>
>> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
>> ---
>> rust/kernel/bitfield.rs | 503 ++++++++++++++++++++++++++++++++++++++++++++++++
>> rust/kernel/lib.rs | 1 +
>> 2 files changed, 504 insertions(+)
>>
>> diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
>> new file mode 100644
>> index 000000000000..2926ab802227
>> --- /dev/null
>> +++ b/rust/kernel/bitfield.rs
>> @@ -0,0 +1,503 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +
>> +//! Support for defining bitfields as Rust structures.
>> +
>> +/// Defines a bitfield struct with bounds-checked accessors for individual bit ranges.
>> +///
>> +/// # Example
>> +///
>> +/// ```rust
>> +/// use kernel::bitfield;
>> +/// use kernel::num::Bounded;
>> +///
>> +/// bitfield! {
>> +/// pub struct Rgb(u16) {
>> +/// 15:11 blue;
>> +/// 10:5 green;
>> +/// 4:0 red;
>> +/// }
>> +/// }
>> +///
>> +/// // Setters can be chained. Bounded::new::<N>() does compile-time bounds checking.
>> +/// let color = Rgb::default()
>> +/// .set_red(Bounded::<u16, _>::new::<0x10>())
>> +/// .set_green(Bounded::<u16, _>::new::<0x1f>())
>> +/// .set_blue(Bounded::<u16, _>::new::<0x18>());
>
> Is there a way to just say:
>
> let color = Rgb::default().
> .set_red(0x10)
> .set_green(0x1f)
> .set_blue(0x18)
>
> I think it should be the default style. Later in the patch you say:
>
> Each field is internally represented as a [`Bounded`]
>
> So, let's keep implementation decoupled from an interface?
That is unfortunately not feasible, but the syntax above should seldomly
be used outside of examples.
>
>> +///
>> +/// assert_eq!(color.red(), 0x10);
>> +/// assert_eq!(color.green(), 0x1f);
>> +/// assert_eq!(color.blue(), 0x18);
>> +/// assert_eq!(
>> +/// color.as_raw(),
>> +/// (0x18 << Rgb::BLUE_SHIFT) + (0x1f << Rgb::GREEN_SHIFT) + 0x10,
>> +/// );
>
> What about:
>
> bitfield! {
> pub struct Rgb(u16) {
> 15:11 blue;
> 10:5 Blue;
> 4:0 BLUE;
> }
> }
>
Oh, all of these will name-clash very badly. :) At the end of the day,
it is still a macro.
> What Rgb::BLUE_SHIFT would mean in this case? Maybe Rgb::SHIFT(blue)?
You wouldn't even have the luxury to yse `BLUE_SHIFT` here because where
would be conflicting definitions and thus a build error.
>
>> +///
>> +/// // Convert to/from the backing storage type.
>> +/// let raw: u16 = color.into();
>
> What about:
>
> bitfield! {
> pub struct Rgb(u16) {
> 15:11 blue;
> 10:5 set_blue;
> 4:0 into;
> }
> }
>
> What color.set_blue() and color.into() would mean? Even if they work,
> I think, to stay on safe side there should be a more conventional set
> of accessors: color.get(into), color.set(set_blue, 0xff) and son on.
This would just not build.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 0/6] rust: add `bitfield!` and `register!` macros
2026-01-21 5:57 ` Yury Norov
2026-01-21 6:55 ` Alexandre Courbot
@ 2026-01-26 14:03 ` Joel Fernandes
1 sibling, 0 replies; 56+ messages in thread
From: Joel Fernandes @ 2026-01-26 14:03 UTC (permalink / raw)
To: Yury Norov
Cc: Miguel Ojeda, Danilo Krummrich, Alexandre Courbot, Yury Norov,
Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
John Hubbard, Alistair Popple, Timur Tabi, Edwin Peer,
Eliot Courtney, Daniel Almeida, Dirk Behme, Steven Price,
rust-for-linux, linux-kernel
On Jan 21, 2026, at 12:57 AM, Yury Norov <ynorov@nvidia.com> wrote:
> On Tue, Jan 20, 2026 at 09:31:14PM +0100, Miguel Ojeda wrote:
>>> On Tue, Jan 20, 2026 at 9:01 PM Danilo Krummrich <dakr@kernel.org> wrote:
>>>
>>> I think it's rather unlikely to land bitfields this cycle. I think Yury
>>> explicitly requested more discussion on bitfields and also encouraged
>>> the "two stage" approach [1] moving register!() first and then extract
>>> bitfields subsequently.
>>
>> Does Yury want to maintain it? From his messages back then I am not
>> sure if he does or not (he suggested "some non-rust person from a
>> related kernel subsystem", not sure if that meant himself in this case
>> too).
>
> I can become a reviewer or maintainer, assuming Alex will become the first
> maintainer as the main developer. I can also move it with my branch if you
> prefer, guys. In this case it's better to make me a maintainer.
Happy to co-maintain it with you guys as well, since the initial idea to add a
Bitfield macro to use for Bitfield and the initial work to improve it based on
nova's register macro came from me. I am also intimately familiar with it due
to usage with the nova memory management series:
https://lore.kernel.org/all/20260120204303.3229303-1-joelagnelf@nvidia.com/
>> At least, he suggested adding a `MAINTAINERS` entry, which I agreed it
>> was a good idea (by the way, this patch series should add it, or at
>> least the eventual one that adds `bitfield!`).
>
> Yes it is. Alexandre, can you please add a maintenance section(s) for
> all new files?
Agreed.
--
Joel Fernandes
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-26 13:35 ` Alexandre Courbot
@ 2026-01-27 2:55 ` Yury Norov
2026-01-27 3:25 ` Joel Fernandes
2026-01-27 9:57 ` Alexandre Courbot
0 siblings, 2 replies; 56+ messages in thread
From: Yury Norov @ 2026-01-27 2:55 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Yury Norov, John Hubbard, Alistair Popple,
Joel Fernandes, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Mon, Jan 26, 2026 at 10:35:49PM +0900, Alexandre Courbot wrote:
> On Wed Jan 21, 2026 at 6:16 PM JST, Yury Norov wrote:
> > On Tue, Jan 20, 2026 at 03:17:56PM +0900, Alexandre Courbot wrote:
> >> Add a macro for defining bitfield structs with bounds-checked accessors.
> >>
> >> Each field is represented as a `Bounded` of the appropriate bit width,
> >> ensuring field values are never silently truncated.
> >>
> >> Fields can optionally be converted to/from custom types, either fallibly
> >> or infallibly.
> >>
> >> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> >> ---
> >> rust/kernel/bitfield.rs | 503 ++++++++++++++++++++++++++++++++++++++++++++++++
> >> rust/kernel/lib.rs | 1 +
> >> 2 files changed, 504 insertions(+)
> >>
> >> diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
> >> new file mode 100644
> >> index 000000000000..2926ab802227
> >> --- /dev/null
> >> +++ b/rust/kernel/bitfield.rs
> >> @@ -0,0 +1,503 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +
> >> +//! Support for defining bitfields as Rust structures.
> >> +
> >> +/// Defines a bitfield struct with bounds-checked accessors for individual bit ranges.
> >> +///
> >> +/// # Example
> >> +///
> >> +/// ```rust
> >> +/// use kernel::bitfield;
> >> +/// use kernel::num::Bounded;
> >> +///
> >> +/// bitfield! {
> >> +/// pub struct Rgb(u16) {
> >> +/// 15:11 blue;
> >> +/// 10:5 green;
> >> +/// 4:0 red;
> >> +/// }
> >> +/// }
> >> +///
> >> +/// // Setters can be chained. Bounded::new::<N>() does compile-time bounds checking.
> >> +/// let color = Rgb::default()
> >> +/// .set_red(Bounded::<u16, _>::new::<0x10>())
> >> +/// .set_green(Bounded::<u16, _>::new::<0x1f>())
> >> +/// .set_blue(Bounded::<u16, _>::new::<0x18>());
> >
> > Is there a way to just say:
> >
> > let color = Rgb::default().
> > .set_red(0x10)
> > .set_green(0x1f)
> > .set_blue(0x18)
> >
> > I think it should be the default style. Later in the patch you say:
> >
> > Each field is internally represented as a [`Bounded`]
> >
> > So, let's keep implementation decoupled from an interface?
>
> That is unfortunately not feasible, but the syntax above should seldomly
> be used outside of examples.
The above short syntax is definitely more desired over that wordy and
non-trivial version that exposes implementation internals.
A regular user doesn't care of the exact mechanism that protects the
bitfields. He wants to just assign numbers to the fields, and let
your machinery to take care of the integrity.
Can you please explain in details why that's not feasible, please
do it in commit message. If it's an implementation constraint,
please consider to re-implement.
> >> +///
> >> +/// assert_eq!(color.red(), 0x10);
> >> +/// assert_eq!(color.green(), 0x1f);
> >> +/// assert_eq!(color.blue(), 0x18);
> >> +/// assert_eq!(
> >> +/// color.as_raw(),
> >> +/// (0x18 << Rgb::BLUE_SHIFT) + (0x1f << Rgb::GREEN_SHIFT) + 0x10,
> >> +/// );
> >
> > What about:
> >
> > bitfield! {
> > pub struct Rgb(u16) {
> > 15:11 blue;
> > 10:5 Blue;
> > 4:0 BLUE;
> > }
> > }
> >
>
> Oh, all of these will name-clash very badly. :) At the end of the day,
> it is still a macro.
>
> > What Rgb::BLUE_SHIFT would mean in this case? Maybe Rgb::SHIFT(blue)?
>
> You wouldn't even have the luxury to yse `BLUE_SHIFT` here because where
> would be conflicting definitions and thus a build error.
>
> >
> >> +///
> >> +/// // Convert to/from the backing storage type.
> >> +/// let raw: u16 = color.into();
> >
> > What about:
> >
> > bitfield! {
> > pub struct Rgb(u16) {
> > 15:11 blue;
> > 10:5 set_blue;
> > 4:0 into;
> > }
> > }
> >
> > What color.set_blue() and color.into() would mean? Even if they work,
> > I think, to stay on safe side there should be a more conventional set
> > of accessors: color.get(into), color.set(set_blue, 0xff) and son on.
>
> This would just not build.
I know it wouldn't. I am just trying to understand corner cases that may
(and would!) frustrate people for decades.
I understand that this implementation works just great for the registers,
but my role is to make sure that it would work equally well for everyone.
Now, for example, Rust, contrary to C in Linux, actively uses camel case.
So, the blue vs Blue vs BLUE restriction is a very valid concern. The
same for reserved words like 'into'. As long as the API matures, the
number of such special words would only grow. The .shr() and .shl() that
you add in this series are the quite good examples.
Let's make a step back and just list all limitations that come with this
implementation.
Again, this all is relevant for a basic generic data structure. If we
consider it a supporting layer for the registers, everything is totally
fine. In that case, we should just give it a more specific name, and
probably place in an different directory, closer to IO APIs.
Thanks,
Yury
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-27 2:55 ` Yury Norov
@ 2026-01-27 3:25 ` Joel Fernandes
2026-01-27 4:49 ` Yury Norov
2026-01-27 15:02 ` Gary Guo
2026-01-27 9:57 ` Alexandre Courbot
1 sibling, 2 replies; 56+ messages in thread
From: Joel Fernandes @ 2026-01-27 3:25 UTC (permalink / raw)
To: Yury Norov
Cc: Alexandre Courbot, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Jan 26, 2026, at 9:55 PM, Yury Norov <ynorov@nvidia.com> wrote:
> On Mon, Jan 26, 2026 at 10:35:49PM +0900, Alexandre Courbot wrote:
> > On Wed Jan 21, 2026 at 6:16 PM JST, Yury Norov wrote:
> > > On Tue, Jan 20, 2026 at 03:17:56PM +0900, Alexandre Courbot wrote:
> > > > Add a macro for defining bitfield structs with bounds-checked accessors.
> > > >
> > > > Each field is represented as a `Bounded` of the appropriate bit width,
> > > > ensuring field values are never silently truncated.
> > > >
> > > > Fields can optionally be converted to/from custom types, either fallibly
> > > > or infallibly.
> > > >
> > > > Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> > > > ---
> > > > rust/kernel/bitfield.rs | 503 ++++++++++++++++++++++++++++++++++++++++++++++++
> > > > rust/kernel/lib.rs | 1 +
> > > > 2 files changed, 504 insertions(+)
[...]
> > > > +/// // Setters can be chained. Bounded::new::<N>() does compile-time bounds checking.
> > > > +/// let color = Rgb::default()
> > > > +/// .set_red(Bounded::<u16, _>::new::<0x10>())
> > > > +/// .set_green(Bounded::<u16, _>::new::<0x1f>())
> > > > +/// .set_blue(Bounded::<u16, _>::new::<0x18>());
> > >
> > > Is there a way to just say:
> > >
> > > let color = Rgb::default().
> > > .set_red(0x10)
> > > .set_green(0x1f)
> > > .set_blue(0x18)
> > >
> > > I think it should be the default style. Later in the patch you say:
> > >
> > > Each field is internally represented as a [`Bounded`]
> > >
> > > So, let's keep implementation decoupled from an interface?
> >
> > That is unfortunately not feasible, but the syntax above should seldomly
> > be used outside of examples.
>
> The above short syntax is definitely more desired over that wordy and
> non-trivial version that exposes implementation internals.
>
> A regular user doesn't care of the exact mechanism that protects the
> bitfields. He wants to just assign numbers to the fields, and let
> your machinery to take care of the integrity.
>
> Can you please explain in details why that's not feasible, please
> do it in commit message. If it's an implementation constraint,
> please consider to re-implement.
If the issue is the excessive turbofish syntax, how about a macro? For
example:
let color = Rgb::default()
.set_red(bounded!(u16, 0x10))
.set_green(bounded!(u16, 0x1f))
.set_blue(bounded!(u16, 0x18));
This hides the turbofish and Bounded internals while still providing
compile-time bounds checking.
[...]
> > > What Rgb::BLUE_SHIFT would mean in this case? Maybe Rgb::SHIFT(blue)?
> >
> > You wouldn't even have the luxury to yse `BLUE_SHIFT` here because where
> > would be conflicting definitions and thus a build error.
[...]
> > > What color.set_blue() and color.into() would mean? Even if they work,
> > > I think, to stay on safe side there should be a more conventional set
> > > of accessors: color.get(into), color.set(set_blue, 0xff) and son on.
> >
> > This would just not build.
>
> I know it wouldn't. I am just trying to understand corner cases that may
> (and would!) frustrate people for decades.
>
> I understand that this implementation works just great for the registers,
> but my role is to make sure that it would work equally well for everyone.
> Now, for example, Rust, contrary to C in Linux, actively uses camel case.
> So, the blue vs Blue vs BLUE restriction is a very valid concern. The
> same for reserved words like 'into'. As long as the API matures, the
> number of such special words would only grow. The .shr() and .shl() that
> you add in this series are the quite good examples.
>
> Let's make a step back and just list all limitations that come with this
> implementation.
Why is a build error not sufficient to alert the user to use better judgement
for naming?
This is no different than using reserved keywords in C. For example, this
won't compile:
int if = 5; // error: 'if' is a reserved keyword
int return = 3; // error: 'return' is a reserved keyword
The user simply learns not to use reserved words, and the compiler enforces
this clearly. The same applies here.
> Again, this all is relevant for a basic generic data structure. If we
> consider it a supporting layer for the registers, everything is totally
> fine. In that case, we should just give it a more specific name, and
> probably place in an different directory, closer to IO APIs.
The Bitfield macro is very much required for non-register use cases too.
--
Joel Fernandes
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-27 3:25 ` Joel Fernandes
@ 2026-01-27 4:49 ` Yury Norov
2026-01-27 10:41 ` Alexandre Courbot
2026-01-27 11:00 ` Joel Fernandes
2026-01-27 15:02 ` Gary Guo
1 sibling, 2 replies; 56+ messages in thread
From: Yury Norov @ 2026-01-27 4:49 UTC (permalink / raw)
To: Joel Fernandes
Cc: Alexandre Courbot, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Mon, Jan 26, 2026 at 10:25:54PM -0500, Joel Fernandes wrote:
> On Jan 26, 2026, at 9:55 PM, Yury Norov <ynorov@nvidia.com> wrote:
> > On Mon, Jan 26, 2026 at 10:35:49PM +0900, Alexandre Courbot wrote:
> > > On Wed Jan 21, 2026 at 6:16 PM JST, Yury Norov wrote:
> > > > On Tue, Jan 20, 2026 at 03:17:56PM +0900, Alexandre Courbot wrote:
> > > > > Add a macro for defining bitfield structs with bounds-checked accessors.
> > > > >
> > > > > Each field is represented as a `Bounded` of the appropriate bit width,
> > > > > ensuring field values are never silently truncated.
> > > > >
> > > > > Fields can optionally be converted to/from custom types, either fallibly
> > > > > or infallibly.
> > > > >
> > > > > Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> > > > > ---
> > > > > rust/kernel/bitfield.rs | 503 ++++++++++++++++++++++++++++++++++++++++++++++++
> > > > > rust/kernel/lib.rs | 1 +
> > > > > 2 files changed, 504 insertions(+)
> [...]
> > > > > +/// // Setters can be chained. Bounded::new::<N>() does compile-time bounds checking.
> > > > > +/// let color = Rgb::default()
> > > > > +/// .set_red(Bounded::<u16, _>::new::<0x10>())
> > > > > +/// .set_green(Bounded::<u16, _>::new::<0x1f>())
> > > > > +/// .set_blue(Bounded::<u16, _>::new::<0x18>());
> > > >
> > > > Is there a way to just say:
> > > >
> > > > let color = Rgb::default().
> > > > .set_red(0x10)
> > > > .set_green(0x1f)
> > > > .set_blue(0x18)
> > > >
> > > > I think it should be the default style. Later in the patch you say:
> > > >
> > > > Each field is internally represented as a [`Bounded`]
> > > >
> > > > So, let's keep implementation decoupled from an interface?
> > >
> > > That is unfortunately not feasible, but the syntax above should seldomly
> > > be used outside of examples.
> >
> > The above short syntax is definitely more desired over that wordy and
> > non-trivial version that exposes implementation internals.
> >
> > A regular user doesn't care of the exact mechanism that protects the
> > bitfields. He wants to just assign numbers to the fields, and let
> > your machinery to take care of the integrity.
> >
> > Can you please explain in details why that's not feasible, please
> > do it in commit message. If it's an implementation constraint,
> > please consider to re-implement.
>
> If the issue is the excessive turbofish syntax, how about a macro? For
> example:
>
> let color = Rgb::default()
> .set_red(bounded!(u16, 0x10))
> .set_green(bounded!(u16, 0x1f))
> .set_blue(bounded!(u16, 0x18));
>
> This hides the turbofish and Bounded internals while still providing
> compile-time bounds checking.
No it does not. I clearly see this 'bounded' in your version. Can you
completely hide it inside the setter?
> [...]
> > > > What Rgb::BLUE_SHIFT would mean in this case? Maybe Rgb::SHIFT(blue)?
> > >
> > > You wouldn't even have the luxury to yse `BLUE_SHIFT` here because where
> > > would be conflicting definitions and thus a build error.
> [...]
> > > > What color.set_blue() and color.into() would mean? Even if they work,
> > > > I think, to stay on safe side there should be a more conventional set
> > > > of accessors: color.get(into), color.set(set_blue, 0xff) and son on.
> > >
> > > This would just not build.
> >
> > I know it wouldn't. I am just trying to understand corner cases that may
> > (and would!) frustrate people for decades.
> >
> > I understand that this implementation works just great for the registers,
> > but my role is to make sure that it would work equally well for everyone.
> > Now, for example, Rust, contrary to C in Linux, actively uses camel case.
> > So, the blue vs Blue vs BLUE restriction is a very valid concern. The
> > same for reserved words like 'into'. As long as the API matures, the
> > number of such special words would only grow. The .shr() and .shl() that
> > you add in this series are the quite good examples.
> >
> > Let's make a step back and just list all limitations that come with this
> > implementation.
>
> Why is a build error not sufficient to alert the user to use better judgement
> for naming?
There should be no error at all - that's why. Any name that one can
use for a regular variable should be acceptable as a field name.
> This is no different than using reserved keywords in C. For example, this
> won't compile:
>
> int if = 5; // error: 'if' is a reserved keyword
> int return = 3; // error: 'return' is a reserved keyword
'Into', 'as_raw', 'shr' and 'shl' *are not* the reserved words in Rust.
Rust makes difference between upper and lower cases and doesn't break
build if you do:
let blue = 1;
let BLUE = 2;
So the bitfields should.
> The user simply learns not to use reserved words, and the compiler enforces
> this clearly. The same applies here.
Most likely he will learn not to use this API at all. The worst thing about
those 'reserved' words is that the set of them is not definite and will
constantly grow.
I'm trying to say that this approach is not scalable. If a random client
gives name 'invert' to one of his fields, and you'll need to implement a
method inverting bits, what are you going to do?
> > Again, this all is relevant for a basic generic data structure. If we
> > consider it a supporting layer for the registers, everything is totally
> > fine. In that case, we should just give it a more specific name, and
> > probably place in an different directory, closer to IO APIs.
>
> The Bitfield macro is very much required for non-register use cases too.
Then let's implement it better. Can you comment why the suggested API
doesn't work for you?
color.set(blue, 10);
color.get(blue);
I think it should solve the problem with name clashing.
Can you share more about the other potential users?
Thanks,
Yury
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-27 2:55 ` Yury Norov
2026-01-27 3:25 ` Joel Fernandes
@ 2026-01-27 9:57 ` Alexandre Courbot
2026-01-27 21:03 ` John Hubbard
1 sibling, 1 reply; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-27 9:57 UTC (permalink / raw)
To: Yury Norov
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Yury Norov, John Hubbard, Alistair Popple,
Joel Fernandes, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Tue Jan 27, 2026 at 11:55 AM JST, Yury Norov wrote:
<snip>
>> >> +/// // Setters can be chained. Bounded::new::<N>() does compile-time bounds checking.
>> >> +/// let color = Rgb::default()
>> >> +/// .set_red(Bounded::<u16, _>::new::<0x10>())
>> >> +/// .set_green(Bounded::<u16, _>::new::<0x1f>())
>> >> +/// .set_blue(Bounded::<u16, _>::new::<0x18>());
>> >
>> > Is there a way to just say:
>> >
>> > let color = Rgb::default().
>> > .set_red(0x10)
>> > .set_green(0x1f)
>> > .set_blue(0x18)
>> >
>> > I think it should be the default style. Later in the patch you say:
>> >
>> > Each field is internally represented as a [`Bounded`]
>> >
>> > So, let's keep implementation decoupled from an interface?
>>
>> That is unfortunately not feasible, but the syntax above should seldomly
>> be used outside of examples.
>
> The above short syntax is definitely more desired over that wordy and
> non-trivial version that exposes implementation internals.
>
> A regular user doesn't care of the exact mechanism that protects the
> bitfields. He wants to just assign numbers to the fields, and let
> your machinery to take care of the integrity.
So while we cannot achieve exactly the short syntax above (which has its
drawbacks as well, such as the inability to operate in const context),
we can introduce a new setter than works with a const argument and
spares us the need to invoke `Bounded::new` ourselves:
let color = Rgb::default().
.with_red::<0x10>()
.with_green::<0x1f>()
.with_blue::<0x18>()
This setter knows which type (and thus which Bounded constructor) to
work with, and is much more concise. It also makes it clear that it
operates in const context, i.e. `color` will directly be
set to the final value. Actually let me check whether we can make the
whole chain `const`.
<snip>
>> >
>> >> +///
>> >> +/// // Convert to/from the backing storage type.
>> >> +/// let raw: u16 = color.into();
>> >
>> > What about:
>> >
>> > bitfield! {
>> > pub struct Rgb(u16) {
>> > 15:11 blue;
>> > 10:5 set_blue;
>> > 4:0 into;
>> > }
>> > }
>> >
>> > What color.set_blue() and color.into() would mean? Even if they work,
>> > I think, to stay on safe side there should be a more conventional set
>> > of accessors: color.get(into), color.set(set_blue, 0xff) and son on.
>>
>> This would just not build.
>
> I know it wouldn't. I am just trying to understand corner cases that may
> (and would!) frustrate people for decades.
>
> I understand that this implementation works just great for the registers,
> but my role is to make sure that it would work equally well for everyone.
> Now, for example, Rust, contrary to C in Linux, actively uses camel case.
> So, the blue vs Blue vs BLUE restriction is a very valid concern. The
> same for reserved words like 'into'. As long as the API matures, the
> number of such special words would only grow. The .shr() and .shl() that
> you add in this series are the quite good examples.
The strong Rust naming conventions are actually (partially) shielding us
from name clashing here.
In Rust, fields are by convention typed in snake_case. So the blue vs
Blue vs BLUE example would only be relevant if one breaks these
conventions.
That being said, we could also avoid the capitalization of the field
constants to avoid name clashing. Or we could have a const method
returning the shift, range and masks of a field as a structure. But we
cannot avoid adding new elements to the namespace, unless we start doing
contorted (and probably quite verbose) things, like having an enum type
describing the fields and a unique setter function using it to know
which field to set.
>
> Let's make a step back and just list all limitations that come with this
> implementation.
We should at the very least mention in the documentation what members
are generated so users know the limits of what they can do, yes.
>
> Again, this all is relevant for a basic generic data structure. If we
> consider it a supporting layer for the registers, everything is totally
> fine. In that case, we should just give it a more specific name, and
> probably place in an different directory, closer to IO APIs.
v2 and v3 of this patchset actually embed the bitfield rules into the
register macro. This is designed to be temporary, but gives us more time
to iron the details you pointed out before extracting the `bitfield!`
macro and making it widely available.
In any case, I will include the `with_` setters in v4 so we can use that
in the register macro already.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-27 4:49 ` Yury Norov
@ 2026-01-27 10:41 ` Alexandre Courbot
2026-01-27 10:55 ` Miguel Ojeda
2026-01-28 5:27 ` Yury Norov
2026-01-27 11:00 ` Joel Fernandes
1 sibling, 2 replies; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-27 10:41 UTC (permalink / raw)
To: Yury Norov
Cc: Joel Fernandes, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Tue Jan 27, 2026 at 1:49 PM JST, Yury Norov wrote:
> On Mon, Jan 26, 2026 at 10:25:54PM -0500, Joel Fernandes wrote:
>> On Jan 26, 2026, at 9:55 PM, Yury Norov <ynorov@nvidia.com> wrote:
>> > On Mon, Jan 26, 2026 at 10:35:49PM +0900, Alexandre Courbot wrote:
>> > > On Wed Jan 21, 2026 at 6:16 PM JST, Yury Norov wrote:
>> > > > On Tue, Jan 20, 2026 at 03:17:56PM +0900, Alexandre Courbot wrote:
>> > > > > Add a macro for defining bitfield structs with bounds-checked accessors.
>> > > > >
>> > > > > Each field is represented as a `Bounded` of the appropriate bit width,
>> > > > > ensuring field values are never silently truncated.
>> > > > >
>> > > > > Fields can optionally be converted to/from custom types, either fallibly
>> > > > > or infallibly.
>> > > > >
>> > > > > Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
>> > > > > ---
>> > > > > rust/kernel/bitfield.rs | 503 ++++++++++++++++++++++++++++++++++++++++++++++++
>> > > > > rust/kernel/lib.rs | 1 +
>> > > > > 2 files changed, 504 insertions(+)
>> [...]
>> > > > > +/// // Setters can be chained. Bounded::new::<N>() does compile-time bounds checking.
>> > > > > +/// let color = Rgb::default()
>> > > > > +/// .set_red(Bounded::<u16, _>::new::<0x10>())
>> > > > > +/// .set_green(Bounded::<u16, _>::new::<0x1f>())
>> > > > > +/// .set_blue(Bounded::<u16, _>::new::<0x18>());
>> > > >
>> > > > Is there a way to just say:
>> > > >
>> > > > let color = Rgb::default().
>> > > > .set_red(0x10)
>> > > > .set_green(0x1f)
>> > > > .set_blue(0x18)
>> > > >
>> > > > I think it should be the default style. Later in the patch you say:
>> > > >
>> > > > Each field is internally represented as a [`Bounded`]
>> > > >
>> > > > So, let's keep implementation decoupled from an interface?
>> > >
>> > > That is unfortunately not feasible, but the syntax above should seldomly
>> > > be used outside of examples.
>> >
>> > The above short syntax is definitely more desired over that wordy and
>> > non-trivial version that exposes implementation internals.
>> >
>> > A regular user doesn't care of the exact mechanism that protects the
>> > bitfields. He wants to just assign numbers to the fields, and let
>> > your machinery to take care of the integrity.
>> >
>> > Can you please explain in details why that's not feasible, please
>> > do it in commit message. If it's an implementation constraint,
>> > please consider to re-implement.
>>
>> If the issue is the excessive turbofish syntax, how about a macro? For
>> example:
>>
>> let color = Rgb::default()
>> .set_red(bounded!(u16, 0x10))
>> .set_green(bounded!(u16, 0x1f))
>> .set_blue(bounded!(u16, 0x18));
>>
>> This hides the turbofish and Bounded internals while still providing
>> compile-time bounds checking.
>
> No it does not. I clearly see this 'bounded' in your version. Can you
> completely hide it inside the setter?
Hopefully the `with_` setter I proposed in my other email settles this
issue.
>
>> [...]
>> > > > What Rgb::BLUE_SHIFT would mean in this case? Maybe Rgb::SHIFT(blue)?
>> > >
>> > > You wouldn't even have the luxury to yse `BLUE_SHIFT` here because where
>> > > would be conflicting definitions and thus a build error.
>> [...]
>> > > > What color.set_blue() and color.into() would mean? Even if they work,
>> > > > I think, to stay on safe side there should be a more conventional set
>> > > > of accessors: color.get(into), color.set(set_blue, 0xff) and son on.
>> > >
>> > > This would just not build.
>> >
>> > I know it wouldn't. I am just trying to understand corner cases that may
>> > (and would!) frustrate people for decades.
>> >
>> > I understand that this implementation works just great for the registers,
>> > but my role is to make sure that it would work equally well for everyone.
>> > Now, for example, Rust, contrary to C in Linux, actively uses camel case.
>> > So, the blue vs Blue vs BLUE restriction is a very valid concern. The
>> > same for reserved words like 'into'. As long as the API matures, the
>> > number of such special words would only grow. The .shr() and .shl() that
>> > you add in this series are the quite good examples.
>> >
>> > Let's make a step back and just list all limitations that come with this
>> > implementation.
>>
>> Why is a build error not sufficient to alert the user to use better judgement
>> for naming?
>
> There should be no error at all - that's why. Any name that one can
> use for a regular variable should be acceptable as a field name.
>
>> This is no different than using reserved keywords in C. For example, this
>> won't compile:
>>
>> int if = 5; // error: 'if' is a reserved keyword
>> int return = 3; // error: 'return' is a reserved keyword
>
> 'Into', 'as_raw', 'shr' and 'shl' *are not* the reserved words in Rust.
> Rust makes difference between upper and lower cases and doesn't break
> build if you do:
>
> let blue = 1;
> let BLUE = 2;
>
> So the bitfields should.
We can solve this case if we break the Rust naming conventions for
constants and keep the original case for the field, e.g. `blue_SHIFT`
and `BLUE_SHIFT`.
But again, by convention fields should all be snake_case or it would
look weird in Rust code.
>
>> The user simply learns not to use reserved words, and the compiler enforces
>> this clearly. The same applies here.
>
> Most likely he will learn not to use this API at all. The worst thing about
> those 'reserved' words is that the set of them is not definite and will
> constantly grow.
>
> I'm trying to say that this approach is not scalable. If a random client
> gives name 'invert' to one of his fields, and you'll need to implement a
> method inverting bits, what are you going to do?
Bitfields are limited to a _get, a _set, and an _update methods (and
possibly try_ variants for the last two). If an `invert` method needs to
be implemented, it can be done on top of _update which takes a closure.
So I am pretty confident we won't need to extend the API beyond these
(famous last words).
>
>> > Again, this all is relevant for a basic generic data structure. If we
>> > consider it a supporting layer for the registers, everything is totally
>> > fine. In that case, we should just give it a more specific name, and
>> > probably place in an different directory, closer to IO APIs.
>>
>> The Bitfield macro is very much required for non-register use cases too.
>
> Then let's implement it better. Can you comment why the suggested API
> doesn't work for you?
>
> color.set(blue, 10);
> color.get(blue);
>
> I think it should solve the problem with name clashing.
That syntax cannot be implemented as it is written. What type is `blue` here?
The closest we could get would be a macro, that would look like
bitfield_set!(color, blue, 10);
And beyond the scenes it would call some more intricate (and unsightly)
machinery. I'd rather define the constraints clearly for users - they
are not so drastic.
>
> Can you share more about the other potential users?
I know Joel is using bitfield for page table structures, but there are
of course many others. Basically any structure with fields defined as a
subset of its bits is a candidate. Registers just happen to be bitfields
with extra properties for I/O.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-27 10:41 ` Alexandre Courbot
@ 2026-01-27 10:55 ` Miguel Ojeda
2026-01-28 5:27 ` Yury Norov
1 sibling, 0 replies; 56+ messages in thread
From: Miguel Ojeda @ 2026-01-27 10:55 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Yury Norov, Joel Fernandes, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Tue, Jan 27, 2026 at 11:41 AM Alexandre Courbot <acourbot@nvidia.com> wrote:
>
> The closest we could get would be a macro, that would look like
>
> bitfield_set!(color, blue, 10);
>
> And beyond the scenes it would call some more intricate (and unsightly)
> machinery. I'd rather define the constraints clearly for users - they
> are not so drastic.
Yeah, our guideline has always been to avoid macros unless really needed.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-27 4:49 ` Yury Norov
2026-01-27 10:41 ` Alexandre Courbot
@ 2026-01-27 11:00 ` Joel Fernandes
1 sibling, 0 replies; 56+ messages in thread
From: Joel Fernandes @ 2026-01-27 11:00 UTC (permalink / raw)
To: Yury Norov
Cc: Alexandre Courbot, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
> On Jan 26, 2026, at 11:49 PM, Yury Norov <ynorov@nvidia.com> wrote:
>
> On Mon, Jan 26, 2026 at 10:25:54PM -0500, Joel Fernandes wrote:
>>> On Jan 26, 2026, at 9:55 PM, Yury Norov <ynorov@nvidia.com> wrote:
>>> On Mon, Jan 26, 2026 at 10:35:49PM +0900, Alexandre Courbot wrote:
>>>> On Wed Jan 21, 2026 at 6:16 PM JST, Yury Norov wrote:
>>>>> On Tue, Jan 20, 2026 at 03:17:56PM +0900, Alexandre Courbot wrote:
>>>>>> Add a macro for defining bitfield structs with bounds-checked accessors.
>>>>>>
>>>>>> Each field is represented as a `Bounded` of the appropriate bit width,
>>>>>> ensuring field values are never silently truncated.
>>>>>>
>>>>>> Fields can optionally be converted to/from custom types, either fallibly
>>>>>> or infallibly.
>>>>>>
>>>>>> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
>>>>>> ---
>>>>>> rust/kernel/bitfield.rs | 503 ++++++++++++++++++++++++++++++++++++++++++++++++
>>>>>> rust/kernel/lib.rs | 1 +
>>>>>> 2 files changed, 504 insertions(+)
>> [...]
>>>>>> +/// // Setters can be chained. Bounded::new::<N>() does compile-time bounds checking.
>>>>>> +/// let color = Rgb::default()
>>>>>> +/// .set_red(Bounded::<u16, _>::new::<0x10>())
>>>>>> +/// .set_green(Bounded::<u16, _>::new::<0x1f>())
>>>>>> +/// .set_blue(Bounded::<u16, _>::new::<0x18>());
>>>>>
>>>>> Is there a way to just say:
>>>>>
>>>>> let color = Rgb::default().
>>>>> .set_red(0x10)
>>>>> .set_green(0x1f)
>>>>> .set_blue(0x18)
>>>>>
>>>>> I think it should be the default style. Later in the patch you say:
>>>>>
>>>>> Each field is internally represented as a [`Bounded`]
>>>>>
>>>>> So, let's keep implementation decoupled from an interface?
>>>>
>>>> That is unfortunately not feasible, but the syntax above should seldomly
>>>> be used outside of examples.
>>>
>>> The above short syntax is definitely more desired over that wordy and
>>> non-trivial version that exposes implementation internals.
>>>
>>> A regular user doesn't care of the exact mechanism that protects the
>>> bitfields. He wants to just assign numbers to the fields, and let
>>> your machinery to take care of the integrity.
>>>
>>> Can you please explain in details why that's not feasible, please
>>> do it in commit message. If it's an implementation constraint,
>>> please consider to re-implement.
>>
>> If the issue is the excessive turbofish syntax, how about a macro? For
>> example:
>>
>> let color = Rgb::default()
>> .set_red(bounded!(u16, 0x10))
>> .set_green(bounded!(u16, 0x1f))
>> .set_blue(bounded!(u16, 0x18));
>>
>> This hides the turbofish and Bounded internals while still providing
>> compile-time bounds checking.
>
> No it does not. I clearly see this 'bounded' in your version. Can you
> completely hide it inside the setter?
Looks like Alex replied with something that does that. So it is good.
>
>> [...]
>>>>> What Rgb::BLUE_SHIFT would mean in this case? Maybe Rgb::SHIFT(blue)?
>>>>
>>>> You wouldn't even have the luxury to yse `BLUE_SHIFT` here because where
>>>> would be conflicting definitions and thus a build error.
>> [...]
>>>>> What color.set_blue() and color.into() would mean? Even if they work,
>>>>> I think, to stay on safe side there should be a more conventional set
>>>>> of accessors: color.get(into), color.set(set_blue, 0xff) and son on.
>>>>
>>>> This would just not build.
>>>
>>> I know it wouldn't. I am just trying to understand corner cases that may
>>> (and would!) frustrate people for decades.
>>>
>>> I understand that this implementation works just great for the registers,
>>> but my role is to make sure that it would work equally well for everyone.
>>> Now, for example, Rust, contrary to C in Linux, actively uses camel case.
>>> So, the blue vs Blue vs BLUE restriction is a very valid concern. The
>>> same for reserved words like 'into'. As long as the API matures, the
>>> number of such special words would only grow. The .shr() and .shl() that
>>> you add in this series are the quite good examples.
>>>
>>> Let's make a step back and just list all limitations that come with this
>>> implementation.
>>
>> Why is a build error not sufficient to alert the user to use better judgement
>> for naming?
>
> There should be no error at all - that's why. Any name that one can
> use for a regular variable should be acceptable as a field name.
Disagreed. Here is why: the Bitfield macro is required because we are extending
the language. We could very well get this as a language feature in the future.
In the event of that, it is non sensical to allow users to use reserved
keywords. The same reason you would run into errors on the C side.
>> This is no different than using reserved keywords in C. For example, this
>> won't compile:
>>
>> int if = 5; // error: 'if' is a reserved keyword
>> int return = 3; // error: 'return' is a reserved keyword
>
> 'Into', 'as_raw', 'shr' and 'shl' *are not* the reserved words in Rust.
They are reserved as far as our Bitfield syntax extension goes? Note that this
is a macro that introduces custom syntax so it is more akin to a language
extension than an api. Note well also that macros in rust for Linux have been
used extensively for filling gaps language features (see pin initialization for
instance).
> Rust makes difference between upper and lower cases and doesn't break
> build if you do:
>
> let blue = 1;
> let BLUE = 2;
>
> So the bitfields should.
Why on earth would anyone want to do this though? It’s utterly nonsensical to
do so and I would rather break the build than encourage bad code. Mixing case
for 2 fields in the same struct is not a usecase anyone should have.
>> The user simply learns not to use reserved words, and the compiler enforces
>> this clearly. The same applies here.
>
> Most likely he will learn not to use this API at all. The worst thing about
> those 'reserved' words is that the set of them is not definite and will
> constantly grow.
>
> I'm trying to say that this approach is not scalable. If a random client
> gives name 'invert' to one of his fields, and you'll need to implement a
> method inverting bits, what are you going to do?
I would error the build out. See above for comments on language extensions and
reserved keywords.
>>> Again, this all is relevant for a basic generic data structure. If we
>>> consider it a supporting layer for the registers, everything is totally
>>> fine. In that case, we should just give it a more specific name, and
>>> probably place in an different directory, closer to IO APIs.
>>
>> The Bitfield macro is very much required for non-register use cases too.
>
> Then let's implement it better. Can you comment why the suggested API
> doesn't work for you?
>
> color.set(blue, 10);
> color.get(blue);
For the right reasons, I do not mind it but it certainly cannot be that we want
to support mixed case of the same field IMO. And the reserved keyword argument
is weak at best, for a language extension.
> I think it should solve the problem with name clashing.
>
> Can you share more about the other potential users?
Sure, take a look at my memory management patches for nova. [1]
[1] https://lore.kernel.org/all/20260120204303.3229303-1-joelagnelf@nvidia.com/
--
Joel Fernandes
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-27 3:25 ` Joel Fernandes
2026-01-27 4:49 ` Yury Norov
@ 2026-01-27 15:02 ` Gary Guo
2026-01-28 1:23 ` Alexandre Courbot
1 sibling, 1 reply; 56+ messages in thread
From: Gary Guo @ 2026-01-27 15:02 UTC (permalink / raw)
To: Joel Fernandes, Yury Norov
Cc: Alexandre Courbot, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Tue Jan 27, 2026 at 3:25 AM GMT, Joel Fernandes wrote:
> On Jan 26, 2026, at 9:55 PM, Yury Norov <ynorov@nvidia.com> wrote:
>> On Mon, Jan 26, 2026 at 10:35:49PM +0900, Alexandre Courbot wrote:
>> > On Wed Jan 21, 2026 at 6:16 PM JST, Yury Norov wrote:
>> > > On Tue, Jan 20, 2026 at 03:17:56PM +0900, Alexandre Courbot wrote:
>> > > > Add a macro for defining bitfield structs with bounds-checked accessors.
>> > > >
>> > > > Each field is represented as a `Bounded` of the appropriate bit width,
>> > > > ensuring field values are never silently truncated.
>> > > >
>> > > > Fields can optionally be converted to/from custom types, either fallibly
>> > > > or infallibly.
>> > > >
>> > > > Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
>> > > > ---
>> > > > rust/kernel/bitfield.rs | 503 ++++++++++++++++++++++++++++++++++++++++++++++++
>> > > > rust/kernel/lib.rs | 1 +
>> > > > 2 files changed, 504 insertions(+)
> [...]
>> > > > +/// // Setters can be chained. Bounded::new::<N>() does compile-time bounds checking.
>> > > > +/// let color = Rgb::default()
>> > > > +/// .set_red(Bounded::<u16, _>::new::<0x10>())
>> > > > +/// .set_green(Bounded::<u16, _>::new::<0x1f>())
>> > > > +/// .set_blue(Bounded::<u16, _>::new::<0x18>());
>> > >
>> > > Is there a way to just say:
>> > >
>> > > let color = Rgb::default().
>> > > .set_red(0x10)
>> > > .set_green(0x1f)
>> > > .set_blue(0x18)
>> > >
>> > > I think it should be the default style. Later in the patch you say:
>> > >
>> > > Each field is internally represented as a [`Bounded`]
>> > >
>> > > So, let's keep implementation decoupled from an interface?
>> >
>> > That is unfortunately not feasible, but the syntax above should seldomly
>> > be used outside of examples.
>>
>> The above short syntax is definitely more desired over that wordy and
>> non-trivial version that exposes implementation internals.
>>
>> A regular user doesn't care of the exact mechanism that protects the
>> bitfields. He wants to just assign numbers to the fields, and let
>> your machinery to take care of the integrity.
>>
>> Can you please explain in details why that's not feasible, please
>> do it in commit message. If it's an implementation constraint,
>> please consider to re-implement.
>
> If the issue is the excessive turbofish syntax, how about a macro? For
> example:
>
> let color = Rgb::default()
> .set_red(bounded!(u16, 0x10))
> .set_green(bounded!(u16, 0x1f))
> .set_blue(bounded!(u16, 0x18));
>
> This hides the turbofish and Bounded internals while still providing
> compile-time bounds checking.
I think this could be the way forward, if we also get type inference working
properly.
Rgb::default()
.set_read(bounded!(0x10))
.set_green(bounded!(0x1f))
.set_blue(bounded!(0x18))
is roughly the limit that I find acceptable (`Bounded::<u16, _>::new::<0x10>()`
is something way too verbose so I find it unacceptable).
I still think if we can get
Rgb::default()
.set_read(0x10)
.set_green(0x1f)
.set_blue(0x18)
to work with implicit `build_assert!` check it'll be ideal, although I
understand the concern about the fragility of `build_assert!()`, especially when
Clippy is used.
I am planning to at least improve the diagnostics when `build_assert!` is used
incorrectly and the build error actually occurs, so hopefully in the long run it
can once again become a tool that we can rely on, but in the meantime,
if all it needed is an extra `bounded!()` call, it doesn't bother me that much
versus the full turbofish.
Best,
Gary
>
> [...]
>> > > What Rgb::BLUE_SHIFT would mean in this case? Maybe Rgb::SHIFT(blue)?
>> >
>> > You wouldn't even have the luxury to yse `BLUE_SHIFT` here because where
>> > would be conflicting definitions and thus a build error.
> [...]
>> > > What color.set_blue() and color.into() would mean? Even if they work,
>> > > I think, to stay on safe side there should be a more conventional set
>> > > of accessors: color.get(into), color.set(set_blue, 0xff) and son on.
>> >
>> > This would just not build.
>>
>> I know it wouldn't. I am just trying to understand corner cases that may
>> (and would!) frustrate people for decades.
>>
>> I understand that this implementation works just great for the registers,
>> but my role is to make sure that it would work equally well for everyone.
>> Now, for example, Rust, contrary to C in Linux, actively uses camel case.
>> So, the blue vs Blue vs BLUE restriction is a very valid concern. The
>> same for reserved words like 'into'. As long as the API matures, the
>> number of such special words would only grow. The .shr() and .shl() that
>> you add in this series are the quite good examples.
>>
>> Let's make a step back and just list all limitations that come with this
>> implementation.
>
> Why is a build error not sufficient to alert the user to use better judgement
> for naming?
>
> This is no different than using reserved keywords in C. For example, this
> won't compile:
>
> int if = 5; // error: 'if' is a reserved keyword
> int return = 3; // error: 'return' is a reserved keyword
>
> The user simply learns not to use reserved words, and the compiler enforces
> this clearly. The same applies here.
>
>> Again, this all is relevant for a basic generic data structure. If we
>> consider it a supporting layer for the registers, everything is totally
>> fine. In that case, we should just give it a more specific name, and
>> probably place in an different directory, closer to IO APIs.
>
> The Bitfield macro is very much required for non-register use cases too.
>
> --
> Joel Fernandes
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-27 9:57 ` Alexandre Courbot
@ 2026-01-27 21:03 ` John Hubbard
2026-01-27 21:10 ` Gary Guo
0 siblings, 1 reply; 56+ messages in thread
From: John Hubbard @ 2026-01-27 21:03 UTC (permalink / raw)
To: Alexandre Courbot, Yury Norov
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Yury Norov, Alistair Popple, Joel Fernandes,
Timur Tabi, Edwin Peer, Eliot Courtney, Daniel Almeida,
Dirk Behme, Steven Price, rust-for-linux, linux-kernel
On 1/27/26 1:57 AM, Alexandre Courbot wrote:
> On Tue Jan 27, 2026 at 11:55 AM JST, Yury Norov wrote:
> <snip>
>
> So while we cannot achieve exactly the short syntax above (which has its
> drawbacks as well, such as the inability to operate in const context),
> we can introduce a new setter than works with a const argument and
> spares us the need to invoke `Bounded::new` ourselves:
>
> let color = Rgb::default().
> .with_red::<0x10>()
> .with_green::<0x1f>()
> .with_blue::<0x18>()
Are we sure that .with_red is a better name than, say, .set_red()?
"with" is not so easy to remember, because it is a bit
surprising and different, for setting a value.
"with" feels like a function call or closure: "sort with
qsort", for example. But here we are setting a color
component.
thanks,
John Hubbard
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-27 21:03 ` John Hubbard
@ 2026-01-27 21:10 ` Gary Guo
2026-01-27 21:22 ` John Hubbard
2026-01-28 1:28 ` Alexandre Courbot
0 siblings, 2 replies; 56+ messages in thread
From: Gary Guo @ 2026-01-27 21:10 UTC (permalink / raw)
To: John Hubbard, Alexandre Courbot, Yury Norov
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Yury Norov, Alistair Popple, Joel Fernandes,
Timur Tabi, Edwin Peer, Eliot Courtney, Daniel Almeida,
Dirk Behme, Steven Price, rust-for-linux, linux-kernel
On Tue Jan 27, 2026 at 9:03 PM GMT, John Hubbard wrote:
> On 1/27/26 1:57 AM, Alexandre Courbot wrote:
>> On Tue Jan 27, 2026 at 11:55 AM JST, Yury Norov wrote:
>> <snip>
>>
>> So while we cannot achieve exactly the short syntax above (which has its
>> drawbacks as well, such as the inability to operate in const context),
>> we can introduce a new setter than works with a const argument and
>> spares us the need to invoke `Bounded::new` ourselves:
>>
>> let color = Rgb::default().
>> .with_red::<0x10>()
>> .with_green::<0x1f>()
>> .with_blue::<0x18>()
>
> Are we sure that .with_red is a better name than, say, .set_red()?
>
> "with" is not so easy to remember, because it is a bit
> surprising and different, for setting a value.
>
> "with" feels like a function call or closure: "sort with
> qsort", for example. But here we are setting a color
> component.
`set_foo` implies that the value is mutated in place (and takes `&mut self`).
`with_foo` implies that value is returned with the specific thing changed. For
example, `pointer::with_addr`, `Path::with_extension`.
Given the signature in the API I would agree with Yury that `with_` is better.
Best,
Gary
>
> thanks,
> John Hubbard
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-27 21:10 ` Gary Guo
@ 2026-01-27 21:22 ` John Hubbard
2026-01-28 1:28 ` Alexandre Courbot
1 sibling, 0 replies; 56+ messages in thread
From: John Hubbard @ 2026-01-27 21:22 UTC (permalink / raw)
To: Gary Guo, Alexandre Courbot, Yury Norov
Cc: Miguel Ojeda, Boqun Feng, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Yury Norov, Alistair Popple, Joel Fernandes, Timur Tabi,
Edwin Peer, Eliot Courtney, Daniel Almeida, Dirk Behme,
Steven Price, rust-for-linux, linux-kernel
On 1/27/26 1:10 PM, Gary Guo wrote:
> On Tue Jan 27, 2026 at 9:03 PM GMT, John Hubbard wrote:
>> On 1/27/26 1:57 AM, Alexandre Courbot wrote:
>>> On Tue Jan 27, 2026 at 11:55 AM JST, Yury Norov wrote:
>>> <snip>
>>>
>>> So while we cannot achieve exactly the short syntax above (which has its
>>> drawbacks as well, such as the inability to operate in const context),
>>> we can introduce a new setter than works with a const argument and
>>> spares us the need to invoke `Bounded::new` ourselves:
>>>
>>> let color = Rgb::default().
>>> .with_red::<0x10>()
>>> .with_green::<0x1f>()
>>> .with_blue::<0x18>()
>>
>> Are we sure that .with_red is a better name than, say, .set_red()?
>>
>> "with" is not so easy to remember, because it is a bit
>> surprising and different, for setting a value.
>>
>> "with" feels like a function call or closure: "sort with
>> qsort", for example. But here we are setting a color
>> component.
>
> `set_foo` implies that the value is mutated in place (and takes `&mut self`).
> `with_foo` implies that value is returned with the specific thing changed. For
> example, `pointer::with_addr`, `Path::with_extension`.
>
> Given the signature in the API I would agree with Yury that `with_` is better.
OK that sounds consistent anyway.
thanks,
--
John Hubbard
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-27 15:02 ` Gary Guo
@ 2026-01-28 1:23 ` Alexandre Courbot
2026-01-28 4:33 ` Yury Norov
0 siblings, 1 reply; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-28 1:23 UTC (permalink / raw)
To: Gary Guo
Cc: Joel Fernandes, Yury Norov, Miguel Ojeda, Boqun Feng,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Wed Jan 28, 2026 at 12:02 AM JST, Gary Guo wrote:
> On Tue Jan 27, 2026 at 3:25 AM GMT, Joel Fernandes wrote:
>> On Jan 26, 2026, at 9:55 PM, Yury Norov <ynorov@nvidia.com> wrote:
>>> On Mon, Jan 26, 2026 at 10:35:49PM +0900, Alexandre Courbot wrote:
>>> > On Wed Jan 21, 2026 at 6:16 PM JST, Yury Norov wrote:
>>> > > On Tue, Jan 20, 2026 at 03:17:56PM +0900, Alexandre Courbot wrote:
>>> > > > Add a macro for defining bitfield structs with bounds-checked accessors.
>>> > > >
>>> > > > Each field is represented as a `Bounded` of the appropriate bit width,
>>> > > > ensuring field values are never silently truncated.
>>> > > >
>>> > > > Fields can optionally be converted to/from custom types, either fallibly
>>> > > > or infallibly.
>>> > > >
>>> > > > Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
>>> > > > ---
>>> > > > rust/kernel/bitfield.rs | 503 ++++++++++++++++++++++++++++++++++++++++++++++++
>>> > > > rust/kernel/lib.rs | 1 +
>>> > > > 2 files changed, 504 insertions(+)
>> [...]
>>> > > > +/// // Setters can be chained. Bounded::new::<N>() does compile-time bounds checking.
>>> > > > +/// let color = Rgb::default()
>>> > > > +/// .set_red(Bounded::<u16, _>::new::<0x10>())
>>> > > > +/// .set_green(Bounded::<u16, _>::new::<0x1f>())
>>> > > > +/// .set_blue(Bounded::<u16, _>::new::<0x18>());
>>> > >
>>> > > Is there a way to just say:
>>> > >
>>> > > let color = Rgb::default().
>>> > > .set_red(0x10)
>>> > > .set_green(0x1f)
>>> > > .set_blue(0x18)
>>> > >
>>> > > I think it should be the default style. Later in the patch you say:
>>> > >
>>> > > Each field is internally represented as a [`Bounded`]
>>> > >
>>> > > So, let's keep implementation decoupled from an interface?
>>> >
>>> > That is unfortunately not feasible, but the syntax above should seldomly
>>> > be used outside of examples.
>>>
>>> The above short syntax is definitely more desired over that wordy and
>>> non-trivial version that exposes implementation internals.
>>>
>>> A regular user doesn't care of the exact mechanism that protects the
>>> bitfields. He wants to just assign numbers to the fields, and let
>>> your machinery to take care of the integrity.
>>>
>>> Can you please explain in details why that's not feasible, please
>>> do it in commit message. If it's an implementation constraint,
>>> please consider to re-implement.
>>
>> If the issue is the excessive turbofish syntax, how about a macro? For
>> example:
>>
>> let color = Rgb::default()
>> .set_red(bounded!(u16, 0x10))
>> .set_green(bounded!(u16, 0x1f))
>> .set_blue(bounded!(u16, 0x18));
>>
>> This hides the turbofish and Bounded internals while still providing
>> compile-time bounds checking.
>
> I think this could be the way forward, if we also get type inference working
> properly.
>
> Rgb::default()
> .set_read(bounded!(0x10))
> .set_green(bounded!(0x1f))
> .set_blue(bounded!(0x18))
>
> is roughly the limit that I find acceptable (`Bounded::<u16, _>::new::<0x10>()`
> is something way too verbose so I find it unacceptable).
>
> I still think if we can get
>
> Rgb::default()
> .set_read(0x10)
> .set_green(0x1f)
> .set_blue(0x18)
>
> to work with implicit `build_assert!` check it'll be ideal, although I
> understand the concern about the fragility of `build_assert!()`, especially when
> Clippy is used.
>
> I am planning to at least improve the diagnostics when `build_assert!` is used
> incorrectly and the build error actually occurs, so hopefully in the long run it
> can once again become a tool that we can rely on, but in the meantime,
> if all it needed is an extra `bounded!()` call, it doesn't bother me that much
> versus the full turbofish.
I think having a dedicated const setter method is better though. On top
of not making use of `build_assert!`, it can also be used in const
contexts to build const values, something that should be pretty useful
once we extract the `bitfield!` macro for wider use.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-27 21:10 ` Gary Guo
2026-01-27 21:22 ` John Hubbard
@ 2026-01-28 1:28 ` Alexandre Courbot
2026-01-28 1:41 ` John Hubbard
1 sibling, 1 reply; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-28 1:28 UTC (permalink / raw)
To: Gary Guo
Cc: John Hubbard, Yury Norov, Miguel Ojeda, Boqun Feng,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, Alistair Popple,
Joel Fernandes, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Wed Jan 28, 2026 at 6:10 AM JST, Gary Guo wrote:
> On Tue Jan 27, 2026 at 9:03 PM GMT, John Hubbard wrote:
>> On 1/27/26 1:57 AM, Alexandre Courbot wrote:
>>> On Tue Jan 27, 2026 at 11:55 AM JST, Yury Norov wrote:
>>> <snip>
>>>
>>> So while we cannot achieve exactly the short syntax above (which has its
>>> drawbacks as well, such as the inability to operate in const context),
>>> we can introduce a new setter than works with a const argument and
>>> spares us the need to invoke `Bounded::new` ourselves:
>>>
>>> let color = Rgb::default().
>>> .with_red::<0x10>()
>>> .with_green::<0x1f>()
>>> .with_blue::<0x18>()
>>
>> Are we sure that .with_red is a better name than, say, .set_red()?
>>
>> "with" is not so easy to remember, because it is a bit
>> surprising and different, for setting a value.
>>
>> "with" feels like a function call or closure: "sort with
>> qsort", for example. But here we are setting a color
>> component.
>
> `set_foo` implies that the value is mutated in place (and takes `&mut self`).
> `with_foo` implies that value is returned with the specific thing changed. For
> example, `pointer::with_addr`, `Path::with_extension`.
>
> Given the signature in the API I would agree with Yury that `with_` is better.
Indeed, given the signature of the method `with_` is definitely more
idiomatic. But that is also true for the non-const setter, and they
unfortunately cannot share the same name.
Alternative names I can think of for the non-const / const setters:
- `with_foo`/`with_foo_const`, but that's a bit verbose,
- `with_foo`/`const_foo`, but that's not idiomatic either,
- `with_foo_val`/`with_foo`, but that increases the risk of name
clashes.
... so unless there are better proposals I guess the
`set_foo`/`with_foo` is at least practical, if a bit unconventional.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-28 1:28 ` Alexandre Courbot
@ 2026-01-28 1:41 ` John Hubbard
0 siblings, 0 replies; 56+ messages in thread
From: John Hubbard @ 2026-01-28 1:41 UTC (permalink / raw)
To: Alexandre Courbot, Gary Guo
Cc: Yury Norov, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Yury Norov, Alistair Popple, Joel Fernandes,
Timur Tabi, Edwin Peer, Eliot Courtney, Daniel Almeida,
Dirk Behme, Steven Price, rust-for-linux, linux-kernel
On 1/27/26 5:28 PM, Alexandre Courbot wrote:
> On Wed Jan 28, 2026 at 6:10 AM JST, Gary Guo wrote:
>> On Tue Jan 27, 2026 at 9:03 PM GMT, John Hubbard wrote:
>>> On 1/27/26 1:57 AM, Alexandre Courbot wrote:
>>>> On Tue Jan 27, 2026 at 11:55 AM JST, Yury Norov wrote:
>>>> <snip>
>> Given the signature in the API I would agree with Yury that `with_` is better.
>
> Indeed, given the signature of the method `with_` is definitely more
> idiomatic. But that is also true for the non-const setter, and they
> unfortunately cannot share the same name.
>
oh that is unfortunate.
> Alternative names I can think of for the non-const / const setters:
>
> - `with_foo`/`with_foo_const`, but that's a bit verbose,
Yes, but it is also at least slightly self-explanatory: "the name
is a little ugly, but you can see what it is for".
> - `with_foo`/`const_foo`, but that's not idiomatic either,
> - `with_foo_val`/`with_foo`, but that increases the risk of name
> clashes.
>
> ... so unless there are better proposals I guess the
> `set_foo`/`with_foo` is at least practical, if a bit unconventional.
...whereas this choice just looks unhinged, at first glance:
"what? whhhyyy???" haha :)
And eventually the reader figures out why. Maybe.
thanks,
--
John Hubbard
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-28 1:23 ` Alexandre Courbot
@ 2026-01-28 4:33 ` Yury Norov
2026-01-28 14:02 ` Alexandre Courbot
0 siblings, 1 reply; 56+ messages in thread
From: Yury Norov @ 2026-01-28 4:33 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Gary Guo, Joel Fernandes, Miguel Ojeda, Boqun Feng,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Wed, Jan 28, 2026 at 10:23:36AM +0900, Alexandre Courbot wrote:
> tatus: O
> Content-Length: 4095
> Lines: 108
>
> On Wed Jan 28, 2026 at 12:02 AM JST, Gary Guo wrote:
> > On Tue Jan 27, 2026 at 3:25 AM GMT, Joel Fernandes wrote:
> >> On Jan 26, 2026, at 9:55 PM, Yury Norov <ynorov@nvidia.com> wrote:
> >>> On Mon, Jan 26, 2026 at 10:35:49PM +0900, Alexandre Courbot wrote:
> >>> > On Wed Jan 21, 2026 at 6:16 PM JST, Yury Norov wrote:
> >>> > > On Tue, Jan 20, 2026 at 03:17:56PM +0900, Alexandre Courbot wrote:
> >>> > > > Add a macro for defining bitfield structs with bounds-checked accessors.
> >>> > > >
> >>> > > > Each field is represented as a `Bounded` of the appropriate bit width,
> >>> > > > ensuring field values are never silently truncated.
> >>> > > >
> >>> > > > Fields can optionally be converted to/from custom types, either fallibly
> >>> > > > or infallibly.
> >>> > > >
> >>> > > > Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> >>> > > > ---
> >>> > > > rust/kernel/bitfield.rs | 503 ++++++++++++++++++++++++++++++++++++++++++++++++
> >>> > > > rust/kernel/lib.rs | 1 +
> >>> > > > 2 files changed, 504 insertions(+)
> >> [...]
> >>> > > > +/// // Setters can be chained. Bounded::new::<N>() does compile-time bounds checking.
> >>> > > > +/// let color = Rgb::default()
> >>> > > > +/// .set_red(Bounded::<u16, _>::new::<0x10>())
> >>> > > > +/// .set_green(Bounded::<u16, _>::new::<0x1f>())
> >>> > > > +/// .set_blue(Bounded::<u16, _>::new::<0x18>());
> >>> > >
> >>> > > Is there a way to just say:
> >>> > >
> >>> > > let color = Rgb::default().
> >>> > > .set_red(0x10)
> >>> > > .set_green(0x1f)
> >>> > > .set_blue(0x18)
> >>> > >
> >>> > > I think it should be the default style. Later in the patch you say:
> >>> > >
> >>> > > Each field is internally represented as a [`Bounded`]
> >>> > >
> >>> > > So, let's keep implementation decoupled from an interface?
> >>> >
> >>> > That is unfortunately not feasible, but the syntax above should seldomly
> >>> > be used outside of examples.
> >>>
> >>> The above short syntax is definitely more desired over that wordy and
> >>> non-trivial version that exposes implementation internals.
> >>>
> >>> A regular user doesn't care of the exact mechanism that protects the
> >>> bitfields. He wants to just assign numbers to the fields, and let
> >>> your machinery to take care of the integrity.
> >>>
> >>> Can you please explain in details why that's not feasible, please
> >>> do it in commit message. If it's an implementation constraint,
> >>> please consider to re-implement.
> >>
> >> If the issue is the excessive turbofish syntax, how about a macro? For
> >> example:
> >>
> >> let color = Rgb::default()
> >> .set_red(bounded!(u16, 0x10))
> >> .set_green(bounded!(u16, 0x1f))
> >> .set_blue(bounded!(u16, 0x18));
> >>
> >> This hides the turbofish and Bounded internals while still providing
> >> compile-time bounds checking.
> >
> > I think this could be the way forward, if we also get type inference working
> > properly.
> >
> > Rgb::default()
> > .set_read(bounded!(0x10))
> > .set_green(bounded!(0x1f))
> > .set_blue(bounded!(0x18))
> >
> > is roughly the limit that I find acceptable (`Bounded::<u16, _>::new::<0x10>()`
> > is something way too verbose so I find it unacceptable).
I agree, this version is on the edge. It probably may be acceptable
because it highlights that the numbers passed in setters are some
special numbers. But yeah, it's a weak excuse.
If it was C, it could be just as simple as
#define set_red(v) __set_red(bounded(v))
So...
I'm not a rust professional, but I've been told many times that macro
rules in rust are so powerful that they can do any magic, even mimic
another languages.
For fun, I asked AI to draw an example where rust structure is
initialized just like normal python does, and that's what I've got:
struct Foo {
bar: i32,
baz: String,
}
// Your specific constructor logic
fn construct_bar(v: i32) -> i32 { v * 2 }
fn construct_baz(v: i32) -> String { v.to_string() }
// Helper macro to select the right function for a single field
macro_rules! get_ctor {
(bar, $val:expr) => { construct_bar($val) };
(baz, $val:expr) => { construct_baz($val) };
}
macro_rules! python_init {
($t:ident { $($field:ident = $val:expr),* $(,)? }) => {
$t {
// For each field, we call the dispatcher separately
$($field: get_ctor!($field, $val)),*
}
};
}
fn main() {
let foo = python_init!(Foo { bar = 10, baz = 500 });
println!("bar: {}", foo.bar); // Output: 20
println!("baz: {}", foo.baz); // Output: "500"
}
Indeed it's possible!
Again, I'm not a rust professional and I can't evaluate quality of the
AI-generated code, neither I can ensure there's no nasty pitfalls.
But as a user, I can say that
let rgb = bitfield!(Rgb { red: 0x10, green: 0x1f, blue: 0x18 })
would be way more readable than this beast:
let color = Rgb::default()
.set_red(Bounded::<u16, _>::new::<0x10>())
.set_green(Bounded::<u16, _>::new::<0x1f>())
.set_blue(Bounded::<u16, _>::new::<0x18>());
Thanks,
Yury
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-27 10:41 ` Alexandre Courbot
2026-01-27 10:55 ` Miguel Ojeda
@ 2026-01-28 5:27 ` Yury Norov
2026-01-28 14:12 ` Alexandre Courbot
1 sibling, 1 reply; 56+ messages in thread
From: Yury Norov @ 2026-01-28 5:27 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Joel Fernandes, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Tue, Jan 27, 2026 at 07:41:50PM +0900, Alexandre Courbot wrote:
> On Tue Jan 27, 2026 at 1:49 PM JST, Yury Norov wrote:
> > On Mon, Jan 26, 2026 at 10:25:54PM -0500, Joel Fernandes wrote:
> >> On Jan 26, 2026, at 9:55 PM, Yury Norov <ynorov@nvidia.com> wrote:
> >> > On Mon, Jan 26, 2026 at 10:35:49PM +0900, Alexandre Courbot wrote:
> >> > > On Wed Jan 21, 2026 at 6:16 PM JST, Yury Norov wrote:
> >> > > > On Tue, Jan 20, 2026 at 03:17:56PM +0900, Alexandre Courbot wrote:
> >> > > > > Add a macro for defining bitfield structs with bounds-checked accessors.
> >> > > > >
...
> > 'Into', 'as_raw', 'shr' and 'shl' *are not* the reserved words in Rust.
> > Rust makes difference between upper and lower cases and doesn't break
> > build if you do:
> >
> > let blue = 1;
> > let BLUE = 2;
> >
> > So the bitfields should.
>
> We can solve this case if we break the Rust naming conventions for
> constants and keep the original case for the field, e.g. `blue_SHIFT`
> and `BLUE_SHIFT`.
Or like C++ handles static methods: Rgb::SHIFT(blue)
> But again, by convention fields should all be snake_case or it would
> look weird in Rust code.
I'm pretty sure that for such a basic data structure people will
eventually find a case that will overweight the convention.
> >> The user simply learns not to use reserved words, and the compiler enforces
> >> this clearly. The same applies here.
> >
> > Most likely he will learn not to use this API at all. The worst thing about
> > those 'reserved' words is that the set of them is not definite and will
> > constantly grow.
> >
> > I'm trying to say that this approach is not scalable. If a random client
> > gives name 'invert' to one of his fields, and you'll need to implement a
> > method inverting bits, what are you going to do?
>
> Bitfields are limited to a _get, a _set, and an _update methods (and
> possibly try_ variants for the last two). If an `invert` method needs to
> be implemented, it can be done on top of _update which takes a closure.
> So I am pretty confident we won't need to extend the API beyond these
> (famous last words).
Not sure I understand this. You already have into(), shr(), shl() and
others. By the way, maybe again follow C++ style, like:
my_bitfield.shr // field
my_bitfield.shr() // method
> >> > Again, this all is relevant for a basic generic data structure. If we
> >> > consider it a supporting layer for the registers, everything is totally
> >> > fine. In that case, we should just give it a more specific name, and
> >> > probably place in an different directory, closer to IO APIs.
> >>
> >> The Bitfield macro is very much required for non-register use cases too.
> >
> > Then let's implement it better. Can you comment why the suggested API
> > doesn't work for you?
> >
> > color.set(blue, 10);
> > color.get(blue);
> >
> > I think it should solve the problem with name clashing.
>
> That syntax cannot be implemented as it is written. What type is `blue` here?
'blue' has no type because it is not a variable but keyword. We do
such things in C all the time:
DEFINE_FREE(kfree, void *, if (_T) kfree(_T))
^^^^^
keyword that becomes a part of cleanup function name
And in another email I seemingly do similar thing for python_init!()
macro in rust to pick the right constructor.
> The closest we could get would be a macro, that would look like
>
> bitfield_set!(color, blue, 10);
>
> And beyond the scenes it would call some more intricate (and unsightly)
> machinery. I'd rather define the constraints clearly for users - they
> are not so drastic.
But that would not be chainable, I guess. I recall, Joel said it's an
important feature for some reason.
> > Can you share more about the other potential users?
>
> I know Joel is using bitfield for page table structures, but there are
> of course many others. Basically any structure with fields defined as a
> subset of its bits is a candidate. Registers just happen to be bitfields
> with extra properties for I/O.
OK. Can I take a look at how bitfields are used there?
Thanks,
Yury
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-28 4:33 ` Yury Norov
@ 2026-01-28 14:02 ` Alexandre Courbot
2026-01-28 18:12 ` Yury Norov
0 siblings, 1 reply; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-28 14:02 UTC (permalink / raw)
To: Yury Norov
Cc: Gary Guo, Joel Fernandes, Miguel Ojeda, Boqun Feng,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Wed Jan 28, 2026 at 1:33 PM JST, Yury Norov wrote:
> On Wed, Jan 28, 2026 at 10:23:36AM +0900, Alexandre Courbot wrote:
>> tatus: O
>> Content-Length: 4095
>> Lines: 108
>>
>> On Wed Jan 28, 2026 at 12:02 AM JST, Gary Guo wrote:
>> > On Tue Jan 27, 2026 at 3:25 AM GMT, Joel Fernandes wrote:
>> >> On Jan 26, 2026, at 9:55 PM, Yury Norov <ynorov@nvidia.com> wrote:
>> >>> On Mon, Jan 26, 2026 at 10:35:49PM +0900, Alexandre Courbot wrote:
>> >>> > On Wed Jan 21, 2026 at 6:16 PM JST, Yury Norov wrote:
>> >>> > > On Tue, Jan 20, 2026 at 03:17:56PM +0900, Alexandre Courbot wrote:
>> >>> > > > Add a macro for defining bitfield structs with bounds-checked accessors.
>> >>> > > >
>> >>> > > > Each field is represented as a `Bounded` of the appropriate bit width,
>> >>> > > > ensuring field values are never silently truncated.
>> >>> > > >
>> >>> > > > Fields can optionally be converted to/from custom types, either fallibly
>> >>> > > > or infallibly.
>> >>> > > >
>> >>> > > > Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
>> >>> > > > ---
>> >>> > > > rust/kernel/bitfield.rs | 503 ++++++++++++++++++++++++++++++++++++++++++++++++
>> >>> > > > rust/kernel/lib.rs | 1 +
>> >>> > > > 2 files changed, 504 insertions(+)
>> >> [...]
>> >>> > > > +/// // Setters can be chained. Bounded::new::<N>() does compile-time bounds checking.
>> >>> > > > +/// let color = Rgb::default()
>> >>> > > > +/// .set_red(Bounded::<u16, _>::new::<0x10>())
>> >>> > > > +/// .set_green(Bounded::<u16, _>::new::<0x1f>())
>> >>> > > > +/// .set_blue(Bounded::<u16, _>::new::<0x18>());
>> >>> > >
>> >>> > > Is there a way to just say:
>> >>> > >
>> >>> > > let color = Rgb::default().
>> >>> > > .set_red(0x10)
>> >>> > > .set_green(0x1f)
>> >>> > > .set_blue(0x18)
>> >>> > >
>> >>> > > I think it should be the default style. Later in the patch you say:
>> >>> > >
>> >>> > > Each field is internally represented as a [`Bounded`]
>> >>> > >
>> >>> > > So, let's keep implementation decoupled from an interface?
>> >>> >
>> >>> > That is unfortunately not feasible, but the syntax above should seldomly
>> >>> > be used outside of examples.
>> >>>
>> >>> The above short syntax is definitely more desired over that wordy and
>> >>> non-trivial version that exposes implementation internals.
>> >>>
>> >>> A regular user doesn't care of the exact mechanism that protects the
>> >>> bitfields. He wants to just assign numbers to the fields, and let
>> >>> your machinery to take care of the integrity.
>> >>>
>> >>> Can you please explain in details why that's not feasible, please
>> >>> do it in commit message. If it's an implementation constraint,
>> >>> please consider to re-implement.
>> >>
>> >> If the issue is the excessive turbofish syntax, how about a macro? For
>> >> example:
>> >>
>> >> let color = Rgb::default()
>> >> .set_red(bounded!(u16, 0x10))
>> >> .set_green(bounded!(u16, 0x1f))
>> >> .set_blue(bounded!(u16, 0x18));
>> >>
>> >> This hides the turbofish and Bounded internals while still providing
>> >> compile-time bounds checking.
>> >
>> > I think this could be the way forward, if we also get type inference working
>> > properly.
>> >
>> > Rgb::default()
>> > .set_read(bounded!(0x10))
>> > .set_green(bounded!(0x1f))
>> > .set_blue(bounded!(0x18))
>> >
>> > is roughly the limit that I find acceptable (`Bounded::<u16, _>::new::<0x10>()`
>> > is something way too verbose so I find it unacceptable).
>
> I agree, this version is on the edge. It probably may be acceptable
> because it highlights that the numbers passed in setters are some
> special numbers. But yeah, it's a weak excuse.
>
> If it was C, it could be just as simple as
>
> #define set_red(v) __set_red(bounded(v))
>
> So...
>
> I'm not a rust professional, but I've been told many times that macro
> rules in rust are so powerful that they can do any magic, even mimic
> another languages.
>
> For fun, I asked AI to draw an example where rust structure is
> initialized just like normal python does, and that's what I've got:
>
> struct Foo {
> bar: i32,
> baz: String,
> }
>
> // Your specific constructor logic
> fn construct_bar(v: i32) -> i32 { v * 2 }
> fn construct_baz(v: i32) -> String { v.to_string() }
>
> // Helper macro to select the right function for a single field
> macro_rules! get_ctor {
> (bar, $val:expr) => { construct_bar($val) };
> (baz, $val:expr) => { construct_baz($val) };
> }
>
> macro_rules! python_init {
> ($t:ident { $($field:ident = $val:expr),* $(,)? }) => {
> $t {
> // For each field, we call the dispatcher separately
> $($field: get_ctor!($field, $val)),*
> }
> };
> }
>
> fn main() {
> let foo = python_init!(Foo { bar = 10, baz = 500 });
>
> println!("bar: {}", foo.bar); // Output: 20
> println!("baz: {}", foo.baz); // Output: "500"
> }
>
> Indeed it's possible!
Oh yeah you can do all sorts of crazy sh** with Rust macros. :)
>
> Again, I'm not a rust professional and I can't evaluate quality of the
> AI-generated code, neither I can ensure there's no nasty pitfalls.
>
> But as a user, I can say that
>
> let rgb = bitfield!(Rgb { red: 0x10, green: 0x1f, blue: 0x18 })
>
> would be way more readable than this beast:
>
> let color = Rgb::default()
> .set_red(Bounded::<u16, _>::new::<0x10>())
> .set_green(Bounded::<u16, _>::new::<0x1f>())
> .set_blue(Bounded::<u16, _>::new::<0x18>());
Without having tested the idea, a macro wrapping the whole bitfield (and
not just trying to create a bounded) looks doable. Of course, it would
have to rely on some underlying mechanism to set the fields, which could
be the abomination above, or something a bit more convenient.
It looks like we are converging towards introducing the
`with_const_field` setter for now with registers ; when we extract the
`bitfield!` I think I would like to entertain the introduction of a
macro close to what you suggested above.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-28 5:27 ` Yury Norov
@ 2026-01-28 14:12 ` Alexandre Courbot
2026-01-28 18:05 ` Yury Norov
0 siblings, 1 reply; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-28 14:12 UTC (permalink / raw)
To: Yury Norov
Cc: Joel Fernandes, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Wed Jan 28, 2026 at 2:27 PM JST, Yury Norov wrote:
<snip>
>> >> The user simply learns not to use reserved words, and the compiler enforces
>> >> this clearly. The same applies here.
>> >
>> > Most likely he will learn not to use this API at all. The worst thing about
>> > those 'reserved' words is that the set of them is not definite and will
>> > constantly grow.
>> >
>> > I'm trying to say that this approach is not scalable. If a random client
>> > gives name 'invert' to one of his fields, and you'll need to implement a
>> > method inverting bits, what are you going to do?
>>
>> Bitfields are limited to a _get, a _set, and an _update methods (and
>> possibly try_ variants for the last two). If an `invert` method needs to
>> be implemented, it can be done on top of _update which takes a closure.
>> So I am pretty confident we won't need to extend the API beyond these
>> (famous last words).
>
> Not sure I understand this. You already have into(), shr(), shl() and
> others. By the way, maybe again follow C++ style, like:
>
> my_bitfield.shr // field
> my_bitfield.shr() // method
The confusion comes from the fact that `shr` and pals are methods of
`Bounded`, not the bitfield structure. I.e. you call them on the field
that you obtained using the getter method - they are not part of the
bitfield interface itself which is only concerned with 3 basic
operations: get a field, set a field, update a field with a closure.
>
>> >> > Again, this all is relevant for a basic generic data structure. If we
>> >> > consider it a supporting layer for the registers, everything is totally
>> >> > fine. In that case, we should just give it a more specific name, and
>> >> > probably place in an different directory, closer to IO APIs.
>> >>
>> >> The Bitfield macro is very much required for non-register use cases too.
>> >
>> > Then let's implement it better. Can you comment why the suggested API
>> > doesn't work for you?
>> >
>> > color.set(blue, 10);
>> > color.get(blue);
>> >
>> > I think it should solve the problem with name clashing.
>>
>> That syntax cannot be implemented as it is written. What type is `blue` here?
>
> 'blue' has no type because it is not a variable but keyword. We do
> such things in C all the time:
>
> DEFINE_FREE(kfree, void *, if (_T) kfree(_T))
> ^^^^^
> keyword that becomes a part of cleanup function name
>
> And in another email I seemingly do similar thing for python_init!()
> macro in rust to pick the right constructor.
Yup, and we could do the same in Rust with a macro, but your example
above was not macro code (macros are always ending with a `!`).
>
>> The closest we could get would be a macro, that would look like
>>
>> bitfield_set!(color, blue, 10);
>>
>> And beyond the scenes it would call some more intricate (and unsightly)
>> machinery. I'd rather define the constraints clearly for users - they
>> are not so drastic.
>
> But that would not be chainable, I guess. I recall, Joel said it's an
> important feature for some reason.
We could make the macros return the modified bitfield, which would make
them chainable. And the bitfield-englobing macro you proposed in your
other email could also reduce the need to do so anyway.
>
>> > Can you share more about the other potential users?
>>
>> I know Joel is using bitfield for page table structures, but there are
>> of course many others. Basically any structure with fields defined as a
>> subset of its bits is a candidate. Registers just happen to be bitfields
>> with extra properties for I/O.
>
> OK. Can I take a look at how bitfields are used there?
Check out these patches:
https://lore.kernel.org/all/20260120204303.3229303-10-joelagnelf@nvidia.com/
https://lore.kernel.org/all/20260120204303.3229303-12-joelagnelf@nvidia.com/
https://lore.kernel.org/all/20260120204303.3229303-13-joelagnelf@nvidia.com/
But be aware that they use the `bitfield!` macro of nova-core, this one
doesn't use `Bounded` yet.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-28 14:12 ` Alexandre Courbot
@ 2026-01-28 18:05 ` Yury Norov
2026-01-29 13:40 ` Alexandre Courbot
0 siblings, 1 reply; 56+ messages in thread
From: Yury Norov @ 2026-01-28 18:05 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Joel Fernandes, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Wed, Jan 28, 2026 at 11:12:27PM +0900, Alexandre Courbot wrote:
> On Wed Jan 28, 2026 at 2:27 PM JST, Yury Norov wrote:
<snip>
> >> > Then let's implement it better. Can you comment why the suggested API
> >> > doesn't work for you?
> >> >
> >> > color.set(blue, 10);
> >> > color.get(blue);
> >> >
> >> > I think it should solve the problem with name clashing.
> >>
> >> That syntax cannot be implemented as it is written. What type is `blue` here?
> >
> > 'blue' has no type because it is not a variable but keyword. We do
> > such things in C all the time:
> >
> > DEFINE_FREE(kfree, void *, if (_T) kfree(_T))
> > ^^^^^
> > keyword that becomes a part of cleanup function name
> >
> > And in another email I seemingly do similar thing for python_init!()
> > macro in rust to pick the right constructor.
>
> Yup, and we could do the same in Rust with a macro, but your example
> above was not macro code (macros are always ending with a `!`).
color.set!(blue, 10) is equally good, if it helps.
If someone will point to the excessive use of macros, you're safe to
ignore it. This is the very basic fundamental type, and I believe that
it brings enough attention and expertise to make it safe.
> >> The closest we could get would be a macro, that would look like
> >>
> >> bitfield_set!(color, blue, 10);
Or maybe bitfield_set!(color.blue, 10), but the above looks more natural.
Is it possible to make it color.blue = 10 by any chance?
Again (and again) - usability and readability is a king, and I'm glad
we're moving toward the right direction.
> >> And beyond the scenes it would call some more intricate (and unsightly)
> >> machinery. I'd rather define the constraints clearly for users - they
> >> are not so drastic.
> >
> > But that would not be chainable, I guess. I recall, Joel said it's an
> > important feature for some reason.
>
> We could make the macros return the modified bitfield, which would make
> them chainable. And the bitfield-englobing macro you proposed in your
> other email could also reduce the need to do so anyway.
>
> >
> >> > Can you share more about the other potential users?
> >>
> >> I know Joel is using bitfield for page table structures, but there are
> >> of course many others. Basically any structure with fields defined as a
> >> subset of its bits is a candidate. Registers just happen to be bitfields
> >> with extra properties for I/O.
> >
> > OK. Can I take a look at how bitfields are used there?
>
> Check out these patches:
>
> https://lore.kernel.org/all/20260120204303.3229303-10-joelagnelf@nvidia.com/
> https://lore.kernel.org/all/20260120204303.3229303-12-joelagnelf@nvidia.com/
> https://lore.kernel.org/all/20260120204303.3229303-13-joelagnelf@nvidia.com/
>
> But be aware that they use the `bitfield!` macro of nova-core, this one
> doesn't use `Bounded` yet.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-28 14:02 ` Alexandre Courbot
@ 2026-01-28 18:12 ` Yury Norov
0 siblings, 0 replies; 56+ messages in thread
From: Yury Norov @ 2026-01-28 18:12 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Gary Guo, Joel Fernandes, Miguel Ojeda, Boqun Feng,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Wed, Jan 28, 2026 at 11:02:03PM +0900, Alexandre Courbot wrote:
> On Wed Jan 28, 2026 at 1:33 PM JST, Yury Norov wrote:
> > On Wed, Jan 28, 2026 at 10:23:36AM +0900, Alexandre Courbot wrote:
> >> tatus: O
> >> Content-Length: 4095
> >> Lines: 108
> >>
> >> On Wed Jan 28, 2026 at 12:02 AM JST, Gary Guo wrote:
> >> > On Tue Jan 27, 2026 at 3:25 AM GMT, Joel Fernandes wrote:
> >> >> On Jan 26, 2026, at 9:55 PM, Yury Norov <ynorov@nvidia.com> wrote:
> >> >>> On Mon, Jan 26, 2026 at 10:35:49PM +0900, Alexandre Courbot wrote:
> >> >>> > On Wed Jan 21, 2026 at 6:16 PM JST, Yury Norov wrote:
> >> >>> > > On Tue, Jan 20, 2026 at 03:17:56PM +0900, Alexandre Courbot wrote:
> >> >>> > > > Add a macro for defining bitfield structs with bounds-checked accessors.
> >> >>> > > >
> >> >>> > > > Each field is represented as a `Bounded` of the appropriate bit width,
> >> >>> > > > ensuring field values are never silently truncated.
> >> >>> > > >
> >> >>> > > > Fields can optionally be converted to/from custom types, either fallibly
> >> >>> > > > or infallibly.
> >> >>> > > >
> >> >>> > > > Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> >> >>> > > > ---
> >> >>> > > > rust/kernel/bitfield.rs | 503 ++++++++++++++++++++++++++++++++++++++++++++++++
> >> >>> > > > rust/kernel/lib.rs | 1 +
> >> >>> > > > 2 files changed, 504 insertions(+)
> >> >> [...]
> >> >>> > > > +/// // Setters can be chained. Bounded::new::<N>() does compile-time bounds checking.
> >> >>> > > > +/// let color = Rgb::default()
> >> >>> > > > +/// .set_red(Bounded::<u16, _>::new::<0x10>())
> >> >>> > > > +/// .set_green(Bounded::<u16, _>::new::<0x1f>())
> >> >>> > > > +/// .set_blue(Bounded::<u16, _>::new::<0x18>());
> >> >>> > >
> >> >>> > > Is there a way to just say:
> >> >>> > >
> >> >>> > > let color = Rgb::default().
> >> >>> > > .set_red(0x10)
> >> >>> > > .set_green(0x1f)
> >> >>> > > .set_blue(0x18)
> >> >>> > >
> >> >>> > > I think it should be the default style. Later in the patch you say:
> >> >>> > >
> >> >>> > > Each field is internally represented as a [`Bounded`]
> >> >>> > >
> >> >>> > > So, let's keep implementation decoupled from an interface?
> >> >>> >
> >> >>> > That is unfortunately not feasible, but the syntax above should seldomly
> >> >>> > be used outside of examples.
> >> >>>
> >> >>> The above short syntax is definitely more desired over that wordy and
> >> >>> non-trivial version that exposes implementation internals.
> >> >>>
> >> >>> A regular user doesn't care of the exact mechanism that protects the
> >> >>> bitfields. He wants to just assign numbers to the fields, and let
> >> >>> your machinery to take care of the integrity.
> >> >>>
> >> >>> Can you please explain in details why that's not feasible, please
> >> >>> do it in commit message. If it's an implementation constraint,
> >> >>> please consider to re-implement.
> >> >>
> >> >> If the issue is the excessive turbofish syntax, how about a macro? For
> >> >> example:
> >> >>
> >> >> let color = Rgb::default()
> >> >> .set_red(bounded!(u16, 0x10))
> >> >> .set_green(bounded!(u16, 0x1f))
> >> >> .set_blue(bounded!(u16, 0x18));
> >> >>
> >> >> This hides the turbofish and Bounded internals while still providing
> >> >> compile-time bounds checking.
> >> >
> >> > I think this could be the way forward, if we also get type inference working
> >> > properly.
> >> >
> >> > Rgb::default()
> >> > .set_read(bounded!(0x10))
> >> > .set_green(bounded!(0x1f))
> >> > .set_blue(bounded!(0x18))
> >> >
> >> > is roughly the limit that I find acceptable (`Bounded::<u16, _>::new::<0x10>()`
> >> > is something way too verbose so I find it unacceptable).
> >
> > I agree, this version is on the edge. It probably may be acceptable
> > because it highlights that the numbers passed in setters are some
> > special numbers. But yeah, it's a weak excuse.
> >
> > If it was C, it could be just as simple as
> >
> > #define set_red(v) __set_red(bounded(v))
> >
> > So...
> >
> > I'm not a rust professional, but I've been told many times that macro
> > rules in rust are so powerful that they can do any magic, even mimic
> > another languages.
> >
> > For fun, I asked AI to draw an example where rust structure is
> > initialized just like normal python does, and that's what I've got:
> >
> > struct Foo {
> > bar: i32,
> > baz: String,
> > }
> >
> > // Your specific constructor logic
> > fn construct_bar(v: i32) -> i32 { v * 2 }
> > fn construct_baz(v: i32) -> String { v.to_string() }
> >
> > // Helper macro to select the right function for a single field
> > macro_rules! get_ctor {
> > (bar, $val:expr) => { construct_bar($val) };
> > (baz, $val:expr) => { construct_baz($val) };
> > }
> >
> > macro_rules! python_init {
> > ($t:ident { $($field:ident = $val:expr),* $(,)? }) => {
> > $t {
> > // For each field, we call the dispatcher separately
> > $($field: get_ctor!($field, $val)),*
> > }
> > };
> > }
> >
> > fn main() {
> > let foo = python_init!(Foo { bar = 10, baz = 500 });
> >
> > println!("bar: {}", foo.bar); // Output: 20
> > println!("baz: {}", foo.baz); // Output: "500"
> > }
> >
> > Indeed it's possible!
>
> Oh yeah you can do all sorts of crazy sh** with Rust macros. :)
>
> >
> > Again, I'm not a rust professional and I can't evaluate quality of the
> > AI-generated code, neither I can ensure there's no nasty pitfalls.
> >
> > But as a user, I can say that
> >
> > let rgb = bitfield!(Rgb { red: 0x10, green: 0x1f, blue: 0x18 })
> >
> > would be way more readable than this beast:
> >
> > let color = Rgb::default()
> > .set_red(Bounded::<u16, _>::new::<0x10>())
> > .set_green(Bounded::<u16, _>::new::<0x1f>())
> > .set_blue(Bounded::<u16, _>::new::<0x18>());
>
> Without having tested the idea, a macro wrapping the whole bitfield (and
> not just trying to create a bounded) looks doable. Of course, it would
> have to rely on some underlying mechanism to set the fields, which could
> be the abomination above, or something a bit more convenient.
As soon as it's not exposed, it's fine.
> It looks like we are converging towards introducing the
> `with_const_field` setter for now with registers ; when we extract the
> `bitfield!` I think I would like to entertain the introduction of a
> macro close to what you suggested above.
Good. Happy you find it useful.
Thanks,
Yury
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-28 18:05 ` Yury Norov
@ 2026-01-29 13:40 ` Alexandre Courbot
2026-01-29 15:12 ` Miguel Ojeda
0 siblings, 1 reply; 56+ messages in thread
From: Alexandre Courbot @ 2026-01-29 13:40 UTC (permalink / raw)
To: Yury Norov
Cc: Joel Fernandes, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Thu Jan 29, 2026 at 3:05 AM JST, Yury Norov wrote:
> On Wed, Jan 28, 2026 at 11:12:27PM +0900, Alexandre Courbot wrote:
>> On Wed Jan 28, 2026 at 2:27 PM JST, Yury Norov wrote:
>
> <snip>
>
>> >> > Then let's implement it better. Can you comment why the suggested API
>> >> > doesn't work for you?
>> >> >
>> >> > color.set(blue, 10);
>> >> > color.get(blue);
>> >> >
>> >> > I think it should solve the problem with name clashing.
>> >>
>> >> That syntax cannot be implemented as it is written. What type is `blue` here?
>> >
>> > 'blue' has no type because it is not a variable but keyword. We do
>> > such things in C all the time:
>> >
>> > DEFINE_FREE(kfree, void *, if (_T) kfree(_T))
>> > ^^^^^
>> > keyword that becomes a part of cleanup function name
>> >
>> > And in another email I seemingly do similar thing for python_init!()
>> > macro in rust to pick the right constructor.
>>
>> Yup, and we could do the same in Rust with a macro, but your example
>> above was not macro code (macros are always ending with a `!`).
>
> color.set!(blue, 10) is equally good, if it helps.
But not doable unfortunately, since macros cannot be declared as
methods. The closest we could get is
bitfield_set!(color, blue, 10);
>
> If someone will point to the excessive use of macros, you're safe to
> ignore it. This is the very basic fundamental type, and I believe that
> it brings enough attention and expertise to make it safe.
I think the consensus is that macros are fine if they can hide
boilerplace. For instance, the `vec!` macro declares and initializes a
vector with content and spares you the need to write a loop. A proper
and elegant bitfield initializer would fit within that category imho.
>
>> >> The closest we could get would be a macro, that would look like
>> >>
>> >> bitfield_set!(color, blue, 10);
>
> Or maybe bitfield_set!(color.blue, 10), but the above looks more natural.
> Is it possible to make it color.blue = 10 by any chance?
the `= 10` part is no problem. However I am not sure we can parse
`color.blue` safely since the macro should also support
`object.bitfield.color` as an argument and won't be able to
differenciate between the path to the bitfield and the field itself
(hopefully that makes sense).
>
> Again (and again) - usability and readability is a king, and I'm glad
> we're moving toward the right direction.
I am confident we can find something that is both nice to use and
efficient under the hood when we start extracting the bitfield macro out
of `register!` during the next cycle. We've already made good progress.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH 3/6] rust: add `bitfield!` macro
2026-01-29 13:40 ` Alexandre Courbot
@ 2026-01-29 15:12 ` Miguel Ojeda
0 siblings, 0 replies; 56+ messages in thread
From: Miguel Ojeda @ 2026-01-29 15:12 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Yury Norov, Joel Fernandes, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Yury Norov, John Hubbard,
Alistair Popple, Timur Tabi, Edwin Peer, Eliot Courtney,
Daniel Almeida, Dirk Behme, Steven Price, rust-for-linux,
linux-kernel
On Thu, Jan 29, 2026 at 2:41 PM Alexandre Courbot <acourbot@nvidia.com> wrote:
>
> But not doable unfortunately, since macros cannot be declared as
> methods.
Indeed -- it may be doable in the future with postfix macros if they land:
https://github.com/rust-lang/rfcs/pull/2442
(The proposed ones do not do type-based dispatch).
Cheers,
Miguel
^ permalink raw reply [flat|nested] 56+ messages in thread
end of thread, other threads:[~2026-01-29 15:13 UTC | newest]
Thread overview: 56+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-20 6:17 [PATCH 0/6] rust: add `bitfield!` and `register!` macros Alexandre Courbot
2026-01-20 6:17 ` [PATCH 1/6] rust: num: add `shr` and `shl` methods to `Bounded` Alexandre Courbot
2026-01-20 8:44 ` Alice Ryhl
2026-01-20 12:53 ` Alexandre Courbot
2026-01-20 16:12 ` kernel test robot
2026-01-21 8:15 ` Yury Norov
2026-01-21 10:10 ` Alice Ryhl
2026-01-20 6:17 ` [PATCH 2/6] rust: num: add `as_bool` method to `Bounded<_, 1>` Alexandre Courbot
2026-01-20 8:45 ` Alice Ryhl
2026-01-20 6:17 ` [PATCH 3/6] rust: add `bitfield!` macro Alexandre Courbot
2026-01-20 11:45 ` Dirk Behme
2026-01-20 12:37 ` Miguel Ojeda
2026-01-20 12:47 ` Dirk Behme
2026-01-20 13:08 ` Miguel Ojeda
2026-01-20 13:20 ` Alexandre Courbot
2026-01-20 21:02 ` Miguel Ojeda
2026-01-20 12:51 ` Alexandre Courbot
2026-01-21 9:16 ` Yury Norov
2026-01-26 13:35 ` Alexandre Courbot
2026-01-27 2:55 ` Yury Norov
2026-01-27 3:25 ` Joel Fernandes
2026-01-27 4:49 ` Yury Norov
2026-01-27 10:41 ` Alexandre Courbot
2026-01-27 10:55 ` Miguel Ojeda
2026-01-28 5:27 ` Yury Norov
2026-01-28 14:12 ` Alexandre Courbot
2026-01-28 18:05 ` Yury Norov
2026-01-29 13:40 ` Alexandre Courbot
2026-01-29 15:12 ` Miguel Ojeda
2026-01-27 11:00 ` Joel Fernandes
2026-01-27 15:02 ` Gary Guo
2026-01-28 1:23 ` Alexandre Courbot
2026-01-28 4:33 ` Yury Norov
2026-01-28 14:02 ` Alexandre Courbot
2026-01-28 18:12 ` Yury Norov
2026-01-27 9:57 ` Alexandre Courbot
2026-01-27 21:03 ` John Hubbard
2026-01-27 21:10 ` Gary Guo
2026-01-27 21:22 ` John Hubbard
2026-01-28 1:28 ` Alexandre Courbot
2026-01-28 1:41 ` John Hubbard
2026-01-20 6:17 ` [PATCH 4/6] rust: bitfield: Add KUNIT tests for bitfield Alexandre Courbot
2026-01-20 6:17 ` [PATCH 5/6] rust: io: add `register!` macro Alexandre Courbot
2026-01-20 6:17 ` [PATCH FOR REFERENCE 6/6] gpu: nova-core: use the kernel `register!` and `bitfield!` macros Alexandre Courbot
2026-01-20 13:14 ` [PATCH 0/6] rust: add `bitfield!` and `register!` macros Miguel Ojeda
2026-01-20 13:38 ` Danilo Krummrich
2026-01-20 13:50 ` Miguel Ojeda
2026-01-20 14:18 ` Danilo Krummrich
2026-01-20 14:57 ` Miguel Ojeda
2026-01-20 15:27 ` Danilo Krummrich
2026-01-20 15:48 ` Miguel Ojeda
2026-01-20 20:01 ` Danilo Krummrich
2026-01-20 20:31 ` Miguel Ojeda
2026-01-21 5:57 ` Yury Norov
2026-01-21 6:55 ` Alexandre Courbot
2026-01-26 14:03 ` Joel Fernandes
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox