* [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/
@ 2025-10-03 15:47 Joel Fernandes
2025-10-03 15:47 ` [PATCH v6 1/5] nova-core: bitfield: Move bitfield-specific code from register! into new macro Joel Fernandes
` (6 more replies)
0 siblings, 7 replies; 41+ messages in thread
From: Joel Fernandes @ 2025-10-03 15:47 UTC (permalink / raw)
To: linux-kernel, rust-for-linux, dri-devel, dakr, acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, joel, Elle Rhumsaa, Yury Norov, Daniel Almeida,
Andrea Righi, nouveau
Hello!
These patches extract and enhance the bitfield support in the register macro in
nova to define Rust structures with bitfields. It then moves out the bitfield
support into the kenrel crate.
Since v5, I dropped several patches and only kept the simple ones that do code
movement, added a few features and added a KUNIT test. After Alex's bounded
integer [1] support is in, we can rewrite the dropped patches.
I also dropped the MAINTAINER entry for now, pending more clarity around that.
I am happy to maintain it, but I need more input on who all will co-maintain,
now that the last 4 patches were dropped. Perhaps we can maintain it was a part
of the core rust-for-linux? I suggest let us create the maintainer entry once
Alex's bounded integer support is integrated but I am open to suggestions.
Here are the v5 patches [2].
[1] https://lore.kernel.org/all/20251002-bounded_ints-v1-0-dd60f5804ea4@nvidia.com/
[2] https://lore.kernel.org/all/20250930144537.3559207-1-joelagnelf@nvidia.com/
Joel Fernandes (5):
nova-core: bitfield: Move bitfield-specific code from register! into
new macro
nova-core: bitfield: Add support for different storage widths
nova-core: bitfield: Add support for custom visiblity
rust: Move register and bitfield macros out of Nova
rust: bitfield: Add KUNIT tests for bitfield
drivers/gpu/nova-core/falcon.rs | 2 +-
drivers/gpu/nova-core/falcon/gsp.rs | 4 +-
drivers/gpu/nova-core/falcon/sec2.rs | 2 +-
drivers/gpu/nova-core/regs.rs | 6 +-
rust/kernel/bitfield.rs | 654 ++++++++++++++++++
rust/kernel/io.rs | 1 +
.../macros.rs => rust/kernel/io/register.rs | 301 +-------
rust/kernel/lib.rs | 1 +
8 files changed, 696 insertions(+), 275 deletions(-)
create mode 100644 rust/kernel/bitfield.rs
rename drivers/gpu/nova-core/regs/macros.rs => rust/kernel/io/register.rs (73%)
base-commit: 299eb32863e584cfff7c6b667c3e92ae7d4d2bf9
--
2.34.1
^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH v6 1/5] nova-core: bitfield: Move bitfield-specific code from register! into new macro
2025-10-03 15:47 [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/ Joel Fernandes
@ 2025-10-03 15:47 ` Joel Fernandes
2025-10-06 17:56 ` Edwin Peer
2025-10-03 15:47 ` [PATCH v6 2/5] nova-core: bitfield: Add support for different storage widths Joel Fernandes
` (5 subsequent siblings)
6 siblings, 1 reply; 41+ messages in thread
From: Joel Fernandes @ 2025-10-03 15:47 UTC (permalink / raw)
To: linux-kernel, rust-for-linux, dri-devel, dakr, acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, joel, Elle Rhumsaa, Yury Norov, Daniel Almeida,
Andrea Righi, nouveau
The bitfield-specific into new macro. This will be used to define
structs with bitfields, similar to C language.
Reviewed-by: Elle Rhumsaa <elle@weathered-steel.dev>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/bitfield.rs | 316 +++++++++++++++++++++++++++
drivers/gpu/nova-core/nova_core.rs | 3 +
drivers/gpu/nova-core/regs/macros.rs | 259 +---------------------
3 files changed, 329 insertions(+), 249 deletions(-)
create mode 100644 drivers/gpu/nova-core/bitfield.rs
diff --git a/drivers/gpu/nova-core/bitfield.rs b/drivers/gpu/nova-core/bitfield.rs
new file mode 100644
index 000000000000..dd0ef2016ff8
--- /dev/null
+++ b/drivers/gpu/nova-core/bitfield.rs
@@ -0,0 +1,316 @@
+// 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 u32 {
+/// fn from(mode: Mode) -> u32 {
+/// mode as u32
+/// }
+/// }
+///
+/// #[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 u32 {
+/// fn from(state: State) -> u32 {
+/// state as u32
+/// }
+/// }
+///
+/// bitfield! {
+/// struct ControlReg {
+/// 3:0 mode as u8 ?=> Mode;
+/// 7:7 state as bool => State;
+/// }
+/// }
+/// ```
+///
+/// This generates a struct with:
+/// - Field accessors: `mode()`, `state()`, etc.
+/// - Field setters: `set_mode()`, `set_state()`, etc. (supports chaining with builder pattern).
+/// - Debug and Default implementations.
+///
+/// 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
+ (struct $name:ident $(, $comment:literal)? { $($fields:tt)* }) => {
+ bitfield!(@core $name $(, $comment)? { $($fields)* });
+ };
+
+ // All rules below are helpers.
+
+ // Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`,
+ // `Default`, `BitOr`, and conversion to the value type) and field accessor methods.
+ (@core $name:ident $(, $comment:literal)? { $($fields:tt)* }) => {
+ $(
+ #[doc=$comment]
+ )?
+ #[repr(transparent)]
+ #[derive(Clone, Copy)]
+ pub(crate) struct $name(u32);
+
+ impl ::core::ops::BitOr for $name {
+ type Output = Self;
+
+ fn bitor(self, rhs: Self) -> Self::Output {
+ Self(self.0 | rhs.0)
+ }
+ }
+
+ impl ::core::convert::From<$name> for u32 {
+ fn from(val: $name) -> u32 {
+ val.0
+ }
+ }
+
+ bitfield!(@fields_dispatcher $name { $($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 $name:ident {
+ $($hi:tt:$lo:tt $field:ident as $type:tt
+ $(?=> $try_into_type:ty)?
+ $(=> $into_type:ty)?
+ $(, $comment:literal)?
+ ;
+ )*
+ }
+ ) => {
+ bitfield!(@field_accessors $name {
+ $(
+ $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 $name:ident {
+ $($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 $name $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 $name:ident $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
+ $(, $comment:literal)?;
+ ) => {
+ bitfield!(
+ @leaf_accessor $name $hi:$lo $field
+ { |f| <$into_type>::from(if f != 0 { true } else { false }) }
+ $into_type => $into_type $(, $comment)?;
+ );
+ };
+
+ // Shortcut for fields defined as `bool` without the `=>` syntax.
+ (
+ @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
+ ) => {
+ bitfield!(@field_accessor $name $hi:$lo $field as bool => bool $(, $comment)?;);
+ };
+
+ // Catches the `?=>` syntax for non-boolean fields.
+ (
+ @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty
+ $(, $comment:literal)?;
+ ) => {
+ bitfield!(@leaf_accessor $name $hi:$lo $field
+ { |f| <$try_into_type>::try_from(f as $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 $name:ident $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty
+ $(, $comment:literal)?;
+ ) => {
+ bitfield!(@leaf_accessor $name $hi:$lo $field
+ { |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
+ };
+
+ // Shortcut for non-boolean fields defined without the `=>` or `?=>` syntax.
+ (
+ @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
+ $(, $comment:literal)?;
+ ) => {
+ bitfield!(@field_accessor $name $hi:$lo $field as $type => $type $(, $comment)?;);
+ };
+
+ // Generates the accessor methods for a single field.
+ (
+ @leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident
+ { $process:expr } $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>]: u32 = ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
+ const [<$field:upper _SHIFT>]: u32 = Self::[<$field:upper _MASK>].trailing_zeros();
+ );
+
+ $(
+ #[doc="Returns the value of this field:"]
+ #[doc=$comment]
+ )?
+ #[inline(always)]
+ pub(crate) fn $field(self) -> $res_type {
+ ::kernel::macros::paste!(
+ const MASK: u32 = $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)]
+ pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self {
+ const MASK: u32 = $name::[<$field:upper _MASK>];
+ const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
+ let value = (u32::from(value) << SHIFT) & MASK;
+ self.0 = (self.0 & !MASK) | value;
+
+ self
+ }
+ );
+ };
+
+ // Generates the `Debug` implementation for `$name`.
+ (@debug $name:ident { $($field:ident;)* }) => {
+ impl ::core::fmt::Debug for $name {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+ f.debug_struct(stringify!($name))
+ .field("<raw>", &format_args!("{:#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/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index fffcaee2249f..112277c7921e 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -2,6 +2,9 @@
//! Nova Core GPU Driver
+#[macro_use]
+mod bitfield;
+
mod dma;
mod driver;
mod falcon;
diff --git a/drivers/gpu/nova-core/regs/macros.rs b/drivers/gpu/nova-core/regs/macros.rs
index 754c14ee7f40..945d15a2c529 100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -8,7 +8,8 @@
//!
//! 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.
+//! 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.
@@ -54,15 +55,6 @@ pub(crate) trait RegisterBase<T> {
/// BOOT_0::alter(&bar, |r| r.set_major_revision(3).set_minor_revision(10));
/// ```
///
-/// 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.
-///
/// 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.
///
@@ -284,25 +276,25 @@ pub(crate) trait RegisterBase<T> {
macro_rules! register {
// Creates a register at a fixed offset of the MMIO space.
($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } ) => {
- register!(@core $name $(, $comment)? { $($fields)* } );
+ bitfield!(struct $name $(, $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)* } ) => {
- register!(@core $name $(, $comment)? { $($fields)* } );
+ bitfield!(struct $name $(, $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)* } ) => {
- register!(@core $name $(, $comment)? { $($fields)* } );
+ bitfield!(struct $name $(, $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)* }) => {
- register!(@core $name $(, $comment)? { $($fields)* } );
+ bitfield!(struct $name $(, $comment)? { $($fields)* } );
register!(@io_relative $name @ $base [ $alias::OFFSET ]);
};
@@ -313,7 +305,7 @@ macro_rules! register {
}
) => {
static_assert!(::core::mem::size_of::<u32>() <= $stride);
- register!(@core $name $(, $comment)? { $($fields)* } );
+ bitfield!(struct $name $(, $comment)? { $($fields)* } );
register!(@io_array $name @ $offset [ $size ; $stride ]);
};
@@ -334,7 +326,7 @@ macro_rules! register {
$(, $comment:literal)? { $($fields:tt)* }
) => {
static_assert!(::core::mem::size_of::<u32>() <= $stride);
- register!(@core $name $(, $comment)? { $($fields)* } );
+ bitfield!(struct $name $(, $comment)? { $($fields)* } );
register!(@io_relative_array $name @ $base [ $offset [ $size ; $stride ] ]);
};
@@ -356,7 +348,7 @@ macro_rules! register {
}
) => {
static_assert!($idx < $alias::SIZE);
- register!(@core $name $(, $comment)? { $($fields)* } );
+ bitfield!(struct $name $(, $comment)? { $($fields)* } );
register!(@io_relative $name @ $base [ $alias::OFFSET + $idx * $alias::STRIDE ] );
};
@@ -365,241 +357,10 @@ macro_rules! register {
// 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);
- register!(@core $name $(, $comment)? { $($fields)* } );
+ bitfield!(struct $name $(, $comment)? { $($fields)* } );
register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE );
};
- // All rules below are helpers.
-
- // Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`,
- // `Default`, `BitOr`, and conversion to the value type) and field accessor methods.
- (@core $name:ident $(, $comment:literal)? { $($fields:tt)* }) => {
- $(
- #[doc=$comment]
- )?
- #[repr(transparent)]
- #[derive(Clone, Copy)]
- pub(crate) struct $name(u32);
-
- impl ::core::ops::BitOr for $name {
- type Output = Self;
-
- fn bitor(self, rhs: Self) -> Self::Output {
- Self(self.0 | rhs.0)
- }
- }
-
- impl ::core::convert::From<$name> for u32 {
- fn from(reg: $name) -> u32 {
- reg.0
- }
- }
-
- register!(@fields_dispatcher $name { $($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 $name:ident {
- $($hi:tt:$lo:tt $field:ident as $type:tt
- $(?=> $try_into_type:ty)?
- $(=> $into_type:ty)?
- $(, $comment:literal)?
- ;
- )*
- }
- ) => {
- register!(@field_accessors $name {
- $(
- $hi:$lo $field as $type
- $(?=> $try_into_type)?
- $(=> $into_type)?
- $(, $comment)?
- ;
- )*
- });
- register!(@debug $name { $($field;)* });
- register!(@default $name { $($field;)* });
- };
-
- // Defines all the field getter/methods methods for `$name`.
- (
- @field_accessors $name:ident {
- $($hi:tt:$lo:tt $field:ident as $type:tt
- $(?=> $try_into_type:ty)?
- $(=> $into_type:ty)?
- $(, $comment:literal)?
- ;
- )*
- }
- ) => {
- $(
- register!(@check_field_bounds $hi:$lo $field as $type);
- )*
-
- #[allow(dead_code)]
- impl $name {
- $(
- register!(@field_accessor $name $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 $name:ident $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
- $(, $comment:literal)?;
- ) => {
- register!(
- @leaf_accessor $name $hi:$lo $field
- { |f| <$into_type>::from(if f != 0 { true } else { false }) }
- $into_type => $into_type $(, $comment)?;
- );
- };
-
- // Shortcut for fields defined as `bool` without the `=>` syntax.
- (
- @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
- ) => {
- register!(@field_accessor $name $hi:$lo $field as bool => bool $(, $comment)?;);
- };
-
- // Catches the `?=>` syntax for non-boolean fields.
- (
- @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty
- $(, $comment:literal)?;
- ) => {
- register!(@leaf_accessor $name $hi:$lo $field
- { |f| <$try_into_type>::try_from(f as $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 $name:ident $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty
- $(, $comment:literal)?;
- ) => {
- register!(@leaf_accessor $name $hi:$lo $field
- { |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
- };
-
- // Shortcut for non-boolean fields defined without the `=>` or `?=>` syntax.
- (
- @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
- $(, $comment:literal)?;
- ) => {
- register!(@field_accessor $name $hi:$lo $field as $type => $type $(, $comment)?;);
- };
-
- // Generates the accessor methods for a single field.
- (
- @leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident
- { $process:expr } $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>]: u32 = ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
- const [<$field:upper _SHIFT>]: u32 = Self::[<$field:upper _MASK>].trailing_zeros();
- );
-
- $(
- #[doc="Returns the value of this field:"]
- #[doc=$comment]
- )?
- #[inline(always)]
- pub(crate) fn $field(self) -> $res_type {
- ::kernel::macros::paste!(
- const MASK: u32 = $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)]
- pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self {
- const MASK: u32 = $name::[<$field:upper _MASK>];
- const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
- let value = (u32::from(value) << SHIFT) & MASK;
- self.0 = (self.0 & !MASK) | value;
-
- self
- }
- );
- };
-
- // Generates the `Debug` implementation for `$name`.
- (@debug $name:ident { $($field:ident;)* }) => {
- impl ::core::fmt::Debug for $name {
- fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
- f.debug_struct(stringify!($name))
- .field("<raw>", &format_args!("{:#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 register 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
- }
- }
- };
-
// Generates the IO accessors for a fixed offset register.
(@io_fixed $name:ident @ $offset:expr) => {
#[allow(dead_code)]
--
2.34.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v6 2/5] nova-core: bitfield: Add support for different storage widths
2025-10-03 15:47 [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/ Joel Fernandes
2025-10-03 15:47 ` [PATCH v6 1/5] nova-core: bitfield: Move bitfield-specific code from register! into new macro Joel Fernandes
@ 2025-10-03 15:47 ` Joel Fernandes
2025-10-03 15:47 ` [PATCH v6 3/5] nova-core: bitfield: Add support for custom visiblity Joel Fernandes
` (4 subsequent siblings)
6 siblings, 0 replies; 41+ messages in thread
From: Joel Fernandes @ 2025-10-03 15:47 UTC (permalink / raw)
To: linux-kernel, rust-for-linux, dri-devel, dakr, acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, joel, Elle Rhumsaa, Yury Norov, Daniel Almeida,
Andrea Righi, nouveau
Previously, bitfields were hardcoded to use u32 as the underlying
storage type. Add support for different storage types (u8, u16, u32,
u64) to the bitfield macro.
New syntax is: struct Name(<type ex., u32>) { ... }
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Elle Rhumsaa <elle@weathered-steel.dev>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/bitfield.rs | 65 ++++++++++++++++------------
drivers/gpu/nova-core/regs/macros.rs | 16 +++----
2 files changed, 46 insertions(+), 35 deletions(-)
diff --git a/drivers/gpu/nova-core/bitfield.rs b/drivers/gpu/nova-core/bitfield.rs
index dd0ef2016ff8..75de1c0fcb3b 100644
--- a/drivers/gpu/nova-core/bitfield.rs
+++ b/drivers/gpu/nova-core/bitfield.rs
@@ -57,7 +57,7 @@
/// }
///
/// bitfield! {
-/// struct ControlReg {
+/// struct ControlReg(u32) {
/// 3:0 mode as u8 ?=> Mode;
/// 7:7 state as bool => State;
/// }
@@ -67,6 +67,8 @@
/// 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.
///
/// Fields are defined as follows:
@@ -79,21 +81,21 @@
/// 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
- (struct $name:ident $(, $comment:literal)? { $($fields:tt)* }) => {
- bitfield!(@core $name $(, $comment)? { $($fields)* });
+ (struct $name:ident($storage:ty) $(, $comment:literal)? { $($fields:tt)* }) => {
+ bitfield!(@core $name $storage $(, $comment)? { $($fields)* });
};
// All rules below are helpers.
// Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`,
// `Default`, `BitOr`, and conversion to the value type) and field accessor methods.
- (@core $name:ident $(, $comment:literal)? { $($fields:tt)* }) => {
+ (@core $name:ident $storage:ty $(, $comment:literal)? { $($fields:tt)* }) => {
$(
#[doc=$comment]
)?
#[repr(transparent)]
#[derive(Clone, Copy)]
- pub(crate) struct $name(u32);
+ pub(crate) struct $name($storage);
impl ::core::ops::BitOr for $name {
type Output = Self;
@@ -103,20 +105,20 @@ fn bitor(self, rhs: Self) -> Self::Output {
}
}
- impl ::core::convert::From<$name> for u32 {
- fn from(val: $name) -> u32 {
+ impl ::core::convert::From<$name> for $storage {
+ fn from(val: $name) -> $storage {
val.0
}
}
- bitfield!(@fields_dispatcher $name { $($fields)* });
+ bitfield!(@fields_dispatcher $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 $name:ident {
+ (@fields_dispatcher $name:ident $storage:ty {
$($hi:tt:$lo:tt $field:ident as $type:tt
$(?=> $try_into_type:ty)?
$(=> $into_type:ty)?
@@ -125,7 +127,7 @@ fn from(val: $name) -> u32 {
)*
}
) => {
- bitfield!(@field_accessors $name {
+ bitfield!(@field_accessors $name $storage {
$(
$hi:$lo $field as $type
$(?=> $try_into_type)?
@@ -140,7 +142,7 @@ fn from(val: $name) -> u32 {
// Defines all the field getter/setter methods for `$name`.
(
- @field_accessors $name:ident {
+ @field_accessors $name:ident $storage:ty {
$($hi:tt:$lo:tt $field:ident as $type:tt
$(?=> $try_into_type:ty)?
$(=> $into_type:ty)?
@@ -156,7 +158,7 @@ fn from(val: $name) -> u32 {
#[allow(dead_code)]
impl $name {
$(
- bitfield!(@field_accessor $name $hi:$lo $field as $type
+ bitfield!(@field_accessor $name $storage, $hi:$lo $field as $type
$(?=> $try_into_type)?
$(=> $into_type)?
$(, $comment)?
@@ -190,11 +192,11 @@ impl $name {
// Catches fields defined as `bool` and convert them into a boolean value.
(
- @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
+ @field_accessor $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
$(, $comment:literal)?;
) => {
bitfield!(
- @leaf_accessor $name $hi:$lo $field
+ @leaf_accessor $name $storage, $hi:$lo $field
{ |f| <$into_type>::from(if f != 0 { true } else { false }) }
$into_type => $into_type $(, $comment)?;
);
@@ -202,17 +204,17 @@ impl $name {
// Shortcut for fields defined as `bool` without the `=>` syntax.
(
- @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
+ @field_accessor $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
) => {
- bitfield!(@field_accessor $name $hi:$lo $field as bool => bool $(, $comment)?;);
+ bitfield!(@field_accessor $name $storage, $hi:$lo $field as bool => bool $(, $comment)?;);
};
// Catches the `?=>` syntax for non-boolean fields.
(
- @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty
+ @field_accessor $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty
$(, $comment:literal)?;
) => {
- bitfield!(@leaf_accessor $name $hi:$lo $field
+ bitfield!(@leaf_accessor $name $storage, $hi:$lo $field
{ |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>
::core::result::Result<
$try_into_type,
@@ -223,29 +225,38 @@ impl $name {
// Catches the `=>` syntax for non-boolean fields.
(
- @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty
+ @field_accessor $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty
$(, $comment:literal)?;
) => {
- bitfield!(@leaf_accessor $name $hi:$lo $field
+ bitfield!(@leaf_accessor $name $storage, $hi:$lo $field
{ |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
};
// Shortcut for non-boolean fields defined without the `=>` or `?=>` syntax.
(
- @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
+ @field_accessor $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as $type:tt
$(, $comment:literal)?;
) => {
- bitfield!(@field_accessor $name $hi:$lo $field as $type => $type $(, $comment)?;);
+ bitfield!(@field_accessor $name $storage, $hi:$lo $field as $type => $type $(, $comment)?;);
};
// Generates the accessor methods for a single field.
(
- @leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident
+ @leaf_accessor $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident
{ $process:expr } $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>]: u32 = ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
+ 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 = Self::[<$field:upper _MASK>].trailing_zeros();
);
@@ -256,7 +267,7 @@ impl $name {
#[inline(always)]
pub(crate) fn $field(self) -> $res_type {
::kernel::macros::paste!(
- const MASK: u32 = $name::[<$field:upper _MASK>];
+ const MASK: $storage = $name::[<$field:upper _MASK>];
const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
);
let field = ((self.0 & MASK) >> SHIFT);
@@ -271,9 +282,9 @@ pub(crate) fn $field(self) -> $res_type {
)?
#[inline(always)]
pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self {
- const MASK: u32 = $name::[<$field:upper _MASK>];
+ const MASK: $storage = $name::[<$field:upper _MASK>];
const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
- let value = (u32::from(value) << SHIFT) & MASK;
+ let value = (<$storage>::from(value) << SHIFT) & MASK;
self.0 = (self.0 & !MASK) | value;
self
diff --git a/drivers/gpu/nova-core/regs/macros.rs b/drivers/gpu/nova-core/regs/macros.rs
index 945d15a2c529..ffd7d5cb73bb 100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -276,25 +276,25 @@ pub(crate) trait RegisterBase<T> {
macro_rules! register {
// Creates a register at a fixed offset of the MMIO space.
($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } ) => {
- bitfield!(struct $name $(, $comment)? { $($fields)* } );
+ bitfield!(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!(struct $name $(, $comment)? { $($fields)* } );
+ bitfield!(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!(struct $name $(, $comment)? { $($fields)* } );
+ bitfield!(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!(struct $name $(, $comment)? { $($fields)* } );
+ bitfield!(struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_relative $name @ $base [ $alias::OFFSET ]);
};
@@ -305,7 +305,7 @@ macro_rules! register {
}
) => {
static_assert!(::core::mem::size_of::<u32>() <= $stride);
- bitfield!(struct $name $(, $comment)? { $($fields)* } );
+ bitfield!(struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_array $name @ $offset [ $size ; $stride ]);
};
@@ -326,7 +326,7 @@ macro_rules! register {
$(, $comment:literal)? { $($fields:tt)* }
) => {
static_assert!(::core::mem::size_of::<u32>() <= $stride);
- bitfield!(struct $name $(, $comment)? { $($fields)* } );
+ bitfield!(struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_relative_array $name @ $base [ $offset [ $size ; $stride ] ]);
};
@@ -348,7 +348,7 @@ macro_rules! register {
}
) => {
static_assert!($idx < $alias::SIZE);
- bitfield!(struct $name $(, $comment)? { $($fields)* } );
+ bitfield!(struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_relative $name @ $base [ $alias::OFFSET + $idx * $alias::STRIDE ] );
};
@@ -357,7 +357,7 @@ macro_rules! register {
// 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!(struct $name $(, $comment)? { $($fields)* } );
+ bitfield!(struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE );
};
--
2.34.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v6 3/5] nova-core: bitfield: Add support for custom visiblity
2025-10-03 15:47 [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/ Joel Fernandes
2025-10-03 15:47 ` [PATCH v6 1/5] nova-core: bitfield: Move bitfield-specific code from register! into new macro Joel Fernandes
2025-10-03 15:47 ` [PATCH v6 2/5] nova-core: bitfield: Add support for different storage widths Joel Fernandes
@ 2025-10-03 15:47 ` Joel Fernandes
2025-10-03 15:47 ` [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova Joel Fernandes
` (3 subsequent siblings)
6 siblings, 0 replies; 41+ messages in thread
From: Joel Fernandes @ 2025-10-03 15:47 UTC (permalink / raw)
To: linux-kernel, rust-for-linux, dri-devel, dakr, acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, joel, Elle Rhumsaa, Yury Norov, Daniel Almeida,
Andrea Righi, nouveau
Add support for custom visiblity to allow for users to control visibility
of the structure and helpers.
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Elle Rhumsaa <elle@weathered-steel.dev>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/bitfield.rs | 49 +++++++++++++++-------------
drivers/gpu/nova-core/regs/macros.rs | 16 ++++-----
2 files changed, 34 insertions(+), 31 deletions(-)
diff --git a/drivers/gpu/nova-core/bitfield.rs b/drivers/gpu/nova-core/bitfield.rs
index 75de1c0fcb3b..cbedbb0078f6 100644
--- a/drivers/gpu/nova-core/bitfield.rs
+++ b/drivers/gpu/nova-core/bitfield.rs
@@ -57,7 +57,7 @@
/// }
///
/// bitfield! {
-/// struct ControlReg(u32) {
+/// pub struct ControlReg(u32) {
/// 3:0 mode as u8 ?=> Mode;
/// 7:7 state as bool => State;
/// }
@@ -71,6 +71,9 @@
/// 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
@@ -81,21 +84,21 @@
/// 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
- (struct $name:ident($storage:ty) $(, $comment:literal)? { $($fields:tt)* }) => {
- bitfield!(@core $name $storage $(, $comment)? { $($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`, `BitOr`, and conversion to the value type) and field accessor methods.
- (@core $name:ident $storage:ty $(, $comment:literal)? { $($fields:tt)* }) => {
+ (@core $vis:vis $name:ident $storage:ty $(, $comment:literal)? { $($fields:tt)* }) => {
$(
#[doc=$comment]
)?
#[repr(transparent)]
#[derive(Clone, Copy)]
- pub(crate) struct $name($storage);
+ $vis struct $name($storage);
impl ::core::ops::BitOr for $name {
type Output = Self;
@@ -111,14 +114,14 @@ fn from(val: $name) -> $storage {
}
}
- bitfield!(@fields_dispatcher $name $storage { $($fields)* });
+ 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 $name:ident $storage:ty {
+ (@fields_dispatcher $vis:vis $name:ident $storage:ty {
$($hi:tt:$lo:tt $field:ident as $type:tt
$(?=> $try_into_type:ty)?
$(=> $into_type:ty)?
@@ -127,7 +130,7 @@ fn from(val: $name) -> $storage {
)*
}
) => {
- bitfield!(@field_accessors $name $storage {
+ bitfield!(@field_accessors $vis $name $storage {
$(
$hi:$lo $field as $type
$(?=> $try_into_type)?
@@ -142,7 +145,7 @@ fn from(val: $name) -> $storage {
// Defines all the field getter/setter methods for `$name`.
(
- @field_accessors $name:ident $storage:ty {
+ @field_accessors $vis:vis $name:ident $storage:ty {
$($hi:tt:$lo:tt $field:ident as $type:tt
$(?=> $try_into_type:ty)?
$(=> $into_type:ty)?
@@ -158,7 +161,7 @@ fn from(val: $name) -> $storage {
#[allow(dead_code)]
impl $name {
$(
- bitfield!(@field_accessor $name $storage, $hi:$lo $field as $type
+ bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field as $type
$(?=> $try_into_type)?
$(=> $into_type)?
$(, $comment)?
@@ -192,11 +195,11 @@ impl $name {
// Catches fields defined as `bool` and convert them into a boolean value.
(
- @field_accessor $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
+ @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
$(, $comment:literal)?;
) => {
bitfield!(
- @leaf_accessor $name $storage, $hi:$lo $field
+ @leaf_accessor $vis $name $storage, $hi:$lo $field
{ |f| <$into_type>::from(if f != 0 { true } else { false }) }
$into_type => $into_type $(, $comment)?;
);
@@ -204,17 +207,17 @@ impl $name {
// Shortcut for fields defined as `bool` without the `=>` syntax.
(
- @field_accessor $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
+ @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
) => {
- bitfield!(@field_accessor $name $storage, $hi:$lo $field as bool => bool $(, $comment)?;);
+ bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field as bool => bool $(, $comment)?;);
};
// Catches the `?=>` syntax for non-boolean fields.
(
- @field_accessor $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty
+ @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 $name $storage, $hi:$lo $field
+ bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field
{ |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>
::core::result::Result<
$try_into_type,
@@ -225,24 +228,24 @@ impl $name {
// Catches the `=>` syntax for non-boolean fields.
(
- @field_accessor $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty
+ @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 $name $storage, $hi:$lo $field
+ bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field
{ |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
};
// Shortcut for non-boolean fields defined without the `=>` or `?=>` syntax.
(
- @field_accessor $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as $type:tt
+ @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as $type:tt
$(, $comment:literal)?;
) => {
- bitfield!(@field_accessor $name $storage, $hi:$lo $field as $type => $type $(, $comment)?;);
+ bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field as $type => $type $(, $comment)?;);
};
// Generates the accessor methods for a single field.
(
- @leaf_accessor $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident
+ @leaf_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident
{ $process:expr } $to_type:ty => $res_type:ty $(, $comment:literal)?;
) => {
::kernel::macros::paste!(
@@ -265,7 +268,7 @@ impl $name {
#[doc=$comment]
)?
#[inline(always)]
- pub(crate) fn $field(self) -> $res_type {
+ $vis fn $field(self) -> $res_type {
::kernel::macros::paste!(
const MASK: $storage = $name::[<$field:upper _MASK>];
const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
@@ -281,7 +284,7 @@ pub(crate) fn $field(self) -> $res_type {
#[doc=$comment]
)?
#[inline(always)]
- pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self {
+ $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(value) << SHIFT) & MASK;
diff --git a/drivers/gpu/nova-core/regs/macros.rs b/drivers/gpu/nova-core/regs/macros.rs
index ffd7d5cb73bb..c0a5194e8d97 100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -276,25 +276,25 @@ pub(crate) trait RegisterBase<T> {
macro_rules! register {
// Creates a register at a fixed offset of the MMIO space.
($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } ) => {
- bitfield!(struct $name(u32) $(, $comment)? { $($fields)* } );
+ 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!(struct $name(u32) $(, $comment)? { $($fields)* } );
+ 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!(struct $name(u32) $(, $comment)? { $($fields)* } );
+ 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!(struct $name(u32) $(, $comment)? { $($fields)* } );
+ bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_relative $name @ $base [ $alias::OFFSET ]);
};
@@ -305,7 +305,7 @@ macro_rules! register {
}
) => {
static_assert!(::core::mem::size_of::<u32>() <= $stride);
- bitfield!(struct $name(u32) $(, $comment)? { $($fields)* } );
+ bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_array $name @ $offset [ $size ; $stride ]);
};
@@ -326,7 +326,7 @@ macro_rules! register {
$(, $comment:literal)? { $($fields:tt)* }
) => {
static_assert!(::core::mem::size_of::<u32>() <= $stride);
- bitfield!(struct $name(u32) $(, $comment)? { $($fields)* } );
+ bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_relative_array $name @ $base [ $offset [ $size ; $stride ] ]);
};
@@ -348,7 +348,7 @@ macro_rules! register {
}
) => {
static_assert!($idx < $alias::SIZE);
- bitfield!(struct $name(u32) $(, $comment)? { $($fields)* } );
+ bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_relative $name @ $base [ $alias::OFFSET + $idx * $alias::STRIDE ] );
};
@@ -357,7 +357,7 @@ macro_rules! register {
// 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!(struct $name(u32) $(, $comment)? { $($fields)* } );
+ bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE );
};
--
2.34.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova
2025-10-03 15:47 [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/ Joel Fernandes
` (2 preceding siblings ...)
2025-10-03 15:47 ` [PATCH v6 3/5] nova-core: bitfield: Add support for custom visiblity Joel Fernandes
@ 2025-10-03 15:47 ` Joel Fernandes
2025-10-06 10:38 ` Alexandre Courbot
` (2 more replies)
2025-10-03 15:47 ` [PATCH v6 5/5] rust: bitfield: Add KUNIT tests for bitfield Joel Fernandes
` (2 subsequent siblings)
6 siblings, 3 replies; 41+ messages in thread
From: Joel Fernandes @ 2025-10-03 15:47 UTC (permalink / raw)
To: linux-kernel, rust-for-linux, dri-devel, dakr, acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, joel, Elle Rhumsaa, Yury Norov, Daniel Almeida,
Andrea Righi, nouveau
Out of broad need for the register and bitfield macros in Rust, move
them out of nova into the kernel crate. Several usecases need them (Nova
is already using these and Tyr developers said they need them).
bitfield moved into kernel crate - defines bitfields in Rust.
register moved into io module - defines hardware registers and accessors.
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Elle Rhumsaa <elle@weathered-steel.dev>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 2 +-
drivers/gpu/nova-core/falcon/gsp.rs | 4 +-
drivers/gpu/nova-core/falcon/sec2.rs | 2 +-
drivers/gpu/nova-core/nova_core.rs | 3 -
drivers/gpu/nova-core/regs.rs | 6 +-
.../gpu/nova-core => rust/kernel}/bitfield.rs | 27 ++++-----
rust/kernel/io.rs | 1 +
.../macros.rs => rust/kernel/io/register.rs | 58 ++++++++++---------
rust/kernel/lib.rs | 1 +
9 files changed, 54 insertions(+), 50 deletions(-)
rename {drivers/gpu/nova-core => rust/kernel}/bitfield.rs (91%)
rename drivers/gpu/nova-core/regs/macros.rs => rust/kernel/io/register.rs (93%)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 37e6298195e4..a15fa98c8614 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -6,6 +6,7 @@
use hal::FalconHal;
use kernel::device;
use kernel::dma::DmaAddress;
+use kernel::io::register::RegisterBase;
use kernel::prelude::*;
use kernel::sync::aref::ARef;
use kernel::time::Delta;
@@ -14,7 +15,6 @@
use crate::driver::Bar0;
use crate::gpu::Chipset;
use crate::regs;
-use crate::regs::macros::RegisterBase;
use crate::util;
pub(crate) mod gsp;
diff --git a/drivers/gpu/nova-core/falcon/gsp.rs b/drivers/gpu/nova-core/falcon/gsp.rs
index f17599cb49fa..cd4960e997c8 100644
--- a/drivers/gpu/nova-core/falcon/gsp.rs
+++ b/drivers/gpu/nova-core/falcon/gsp.rs
@@ -1,9 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
+use kernel::io::register::RegisterBase;
+
use crate::{
driver::Bar0,
falcon::{Falcon, FalconEngine, PFalcon2Base, PFalconBase},
- regs::{self, macros::RegisterBase},
+ regs::self,
};
/// Type specifying the `Gsp` falcon engine. Cannot be instantiated.
diff --git a/drivers/gpu/nova-core/falcon/sec2.rs b/drivers/gpu/nova-core/falcon/sec2.rs
index 815786c8480d..81717868a8a8 100644
--- a/drivers/gpu/nova-core/falcon/sec2.rs
+++ b/drivers/gpu/nova-core/falcon/sec2.rs
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
use crate::falcon::{FalconEngine, PFalcon2Base, PFalconBase};
-use crate::regs::macros::RegisterBase;
+use kernel::io::register::RegisterBase;
/// Type specifying the `Sec2` falcon engine. Cannot be instantiated.
pub(crate) struct Sec2(());
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index 112277c7921e..fffcaee2249f 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 206dab2e1335..1f08e6d4045a 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -4,15 +4,13 @@
// but are mapped to types.
#![allow(non_camel_case_types)]
-#[macro_use]
-pub(crate) mod macros;
-
use crate::falcon::{
DmaTrfCmdSize, FalconCoreRev, FalconCoreRevSubversion, FalconFbifMemType, FalconFbifTarget,
FalconModSelAlgo, FalconSecurityModel, PFalcon2Base, PFalconBase, PeregrineCoreSelect,
};
use crate::gpu::{Architecture, Chipset};
use kernel::prelude::*;
+use kernel::register;
// PMC
@@ -331,6 +329,7 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
pub(crate) mod gm107 {
// FUSE
+ use kernel::register;
register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00021c04 {
0:0 display_disabled as bool;
@@ -339,6 +338,7 @@ pub(crate) mod gm107 {
pub(crate) mod ga100 {
// FUSE
+ use kernel::register;
register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00820c04 {
0:0 display_disabled as bool;
diff --git a/drivers/gpu/nova-core/bitfield.rs b/rust/kernel/bitfield.rs
similarity index 91%
rename from drivers/gpu/nova-core/bitfield.rs
rename to rust/kernel/bitfield.rs
index cbedbb0078f6..09cd5741598c 100644
--- a/drivers/gpu/nova-core/bitfield.rs
+++ b/rust/kernel/bitfield.rs
@@ -9,7 +9,7 @@
/// # Syntax
///
/// ```rust
-/// use nova_core::bitfield;
+/// use kernel::bitfield;
///
/// #[derive(Debug, Clone, Copy, Default)]
/// enum Mode {
@@ -82,10 +82,11 @@
/// 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_export]
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)* });
+ ::kernel::bitfield!(@core $vis $name $storage $(, $comment)? { $($fields)* });
};
// All rules below are helpers.
@@ -114,7 +115,7 @@ fn from(val: $name) -> $storage {
}
}
- bitfield!(@fields_dispatcher $vis $name $storage { $($fields)* });
+ ::kernel::bitfield!(@fields_dispatcher $vis $name $storage { $($fields)* });
};
// Captures the fields and passes them to all the implementers that require field information.
@@ -130,7 +131,7 @@ fn from(val: $name) -> $storage {
)*
}
) => {
- bitfield!(@field_accessors $vis $name $storage {
+ ::kernel::bitfield!(@field_accessors $vis $name $storage {
$(
$hi:$lo $field as $type
$(?=> $try_into_type)?
@@ -139,8 +140,8 @@ fn from(val: $name) -> $storage {
;
)*
});
- bitfield!(@debug $name { $($field;)* });
- bitfield!(@default $name { $($field;)* });
+ ::kernel::bitfield!(@debug $name { $($field;)* });
+ ::kernel::bitfield!(@default $name { $($field;)* });
};
// Defines all the field getter/setter methods for `$name`.
@@ -155,13 +156,13 @@ fn from(val: $name) -> $storage {
}
) => {
$(
- bitfield!(@check_field_bounds $hi:$lo $field as $type);
+ ::kernel::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
+ ::kernel::bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field as $type
$(?=> $try_into_type)?
$(=> $into_type)?
$(, $comment)?
@@ -198,7 +199,7 @@ impl $name {
@field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
$(, $comment:literal)?;
) => {
- bitfield!(
+ ::kernel::bitfield!(
@leaf_accessor $vis $name $storage, $hi:$lo $field
{ |f| <$into_type>::from(if f != 0 { true } else { false }) }
$into_type => $into_type $(, $comment)?;
@@ -209,7 +210,7 @@ impl $name {
(
@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)?;);
+ ::kernel::bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field as bool => bool $(, $comment)?;);
};
// Catches the `?=>` syntax for non-boolean fields.
@@ -217,7 +218,7 @@ impl $name {
@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
+ ::kernel::bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field
{ |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>
::core::result::Result<
$try_into_type,
@@ -231,7 +232,7 @@ impl $name {
@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
+ ::kernel::bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field
{ |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
};
@@ -240,7 +241,7 @@ impl $name {
@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)?;);
+ ::kernel::bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field as $type => $type $(, $comment)?;);
};
// Generates the accessor methods for a single field.
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index 03b467722b86..a79b603604b1 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -8,6 +8,7 @@
use crate::{bindings, build_assert, ffi::c_void};
pub mod mem;
+pub mod register;
pub mod resource;
pub use resource::Resource;
diff --git a/drivers/gpu/nova-core/regs/macros.rs b/rust/kernel/io/register.rs
similarity index 93%
rename from drivers/gpu/nova-core/regs/macros.rs
rename to rust/kernel/io/register.rs
index c0a5194e8d97..c24d956f122f 100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/rust/kernel/io/register.rs
@@ -17,7 +17,8 @@
/// 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> {
+pub trait RegisterBase<T> {
+ /// The base address for the register.
const BASE: usize;
}
@@ -26,7 +27,7 @@ pub(crate) trait RegisterBase<T> {
///
/// Example:
///
-/// ```no_run
+/// ```ignore
/// 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";
@@ -39,7 +40,7 @@ pub(crate) trait RegisterBase<T> {
/// significant bits of the register. Each field can be accessed and modified using accessor
/// methods:
///
-/// ```no_run
+/// ```ignore
/// // Read from the register's defined offset (0x100).
/// let boot0 = BOOT_0::read(&bar);
/// pr_info!("chip revision: {}.{}", boot0.major_revision(), boot0.minor_revision());
@@ -61,7 +62,7 @@ pub(crate) trait RegisterBase<T> {
/// 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
+/// ```ignore
/// register!(SCRATCH @ 0x00000200, "Scratch register" {
/// 31:0 value as u32, "Raw value";
/// });
@@ -111,7 +112,7 @@ pub(crate) trait RegisterBase<T> {
/// this register needs to implement `RegisterBase<Base>`. Here is the above example translated
/// into code:
///
-/// ```no_run
+/// ```ignore
/// // Type used to identify the base.
/// pub(crate) struct CpuCtlBase;
///
@@ -162,7 +163,7 @@ pub(crate) trait RegisterBase<T> {
/// compile-time or runtime bound checking. Simply define their address as `Address[Size]`, and add
/// an `idx` parameter to their `read`, `write` and `alter` methods:
///
-/// ```no_run
+/// ```ignore
/// # fn no_run() -> Result<(), Error> {
/// # fn get_scratch_idx() -> usize {
/// # 0x15
@@ -211,7 +212,7 @@ pub(crate) trait RegisterBase<T> {
/// Combining the two features described in the sections above, arrays of registers accessible from
/// a base can also be defined:
///
-/// ```no_run
+/// ```ignore
/// # fn no_run() -> Result<(), Error> {
/// # fn get_scratch_idx() -> usize {
/// # 0x15
@@ -273,28 +274,29 @@ pub(crate) trait RegisterBase<T> {
/// # Ok(())
/// # }
/// ```
+#[macro_export]
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)* } );
+ ::kernel::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)* } );
+ ::kernel::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)* } );
+ ::kernel::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)* } );
+ ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_relative $name @ $base [ $alias::OFFSET ]);
};
@@ -305,7 +307,7 @@ macro_rules! register {
}
) => {
static_assert!(::core::mem::size_of::<u32>() <= $stride);
- bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
+ ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_array $name @ $offset [ $size ; $stride ]);
};
@@ -326,7 +328,7 @@ macro_rules! register {
$(, $comment:literal)? { $($fields:tt)* }
) => {
static_assert!(::core::mem::size_of::<u32>() <= $stride);
- bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
+ ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_relative_array $name @ $base [ $offset [ $size ; $stride ] ]);
};
@@ -348,7 +350,7 @@ macro_rules! register {
}
) => {
static_assert!($idx < $alias::SIZE);
- bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
+ ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_relative $name @ $base [ $alias::OFFSET + $idx * $alias::STRIDE ] );
};
@@ -357,7 +359,7 @@ macro_rules! register {
// 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)* } );
+ ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE );
};
@@ -414,12 +416,12 @@ pub(crate) fn read<const SIZE: usize, T, B>(
base: &B,
) -> Self where
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- B: crate::regs::macros::RegisterBase<$base>,
+ B: ::kernel::io::register::RegisterBase<$base>,
{
const OFFSET: usize = $name::OFFSET;
let value = io.read32(
- <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
+ <B as ::kernel::io::register::RegisterBase<$base>>::BASE + OFFSET
);
Self(value)
@@ -435,13 +437,13 @@ pub(crate) fn write<const SIZE: usize, T, B>(
base: &B,
) where
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- B: crate::regs::macros::RegisterBase<$base>,
+ B: ::kernel::io::register::RegisterBase<$base>,
{
const OFFSET: usize = $name::OFFSET;
io.write32(
self.0,
- <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
+ <B as ::kernel::io::register::RegisterBase<$base>>::BASE + OFFSET
);
}
@@ -455,7 +457,7 @@ pub(crate) fn alter<const SIZE: usize, T, B, F>(
f: F,
) where
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- B: crate::regs::macros::RegisterBase<$base>,
+ B: ::kernel::io::register::RegisterBase<$base>,
F: ::core::ops::FnOnce(Self) -> Self,
{
let reg = f(Self::read(io, base));
@@ -600,11 +602,11 @@ pub(crate) fn read<const SIZE: usize, T, B>(
idx: usize,
) -> Self where
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- B: crate::regs::macros::RegisterBase<$base>,
+ B: ::kernel::io::register::RegisterBase<$base>,
{
build_assert!(idx < Self::SIZE);
- let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +
+ let offset = <B as ::kernel::io::register::RegisterBase<$base>>::BASE +
Self::OFFSET + (idx * Self::STRIDE);
let value = io.read32(offset);
@@ -622,11 +624,11 @@ pub(crate) fn write<const SIZE: usize, T, B>(
idx: usize
) where
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- B: crate::regs::macros::RegisterBase<$base>,
+ B: ::kernel::io::register::RegisterBase<$base>,
{
build_assert!(idx < Self::SIZE);
- let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +
+ let offset = <B as ::kernel::io::register::RegisterBase<$base>>::BASE +
Self::OFFSET + (idx * Self::STRIDE);
io.write32(self.0, offset);
@@ -643,7 +645,7 @@ pub(crate) fn alter<const SIZE: usize, T, B, F>(
f: F,
) where
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- B: crate::regs::macros::RegisterBase<$base>,
+ B: ::kernel::io::register::RegisterBase<$base>,
F: ::core::ops::FnOnce(Self) -> Self,
{
let reg = f(Self::read(io, base, idx));
@@ -662,7 +664,7 @@ pub(crate) fn try_read<const SIZE: usize, T, B>(
idx: usize,
) -> ::kernel::error::Result<Self> where
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- B: crate::regs::macros::RegisterBase<$base>,
+ B: ::kernel::io::register::RegisterBase<$base>,
{
if idx < Self::SIZE {
Ok(Self::read(io, base, idx))
@@ -684,7 +686,7 @@ pub(crate) fn try_write<const SIZE: usize, T, B>(
idx: usize,
) -> ::kernel::error::Result where
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- B: crate::regs::macros::RegisterBase<$base>,
+ B: ::kernel::io::register::RegisterBase<$base>,
{
if idx < Self::SIZE {
Ok(self.write(io, base, idx))
@@ -707,7 +709,7 @@ pub(crate) fn try_alter<const SIZE: usize, T, B, F>(
f: F,
) -> ::kernel::error::Result where
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
- B: crate::regs::macros::RegisterBase<$base>,
+ B: ::kernel::io::register::RegisterBase<$base>,
F: ::core::ops::FnOnce(Self) -> Self,
{
if idx < Self::SIZE {
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index fcffc3988a90..8f8260090c02 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -63,6 +63,7 @@
pub mod alloc;
#[cfg(CONFIG_AUXILIARY_BUS)]
pub mod auxiliary;
+pub mod bitfield;
pub mod bits;
#[cfg(CONFIG_BLOCK)]
pub mod block;
--
2.34.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH v6 5/5] rust: bitfield: Add KUNIT tests for bitfield
2025-10-03 15:47 [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/ Joel Fernandes
` (3 preceding siblings ...)
2025-10-03 15:47 ` [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova Joel Fernandes
@ 2025-10-03 15:47 ` Joel Fernandes
2025-10-06 10:37 ` Alexandre Courbot
2025-10-06 20:36 ` [PATCH v7] " Joel Fernandes
2025-10-06 18:05 ` [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/ Edwin Peer
2025-10-06 22:29 ` Yury Norov
6 siblings, 2 replies; 41+ messages in thread
From: Joel Fernandes @ 2025-10-03 15:47 UTC (permalink / raw)
To: linux-kernel, rust-for-linux, dri-devel, dakr, acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, joel, Elle Rhumsaa, Yury Norov, Daniel Almeida,
Andrea Righi, nouveau
Add KUNIT tests to make sure the macro is working correctly.
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
rust/kernel/bitfield.rs | 323 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 323 insertions(+)
diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
index 09cd5741598c..f0e341a1a979 100644
--- a/rust/kernel/bitfield.rs
+++ b/rust/kernel/bitfield.rs
@@ -329,3 +329,326 @@ fn default() -> Self {
}
};
}
+
+#[::kernel::macros::kunit_tests(kernel_bitfield)]
+mod tests {
+ use core::convert::TryFrom;
+
+ // 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<u8> for MemoryType {
+ type Error = u8;
+ fn try_from(value: u8) -> Result<Self, Self::Error> {
+ match value {
+ 0 => Ok(MemoryType::Unmapped),
+ 1 => Ok(MemoryType::Normal),
+ 2 => Ok(MemoryType::Device),
+ 3 => Ok(MemoryType::Reserved),
+ _ => Err(value),
+ }
+ }
+ }
+
+ impl From<MemoryType> for u64 {
+ fn from(mt: MemoryType) -> u64 {
+ mt as u64
+ }
+ }
+
+ #[derive(Debug, Default, Clone, Copy, PartialEq)]
+ enum Priority {
+ #[default]
+ Low = 0,
+ Medium = 1,
+ High = 2,
+ Critical = 3,
+ }
+
+ impl From<u8> for Priority {
+ fn from(value: u8) -> Self {
+ match value & 0x3 {
+ 0 => Priority::Low,
+ 1 => Priority::Medium,
+ 2 => Priority::High,
+ _ => Priority::Critical,
+ }
+ }
+ }
+
+ impl From<Priority> for u16 {
+ fn from(p: Priority) -> u16 {
+ p as u16
+ }
+ }
+
+ bitfield! {
+ struct TestPageTableEntry(u64) {
+ 0:0 present as bool;
+ 1:1 writable as bool;
+ 11:9 available as u8;
+ 13:12 mem_type as u8 ?=> MemoryType;
+ 17:14 extended_type as u8 ?=> MemoryType; // For testing failures
+ 51:12 pfn as u64;
+ 51:12 pfn_overlap as u64;
+ 61:52 available2 as u16;
+ }
+ }
+
+ bitfield! {
+ struct TestControlRegister(u16) {
+ 0:0 enable as bool;
+ 3:1 mode as u8;
+ 5:4 priority as u8 => Priority;
+ 7:4 priority_nibble as u8;
+ 15:8 channel as u8;
+ }
+ }
+
+ bitfield! {
+ struct TestStatusRegister(u8) {
+ 0:0 ready as bool;
+ 1:1 error as bool;
+ 3:2 state as u8;
+ 7:4 reserved as u8;
+ 7:0 full_byte as u8; // For entire register
+ }
+ }
+
+ #[test]
+ fn test_single_bits() {
+ let mut pte = TestPageTableEntry::default();
+
+ assert!(!pte.present());
+ assert!(!pte.writable());
+ assert_eq!(u64::from(pte), 0x0);
+
+ pte = pte.set_present(true);
+ assert!(pte.present());
+ assert_eq!(u64::from(pte), 0x1);
+
+ pte = pte.set_writable(true);
+ assert!(pte.writable());
+ assert_eq!(u64::from(pte), 0x3);
+
+ pte = pte.set_writable(false);
+ assert!(!pte.writable());
+ assert_eq!(u64::from(pte), 0x1);
+
+ assert_eq!(pte.available(), 0);
+ pte = pte.set_available(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(0x123456);
+ assert_eq!(pte.pfn(), 0x123456);
+ // Test overlapping field reads same value
+ assert_eq!(pte.pfn_overlap(), 0x123456);
+ assert_eq!(u64::from(pte), 0x123456000);
+
+ pte = pte.set_available(0x7);
+ assert_eq!(pte.available(), 0x7);
+ assert_eq!(u64::from(pte), 0x123456E00);
+
+ pte = pte.set_available2(0x3FF);
+ assert_eq!(pte.available2(), 0x3FF);
+ assert_eq!(u64::from(pte), 0x3FF0000123456E00);
+
+ // 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), 0x3FF0000123456E00);
+
+ pte = pte.set_mem_type(MemoryType::Normal);
+ assert_eq!(pte.mem_type(), Ok(MemoryType::Normal));
+ assert_eq!(u64::from(pte), 0x3FF0000123455E00);
+
+ // 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), 0x3FF0000123457E00);
+
+ // Test failure case using extended_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 17:14 to 7 (invalid for MemoryType)
+ raw = (raw & !::kernel::bits::genmask_u64(14..=17)) | (0x7 << 14);
+ let invalid_pte = TestPageTableEntry(raw);
+ // Should return Err with the invalid value
+ assert_eq!(invalid_pte.extended_type(), Err(0x7));
+
+ // Test a valid value after testing invalid to ensure both cases work
+ // Set bits 17:14 to 2 (valid: Device)
+ raw = (raw & !::kernel::bits::genmask_u64(14..=17)) | (0x2 << 14);
+ let valid_pte = TestPageTableEntry(raw);
+ assert_eq!(valid_pte.extended_type(), Ok(MemoryType::Device));
+
+ let max_pfn = ::kernel::bits::genmask_u64(0..=39);
+ pte = pte.set_pfn(max_pfn);
+ assert_eq!(pte.pfn(), max_pfn);
+ assert_eq!(pte.pfn_overlap(), max_pfn);
+ }
+
+ #[test]
+ fn test_builder_pattern() {
+ let pte = TestPageTableEntry::default()
+ .set_present(true)
+ .set_writable(true)
+ .set_available(0x7)
+ .set_pfn(0xABCDEF)
+ .set_mem_type(MemoryType::Reserved)
+ .set_available2(0x3FF);
+
+ assert!(pte.present());
+ assert!(pte.writable());
+ assert_eq!(pte.available(), 0x7);
+ assert_eq!(pte.pfn(), 0xABCDEF);
+ assert_eq!(pte.pfn_overlap(), 0xABCDEF);
+ assert_eq!(pte.mem_type(), Ok(MemoryType::Reserved));
+ assert_eq!(pte.available2(), 0x3FF);
+ }
+
+ #[test]
+ fn test_raw_operations() {
+ let raw_value = 0x3FF0000003123E03u64;
+
+ let pte = TestPageTableEntry(raw_value);
+ assert_eq!(u64::from(pte), raw_value);
+
+ assert!(pte.present());
+ assert!(pte.writable());
+ assert_eq!(pte.available(), 0x7);
+ assert_eq!(pte.pfn(), 0x3123);
+ assert_eq!(pte.pfn_overlap(), 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());
+ 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());
+
+ ctrl = ctrl.set_mode(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(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(0x3)
+ .set_priority(Priority::Medium)
+ .set_channel(0x42);
+
+ assert!(ctrl2.enable());
+ 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());
+ 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());
+ assert!(!status.error());
+ assert_eq!(status.state(), 0);
+ assert_eq!(status.reserved(), 0);
+ assert_eq!(status.full_byte(), 0);
+
+ status = status.set_ready(true);
+ assert!(status.ready());
+ assert_eq!(status.full_byte(), 0x01);
+
+ status = status.set_error(true);
+ assert!(status.error());
+ assert_eq!(status.full_byte(), 0x03);
+
+ status = status.set_state(0x3);
+ assert_eq!(status.state(), 0x3);
+ assert_eq!(status.full_byte(), 0x0F);
+
+ status = status.set_reserved(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());
+ assert!(!status.error());
+ assert_eq!(status.state(), 0x1);
+ assert_eq!(status.reserved(), 0x5);
+
+ let status2 = TestStatusRegister::default()
+ .set_ready(true)
+ .set_state(0x2)
+ .set_reserved(0x5);
+
+ assert!(status2.ready());
+ assert!(!status2.error());
+ 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());
+ assert!(!status3.error());
+ assert_eq!(status3.state(), 0x2);
+ assert_eq!(status3.reserved(), 0x5);
+ assert_eq!(status3.full_byte(), 0x59);
+
+ let status4 = TestStatusRegister(0xFF);
+ assert!(status4.ready());
+ assert!(status4.error());
+ assert_eq!(status4.state(), 0x3);
+ assert_eq!(status4.reserved(), 0xF);
+ assert_eq!(status4.full_byte(), 0xFF);
+ }
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* Re: [PATCH v6 5/5] rust: bitfield: Add KUNIT tests for bitfield
2025-10-03 15:47 ` [PATCH v6 5/5] rust: bitfield: Add KUNIT tests for bitfield Joel Fernandes
@ 2025-10-06 10:37 ` Alexandre Courbot
2025-10-06 19:38 ` Joel Fernandes
2025-10-06 20:36 ` [PATCH v7] " Joel Fernandes
1 sibling, 1 reply; 41+ messages in thread
From: Alexandre Courbot @ 2025-10-06 10:37 UTC (permalink / raw)
To: Joel Fernandes, linux-kernel, rust-for-linux, dri-devel, dakr,
acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Timur Tabi, joel,
Elle Rhumsaa, Yury Norov, Daniel Almeida, Andrea Righi, nouveau
On Sat Oct 4, 2025 at 12:47 AM JST, Joel Fernandes wrote:
> Add KUNIT tests to make sure the macro is working correctly.
>
> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
> ---
> rust/kernel/bitfield.rs | 323 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 323 insertions(+)
>
> diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
> index 09cd5741598c..f0e341a1a979 100644
> --- a/rust/kernel/bitfield.rs
> +++ b/rust/kernel/bitfield.rs
> @@ -329,3 +329,326 @@ fn default() -> Self {
> }
> };
> }
> +
> +#[::kernel::macros::kunit_tests(kernel_bitfield)]
> +mod tests {
> + use core::convert::TryFrom;
> +
> + // 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<u8> for MemoryType {
> + type Error = u8;
> + fn try_from(value: u8) -> Result<Self, Self::Error> {
> + match value {
> + 0 => Ok(MemoryType::Unmapped),
> + 1 => Ok(MemoryType::Normal),
> + 2 => Ok(MemoryType::Device),
> + 3 => Ok(MemoryType::Reserved),
> + _ => Err(value),
> + }
> + }
> + }
> +
> + impl From<MemoryType> for u64 {
> + fn from(mt: MemoryType) -> u64 {
> + mt as u64
> + }
> + }
> +
> + #[derive(Debug, Default, Clone, Copy, PartialEq)]
> + enum Priority {
> + #[default]
> + Low = 0,
> + Medium = 1,
> + High = 2,
> + Critical = 3,
> + }
> +
> + impl From<u8> for Priority {
> + fn from(value: u8) -> Self {
> + match value & 0x3 {
> + 0 => Priority::Low,
> + 1 => Priority::Medium,
> + 2 => Priority::High,
> + _ => Priority::Critical,
> + }
> + }
> + }
> +
> + impl From<Priority> for u16 {
> + fn from(p: Priority) -> u16 {
> + p as u16
> + }
> + }
> +
> + bitfield! {
> + struct TestPageTableEntry(u64) {
> + 0:0 present as bool;
> + 1:1 writable as bool;
> + 11:9 available as u8;
> + 13:12 mem_type as u8 ?=> MemoryType;
> + 17:14 extended_type as u8 ?=> MemoryType; // For testing failures
> + 51:12 pfn as u64;
Is the overlap with `mem_type` and `extended_type` on purpose? It looks
strange to me that the PFN also includes the memory type.
> + 51:12 pfn_overlap as u64;
If `pfn` needs to be adjusted then I guess this one also needs to be.
> + 61:52 available2 as u16;
> + }
> + }
> +
> + bitfield! {
> + struct TestControlRegister(u16) {
> + 0:0 enable as bool;
> + 3:1 mode as u8;
> + 5:4 priority as u8 => Priority;
> + 7:4 priority_nibble as u8;
> + 15:8 channel as u8;
> + }
> + }
> +
> + bitfield! {
> + struct TestStatusRegister(u8) {
> + 0:0 ready as bool;
> + 1:1 error as bool;
> + 3:2 state as u8;
> + 7:4 reserved as u8;
> + 7:0 full_byte as u8; // For entire register
> + }
> + }
> +
> + #[test]
> + fn test_single_bits() {
> + let mut pte = TestPageTableEntry::default();
> +
> + assert!(!pte.present());
> + assert!(!pte.writable());
> + assert_eq!(u64::from(pte), 0x0);
> +
> + pte = pte.set_present(true);
> + assert!(pte.present());
> + assert_eq!(u64::from(pte), 0x1);
> +
> + pte = pte.set_writable(true);
> + assert!(pte.writable());
> + assert_eq!(u64::from(pte), 0x3);
> +
> + pte = pte.set_writable(false);
> + assert!(!pte.writable());
> + assert_eq!(u64::from(pte), 0x1);
> +
> + assert_eq!(pte.available(), 0);
> + pte = pte.set_available(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(0x123456);
> + assert_eq!(pte.pfn(), 0x123456);
> + // Test overlapping field reads same value
> + assert_eq!(pte.pfn_overlap(), 0x123456);
> + assert_eq!(u64::from(pte), 0x123456000);
> +
> + pte = pte.set_available(0x7);
> + assert_eq!(pte.available(), 0x7);
> + assert_eq!(u64::from(pte), 0x123456E00);
> +
> + pte = pte.set_available2(0x3FF);
> + assert_eq!(pte.available2(), 0x3FF);
> + assert_eq!(u64::from(pte), 0x3FF0000123456E00);
> +
> + // 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), 0x3FF0000123456E00);
> +
> + pte = pte.set_mem_type(MemoryType::Normal);
> + assert_eq!(pte.mem_type(), Ok(MemoryType::Normal));
> + assert_eq!(u64::from(pte), 0x3FF0000123455E00);
> +
> + // 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), 0x3FF0000123457E00);
> +
> + // Test failure case using extended_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 17:14 to 7 (invalid for MemoryType)
> + raw = (raw & !::kernel::bits::genmask_u64(14..=17)) | (0x7 << 14);
> + let invalid_pte = TestPageTableEntry(raw);
> + // Should return Err with the invalid value
> + assert_eq!(invalid_pte.extended_type(), Err(0x7));
> +
> + // Test a valid value after testing invalid to ensure both cases work
> + // Set bits 17:14 to 2 (valid: Device)
> + raw = (raw & !::kernel::bits::genmask_u64(14..=17)) | (0x2 << 14);
> + let valid_pte = TestPageTableEntry(raw);
> + assert_eq!(valid_pte.extended_type(), Ok(MemoryType::Device));
> +
> + let max_pfn = ::kernel::bits::genmask_u64(0..=39);
> + pte = pte.set_pfn(max_pfn);
> + assert_eq!(pte.pfn(), max_pfn);
> + assert_eq!(pte.pfn_overlap(), max_pfn);
> + }
> +
> + #[test]
> + fn test_builder_pattern() {
> + let pte = TestPageTableEntry::default()
> + .set_present(true)
> + .set_writable(true)
> + .set_available(0x7)
> + .set_pfn(0xABCDEF)
> + .set_mem_type(MemoryType::Reserved)
> + .set_available2(0x3FF);
> +
> + assert!(pte.present());
> + assert!(pte.writable());
> + assert_eq!(pte.available(), 0x7);
> + assert_eq!(pte.pfn(), 0xABCDEF);
> + assert_eq!(pte.pfn_overlap(), 0xABCDEF);
> + assert_eq!(pte.mem_type(), Ok(MemoryType::Reserved));
> + assert_eq!(pte.available2(), 0x3FF);
Maybe check the raw value here as well, although I guess the previous
test already covered this anyway.
With these points confirmed,
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova
2025-10-03 15:47 ` [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova Joel Fernandes
@ 2025-10-06 10:38 ` Alexandre Courbot
2025-10-09 6:59 ` Dirk Behme
2025-10-22 18:40 ` Beata Michalska
2 siblings, 0 replies; 41+ messages in thread
From: Alexandre Courbot @ 2025-10-06 10:38 UTC (permalink / raw)
To: Joel Fernandes, linux-kernel, rust-for-linux, dri-devel, dakr,
acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Timur Tabi, joel,
Elle Rhumsaa, Yury Norov, Daniel Almeida, Andrea Righi, nouveau
On Sat Oct 4, 2025 at 12:47 AM JST, Joel Fernandes wrote:
> Out of broad need for the register and bitfield macros in Rust, move
> them out of nova into the kernel crate. Several usecases need them (Nova
> is already using these and Tyr developers said they need them).
>
> bitfield moved into kernel crate - defines bitfields in Rust.
> register moved into io module - defines hardware registers and accessors.
>
> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
> Reviewed-by: Elle Rhumsaa <elle@weathered-steel.dev>
> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
> ---
> drivers/gpu/nova-core/falcon.rs | 2 +-
> drivers/gpu/nova-core/falcon/gsp.rs | 4 +-
> drivers/gpu/nova-core/falcon/sec2.rs | 2 +-
> drivers/gpu/nova-core/nova_core.rs | 3 -
> drivers/gpu/nova-core/regs.rs | 6 +-
> .../gpu/nova-core => rust/kernel}/bitfield.rs | 27 ++++-----
> rust/kernel/io.rs | 1 +
> .../macros.rs => rust/kernel/io/register.rs | 58 ++++++++++---------
> rust/kernel/lib.rs | 1 +
> 9 files changed, 54 insertions(+), 50 deletions(-)
> rename {drivers/gpu/nova-core => rust/kernel}/bitfield.rs (91%)
> rename drivers/gpu/nova-core/regs/macros.rs => rust/kernel/io/register.rs (93%)
>
> diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
> index 37e6298195e4..a15fa98c8614 100644
> --- a/drivers/gpu/nova-core/falcon.rs
> +++ b/drivers/gpu/nova-core/falcon.rs
> @@ -6,6 +6,7 @@
> use hal::FalconHal;
> use kernel::device;
> use kernel::dma::DmaAddress;
> +use kernel::io::register::RegisterBase;
> use kernel::prelude::*;
> use kernel::sync::aref::ARef;
> use kernel::time::Delta;
> @@ -14,7 +15,6 @@
> use crate::driver::Bar0;
> use crate::gpu::Chipset;
> use crate::regs;
> -use crate::regs::macros::RegisterBase;
> use crate::util;
>
> pub(crate) mod gsp;
> diff --git a/drivers/gpu/nova-core/falcon/gsp.rs b/drivers/gpu/nova-core/falcon/gsp.rs
> index f17599cb49fa..cd4960e997c8 100644
> --- a/drivers/gpu/nova-core/falcon/gsp.rs
> +++ b/drivers/gpu/nova-core/falcon/gsp.rs
> @@ -1,9 +1,11 @@
> // SPDX-License-Identifier: GPL-2.0
>
> +use kernel::io::register::RegisterBase;
> +
> use crate::{
> driver::Bar0,
> falcon::{Falcon, FalconEngine, PFalcon2Base, PFalconBase},
> - regs::{self, macros::RegisterBase},
> + regs::self,
`rustfmt` was not happy with this one:
--- a/drivers/gpu/nova-core/falcon/gsp.rs
+++ b/drivers/gpu/nova-core/falcon/gsp.rs
@@ -5,7 +5,7 @@
use crate::{
driver::Bar0,
falcon::{Falcon, FalconEngine, PFalcon2Base, PFalconBase},
- regs::self,
+ regs,
};
No need to resend just for this though.
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 1/5] nova-core: bitfield: Move bitfield-specific code from register! into new macro
2025-10-03 15:47 ` [PATCH v6 1/5] nova-core: bitfield: Move bitfield-specific code from register! into new macro Joel Fernandes
@ 2025-10-06 17:56 ` Edwin Peer
2025-10-07 6:49 ` Alexandre Courbot
0 siblings, 1 reply; 41+ messages in thread
From: Edwin Peer @ 2025-10-06 17:56 UTC (permalink / raw)
To: Joel Fernandes
Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
dri-devel@lists.freedesktop.org, dakr@kernel.org,
Alexandre Courbot, Alistair Popple, Miguel Ojeda, Alex Gaynor,
Boqun Feng, Gary Guo, bjorn3_gh@protonmail.com, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, John Hubbard, Timur Tabi,
joel@joelfernandes.org, Elle Rhumsaa, Yury Norov, Daniel Almeida,
Andrea Righi, nouveau@lists.freedesktop.org
Hi Joel,
I recognize that this patch is intended to be a code move, but I’m seeing this code for the first time, hence the comments regarding the content below.
On Oct 3, 2025, at 8:47 AM, Joel Fernandes <joelagnelf@nvidia.com> wrote:
> The bitfield-specific into new macro. This will be used to define
> structs with bitfields, similar to C language.
>
> Reviewed-by: Elle Rhumsaa <elle@weathered-steel.dev>
> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
> ---
> drivers/gpu/nova-core/bitfield.rs | 316 +++++++++++++++++++++++++++
> drivers/gpu/nova-core/nova_core.rs | 3 +
> drivers/gpu/nova-core/regs/macros.rs | 259 +---------------------
> 3 files changed, 329 insertions(+), 249 deletions(-)
> create mode 100644 drivers/gpu/nova-core/bitfield.rs
>
> diff --git a/drivers/gpu/nova-core/bitfield.rs b/drivers/gpu/nova-core/bitfield.rs
> new file mode 100644
> index 000000000000..dd0ef2016ff8
> --- /dev/null
> +++ b/drivers/gpu/nova-core/bitfield.rs
> @@ -0,0 +1,316 @@
> +// 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 u32 {
> +/// fn from(mode: Mode) -> u32 {
> +/// mode as u32
> +/// }
> +/// }
Is there a reason for the asymmetry in the From conversions for the user facing enum type? Here we have u8 in the one direction and u32 in the other. It looks like the latter is required by the use of u32:from() in the set_<field>() accessor, but the macro would be more ergonomic if it handled the necessary upcast conversions internally too.
> +///
> +/// #[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 u32 {
> +/// fn from(state: State) -> u32 {
> +/// state as u32
> +/// }
> +/// }
Similarly for bool vs u32.
> +///
> +/// bitfield! {
> +/// struct ControlReg {
> +/// 3:0 mode as u8 ?=> Mode;
> +/// 7:7 state as bool => State;
> +/// }
> +/// }
> +/// ```
> +///
> +/// This generates a struct with:
> +/// - Field accessors: `mode()`, `state()`, etc.
> +/// - Field setters: `set_mode()`, `set_state()`, etc. (supports chaining with builder pattern).
> +/// - Debug and Default implementations.
> +///
> +/// 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
> + (struct $name:ident $(, $comment:literal)? { $($fields:tt)* }) => {
> + bitfield!(@core $name $(, $comment)? { $($fields)* });
> + };
> +
> + // All rules below are helpers.
> +
> + // Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`,
> + // `Default`, `BitOr`, and conversion to the value type) and field accessor methods.
> + (@core $name:ident $(, $comment:literal)? { $($fields:tt)* }) => {
> + $(
> + #[doc=$comment]
> + )?
> + #[repr(transparent)]
> + #[derive(Clone, Copy)]
> + pub(crate) struct $name(u32);
> +
> + impl ::core::ops::BitOr for $name {
> + type Output = Self;
> +
> + fn bitor(self, rhs: Self) -> Self::Output {
> + Self(self.0 | rhs.0)
> + }
> + }
Why do we implement BitOr for the register type? Seems a bit out of place for an abstraction that is trying to provide a safer field level access API.
> +
> + impl ::core::convert::From<$name> for u32 {
> + fn from(val: $name) -> u32 {
> + val.0
> + }
> + }
> +
> + bitfield!(@fields_dispatcher $name { $($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 $name:ident {
> + $($hi:tt:$lo:tt $field:ident as $type:tt
> + $(?=> $try_into_type:ty)?
> + $(=> $into_type:ty)?
> + $(, $comment:literal)?
> + ;
> + )*
> + }
> + ) => {
> + bitfield!(@field_accessors $name {
> + $(
> + $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 $name:ident {
> + $($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 $name $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")
> + );
> + };
> + };
It would also be good to find a way to catch overlapping field definitions.
> +
> + // Catches fields defined as `bool` and convert them into a boolean value.
> + (
> + @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
> + $(, $comment:literal)?;
> + ) => {
> + bitfield!(
> + @leaf_accessor $name $hi:$lo $field
> + { |f| <$into_type>::from(if f != 0 { true } else { false }) }
if f != 0 { true } else {false} => f != 0
> + $into_type => $into_type $(, $comment)?;
> + );
> + };
> +
> + // Shortcut for fields defined as `bool` without the `=>` syntax.
> + (
> + @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
> + ) => {
> + bitfield!(@field_accessor $name $hi:$lo $field as bool => bool $(, $comment)?;);
> + };
> +
> + // Catches the `?=>` syntax for non-boolean fields.
> + (
> + @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty
> + $(, $comment:literal)?;
> + ) => {
> + bitfield!(@leaf_accessor $name $hi:$lo $field
> + { |f| <$try_into_type>::try_from(f as $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 $name:ident $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty
> + $(, $comment:literal)?;
> + ) => {
> + bitfield!(@leaf_accessor $name $hi:$lo $field
> + { |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
> + };
> +
> + // Shortcut for non-boolean fields defined without the `=>` or `?=>` syntax.
> + (
> + @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
> + $(, $comment:literal)?;
> + ) => {
> + bitfield!(@field_accessor $name $hi:$lo $field as $type => $type $(, $comment)?;);
> + };
I realize that the additional From conversion and closure invocation this produces will likely be optimized out when compiling at a suitable optimization level, but this is inefficient for register accesses in non-optimized debug builds:
https://godbolt.org/z/YcTGh9xor
It should be relatively straightforward to have the macro construct appropriate wrappers for an inline accessor function only when the conversions are needed, which would compile to something more efficient by construction.
> +
> + // Generates the accessor methods for a single field.
> + (
> + @leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident
> + { $process:expr } $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>]: u32 = ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
I note that genmask, introduced in a subsequent patch, also uses this construction (based on the range, which does not appear to be used by current code).
Using const MASK: u32 = (1 << $hi - $lo + 1) - 1 << $lo would be more clear.
It can also be a good idea to first right shift the value in the accessor and then utilize an unshifted mask, since smaller constant masks can lend themselves to more efficient immediate instructions (as in the godbolt link above).
> + const [<$field:upper _SHIFT>]: u32 = Self::[<$field:upper _MASK>].trailing_zeros();
const SHIFT: u32 = $lo;
> + );
> +
> + $(
> + #[doc="Returns the value of this field:"]
> + #[doc=$comment]
> + )?
> + #[inline(always)]
> + pub(crate) fn $field(self) -> $res_type {
> + ::kernel::macros::paste!(
> + const MASK: u32 = $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)]
> + pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self {
> + const MASK: u32 = $name::[<$field:upper _MASK>];
> + const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
> + let value = (u32::from(value) << SHIFT) & MASK;
> + self.0 = (self.0 & !MASK) | value;
> +
> + self
> + }
> + );
> + };
> +
> + // Generates the `Debug` implementation for `$name`.
> + (@debug $name:ident { $($field:ident;)* }) => {
> + impl ::core::fmt::Debug for $name {
> + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
> + f.debug_struct(stringify!($name))
> + .field("<raw>", &format_args!("{:#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/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
> index fffcaee2249f..112277c7921e 100644
> --- a/drivers/gpu/nova-core/nova_core.rs
> +++ b/drivers/gpu/nova-core/nova_core.rs
> @@ -2,6 +2,9 @@
>
> //! Nova Core GPU Driver
>
> +#[macro_use]
> +mod bitfield;
> +
> mod dma;
> mod driver;
> mod falcon;
> diff --git a/drivers/gpu/nova-core/regs/macros.rs b/drivers/gpu/nova-core/regs/macros.rs
> index 754c14ee7f40..945d15a2c529 100644
> --- a/drivers/gpu/nova-core/regs/macros.rs
> +++ b/drivers/gpu/nova-core/regs/macros.rs
> @@ -8,7 +8,8 @@
> //!
> //! 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.
> +//! 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.
> @@ -54,15 +55,6 @@ pub(crate) trait RegisterBase<T> {
> /// BOOT_0::alter(&bar, |r| r.set_major_revision(3).set_minor_revision(10));
> /// ```
> ///
> -/// 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.
> -///
> /// 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.
> ///
> @@ -284,25 +276,25 @@ pub(crate) trait RegisterBase<T> {
> macro_rules! register {
> // Creates a register at a fixed offset of the MMIO space.
> ($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } ) => {
> - register!(@core $name $(, $comment)? { $($fields)* } );
> + bitfield!(struct $name $(, $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)* } ) => {
> - register!(@core $name $(, $comment)? { $($fields)* } );
> + bitfield!(struct $name $(, $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)* } ) => {
> - register!(@core $name $(, $comment)? { $($fields)* } );
> + bitfield!(struct $name $(, $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)* }) => {
> - register!(@core $name $(, $comment)? { $($fields)* } );
> + bitfield!(struct $name $(, $comment)? { $($fields)* } );
> register!(@io_relative $name @ $base [ $alias::OFFSET ]);
> };
>
> @@ -313,7 +305,7 @@ macro_rules! register {
> }
> ) => {
> static_assert!(::core::mem::size_of::<u32>() <= $stride);
> - register!(@core $name $(, $comment)? { $($fields)* } );
> + bitfield!(struct $name $(, $comment)? { $($fields)* } );
> register!(@io_array $name @ $offset [ $size ; $stride ]);
> };
>
> @@ -334,7 +326,7 @@ macro_rules! register {
> $(, $comment:literal)? { $($fields:tt)* }
> ) => {
> static_assert!(::core::mem::size_of::<u32>() <= $stride);
> - register!(@core $name $(, $comment)? { $($fields)* } );
> + bitfield!(struct $name $(, $comment)? { $($fields)* } );
> register!(@io_relative_array $name @ $base [ $offset [ $size ; $stride ] ]);
> };
>
> @@ -356,7 +348,7 @@ macro_rules! register {
> }
> ) => {
> static_assert!($idx < $alias::SIZE);
> - register!(@core $name $(, $comment)? { $($fields)* } );
> + bitfield!(struct $name $(, $comment)? { $($fields)* } );
> register!(@io_relative $name @ $base [ $alias::OFFSET + $idx * $alias::STRIDE ] );
> };
>
> @@ -365,241 +357,10 @@ macro_rules! register {
> // 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);
> - register!(@core $name $(, $comment)? { $($fields)* } );
> + bitfield!(struct $name $(, $comment)? { $($fields)* } );
> register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE );
> };
>
> - // All rules below are helpers.
> -
> - // Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`,
> - // `Default`, `BitOr`, and conversion to the value type) and field accessor methods.
> - (@core $name:ident $(, $comment:literal)? { $($fields:tt)* }) => {
> - $(
> - #[doc=$comment]
> - )?
> - #[repr(transparent)]
> - #[derive(Clone, Copy)]
> - pub(crate) struct $name(u32);
> -
> - impl ::core::ops::BitOr for $name {
> - type Output = Self;
> -
> - fn bitor(self, rhs: Self) -> Self::Output {
> - Self(self.0 | rhs.0)
> - }
> - }
> -
> - impl ::core::convert::From<$name> for u32 {
> - fn from(reg: $name) -> u32 {
> - reg.0
> - }
> - }
> -
> - register!(@fields_dispatcher $name { $($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 $name:ident {
> - $($hi:tt:$lo:tt $field:ident as $type:tt
> - $(?=> $try_into_type:ty)?
> - $(=> $into_type:ty)?
> - $(, $comment:literal)?
> - ;
> - )*
> - }
> - ) => {
> - register!(@field_accessors $name {
> - $(
> - $hi:$lo $field as $type
> - $(?=> $try_into_type)?
> - $(=> $into_type)?
> - $(, $comment)?
> - ;
> - )*
> - });
> - register!(@debug $name { $($field;)* });
> - register!(@default $name { $($field;)* });
> - };
> -
> - // Defines all the field getter/methods methods for `$name`.
> - (
> - @field_accessors $name:ident {
> - $($hi:tt:$lo:tt $field:ident as $type:tt
> - $(?=> $try_into_type:ty)?
> - $(=> $into_type:ty)?
> - $(, $comment:literal)?
> - ;
> - )*
> - }
> - ) => {
> - $(
> - register!(@check_field_bounds $hi:$lo $field as $type);
> - )*
> -
> - #[allow(dead_code)]
> - impl $name {
> - $(
> - register!(@field_accessor $name $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 $name:ident $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
> - $(, $comment:literal)?;
> - ) => {
> - register!(
> - @leaf_accessor $name $hi:$lo $field
> - { |f| <$into_type>::from(if f != 0 { true } else { false }) }
> - $into_type => $into_type $(, $comment)?;
> - );
> - };
> -
> - // Shortcut for fields defined as `bool` without the `=>` syntax.
> - (
> - @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
> - ) => {
> - register!(@field_accessor $name $hi:$lo $field as bool => bool $(, $comment)?;);
> - };
> -
> - // Catches the `?=>` syntax for non-boolean fields.
> - (
> - @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty
> - $(, $comment:literal)?;
> - ) => {
> - register!(@leaf_accessor $name $hi:$lo $field
> - { |f| <$try_into_type>::try_from(f as $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 $name:ident $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty
> - $(, $comment:literal)?;
> - ) => {
> - register!(@leaf_accessor $name $hi:$lo $field
> - { |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
> - };
> -
> - // Shortcut for non-boolean fields defined without the `=>` or `?=>` syntax.
> - (
> - @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
> - $(, $comment:literal)?;
> - ) => {
> - register!(@field_accessor $name $hi:$lo $field as $type => $type $(, $comment)?;);
> - };
> -
> - // Generates the accessor methods for a single field.
> - (
> - @leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident
> - { $process:expr } $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>]: u32 = ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
> - const [<$field:upper _SHIFT>]: u32 = Self::[<$field:upper _MASK>].trailing_zeros();
> - );
> -
> - $(
> - #[doc="Returns the value of this field:"]
> - #[doc=$comment]
> - )?
> - #[inline(always)]
> - pub(crate) fn $field(self) -> $res_type {
> - ::kernel::macros::paste!(
> - const MASK: u32 = $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)]
> - pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self {
> - const MASK: u32 = $name::[<$field:upper _MASK>];
> - const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
> - let value = (u32::from(value) << SHIFT) & MASK;
> - self.0 = (self.0 & !MASK) | value;
> -
> - self
> - }
> - );
> - };
> -
> - // Generates the `Debug` implementation for `$name`.
> - (@debug $name:ident { $($field:ident;)* }) => {
> - impl ::core::fmt::Debug for $name {
> - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
> - f.debug_struct(stringify!($name))
> - .field("<raw>", &format_args!("{:#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 register 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
> - }
> - }
> - };
> -
> // Generates the IO accessors for a fixed offset register.
> (@io_fixed $name:ident @ $offset:expr) => {
> #[allow(dead_code)]
> --
> 2.34.1
Regards,
Edwin Peer
>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/
2025-10-03 15:47 [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/ Joel Fernandes
` (4 preceding siblings ...)
2025-10-03 15:47 ` [PATCH v6 5/5] rust: bitfield: Add KUNIT tests for bitfield Joel Fernandes
@ 2025-10-06 18:05 ` Edwin Peer
2025-10-06 22:29 ` Yury Norov
6 siblings, 0 replies; 41+ messages in thread
From: Edwin Peer @ 2025-10-06 18:05 UTC (permalink / raw)
To: Joel Fernandes
Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
dri-devel@lists.freedesktop.org, dakr@kernel.org,
Alexandre Courbot, Alistair Popple, Miguel Ojeda, Alex Gaynor,
Boqun Feng, Gary Guo, bjorn3_gh@protonmail.com, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, John Hubbard, Timur Tabi,
joel@joelfernandes.org, Elle Rhumsaa, Yury Norov, Daniel Almeida,
Andrea Righi, nouveau@lists.freedesktop.org
On Oct 3, 2025, at 8:47 AM, Joel Fernandes <joelagnelf@nvidia.com> wrote:
> Hello!
>
> These patches extract and enhance the bitfield support in the register macro in
> nova to define Rust structures with bitfields. It then moves out the bitfield
> support into the kenrel crate.
>
> Since v5, I dropped several patches and only kept the simple ones that do code
> movement, added a few features and added a KUNIT test. After Alex's bounded
> integer [1] support is in, we can rewrite the dropped patches.
>
> I also dropped the MAINTAINER entry for now, pending more clarity around that.
> I am happy to maintain it, but I need more input on who all will co-maintain,
> now that the last 4 patches were dropped. Perhaps we can maintain it was a part
> of the core rust-for-linux? I suggest let us create the maintainer entry once
> Alex's bounded integer support is integrated but I am open to suggestions.
>
> Here are the v5 patches [2].
>
> [1] https://lore.kernel.org/all/20251002-bounded_ints-v1-0-dd60f5804ea4@nvidia.com/
> [2] https://lore.kernel.org/all/20250930144537.3559207-1-joelagnelf@nvidia.com/
Reviewed-by: Edwin Peer <epeer@nvidia.com>
Regards,
Edwin Peer
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 5/5] rust: bitfield: Add KUNIT tests for bitfield
2025-10-06 10:37 ` Alexandre Courbot
@ 2025-10-06 19:38 ` Joel Fernandes
0 siblings, 0 replies; 41+ messages in thread
From: Joel Fernandes @ 2025-10-06 19:38 UTC (permalink / raw)
To: Alexandre Courbot, linux-kernel, rust-for-linux, dri-devel, dakr
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Timur Tabi, joel,
Elle Rhumsaa, Yury Norov, Daniel Almeida, Andrea Righi, nouveau
On 10/6/2025 6:37 AM, Alexandre Courbot wrote:
> On Sat Oct 4, 2025 at 12:47 AM JST, Joel Fernandes wrote:
>> Add KUNIT tests to make sure the macro is working correctly.
>>
>> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
>> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
>> ---
>> rust/kernel/bitfield.rs | 323 ++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 323 insertions(+)
>>
>> diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
>> index 09cd5741598c..f0e341a1a979 100644
>> --- a/rust/kernel/bitfield.rs
>> +++ b/rust/kernel/bitfield.rs
>> @@ -329,3 +329,326 @@ fn default() -> Self {
>> }
>> };
>> }
>> +
>> +#[::kernel::macros::kunit_tests(kernel_bitfield)]
>> +mod tests {
>> + use core::convert::TryFrom;
>> +
>> + // 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<u8> for MemoryType {
>> + type Error = u8;
>> + fn try_from(value: u8) -> Result<Self, Self::Error> {
>> + match value {
>> + 0 => Ok(MemoryType::Unmapped),
>> + 1 => Ok(MemoryType::Normal),
>> + 2 => Ok(MemoryType::Device),
>> + 3 => Ok(MemoryType::Reserved),
>> + _ => Err(value),
>> + }
>> + }
>> + }
>> +
>> + impl From<MemoryType> for u64 {
>> + fn from(mt: MemoryType) -> u64 {
>> + mt as u64
>> + }
>> + }
>> +
>> + #[derive(Debug, Default, Clone, Copy, PartialEq)]
>> + enum Priority {
>> + #[default]
>> + Low = 0,
>> + Medium = 1,
>> + High = 2,
>> + Critical = 3,
>> + }
>> +
>> + impl From<u8> for Priority {
>> + fn from(value: u8) -> Self {
>> + match value & 0x3 {
>> + 0 => Priority::Low,
>> + 1 => Priority::Medium,
>> + 2 => Priority::High,
>> + _ => Priority::Critical,
>> + }
>> + }
>> + }
>> +
>> + impl From<Priority> for u16 {
>> + fn from(p: Priority) -> u16 {
>> + p as u16
>> + }
>> + }
>> +
>> + bitfield! {
>> + struct TestPageTableEntry(u64) {
>> + 0:0 present as bool;
>> + 1:1 writable as bool;
>> + 11:9 available as u8;
>> + 13:12 mem_type as u8 ?=> MemoryType;
>> + 17:14 extended_type as u8 ?=> MemoryType; // For testing failures
>> + 51:12 pfn as u64;
>
> Is the overlap with `mem_type` and `extended_type` on purpose?
Yes, here I was testing the failure mode of ?=> without having to introduce a
new enum type. But I could just do so with mem_type by adding more bits to it,
so I'll do that and remove extended_type.
> It looks strange to me that the PFN also includes the memory type.
I agree with this (even though these structs are just approximately accurate and
for testing purposes). Since we're testing overlap already in later tests, I
will just remove it from this test.
Following is the new struct now, hope it looks ok:
bitfield! {
struct TestPageTableEntry(u64) {
0:0 present as bool;
1:1 writable as bool;
11:9 available as u8;
15:12 mem_type as u8 ?=> MemoryType;
51:16 pfn as u64;
61:52 available2 as u16;
}
}
>> + 51:12 pfn_overlap as u64;
>
> If `pfn` needs to be adjusted then I guess this one also needs to be.
>
>> + 61:52 available2 as u16;
>> + }
>> + }
>> +
>> + bitfield! {
>> + struct TestControlRegister(u16) {
>> + 0:0 enable as bool;
>> + 3:1 mode as u8;
>> + 5:4 priority as u8 => Priority;
>> + 7:4 priority_nibble as u8;
>> + 15:8 channel as u8;
>> + }
>> + }
>> +
>> + bitfield! {
>> + struct TestStatusRegister(u8) {
>> + 0:0 ready as bool;
>> + 1:1 error as bool;
>> + 3:2 state as u8;
>> + 7:4 reserved as u8;
>> + 7:0 full_byte as u8; // For entire register
>> + }
>> + }
>> +
>> + #[test]
>> + fn test_single_bits() {
>> + let mut pte = TestPageTableEntry::default();
>> +
>> + assert!(!pte.present());
>> + assert!(!pte.writable());
>> + assert_eq!(u64::from(pte), 0x0);
>> +
>> + pte = pte.set_present(true);
>> + assert!(pte.present());
>> + assert_eq!(u64::from(pte), 0x1);
>> +
>> + pte = pte.set_writable(true);
>> + assert!(pte.writable());
>> + assert_eq!(u64::from(pte), 0x3);
>> +
>> + pte = pte.set_writable(false);
>> + assert!(!pte.writable());
>> + assert_eq!(u64::from(pte), 0x1);
>> +
>> + assert_eq!(pte.available(), 0);
>> + pte = pte.set_available(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(0x123456);
>> + assert_eq!(pte.pfn(), 0x123456);
>> + // Test overlapping field reads same value
>> + assert_eq!(pte.pfn_overlap(), 0x123456);
>> + assert_eq!(u64::from(pte), 0x123456000);
>> +
>> + pte = pte.set_available(0x7);
>> + assert_eq!(pte.available(), 0x7);
>> + assert_eq!(u64::from(pte), 0x123456E00);
>> +
>> + pte = pte.set_available2(0x3FF);
>> + assert_eq!(pte.available2(), 0x3FF);
>> + assert_eq!(u64::from(pte), 0x3FF0000123456E00);
>> +
>> + // 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), 0x3FF0000123456E00);
>> +
>> + pte = pte.set_mem_type(MemoryType::Normal);
>> + assert_eq!(pte.mem_type(), Ok(MemoryType::Normal));
>> + assert_eq!(u64::from(pte), 0x3FF0000123455E00);
>> +
>> + // 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), 0x3FF0000123457E00);
>> +
>> + // Test failure case using extended_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 17:14 to 7 (invalid for MemoryType)
>> + raw = (raw & !::kernel::bits::genmask_u64(14..=17)) | (0x7 << 14);
>> + let invalid_pte = TestPageTableEntry(raw);
>> + // Should return Err with the invalid value
>> + assert_eq!(invalid_pte.extended_type(), Err(0x7));
>> +
>> + // Test a valid value after testing invalid to ensure both cases work
>> + // Set bits 17:14 to 2 (valid: Device)
>> + raw = (raw & !::kernel::bits::genmask_u64(14..=17)) | (0x2 << 14);
>> + let valid_pte = TestPageTableEntry(raw);
>> + assert_eq!(valid_pte.extended_type(), Ok(MemoryType::Device));
>> +
>> + let max_pfn = ::kernel::bits::genmask_u64(0..=39);
>> + pte = pte.set_pfn(max_pfn);
>> + assert_eq!(pte.pfn(), max_pfn);
>> + assert_eq!(pte.pfn_overlap(), max_pfn);
>> + }
>> +
>> + #[test]
>> + fn test_builder_pattern() {
>> + let pte = TestPageTableEntry::default()
>> + .set_present(true)
>> + .set_writable(true)
>> + .set_available(0x7)
>> + .set_pfn(0xABCDEF)
>> + .set_mem_type(MemoryType::Reserved)
>> + .set_available2(0x3FF);
>> +
>> + assert!(pte.present());
>> + assert!(pte.writable());
>> + assert_eq!(pte.available(), 0x7);
>> + assert_eq!(pte.pfn(), 0xABCDEF);
>> + assert_eq!(pte.pfn_overlap(), 0xABCDEF);
>> + assert_eq!(pte.mem_type(), Ok(MemoryType::Reserved));
>> + assert_eq!(pte.available2(), 0x3FF);
>
> Maybe check the raw value here as well, although I guess the previous
> test already covered this anyway.
>
> With these points confirmed,
>
> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Thanks! I will resend just this patch as a reply-to this patch (hope that's Ok).
- Joel
^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH v7] rust: bitfield: Add KUNIT tests for bitfield
2025-10-03 15:47 ` [PATCH v6 5/5] rust: bitfield: Add KUNIT tests for bitfield Joel Fernandes
2025-10-06 10:37 ` Alexandre Courbot
@ 2025-10-06 20:36 ` Joel Fernandes
1 sibling, 0 replies; 41+ messages in thread
From: Joel Fernandes @ 2025-10-06 20:36 UTC (permalink / raw)
To: linux-kernel, rust-for-linux, dri-devel, dakr, acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Joel Fernandes,
Timur Tabi, joel, Elle Rhumsaa, Yury Norov, Daniel Almeida,
Andrea Righi, nouveau
Add KUNIT tests to make sure the macro is working correctly.
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
---
rust/kernel/bitfield.rs | 316 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 316 insertions(+)
diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
index 09cd5741598c..c3e0ef531256 100644
--- a/rust/kernel/bitfield.rs
+++ b/rust/kernel/bitfield.rs
@@ -329,3 +329,319 @@ fn default() -> Self {
}
};
}
+
+#[::kernel::macros::kunit_tests(kernel_bitfield)]
+mod tests {
+ use core::convert::TryFrom;
+
+ // 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<u8> for MemoryType {
+ type Error = u8;
+ fn try_from(value: u8) -> Result<Self, Self::Error> {
+ match value {
+ 0 => Ok(MemoryType::Unmapped),
+ 1 => Ok(MemoryType::Normal),
+ 2 => Ok(MemoryType::Device),
+ 3 => Ok(MemoryType::Reserved),
+ _ => Err(value),
+ }
+ }
+ }
+
+ impl From<MemoryType> for u64 {
+ fn from(mt: MemoryType) -> u64 {
+ mt as u64
+ }
+ }
+
+ #[derive(Debug, Default, Clone, Copy, PartialEq)]
+ enum Priority {
+ #[default]
+ Low = 0,
+ Medium = 1,
+ High = 2,
+ Critical = 3,
+ }
+
+ impl From<u8> for Priority {
+ fn from(value: u8) -> Self {
+ match value & 0x3 {
+ 0 => Priority::Low,
+ 1 => Priority::Medium,
+ 2 => Priority::High,
+ _ => Priority::Critical,
+ }
+ }
+ }
+
+ impl From<Priority> for u16 {
+ fn from(p: Priority) -> u16 {
+ p as u16
+ }
+ }
+
+ bitfield! {
+ struct TestPageTableEntry(u64) {
+ 0:0 present as bool;
+ 1:1 writable as bool;
+ 11:9 available as u8;
+ 15:12 mem_type as u8 ?=> MemoryType;
+ 51:16 pfn as u64;
+ 61:52 available2 as u16;
+ }
+ }
+
+ bitfield! {
+ struct TestControlRegister(u16) {
+ 0:0 enable as bool;
+ 3:1 mode as u8;
+ 5:4 priority as u8 => Priority;
+ 7:4 priority_nibble as u8;
+ 15:8 channel as u8;
+ }
+ }
+
+ bitfield! {
+ struct TestStatusRegister(u8) {
+ 0:0 ready as bool;
+ 1:1 error as bool;
+ 3:2 state as u8;
+ 7:4 reserved as u8;
+ 7:0 full_byte as u8; // For entire register
+ }
+ }
+
+ #[test]
+ fn test_single_bits() {
+ let mut pte = TestPageTableEntry::default();
+
+ assert!(!pte.present());
+ assert!(!pte.writable());
+ assert_eq!(u64::from(pte), 0x0);
+
+ pte = pte.set_present(true);
+ assert!(pte.present());
+ assert_eq!(u64::from(pte), 0x1);
+
+ pte = pte.set_writable(true);
+ assert!(pte.writable());
+ assert_eq!(u64::from(pte), 0x3);
+
+ pte = pte.set_writable(false);
+ assert!(!pte.writable());
+ assert_eq!(u64::from(pte), 0x1);
+
+ assert_eq!(pte.available(), 0);
+ pte = pte.set_available(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(0x123456);
+ assert_eq!(pte.pfn(), 0x123456);
+ assert_eq!(u64::from(pte), 0x1234560000);
+
+ pte = pte.set_available(0x7);
+ assert_eq!(pte.available(), 0x7);
+ assert_eq!(u64::from(pte), 0x1234560E00);
+
+ pte = pte.set_available2(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));
+
+ let max_pfn = ::kernel::bits::genmask_u64(0..=35);
+ pte = pte.set_pfn(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(0x7)
+ .set_pfn(0xABCDEF)
+ .set_mem_type(MemoryType::Reserved)
+ .set_available2(0x3FF);
+
+ assert!(pte.present());
+ assert!(pte.writable());
+ 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());
+ assert!(pte.writable());
+ 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());
+ 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());
+
+ ctrl = ctrl.set_mode(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(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(0x3)
+ .set_priority(Priority::Medium)
+ .set_channel(0x42);
+
+ assert!(ctrl2.enable());
+ 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());
+ 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());
+ assert!(!status.error());
+ assert_eq!(status.state(), 0);
+ assert_eq!(status.reserved(), 0);
+ assert_eq!(status.full_byte(), 0);
+
+ status = status.set_ready(true);
+ assert!(status.ready());
+ assert_eq!(status.full_byte(), 0x01);
+
+ status = status.set_error(true);
+ assert!(status.error());
+ assert_eq!(status.full_byte(), 0x03);
+
+ status = status.set_state(0x3);
+ assert_eq!(status.state(), 0x3);
+ assert_eq!(status.full_byte(), 0x0F);
+
+ status = status.set_reserved(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());
+ assert!(!status.error());
+ assert_eq!(status.state(), 0x1);
+ assert_eq!(status.reserved(), 0x5);
+
+ let status2 = TestStatusRegister::default()
+ .set_ready(true)
+ .set_state(0x2)
+ .set_reserved(0x5);
+
+ assert!(status2.ready());
+ assert!(!status2.error());
+ 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());
+ assert!(!status3.error());
+ assert_eq!(status3.state(), 0x2);
+ assert_eq!(status3.reserved(), 0x5);
+ assert_eq!(status3.full_byte(), 0x59);
+
+ let status4 = TestStatusRegister(0xFF);
+ assert!(status4.ready());
+ assert!(status4.error());
+ assert_eq!(status4.state(), 0x3);
+ assert_eq!(status4.reserved(), 0xF);
+ assert_eq!(status4.full_byte(), 0xFF);
+ }
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* Re: [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/
2025-10-03 15:47 [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/ Joel Fernandes
` (5 preceding siblings ...)
2025-10-06 18:05 ` [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/ Edwin Peer
@ 2025-10-06 22:29 ` Yury Norov
2025-10-07 10:36 ` Alexandre Courbot
6 siblings, 1 reply; 41+ messages in thread
From: Yury Norov @ 2025-10-06 22:29 UTC (permalink / raw)
To: Joel Fernandes
Cc: linux-kernel, rust-for-linux, dri-devel, dakr, acourbot,
Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Timur Tabi, joel,
Elle Rhumsaa, Daniel Almeida, Andrea Righi, nouveau
On Fri, Oct 03, 2025 at 11:47:43AM -0400, Joel Fernandes wrote:
> Hello!
>
> These patches extract and enhance the bitfield support in the register macro in
> nova to define Rust structures with bitfields. It then moves out the bitfield
> support into the kenrel crate.
>
> Since v5, I dropped several patches and only kept the simple ones that do code
> movement, added a few features and added a KUNIT test. After Alex's bounded
> integer [1] support is in, we can rewrite the dropped patches.
>
> I also dropped the MAINTAINER entry for now, pending more clarity around that.
> I am happy to maintain it, but I need more input on who all will co-maintain,
> now that the last 4 patches were dropped. Perhaps we can maintain it was a part
> of the core rust-for-linux? I suggest let us create the maintainer entry once
> Alex's bounded integer support is integrated but I am open to suggestions.
>
> Here are the v5 patches [2].
>
> [1] https://lore.kernel.org/all/20251002-bounded_ints-v1-0-dd60f5804ea4@nvidia.com/
> [2] https://lore.kernel.org/all/20250930144537.3559207-1-joelagnelf@nvidia.com/
Hi Joel,
I returned back to v5 today to just find that you have posted a v6.
There's just 3 days between the versions, and I'm pretty sure most
people were not able to even read the material. Moreover, there's
an -rc1 window ongoing, which means that people may be busy.
You're still receiving feedback to v5, and this makes even more mess
because now I'm completely lost about what I should review and what
should ignore.
Please allow a usual 2 weeks between versions to let people have
a chance to give you a valuable feedback.
It seems that you decided to drop some material, comparing to v5, but
don't even notice what exactly has been removed, except that vague
"code movement and few features" notice.
Regardless, I don't think that this is the right path to move the
bitfields into the core. The natural path for a feature that has
been originally developed on driver side is to mature in there and
get merged to core libraries after a while. Resctrl from Intel is one
recent example.
With that said, I'm OK if you move the bitfields as a whole, like you
do in v5, and I'm also OK if you split out the part essential for nova
and take it into the driver. In that case the bitfields will stay in
drivers and you'll be able to focus on the features that _you_ need,
not on generic considerations.
I'm not OK to move bitfields in their current (v6) incomplete form in
rust/kernel. We still have no solid understanding on the API and
implementation that we've been all agreed on.
On maintenance: no core functionality can be merged unmaintained - it's
a strict rule. While in drivers, bitfields are maintained by the nova
maintainers as part of the driver. If you make it a generic library,
you need to define a responsible person(s) in advance. It's also a good
practice to add a core maintainer as a reviewer or co-maintainer. Alice
and Burak added me for bitmap/rust because I already look after bitmaps
in C. You can do the same for bitfields, for the same reason.
It looks like you have some offline discussions on the bitfields.
(Particularly with me.) Before we move forward, can you please wrap
up all the input you've got, so we'll all be sure that we are on the
same page. Right now the process look really messy.
Thanks,
Yury
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 1/5] nova-core: bitfield: Move bitfield-specific code from register! into new macro
2025-10-06 17:56 ` Edwin Peer
@ 2025-10-07 6:49 ` Alexandre Courbot
0 siblings, 0 replies; 41+ messages in thread
From: Alexandre Courbot @ 2025-10-07 6:49 UTC (permalink / raw)
To: Edwin Peer, Joel Fernandes
Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
dri-devel@lists.freedesktop.org, dakr@kernel.org,
Alexandre Courbot, Alistair Popple, Miguel Ojeda, Alex Gaynor,
Boqun Feng, Gary Guo, bjorn3_gh@protonmail.com, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, John Hubbard, Timur Tabi,
joel@joelfernandes.org, Elle Rhumsaa, Yury Norov, Daniel Almeida,
Andrea Righi, nouveau@lists.freedesktop.org
Hi Edwin,
(replying as I wrote the original code and introduced the points you are
discussing).
On Tue Oct 7, 2025 at 2:56 AM JST, Edwin Peer wrote:
> Hi Joel,
>
> I recognize that this patch is intended to be a code move, but I’m seeing this code for the first time, hence the comments regarding the content below.
>
> On Oct 3, 2025, at 8:47 AM, Joel Fernandes <joelagnelf@nvidia.com> wrote:
>
>> The bitfield-specific into new macro. This will be used to define
>> structs with bitfields, similar to C language.
>>
>> Reviewed-by: Elle Rhumsaa <elle@weathered-steel.dev>
>> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
>> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
>> ---
>> drivers/gpu/nova-core/bitfield.rs | 316 +++++++++++++++++++++++++++
>> drivers/gpu/nova-core/nova_core.rs | 3 +
>> drivers/gpu/nova-core/regs/macros.rs | 259 +---------------------
>> 3 files changed, 329 insertions(+), 249 deletions(-)
>> create mode 100644 drivers/gpu/nova-core/bitfield.rs
>>
>> diff --git a/drivers/gpu/nova-core/bitfield.rs b/drivers/gpu/nova-core/bitfield.rs
>> new file mode 100644
>> index 000000000000..dd0ef2016ff8
>> --- /dev/null
>> +++ b/drivers/gpu/nova-core/bitfield.rs
>> @@ -0,0 +1,316 @@
>> +// 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 u32 {
>> +/// fn from(mode: Mode) -> u32 {
>> +/// mode as u32
>> +/// }
>> +/// }
>
> Is there a reason for the asymmetry in the From conversions for the user facing enum type? Here we have u8 in the one direction and u32 in the other. It looks like the latter is required by the use of u32:from() in the set_<field>() accessor, but the macro would be more ergonomic if it handled the necessary upcast conversions internally too.
Mmm, good point - actually it turns out the field setters work with the
type of the storage, and not of the field. This problem was in my
original code and is not introduced by this series.
I agree that we should have symmetry here, and actually that will be a
requirement to start using the TryFrom/Into derive macros [0].
I do have a patch available for this, which I kept for when we start
using the derive macros, but we should probably integrate it at the
beginning of this series.
[0] https://lore.kernel.org/rust-for-linux/cover.1755235180.git.y.j3ms.n@gmail.com/
>
>> +///
>> +/// #[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 u32 {
>> +/// fn from(state: State) -> u32 {
>> +/// state as u32
>> +/// }
>> +/// }
>
> Similarly for bool vs u32.
>
>> +///
>> +/// bitfield! {
>> +/// struct ControlReg {
>> +/// 3:0 mode as u8 ?=> Mode;
>> +/// 7:7 state as bool => State;
>> +/// }
>> +/// }
>> +/// ```
>> +///
>> +/// This generates a struct with:
>> +/// - Field accessors: `mode()`, `state()`, etc.
>> +/// - Field setters: `set_mode()`, `set_state()`, etc. (supports chaining with builder pattern).
>> +/// - Debug and Default implementations.
>> +///
>> +/// 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
>> + (struct $name:ident $(, $comment:literal)? { $($fields:tt)* }) => {
>> + bitfield!(@core $name $(, $comment)? { $($fields)* });
>> + };
>> +
>> + // All rules below are helpers.
>> +
>> + // Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`,
>> + // `Default`, `BitOr`, and conversion to the value type) and field accessor methods.
>> + (@core $name:ident $(, $comment:literal)? { $($fields:tt)* }) => {
>> + $(
>> + #[doc=$comment]
>> + )?
>> + #[repr(transparent)]
>> + #[derive(Clone, Copy)]
>> + pub(crate) struct $name(u32);
>> +
>> + impl ::core::ops::BitOr for $name {
>> + type Output = Self;
>> +
>> + fn bitor(self, rhs: Self) -> Self::Output {
>> + Self(self.0 | rhs.0)
>> + }
>> + }
>
> Why do we implement BitOr for the register type? Seems a bit out of place for an abstraction that is trying to provide a safer field level access API.
Nice catch. I guess I wanted to make it easy to combine fields into a
new value, but that's what the builder pattern is for. BitOr makes it
possible to build an invalid value from two valid ones. I'll remove it.
>
>> +
>> + impl ::core::convert::From<$name> for u32 {
>> + fn from(val: $name) -> u32 {
>> + val.0
>> + }
>> + }
>> +
>> + bitfield!(@fields_dispatcher $name { $($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 $name:ident {
>> + $($hi:tt:$lo:tt $field:ident as $type:tt
>> + $(?=> $try_into_type:ty)?
>> + $(=> $into_type:ty)?
>> + $(, $comment:literal)?
>> + ;
>> + )*
>> + }
>> + ) => {
>> + bitfield!(@field_accessors $name {
>> + $(
>> + $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 $name:ident {
>> + $($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 $name $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")
>> + );
>> + };
>> + };
>
> It would also be good to find a way to catch overlapping field definitions.
Overlapping fields are actually a feature.
>
>> +
>> + // Catches fields defined as `bool` and convert them into a boolean value.
>> + (
>> + @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
>> + $(, $comment:literal)?;
>> + ) => {
>> + bitfield!(
>> + @leaf_accessor $name $hi:$lo $field
>> + { |f| <$into_type>::from(if f != 0 { true } else { false }) }
>
> if f != 0 { true } else {false} => f != 0
Oops, thank - will fix.
>
>> + $into_type => $into_type $(, $comment)?;
>> + );
>> + };
>> +
>> + // Shortcut for fields defined as `bool` without the `=>` syntax.
>> + (
>> + @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
>> + ) => {
>> + bitfield!(@field_accessor $name $hi:$lo $field as bool => bool $(, $comment)?;);
>> + };
>> +
>> + // Catches the `?=>` syntax for non-boolean fields.
>> + (
>> + @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty
>> + $(, $comment:literal)?;
>> + ) => {
>> + bitfield!(@leaf_accessor $name $hi:$lo $field
>> + { |f| <$try_into_type>::try_from(f as $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 $name:ident $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty
>> + $(, $comment:literal)?;
>> + ) => {
>> + bitfield!(@leaf_accessor $name $hi:$lo $field
>> + { |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
>> + };
>> +
>> + // Shortcut for non-boolean fields defined without the `=>` or `?=>` syntax.
>> + (
>> + @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
>> + $(, $comment:literal)?;
>> + ) => {
>> + bitfield!(@field_accessor $name $hi:$lo $field as $type => $type $(, $comment)?;);
>> + };
>
> I realize that the additional From conversion and closure invocation this produces will likely be optimized out when compiling at a suitable optimization level, but this is inefficient for register accesses in non-optimized debug builds:
>
> https://godbolt.org/z/YcTGh9xor
>
> It should be relatively straightforward to have the macro construct appropriate wrappers for an inline accessor function only when the conversions are needed, which would compile to something more efficient by construction.
This would only be possible if the field has the same type as the
storage type, which it seldom the case. I don't think it is worth
complicating the code to support some edge case that is only relevant on
non-optimized builds.
Furthermore, conversion will be unavoidable once we start using bounded
integers.
>
>> +
>> + // Generates the accessor methods for a single field.
>> + (
>> + @leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident
>> + { $process:expr } $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>]: u32 = ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
>
> I note that genmask, introduced in a subsequent patch, also uses this construction (based on the range, which does not appear to be used by current code).
>
> Using const MASK: u32 = (1 << $hi - $lo + 1) - 1 << $lo would be more clear.
>
> It can also be a good idea to first right shift the value in the accessor and then utilize an unshifted mask, since smaller constant masks can lend themselves to more efficient immediate instructions (as in the godbolt link above).
The next patch addresses this by using genmask. Let's not modify code on
a patch that is a strict move, as this would make it more difficult to
review.
>
>> + const [<$field:upper _SHIFT>]: u32 = Self::[<$field:upper _MASK>].trailing_zeros();
>
> const SHIFT: u32 = $lo;
Indeed. Let's address that in its own patch.
Thanks for the review!
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/
2025-10-06 22:29 ` Yury Norov
@ 2025-10-07 10:36 ` Alexandre Courbot
2025-10-07 10:42 ` Miguel Ojeda
` (2 more replies)
0 siblings, 3 replies; 41+ messages in thread
From: Alexandre Courbot @ 2025-10-07 10:36 UTC (permalink / raw)
To: Yury Norov, Joel Fernandes
Cc: linux-kernel, rust-for-linux, dri-devel, dakr, acourbot,
Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Timur Tabi, joel,
Elle Rhumsaa, Daniel Almeida, Andrea Righi, nouveau
Hi Yuri,
On Tue Oct 7, 2025 at 7:29 AM JST, Yury Norov wrote:
<snip>
> Regardless, I don't think that this is the right path to move the
> bitfields into the core. The natural path for a feature that has
> been originally developed on driver side is to mature in there and
> get merged to core libraries after a while. Resctrl from Intel is one
> recent example.
>
> With that said, I'm OK if you move the bitfields as a whole, like you
> do in v5, and I'm also OK if you split out the part essential for nova
> and take it into the driver. In that case the bitfields will stay in
> drivers and you'll be able to focus on the features that _you_ need,
> not on generic considerations.
>
> I'm not OK to move bitfields in their current (v6) incomplete form in
> rust/kernel. We still have no solid understanding on the API and
> implementation that we've been all agreed on.
Initially the plan was indeed to give this code some more time to mature
in nova-core before moving it out.
The reason for the early move is that we have another driver (Tyr) who
wants to start using the register macro. Without it, they would be left
with the option of either reinventing the wheel, or poking at registers
the old-fashioned way, which I think we can agree is not going to be any
safer than the current macro. :)
IIUC your remaining concern is with the possible loss of data when
setting a field that is smaller than its primitive type? That should be
addressed by [0], but as it introduces a new core feature I expect some
discussion to take place before it can be merged. In the meantime, it
would be great if we can make the register macro available.
Because letting it fully mature within nova-core also has the drawback
that we might miss the perspective of other potential users, which may
make us draw ourselves into a corner that will make the macro less
useful generally speaking. We are at a stage where we can still make
design changes if needed, but we need to hear from other users, and
these won't come as long as the macro is in nova-core.
[0] https://lore.kernel.org/rust-for-linux/20251002-bounded_ints-v1-0-dd60f5804ea4@nvidia.com/
> On maintenance: no core functionality can be merged unmaintained - it's
> a strict rule. While in drivers, bitfields are maintained by the nova
> maintainers as part of the driver. If you make it a generic library,
> you need to define a responsible person(s) in advance. It's also a good
> practice to add a core maintainer as a reviewer or co-maintainer. Alice
> and Burak added me for bitmap/rust because I already look after bitmaps
> in C. You can do the same for bitfields, for the same reason.
We can assume maintainership of this of course, but is there a problem
if this falls under the core Rust umbrella? As this is a pretty core
functionality. Miguel and other core folks, WDYT?
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/
2025-10-07 10:36 ` Alexandre Courbot
@ 2025-10-07 10:42 ` Miguel Ojeda
2025-10-07 13:20 ` Alexandre Courbot
2025-10-07 13:16 ` Danilo Krummrich
2025-10-07 15:41 ` Yury Norov
2 siblings, 1 reply; 41+ messages in thread
From: Miguel Ojeda @ 2025-10-07 10:42 UTC (permalink / raw)
To: Alexandre Courbot, Yury Norov
Cc: Joel Fernandes, linux-kernel, rust-for-linux, dri-devel, dakr,
Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Timur Tabi, joel,
Elle Rhumsaa, Daniel Almeida, Andrea Righi, nouveau
On Tue, Oct 7, 2025 at 12:36 PM Alexandre Courbot <acourbot@nvidia.com> wrote:
>
> We can assume maintainership of this of course, but is there a problem
> if this falls under the core Rust umbrella? As this is a pretty core
> functionality. Miguel and other core folks, WDYT?
I think what Yury may mean is that this should get an explicit
`MAINTAINERS` subentry even if it falls under `rust/kernel/` -- I
agree that is a good idea.
Thanks!
Cheers,
Miguel
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/
2025-10-07 10:36 ` Alexandre Courbot
2025-10-07 10:42 ` Miguel Ojeda
@ 2025-10-07 13:16 ` Danilo Krummrich
2025-10-07 21:08 ` Joel Fernandes
2025-10-07 15:41 ` Yury Norov
2 siblings, 1 reply; 41+ messages in thread
From: Danilo Krummrich @ 2025-10-07 13:16 UTC (permalink / raw)
To: Alexandre Courbot, Yury Norov, Joel Fernandes
Cc: linux-kernel, rust-for-linux, dri-devel, Alistair Popple,
Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo, bjorn3_gh,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, John Hubbard, Timur Tabi, joel, Elle Rhumsaa,
Daniel Almeida, Andrea Righi, nouveau
On Tue Oct 7, 2025 at 12:36 PM CEST, Alexandre Courbot wrote:
> Because letting it fully mature within nova-core also has the drawback
> that we might miss the perspective of other potential users, which may
> make us draw ourselves into a corner that will make the macro less
> useful generally speaking. We are at a stage where we can still make
> design changes if needed, but we need to hear from other users, and
> these won't come as long as the macro is in nova-core.
There are two different things here that are getting mixed up a bit.
(1) Moving the register!() code out of nova-core to make it accessible for
other drivers.
(2) Generalize the bitfield implementation that so far is baked into the
register!() code.
Both of those make sense, but they don't have to happen at the same time
necessarily.
Now, I'm not saying that we necessarily have to change the approach here. The
current merge window isn't even closed, so we have plently of time left, i.e.
there's no rush with with patch series.
However, if it helps, I'm perfectly fine to take the register!() implementation
into the I/O entry in a first step and in a second step generalize the bitfield
implementation and move it out of the register!() code.
Again, there's no rush as far as I'm concerned, yet the latter approach might
add a bit more structure and hence run a bit smoother.
- Danilo
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/
2025-10-07 10:42 ` Miguel Ojeda
@ 2025-10-07 13:20 ` Alexandre Courbot
2025-10-07 16:06 ` Yury Norov
2025-10-07 16:12 ` Miguel Ojeda
0 siblings, 2 replies; 41+ messages in thread
From: Alexandre Courbot @ 2025-10-07 13:20 UTC (permalink / raw)
To: Miguel Ojeda, Alexandre Courbot, Yury Norov
Cc: Joel Fernandes, linux-kernel, rust-for-linux, dri-devel, dakr,
Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Timur Tabi, joel,
Elle Rhumsaa, Daniel Almeida, Andrea Righi, nouveau
On Tue Oct 7, 2025 at 7:42 PM JST, Miguel Ojeda wrote:
> On Tue, Oct 7, 2025 at 12:36 PM Alexandre Courbot <acourbot@nvidia.com> wrote:
>>
>> We can assume maintainership of this of course, but is there a problem
>> if this falls under the core Rust umbrella? As this is a pretty core
>> functionality. Miguel and other core folks, WDYT?
>
> I think what Yury may mean is that this should get an explicit
> `MAINTAINERS` subentry even if it falls under `rust/kernel/` -- I
> agree that is a good idea.
Ack - how do you expect things to work in terms of code flow? Do we need
to have a dedicated tree and send you pull requests? If so, should we
host it under the Rust-for-Linux Github org?
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/
2025-10-07 10:36 ` Alexandre Courbot
2025-10-07 10:42 ` Miguel Ojeda
2025-10-07 13:16 ` Danilo Krummrich
@ 2025-10-07 15:41 ` Yury Norov
2025-10-07 21:41 ` Daniel Almeida
2 siblings, 1 reply; 41+ messages in thread
From: Yury Norov @ 2025-10-07 15:41 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Joel Fernandes, linux-kernel, rust-for-linux, dri-devel, dakr,
Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Timur Tabi, joel,
Elle Rhumsaa, Daniel Almeida, Andrea Righi, nouveau
On Tue, Oct 07, 2025 at 07:36:21PM +0900, Alexandre Courbot wrote:
> Hi Yuri,
>
> On Tue Oct 7, 2025 at 7:29 AM JST, Yury Norov wrote:
> <snip>
> > Regardless, I don't think that this is the right path to move the
> > bitfields into the core. The natural path for a feature that has
> > been originally developed on driver side is to mature in there and
> > get merged to core libraries after a while. Resctrl from Intel is one
> > recent example.
> >
> > With that said, I'm OK if you move the bitfields as a whole, like you
> > do in v5, and I'm also OK if you split out the part essential for nova
> > and take it into the driver. In that case the bitfields will stay in
> > drivers and you'll be able to focus on the features that _you_ need,
> > not on generic considerations.
> >
> > I'm not OK to move bitfields in their current (v6) incomplete form in
> > rust/kernel. We still have no solid understanding on the API and
> > implementation that we've been all agreed on.
>
> Initially the plan was indeed to give this code some more time to mature
> in nova-core before moving it out.
>
> The reason for the early move is that we have another driver (Tyr) who
> wants to start using the register macro. Without it, they would be left
> with the option of either reinventing the wheel, or poking at registers
> the old-fashioned way, which I think we can agree is not going to be any
> safer than the current macro. :)
>
> IIUC your remaining concern is with the possible loss of data when
> setting a field that is smaller than its primitive type? That should be
> addressed by [0], but as it introduces a new core feature I expect some
> discussion to take place before it can be merged. In the meantime, it
> would be great if we can make the register macro available.
>
> Because letting it fully mature within nova-core also has the drawback
> that we might miss the perspective of other potential users, which may
> make us draw ourselves into a corner that will make the macro less
> useful generally speaking. We are at a stage where we can still make
> design changes if needed, but we need to hear from other users, and
> these won't come as long as the macro is in nova-core.
Hi Alexandre,
Thanks for the broader perspective.
So if there's another user for register!(), then yeah - it's worth to
move it out of the nova earlier. It doesn't mean that we need to split
bitfields out of it immediately.
> [0] https://lore.kernel.org/rust-for-linux/20251002-bounded_ints-v1-0-dd60f5804ea4@nvidia.com/
This resembles the _BitInt from C23 standard, and it looks quite
reasonable to me. I'll get back to your RFC shortly.
https://en.cppreference.com/w/c/language/arithmetic_types.html
--
I'm glad that we started this discussion. From my point, what happens now
is inventing the whole new language, and basic bit operations is the heart
of it.
I would really like to avoid adopting an API that will frustrate people
for decades after invention. Please read the following rant to taste
exactly what I mean:
https://lore.kernel.org/all/CAHk-=whoOUsqPKb7OQwhQf9H_3=5sXGPJrDbfQfwLB3Bi13tcQ@mail.gmail.com/
Thanks,
Yury
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/
2025-10-07 13:20 ` Alexandre Courbot
@ 2025-10-07 16:06 ` Yury Norov
2025-10-07 16:12 ` Miguel Ojeda
1 sibling, 0 replies; 41+ messages in thread
From: Yury Norov @ 2025-10-07 16:06 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Miguel Ojeda, Joel Fernandes, linux-kernel, rust-for-linux,
dri-devel, dakr, Alistair Popple, Miguel Ojeda, Alex Gaynor,
Boqun Feng, Gary Guo, bjorn3_gh, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, John Hubbard,
Timur Tabi, joel, Elle Rhumsaa, Daniel Almeida, Andrea Righi,
nouveau
On Tue, Oct 07, 2025 at 10:20:54PM +0900, Alexandre Courbot wrote:
> On Tue Oct 7, 2025 at 7:42 PM JST, Miguel Ojeda wrote:
> > On Tue, Oct 7, 2025 at 12:36 PM Alexandre Courbot <acourbot@nvidia.com> wrote:
> >>
> >> We can assume maintainership of this of course, but is there a problem
> >> if this falls under the core Rust umbrella? As this is a pretty core
> >> functionality. Miguel and other core folks, WDYT?
> >
> > I think what Yury may mean is that this should get an explicit
> > `MAINTAINERS` subentry even if it falls under `rust/kernel/` -- I
> > agree that is a good idea.
Exactly. Otherwise we'll end up with a single maintainer for a huge
codebase written by different people for different reasons. This is how
lib/ is maintained now. Not very effective.
> Ack - how do you expect things to work in terms of code flow? Do we need
> to have a dedicated tree and send you pull requests? If so, should we
> host it under the Rust-for-Linux Github org?
(Not sure you've asked me but anyways)
For maintenance hierarchy I'd suggest a structure where an author of
the new subsystem obviously becomes a maintainer, then some acknowledged
Rust person co-maintains it, and lately some non-rust person from a
related kernel subsystem becomes a reviewer or co-maintainer.
In 6.18 we did this for bitmaps, and the maintenance entry looks like:
BITMAP API [RUST]
M: Alice Ryhl <aliceryhl@google.com>
M: Burak Emir <bqe@google.com>
R: Yury Norov <yury.norov@gmail.com>
S: Maintained
F: rust/kernel/bitmap.rs
Check 11eca92a2cae ("rust: add bitmap API").
Thanks,
Yury
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/
2025-10-07 13:20 ` Alexandre Courbot
2025-10-07 16:06 ` Yury Norov
@ 2025-10-07 16:12 ` Miguel Ojeda
1 sibling, 0 replies; 41+ messages in thread
From: Miguel Ojeda @ 2025-10-07 16:12 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Yury Norov, Joel Fernandes, linux-kernel, rust-for-linux,
dri-devel, dakr, Alistair Popple, Miguel Ojeda, Alex Gaynor,
Boqun Feng, Gary Guo, bjorn3_gh, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, John Hubbard,
Timur Tabi, joel, Elle Rhumsaa, Daniel Almeida, Andrea Righi,
nouveau
On Tue, Oct 7, 2025 at 3:21 PM Alexandre Courbot <acourbot@nvidia.com> wrote:
>
> Ack - how do you expect things to work in terms of code flow? Do we need
> to have a dedicated tree and send you pull requests? If so, should we
> host it under the Rust-for-Linux Github org?
In general, if there is a better tree (i.e. an existing C subsystem),
then what we always do is ask those maintainers first.
But, otherwise, if there is no good choice because it is something too
generic/core, then yes, what you propose would be ideal (and it should
be easy since you are already maintainers of other trees etc.).
If the amount of patches ends up being very low, we can skip the tree
and I can take the patches, but PRs are simpler and scale better for
me.
Thanks!
Cheers,
Miguel
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/
2025-10-07 13:16 ` Danilo Krummrich
@ 2025-10-07 21:08 ` Joel Fernandes
2025-10-07 22:08 ` Danilo Krummrich
0 siblings, 1 reply; 41+ messages in thread
From: Joel Fernandes @ 2025-10-07 21:08 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot, Yury Norov
Cc: linux-kernel, rust-for-linux, dri-devel, Alistair Popple,
Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo, bjorn3_gh,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, John Hubbard, Timur Tabi, joel, Elle Rhumsaa,
Daniel Almeida, Andrea Righi, nouveau
Danilo, Yuri, Miguel, John, all,
On 10/7/2025 9:16 AM, Danilo Krummrich wrote:
> On Tue Oct 7, 2025 at 12:36 PM CEST, Alexandre Courbot wrote:
>> Because letting it fully mature within nova-core also has the drawback
>> that we might miss the perspective of other potential users, which may
>> make us draw ourselves into a corner that will make the macro less
>> useful generally speaking. We are at a stage where we can still make
>> design changes if needed, but we need to hear from other users, and
>> these won't come as long as the macro is in nova-core.
>
> There are two different things here that are getting mixed up a bit.
>
> (1) Moving the register!() code out of nova-core to make it accessible for
> other drivers.
>
> (2) Generalize the bitfield implementation that so far is baked into the
> register!() code.
>
> Both of those make sense, but they don't have to happen at the same time
> necessarily.
>
> Now, I'm not saying that we necessarily have to change the approach here. The
> current merge window isn't even closed, so we have plently of time left, i.e.
> there's no rush with with patch series.
>
> However, if it helps, I'm perfectly fine to take the register!() implementation
> into the I/O entry in a first step and in a second step generalize the bitfield
> implementation and move it out of the register!() code.
>
> Again, there's no rush as far as I'm concerned, yet the latter approach might
> add a bit more structure and hence run a bit smoother.
In my view it is better to move both bitfield and register macros together
because if we only moved register, it means we would have no bitfield support
for the page table / mm use case I just posted a patch for (which is why I
started looking into Bitfield support initially) unless we create a copy of just
the bitfield code within nova which we definitely shouldn't I think. So I think
it is best to move both.
I think we can address Yuri’s concerns shortly. All the outstanding concerns
(including Yuri's) are as follows (and sorry Yuri to send the v6 when we were
still discussing v5, but please let me know if I missed anything):
1. Undefined bitfields to be zero'ed to avoid undefined behavior: Unlike C,
this is not a concern in Rust because we will be *defining* the raw value
explicitly as an inner integer. In register! macro usecases in fact, we do
store/read the entire inner value some times so we have to support that (without
zero'ing).
2. The setter API should fail or trim inputs based on the (un)defined bitfields:
This should not be an issue after Alex's idea on moving this issue to the type
system [1]. The compilation simply fails for invalid inputs.
3. Build pattern chaining of .set_XX(...).set_YY(...): Yuri requested to drop
this. I discussed with Alex and we feel this is idiomatic rust and we ought to
support it.
4. Concerns about "a.set_foo(XX)" pattern ignoring returning value resulting in
a NOOP: Mark the setter API #[must_use] to prevent patterns where the return
value is discarded, ex: a.set_foo(XX);
5. MAINTAINERS file updates: Miguel and Yuri replied to this so we can adopt
their guidance. Currently I am considering Alex and me as M: and Yuri as R:. For
git tree, let me know which tree we want to use or could create a new one. For
the IO (register macro) change, I can add add an entry to the existing IO record.
Does this all sound good? If not please raise your concern as a reply and let us
discuss it.
As next steps, the only thing left is to address #2 (unless anyone replies with
any objections). However, IMO #2 can be addressed once the code moves out as
well (it might be a bit of time as we need the bounded int changes in case we
want to address #2 before moving code outside of nova). Thoughts? Everything
else above (expect #2) is good to go and I can roll it into v7 soon.
Any other thoughts or concerns? Thanks.
[1] https://lore.kernel.org/all/20251002-bounded_ints-v1-0-dd60f5804ea4@nvidia.com/
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/
2025-10-07 15:41 ` Yury Norov
@ 2025-10-07 21:41 ` Daniel Almeida
2025-10-08 15:49 ` Yury Norov
0 siblings, 1 reply; 41+ messages in thread
From: Daniel Almeida @ 2025-10-07 21:41 UTC (permalink / raw)
To: Yury Norov
Cc: Alexandre Courbot, Joel Fernandes, linux-kernel, rust-for-linux,
dri-devel, dakr, Alistair Popple, Miguel Ojeda, Alex Gaynor,
Boqun Feng, Gary Guo, bjorn3_gh, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, John Hubbard,
Timur Tabi, joel, Elle Rhumsaa, Andrea Righi, nouveau
Hi,
First and foremost I’d like to say sorry for not having the bandwidth to
chime in here earlier. I’ve been pretty consumed by Tyr itself lately.
> On 7 Oct 2025, at 12:41, Yury Norov <yury.norov@gmail.com> wrote:
>
> On Tue, Oct 07, 2025 at 07:36:21PM +0900, Alexandre Courbot wrote:
>> Hi Yuri,
>>
>> On Tue Oct 7, 2025 at 7:29 AM JST, Yury Norov wrote:
>> <snip>
>>> Regardless, I don't think that this is the right path to move the
>>> bitfields into the core. The natural path for a feature that has
>>> been originally developed on driver side is to mature in there and
>>> get merged to core libraries after a while. Resctrl from Intel is one
>>> recent example.
>>>
>>> With that said, I'm OK if you move the bitfields as a whole, like you
>>> do in v5, and I'm also OK if you split out the part essential for nova
>>> and take it into the driver. In that case the bitfields will stay in
>>> drivers and you'll be able to focus on the features that _you_ need,
>>> not on generic considerations.
>>>
>>> I'm not OK to move bitfields in their current (v6) incomplete form in
>>> rust/kernel. We still have no solid understanding on the API and
>>> implementation that we've been all agreed on.
>>
>> Initially the plan was indeed to give this code some more time to mature
>> in nova-core before moving it out.
>>
>> The reason for the early move is that we have another driver (Tyr) who
>> wants to start using the register macro. Without it, they would be left
>> with the option of either reinventing the wheel, or poking at registers
>> the old-fashioned way, which I think we can agree is not going to be any
>> safer than the current macro. :)
>>
Tyr could use both register!() and bitfield!() today FYI. If you move it out, I can
follow with an actual patch to do so.
— Daniel
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/
2025-10-07 21:08 ` Joel Fernandes
@ 2025-10-07 22:08 ` Danilo Krummrich
2025-10-08 14:28 ` Yury Norov
0 siblings, 1 reply; 41+ messages in thread
From: Danilo Krummrich @ 2025-10-07 22:08 UTC (permalink / raw)
To: Joel Fernandes
Cc: Alexandre Courbot, Yury Norov, linux-kernel, rust-for-linux,
dri-devel, Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng,
Gary Guo, bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Timur Tabi, joel,
Elle Rhumsaa, Daniel Almeida, Andrea Righi, nouveau
On Tue Oct 7, 2025 at 11:08 PM CEST, Joel Fernandes wrote:
> Danilo, Yuri, Miguel, John, all,
>
> On 10/7/2025 9:16 AM, Danilo Krummrich wrote:
>> On Tue Oct 7, 2025 at 12:36 PM CEST, Alexandre Courbot wrote:
>>> Because letting it fully mature within nova-core also has the drawback
>>> that we might miss the perspective of other potential users, which may
>>> make us draw ourselves into a corner that will make the macro less
>>> useful generally speaking. We are at a stage where we can still make
>>> design changes if needed, but we need to hear from other users, and
>>> these won't come as long as the macro is in nova-core.
>>
>> There are two different things here that are getting mixed up a bit.
>>
>> (1) Moving the register!() code out of nova-core to make it accessible for
>> other drivers.
>>
>> (2) Generalize the bitfield implementation that so far is baked into the
>> register!() code.
>>
>> Both of those make sense, but they don't have to happen at the same time
>> necessarily.
>>
>> Now, I'm not saying that we necessarily have to change the approach here. The
>> current merge window isn't even closed, so we have plently of time left, i.e.
>> there's no rush with with patch series.
>>
>> However, if it helps, I'm perfectly fine to take the register!() implementation
>> into the I/O entry in a first step and in a second step generalize the bitfield
>> implementation and move it out of the register!() code.
>>
>> Again, there's no rush as far as I'm concerned, yet the latter approach might
>> add a bit more structure and hence run a bit smoother.
>
> In my view it is better to move both bitfield and register macros together
> because if we only moved register, it means we would have no bitfield support
> for the page table / mm use case I just posted a patch for (which is why I
> started looking into Bitfield support initially) unless we create a copy of just
> the bitfield code within nova which we definitely shouldn't I think. So I think
> it is best to move both.
Again, fine for me either way, but I wanted to open the possibility.
Typically, things run more smoothly when focusing on one thing at a time.
Especially when one thing is done to unblock something else, while the other
things needs some more discussion and might require a more slow-paced approach.)
(Slightly off-topic: Regarding the bitfields for page table management: Are we
sure that we can use raw bitfields for this? I.e. will we always be able to
configure the GPU to match CPU endianness?)
> For the IO (register macro) change, I can add add an entry to the existing IO
> record.
I don't think any changes are needed, it should be covered by just moving it to
rust/kernel/io/register.rs.
Thanks,
Danilo
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/
2025-10-07 22:08 ` Danilo Krummrich
@ 2025-10-08 14:28 ` Yury Norov
2025-10-08 15:00 ` Danilo Krummrich
0 siblings, 1 reply; 41+ messages in thread
From: Yury Norov @ 2025-10-08 14:28 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Joel Fernandes, Alexandre Courbot, linux-kernel, rust-for-linux,
dri-devel, Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng,
Gary Guo, bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Timur Tabi, joel,
Elle Rhumsaa, Daniel Almeida, Andrea Righi, nouveau
On Wed, Oct 08, 2025 at 12:08:59AM +0200, Danilo Krummrich wrote:
> Regarding the bitfields for page table management: Are we
> sure that we can use raw bitfields for this?
As per my current understanding we can't. Bitfields are not suitable for
direct I/O and considered as a data storage. In the current scheme:
regs::NV_PFALCON_FALCON_DMATRFBASE::default()
.set_base((dma_start >> 8) as u32)
.write(bar, &E::ID);
we account for endianess in the .write() method, which would be a part
of register API, not bitfields.
FYI: ARM64 is in process of dropping BE, and Linus announced the end
of BE support for RISC-V:
https://lore.kernel.org/all/CAHk-=wgYcOiFvsJzFb+HfB4n6Wj6zM5H5EghUMfpXSCzyQVSfA@mail.gmail.com/
Thanks,
Yury
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/
2025-10-08 14:28 ` Yury Norov
@ 2025-10-08 15:00 ` Danilo Krummrich
0 siblings, 0 replies; 41+ messages in thread
From: Danilo Krummrich @ 2025-10-08 15:00 UTC (permalink / raw)
To: Yury Norov
Cc: Joel Fernandes, Alexandre Courbot, linux-kernel, rust-for-linux,
dri-devel, Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng,
Gary Guo, bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Timur Tabi, joel,
Elle Rhumsaa, Daniel Almeida, Andrea Righi, nouveau
On Wed Oct 8, 2025 at 4:28 PM CEST, Yury Norov wrote:
> On Wed, Oct 08, 2025 at 12:08:59AM +0200, Danilo Krummrich wrote:
>
>> Regarding the bitfields for page table management: Are we
>> sure that we can use raw bitfields for this?
>
> As per my current understanding we can't. Bitfields are not suitable for
> direct I/O and considered as a data storage. In the current scheme:
>
> regs::NV_PFALCON_FALCON_DMATRFBASE::default()
> .set_base((dma_start >> 8) as u32)
> .write(bar, &E::ID);
>
> we account for endianess in the .write() method, which would be a part
> of register API, not bitfields.
I know, I proposed this register API design about a year ago, and Alex came up
with an awesome implementation for it. :)
And yes, your understanding is correct, the idea is that the I/O backend that
knows about the underlying bus, etc. takes care of the endianess.
However, we might still be able to use bitfields natively for page table
management structures: In [1] I asked for the NV_PMC_BOOT_1 register, which
might provide some endianess switch to ensure that we always match CPU
endianess.
> FYI: ARM64 is in process of dropping BE, and Linus announced the end
> of BE support for RISC-V:
Yeah, I'm aware of the thread. Also note that for RISC-V it was also clarified
that if it turns out that BE for RISC-V becomes relevant it would also find its
way into the kernel. If that's likely is of course a different question.
However, there are still architectures such as s390x that could be supported.
So, long story short, my question from above is more meant to challenge if we
can really always guarantee that CPU and GPU endianess match for nova-core.
[1] https://lore.kernel.org/lkml/DDCV84IJHUML.126CB1CT0XMX5@kernel.org/
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/
2025-10-07 21:41 ` Daniel Almeida
@ 2025-10-08 15:49 ` Yury Norov
0 siblings, 0 replies; 41+ messages in thread
From: Yury Norov @ 2025-10-08 15:49 UTC (permalink / raw)
To: Daniel Almeida
Cc: Alexandre Courbot, Joel Fernandes, linux-kernel, rust-for-linux,
dri-devel, dakr, Alistair Popple, Miguel Ojeda, Alex Gaynor,
Boqun Feng, Gary Guo, bjorn3_gh, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, John Hubbard,
Timur Tabi, joel, Elle Rhumsaa, Andrea Righi, nouveau
On Tue, Oct 07, 2025 at 06:41:58PM -0300, Daniel Almeida wrote:
> Hi,
>
> First and foremost I’d like to say sorry for not having the bandwidth to
> chime in here earlier. I’ve been pretty consumed by Tyr itself lately.
>
> > On 7 Oct 2025, at 12:41, Yury Norov <yury.norov@gmail.com> wrote:
> >
> > On Tue, Oct 07, 2025 at 07:36:21PM +0900, Alexandre Courbot wrote:
> >> Hi Yuri,
> >>
> >> On Tue Oct 7, 2025 at 7:29 AM JST, Yury Norov wrote:
> >> <snip>
> >>> Regardless, I don't think that this is the right path to move the
> >>> bitfields into the core. The natural path for a feature that has
> >>> been originally developed on driver side is to mature in there and
> >>> get merged to core libraries after a while. Resctrl from Intel is one
> >>> recent example.
> >>>
> >>> With that said, I'm OK if you move the bitfields as a whole, like you
> >>> do in v5, and I'm also OK if you split out the part essential for nova
> >>> and take it into the driver. In that case the bitfields will stay in
> >>> drivers and you'll be able to focus on the features that _you_ need,
> >>> not on generic considerations.
> >>>
> >>> I'm not OK to move bitfields in their current (v6) incomplete form in
> >>> rust/kernel. We still have no solid understanding on the API and
> >>> implementation that we've been all agreed on.
> >>
> >> Initially the plan was indeed to give this code some more time to mature
> >> in nova-core before moving it out.
> >>
> >> The reason for the early move is that we have another driver (Tyr) who
> >> wants to start using the register macro. Without it, they would be left
> >> with the option of either reinventing the wheel, or poking at registers
> >> the old-fashioned way, which I think we can agree is not going to be any
> >> safer than the current macro. :)
> >>
>
> Tyr could use both register!() and bitfield!() today FYI. If you move it out, I can
> follow with an actual patch to do so.
Not sure I understand this. Does it mean that you have use cases
for bitfields unrelated to I/O registers? Can you please share
details on what you need and sketch some examples? Particularly,
do you have arrays of bitfields in mind?
If my understanding is correct, then I think the strategy that
meets your interests at best would be moving the registers out of
Nova earlier. After that both Tyr and Nova people will get equally
involved in bitfields (and possible BoundedInt) development.
This is what Danilo proposed in the other email, and I agree that it's
the most structured path.
Regarding timeline, I think 2-stage approach will eventually become
faster.
Thanks,
Yury
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova
2025-10-03 15:47 ` [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova Joel Fernandes
2025-10-06 10:38 ` Alexandre Courbot
@ 2025-10-09 6:59 ` Dirk Behme
2025-10-09 11:16 ` Danilo Krummrich
2025-10-22 18:40 ` Beata Michalska
2 siblings, 1 reply; 41+ messages in thread
From: Dirk Behme @ 2025-10-09 6:59 UTC (permalink / raw)
To: Joel Fernandes, linux-kernel, rust-for-linux, dri-devel, dakr,
acourbot
Cc: Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Timur Tabi, joel,
Elle Rhumsaa, Yury Norov, Daniel Almeida, Andrea Righi, nouveau
On 03/10/2025 17:47, Joel Fernandes wrote:
> Out of broad need for the register and bitfield macros in Rust, move
> them out of nova into the kernel crate. Several usecases need them (Nova
> is already using these and Tyr developers said they need them).
>
> bitfield moved into kernel crate - defines bitfields in Rust.
> register moved into io module - defines hardware registers and accessors.
>
> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
> Reviewed-by: Elle Rhumsaa <elle@weathered-steel.dev>
> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
> ---
> drivers/gpu/nova-core/falcon.rs | 2 +-
> drivers/gpu/nova-core/falcon/gsp.rs | 4 +-
> drivers/gpu/nova-core/falcon/sec2.rs | 2 +-
> drivers/gpu/nova-core/nova_core.rs | 3 -
> drivers/gpu/nova-core/regs.rs | 6 +-
> .../gpu/nova-core => rust/kernel}/bitfield.rs | 27 ++++-----
> rust/kernel/io.rs | 1 +
> .../macros.rs => rust/kernel/io/register.rs | 58 ++++++++++---------
> rust/kernel/lib.rs | 1 +
> 9 files changed, 54 insertions(+), 50 deletions(-)
> rename {drivers/gpu/nova-core => rust/kernel}/bitfield.rs (91%)
> rename drivers/gpu/nova-core/regs/macros.rs => rust/kernel/io/register.rs (93%)
>
...
> index c0a5194e8d97..c24d956f122f 100644
> --- a/drivers/gpu/nova-core/regs/macros.rs
> +++ b/rust/kernel/io/register.rs
Assuming that register.rs is supposed to become the "generic" way to
access hardware registers I started to have a look to it. Some weeks
back testing interrupts I used some quite simple timer with 4 registers
[1]. Now, thinking about converting it to register!() I have three
understanding / usage questions:
* At the moment register!() is for 32-bit registers, only? So it can't
be used for my example having 8-bit and 16-bit registers as well?
* In my example I used io.try_write*() and io.try_read*() for the
register access. What is the relationship between these and the
register!() accessors (e.g. from the examples BOOT_0::read(&bar);)? Will
both stay? When to use which?
Note: Due to the file move obviously not the full content of the "new"
file register.rs is shown in this patch. Therefore, let me try it this
way, citing from register.rs:
-- cut --
...
/// This defines a `BOOT_0` type which can be read or written from
offset `0x100` of an `Io`
/// region
....
/// ```ignore
/// // Read from the register's defined offset (0x100).
/// let boot0 = BOOT_0::read(&bar);
-- cut --
* What is "&bar" in this example? Is it the `Io` region the explanation
talks about?
Thanks!
Dirk
[1]
https://lore.kernel.org/rust-for-linux/dd34e5f4-5027-4096-9f32-129c8a067d0a@de.bosch.com/
The registers:
const TSTR: usize = 0x4; // 8 Bit register
const TCOR: usize = 0x8; // 32 Bit register
const TCNT: usize = 0xC; // 32 Bit register
const TCR: usize = 0x10; // 16 Bit register
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova
2025-10-09 6:59 ` Dirk Behme
@ 2025-10-09 11:16 ` Danilo Krummrich
2025-10-09 11:28 ` Alexandre Courbot
2025-10-10 7:28 ` Dirk Behme
0 siblings, 2 replies; 41+ messages in thread
From: Danilo Krummrich @ 2025-10-09 11:16 UTC (permalink / raw)
To: Dirk Behme
Cc: Joel Fernandes, linux-kernel, rust-for-linux, dri-devel, acourbot,
Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Timur Tabi, joel,
Elle Rhumsaa, Yury Norov, Daniel Almeida, Andrea Righi, nouveau
On Thu Oct 9, 2025 at 8:59 AM CEST, Dirk Behme wrote:
> Assuming that register.rs is supposed to become the "generic" way to
> access hardware registers I started to have a look to it. Some weeks
> back testing interrupts I used some quite simple timer with 4 registers
> [1]. Now, thinking about converting it to register!() I have three
> understanding / usage questions:
>
> * At the moment register!() is for 32-bit registers, only? So it can't
> be used for my example having 8-bit and 16-bit registers as well?
Yes, currently the register!() macro always generates a 32-bit register type
(mainly because nova-core did not need anything else). However, this will of
course be generalized (which should be pretty straight forward).
Having a brief look at the TMU datasheet it looks like you should be able to
treat TSTR and TCR as 32-bit registers without any issues for testing the
register!() macro today. I.e. you can just define it as:
register!(TSTR @ 0x04, "Timer Start Register" {
2:2 str2 as bool, "Specifies whether TCNT2 is operated or stopped.";
1:1 str1 as bool, "Specifies whether TCNT1 is operated or stopped.";
0:0 str0 as bool, "Specifies whether TCNT0 is operated or stopped.";
});
Same for TCR.
> * In my example I used io.try_write*() and io.try_read*() for the
> register access. What is the relationship between these and the
> register!() accessors (e.g. from the examples BOOT_0::read(&bar);)? Will
> both stay? When to use which?
The try_*() variants should only be used of the offset of the register is not
known at compile time.
If it is known at compile time (such as in your case) you should use the
infallible variants that perform a range check at compile time.
For this to work you need to specify the minimum MMIO range your driver expects,
i.e. instead of
let iomem = Arc::pin_init(request.iomap()?, GFP_KERNEL)?;
you call
let iomem = Arc::pin_init(request.iomap_sized::<TMU_MMIO_SIZE>()?, GFP_KERNEL)?;
where TMU_MMIO_SIZE is the minimum MMIO region size your driver is able to
operate on. In your case this would be 0x12, given that TCR has a width of
16-bit. However, if you treat it as 32-bit register it would be 0x14.
Even without the register!() macro this would be a huge improvement. For
instance, your IRQ handler from [1] can do
let tcr = io.read16_relaxed(TCR);
if tcr & (0x1 << 8) != 0 {
io.write16_relaxed(tcr & !(0x1 << 8), TCR);
}
instead of
let tcr = io.try_read16_relaxed(TCR).unwrap_or(0);
if tcr & (0x1 << 8) != 0 {
io.try_write16_relaxed(tcr & !(0x1 << 8), TCR).unwrap_or(());
}
And with the register macro it becomes
let tcr = TCR::read(io);
if tcr.underflow() {
tcr.set_underflow(false);
tcr.write(io);
}
Note that you can also extend the generated TCR type accordingly, for instance:
impl TCR {
fn handle_underflow<const SIZE: usize, T>(io: &T)
where
T: Deref<Target = Io<SIZE>>,
{
let this = Self::read(io);
if this.underflow() {
this.set_underflow(false);
this.write(io);
}
}
}
and then from your IRQ handler you can just call
TCR::handle_underflow();
> Note: Due to the file move obviously not the full content of the "new"
> file register.rs is shown in this patch. Therefore, let me try it this
> way, citing from register.rs:
>
> -- cut --
> ...
> /// This defines a `BOOT_0` type which can be read or written from
> offset `0x100` of an `Io`
> /// region
> ....
> /// ```ignore
> /// // Read from the register's defined offset (0x100).
> /// let boot0 = BOOT_0::read(&bar);
> -- cut --
>
> * What is "&bar" in this example? Is it the `Io` region the explanation
> talks about?
Yes, it's the I/O backend (a pci::Bar in this case). However, we should probably
use a more generic name in the examples.
> [1]
> https://lore.kernel.org/rust-for-linux/dd34e5f4-5027-4096-9f32-129c8a067d0a@de.bosch.com/
>
> The registers:
>
> const TSTR: usize = 0x4; // 8 Bit register
> const TCOR: usize = 0x8; // 32 Bit register
> const TCNT: usize = 0xC; // 32 Bit register
> const TCR: usize = 0x10; // 16 Bit register
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova
2025-10-09 11:16 ` Danilo Krummrich
@ 2025-10-09 11:28 ` Alexandre Courbot
2025-10-09 12:54 ` Danilo Krummrich
2025-10-10 7:28 ` Dirk Behme
1 sibling, 1 reply; 41+ messages in thread
From: Alexandre Courbot @ 2025-10-09 11:28 UTC (permalink / raw)
To: Danilo Krummrich, Dirk Behme
Cc: Joel Fernandes, linux-kernel, rust-for-linux, dri-devel, acourbot,
Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Timur Tabi, joel,
Elle Rhumsaa, Yury Norov, Daniel Almeida, Andrea Righi, nouveau
On Thu Oct 9, 2025 at 8:16 PM JST, Danilo Krummrich wrote:
> On Thu Oct 9, 2025 at 8:59 AM CEST, Dirk Behme wrote:
>> Assuming that register.rs is supposed to become the "generic" way to
>> access hardware registers I started to have a look to it. Some weeks
>> back testing interrupts I used some quite simple timer with 4 registers
>> [1]. Now, thinking about converting it to register!() I have three
>> understanding / usage questions:
>>
>> * At the moment register!() is for 32-bit registers, only? So it can't
>> be used for my example having 8-bit and 16-bit registers as well?
>
> Yes, currently the register!() macro always generates a 32-bit register type
> (mainly because nova-core did not need anything else). However, this will of
> course be generalized (which should be pretty straight forward).
>
> Having a brief look at the TMU datasheet it looks like you should be able to
> treat TSTR and TCR as 32-bit registers without any issues for testing the
> register!() macro today. I.e. you can just define it as:
>
> register!(TSTR @ 0x04, "Timer Start Register" {
> 2:2 str2 as bool, "Specifies whether TCNT2 is operated or stopped.";
> 1:1 str1 as bool, "Specifies whether TCNT1 is operated or stopped.";
> 0:0 str0 as bool, "Specifies whether TCNT0 is operated or stopped.";
> });
>
> Same for TCR.
Patch 2 of this series actually adds support for 16 and 8 bit register
storage.
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova
2025-10-09 11:28 ` Alexandre Courbot
@ 2025-10-09 12:54 ` Danilo Krummrich
0 siblings, 0 replies; 41+ messages in thread
From: Danilo Krummrich @ 2025-10-09 12:54 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Dirk Behme, Joel Fernandes, linux-kernel, rust-for-linux,
dri-devel, Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng,
Gary Guo, bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Timur Tabi, joel,
Elle Rhumsaa, Yury Norov, Daniel Almeida, Andrea Righi, nouveau
On 10/9/25 1:28 PM, Alexandre Courbot wrote:
> On Thu Oct 9, 2025 at 8:16 PM JST, Danilo Krummrich wrote:
>> On Thu Oct 9, 2025 at 8:59 AM CEST, Dirk Behme wrote:
>>> Assuming that register.rs is supposed to become the "generic" way to
>>> access hardware registers I started to have a look to it. Some weeks
>>> back testing interrupts I used some quite simple timer with 4 registers
>>> [1]. Now, thinking about converting it to register!() I have three
>>> understanding / usage questions:
>>>
>>> * At the moment register!() is for 32-bit registers, only? So it can't
>>> be used for my example having 8-bit and 16-bit registers as well?
>>
>> Yes, currently the register!() macro always generates a 32-bit register type
>> (mainly because nova-core did not need anything else). However, this will of
>> course be generalized (which should be pretty straight forward).
>>
>> Having a brief look at the TMU datasheet it looks like you should be able to
>> treat TSTR and TCR as 32-bit registers without any issues for testing the
>> register!() macro today. I.e. you can just define it as:
>>
>> register!(TSTR @ 0x04, "Timer Start Register" {
>> 2:2 str2 as bool, "Specifies whether TCNT2 is operated or stopped.";
>> 1:1 str1 as bool, "Specifies whether TCNT1 is operated or stopped.";
>> 0:0 str0 as bool, "Specifies whether TCNT0 is operated or stopped.";
>> });
>>
>> Same for TCR.
>
> Patch 2 of this series actually adds support for 16 and 8 bit register
> storage.
Heh! I knew I saw a patch for this already somewhere, seems like I missed the
forest for the trees. :)
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova
2025-10-09 11:16 ` Danilo Krummrich
2025-10-09 11:28 ` Alexandre Courbot
@ 2025-10-10 7:28 ` Dirk Behme
1 sibling, 0 replies; 41+ messages in thread
From: Dirk Behme @ 2025-10-10 7:28 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Joel Fernandes, linux-kernel, rust-for-linux, dri-devel, acourbot,
Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Timur Tabi, joel,
Elle Rhumsaa, Yury Norov, Daniel Almeida, Andrea Righi, nouveau
On 09/10/2025 13:16, Danilo Krummrich wrote:
> On Thu Oct 9, 2025 at 8:59 AM CEST, Dirk Behme wrote:
>> Assuming that register.rs is supposed to become the "generic" way to
>> access hardware registers I started to have a look to it. Some weeks
>> back testing interrupts I used some quite simple timer with 4 registers
>> [1]. Now, thinking about converting it to register!() I have three
>> understanding / usage questions:
>>
>> * At the moment register!() is for 32-bit registers, only? So it can't
>> be used for my example having 8-bit and 16-bit registers as well?
>
> Yes, currently the register!() macro always generates a 32-bit register type
> (mainly because nova-core did not need anything else). However, this will of
> course be generalized (which should be pretty straight forward).
>
> Having a brief look at the TMU datasheet it looks like you should be able to
> treat TSTR and TCR as 32-bit registers without any issues for testing the
> register!() macro today. I.e. you can just define it as:
>
> register!(TSTR @ 0x04, "Timer Start Register" {
> 2:2 str2 as bool, "Specifies whether TCNT2 is operated or stopped.";
> 1:1 str1 as bool, "Specifies whether TCNT1 is operated or stopped.";
> 0:0 str0 as bool, "Specifies whether TCNT0 is operated or stopped.";
> });
>
> Same for TCR.
>
>> * In my example I used io.try_write*() and io.try_read*() for the
>> register access. What is the relationship between these and the
>> register!() accessors (e.g. from the examples BOOT_0::read(&bar);)? Will
>> both stay? When to use which?
>
> The try_*() variants should only be used of the offset of the register is not
> known at compile time.
>
> If it is known at compile time (such as in your case) you should use the
> infallible variants that perform a range check at compile time.
>
> For this to work you need to specify the minimum MMIO range your driver expects,
> i.e. instead of
>
> let iomem = Arc::pin_init(request.iomap()?, GFP_KERNEL)?;
>
> you call
>
> let iomem = Arc::pin_init(request.iomap_sized::<TMU_MMIO_SIZE>()?, GFP_KERNEL)?;
>
> where TMU_MMIO_SIZE is the minimum MMIO region size your driver is able to
> operate on. In your case this would be 0x12, given that TCR has a width of
> 16-bit. However, if you treat it as 32-bit register it would be 0x14.
>
> Even without the register!() macro this would be a huge improvement. For
> instance, your IRQ handler from [1] can do
>
> let tcr = io.read16_relaxed(TCR);
> if tcr & (0x1 << 8) != 0 {
> io.write16_relaxed(tcr & !(0x1 << 8), TCR);
> }
>
> instead of
>
> let tcr = io.try_read16_relaxed(TCR).unwrap_or(0);
> if tcr & (0x1 << 8) != 0 {
> io.try_write16_relaxed(tcr & !(0x1 << 8), TCR).unwrap_or(());
> }
>
> And with the register macro it becomes
>
> let tcr = TCR::read(io);
> if tcr.underflow() {
> tcr.set_underflow(false);
> tcr.write(io);
> }
>
> Note that you can also extend the generated TCR type accordingly, for instance:
>
> impl TCR {
> fn handle_underflow<const SIZE: usize, T>(io: &T)
> where
> T: Deref<Target = Io<SIZE>>,
> {
> let this = Self::read(io);
> if this.underflow() {
> this.set_underflow(false);
> this.write(io);
> }
> }
> }
>
> and then from your IRQ handler you can just call
>
> TCR::handle_underflow();
Many thanks! I think such verbose explanation helps a lot!
>> Note: Due to the file move obviously not the full content of the "new"
>> file register.rs is shown in this patch. Therefore, let me try it this
>> way, citing from register.rs:
>>
>> -- cut --
>> ...
>> /// This defines a `BOOT_0` type which can be read or written from
>> offset `0x100` of an `Io`
>> /// region
>> ....
>> /// ```ignore
>> /// // Read from the register's defined offset (0x100).
>> /// let boot0 = BOOT_0::read(&bar);
>> -- cut --
>>
>> * What is "&bar" in this example? Is it the `Io` region the explanation
>> talks about?
>
> Yes, it's the I/O backend (a pci::Bar in this case). However, we should probably
> use a more generic name in the examples.
Intuitively for this example I was thinking about &io ;)
Thanks again!
Dirk
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova
2025-10-03 15:47 ` [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova Joel Fernandes
2025-10-06 10:38 ` Alexandre Courbot
2025-10-09 6:59 ` Dirk Behme
@ 2025-10-22 18:40 ` Beata Michalska
2025-10-22 19:37 ` Joel Fernandes
2 siblings, 1 reply; 41+ messages in thread
From: Beata Michalska @ 2025-10-22 18:40 UTC (permalink / raw)
To: Joel Fernandes
Cc: linux-kernel, rust-for-linux, dri-devel, dakr, acourbot,
Alistair Popple, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
bjorn3_gh, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, John Hubbard, Timur Tabi, joel,
Elle Rhumsaa, Yury Norov, Daniel Almeida, Andrea Righi, nouveau
Hi Joel,
I know I'm chiming in a bit late, so apologies for that.
The register! macro does seem to be a solid foundation for MMIO register
definitions, thought there are few points that could be potentially
[re]considered.
The current design assumes a fixed, compile-time-known MMIO region size.
It does not cover cases when the region size is known only at runtime.
I do appreciate that in cases like that, we are loosing all the deliberate
compile-time checks but it might be necessary to provide support for those as
well (at some point at least).
On the (potential) improvement side:
Allowing offsets to be expressions rather than literals would make the macro
easier to use for regions defined at a fixed base offset, where subsequent
offsets are derived from that base, i.e:
REG_1_BASE -> 0x100
REG_1_STATUS -> REG_1_BASE + 0x0
REG_1_CONTROL -> REG_1_BASE + 0x04
...
The alias mechanism is a nice touch. It might be worth allowing arrays of
registers with explicit aliases to be defined in a single macro invocation,
instead of repeating similar definitions, smth along the lines of:
register!(
REG_STATUS @ 0x300[8; STRIDE] {
0:0 enabled as bool;
3:1 mode as u8;
7:4 flags as u8;
}
aliases {
REG_STATUS_ENABLED[0] {
0:0 enabled as bool;
}
REG_STATUS_MODE[0] {
3:1 mode as u8;
}
REG_STATUS_FLAGS[4] {
7:4 flags as u8;
}
}
);
Finally, for runtime values such as indexes, it could be useful to verify once
and then allow infallible reads/writes through some kind access token.
That might make runtime-safe access patterns simpler and more efficient.
I'm still pondering on how that could look like though (implementation-wise)
---
BR
Beata
On Fri, Oct 03, 2025 at 11:47:47AM -0400, Joel Fernandes wrote:
> Out of broad need for the register and bitfield macros in Rust, move
> them out of nova into the kernel crate. Several usecases need them (Nova
> is already using these and Tyr developers said they need them).
>
> bitfield moved into kernel crate - defines bitfields in Rust.
> register moved into io module - defines hardware registers and accessors.
>
> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
> Reviewed-by: Elle Rhumsaa <elle@weathered-steel.dev>
> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
> ---
> drivers/gpu/nova-core/falcon.rs | 2 +-
> drivers/gpu/nova-core/falcon/gsp.rs | 4 +-
> drivers/gpu/nova-core/falcon/sec2.rs | 2 +-
> drivers/gpu/nova-core/nova_core.rs | 3 -
> drivers/gpu/nova-core/regs.rs | 6 +-
> .../gpu/nova-core => rust/kernel}/bitfield.rs | 27 ++++-----
> rust/kernel/io.rs | 1 +
> .../macros.rs => rust/kernel/io/register.rs | 58 ++++++++++---------
> rust/kernel/lib.rs | 1 +
> 9 files changed, 54 insertions(+), 50 deletions(-)
> rename {drivers/gpu/nova-core => rust/kernel}/bitfield.rs (91%)
> rename drivers/gpu/nova-core/regs/macros.rs => rust/kernel/io/register.rs (93%)
>
> diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
> index 37e6298195e4..a15fa98c8614 100644
> --- a/drivers/gpu/nova-core/falcon.rs
> +++ b/drivers/gpu/nova-core/falcon.rs
> @@ -6,6 +6,7 @@
> use hal::FalconHal;
> use kernel::device;
> use kernel::dma::DmaAddress;
> +use kernel::io::register::RegisterBase;
> use kernel::prelude::*;
> use kernel::sync::aref::ARef;
> use kernel::time::Delta;
> @@ -14,7 +15,6 @@
> use crate::driver::Bar0;
> use crate::gpu::Chipset;
> use crate::regs;
> -use crate::regs::macros::RegisterBase;
> use crate::util;
>
> pub(crate) mod gsp;
> diff --git a/drivers/gpu/nova-core/falcon/gsp.rs b/drivers/gpu/nova-core/falcon/gsp.rs
> index f17599cb49fa..cd4960e997c8 100644
> --- a/drivers/gpu/nova-core/falcon/gsp.rs
> +++ b/drivers/gpu/nova-core/falcon/gsp.rs
> @@ -1,9 +1,11 @@
> // SPDX-License-Identifier: GPL-2.0
>
> +use kernel::io::register::RegisterBase;
> +
> use crate::{
> driver::Bar0,
> falcon::{Falcon, FalconEngine, PFalcon2Base, PFalconBase},
> - regs::{self, macros::RegisterBase},
> + regs::self,
> };
>
> /// Type specifying the `Gsp` falcon engine. Cannot be instantiated.
> diff --git a/drivers/gpu/nova-core/falcon/sec2.rs b/drivers/gpu/nova-core/falcon/sec2.rs
> index 815786c8480d..81717868a8a8 100644
> --- a/drivers/gpu/nova-core/falcon/sec2.rs
> +++ b/drivers/gpu/nova-core/falcon/sec2.rs
> @@ -1,7 +1,7 @@
> // SPDX-License-Identifier: GPL-2.0
>
> use crate::falcon::{FalconEngine, PFalcon2Base, PFalconBase};
> -use crate::regs::macros::RegisterBase;
> +use kernel::io::register::RegisterBase;
>
> /// Type specifying the `Sec2` falcon engine. Cannot be instantiated.
> pub(crate) struct Sec2(());
> diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
> index 112277c7921e..fffcaee2249f 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 206dab2e1335..1f08e6d4045a 100644
> --- a/drivers/gpu/nova-core/regs.rs
> +++ b/drivers/gpu/nova-core/regs.rs
> @@ -4,15 +4,13 @@
> // but are mapped to types.
> #![allow(non_camel_case_types)]
>
> -#[macro_use]
> -pub(crate) mod macros;
> -
> use crate::falcon::{
> DmaTrfCmdSize, FalconCoreRev, FalconCoreRevSubversion, FalconFbifMemType, FalconFbifTarget,
> FalconModSelAlgo, FalconSecurityModel, PFalcon2Base, PFalconBase, PeregrineCoreSelect,
> };
> use crate::gpu::{Architecture, Chipset};
> use kernel::prelude::*;
> +use kernel::register;
>
> // PMC
>
> @@ -331,6 +329,7 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
>
> pub(crate) mod gm107 {
> // FUSE
> + use kernel::register;
>
> register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00021c04 {
> 0:0 display_disabled as bool;
> @@ -339,6 +338,7 @@ pub(crate) mod gm107 {
>
> pub(crate) mod ga100 {
> // FUSE
> + use kernel::register;
>
> register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00820c04 {
> 0:0 display_disabled as bool;
> diff --git a/drivers/gpu/nova-core/bitfield.rs b/rust/kernel/bitfield.rs
> similarity index 91%
> rename from drivers/gpu/nova-core/bitfield.rs
> rename to rust/kernel/bitfield.rs
> index cbedbb0078f6..09cd5741598c 100644
> --- a/drivers/gpu/nova-core/bitfield.rs
> +++ b/rust/kernel/bitfield.rs
> @@ -9,7 +9,7 @@
> /// # Syntax
> ///
> /// ```rust
> -/// use nova_core::bitfield;
> +/// use kernel::bitfield;
> ///
> /// #[derive(Debug, Clone, Copy, Default)]
> /// enum Mode {
> @@ -82,10 +82,11 @@
> /// 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_export]
> 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)* });
> + ::kernel::bitfield!(@core $vis $name $storage $(, $comment)? { $($fields)* });
> };
>
> // All rules below are helpers.
> @@ -114,7 +115,7 @@ fn from(val: $name) -> $storage {
> }
> }
>
> - bitfield!(@fields_dispatcher $vis $name $storage { $($fields)* });
> + ::kernel::bitfield!(@fields_dispatcher $vis $name $storage { $($fields)* });
> };
>
> // Captures the fields and passes them to all the implementers that require field information.
> @@ -130,7 +131,7 @@ fn from(val: $name) -> $storage {
> )*
> }
> ) => {
> - bitfield!(@field_accessors $vis $name $storage {
> + ::kernel::bitfield!(@field_accessors $vis $name $storage {
> $(
> $hi:$lo $field as $type
> $(?=> $try_into_type)?
> @@ -139,8 +140,8 @@ fn from(val: $name) -> $storage {
> ;
> )*
> });
> - bitfield!(@debug $name { $($field;)* });
> - bitfield!(@default $name { $($field;)* });
> + ::kernel::bitfield!(@debug $name { $($field;)* });
> + ::kernel::bitfield!(@default $name { $($field;)* });
> };
>
> // Defines all the field getter/setter methods for `$name`.
> @@ -155,13 +156,13 @@ fn from(val: $name) -> $storage {
> }
> ) => {
> $(
> - bitfield!(@check_field_bounds $hi:$lo $field as $type);
> + ::kernel::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
> + ::kernel::bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field as $type
> $(?=> $try_into_type)?
> $(=> $into_type)?
> $(, $comment)?
> @@ -198,7 +199,7 @@ impl $name {
> @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
> $(, $comment:literal)?;
> ) => {
> - bitfield!(
> + ::kernel::bitfield!(
> @leaf_accessor $vis $name $storage, $hi:$lo $field
> { |f| <$into_type>::from(if f != 0 { true } else { false }) }
> $into_type => $into_type $(, $comment)?;
> @@ -209,7 +210,7 @@ impl $name {
> (
> @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)?;);
> + ::kernel::bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field as bool => bool $(, $comment)?;);
> };
>
> // Catches the `?=>` syntax for non-boolean fields.
> @@ -217,7 +218,7 @@ impl $name {
> @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
> + ::kernel::bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field
> { |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>
> ::core::result::Result<
> $try_into_type,
> @@ -231,7 +232,7 @@ impl $name {
> @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
> + ::kernel::bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field
> { |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
> };
>
> @@ -240,7 +241,7 @@ impl $name {
> @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)?;);
> + ::kernel::bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field as $type => $type $(, $comment)?;);
> };
>
> // Generates the accessor methods for a single field.
> diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
> index 03b467722b86..a79b603604b1 100644
> --- a/rust/kernel/io.rs
> +++ b/rust/kernel/io.rs
> @@ -8,6 +8,7 @@
> use crate::{bindings, build_assert, ffi::c_void};
>
> pub mod mem;
> +pub mod register;
> pub mod resource;
>
> pub use resource::Resource;
> diff --git a/drivers/gpu/nova-core/regs/macros.rs b/rust/kernel/io/register.rs
> similarity index 93%
> rename from drivers/gpu/nova-core/regs/macros.rs
> rename to rust/kernel/io/register.rs
> index c0a5194e8d97..c24d956f122f 100644
> --- a/drivers/gpu/nova-core/regs/macros.rs
> +++ b/rust/kernel/io/register.rs
> @@ -17,7 +17,8 @@
> /// 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> {
> +pub trait RegisterBase<T> {
> + /// The base address for the register.
> const BASE: usize;
> }
>
> @@ -26,7 +27,7 @@ pub(crate) trait RegisterBase<T> {
> ///
> /// Example:
> ///
> -/// ```no_run
> +/// ```ignore
> /// 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";
> @@ -39,7 +40,7 @@ pub(crate) trait RegisterBase<T> {
> /// significant bits of the register. Each field can be accessed and modified using accessor
> /// methods:
> ///
> -/// ```no_run
> +/// ```ignore
> /// // Read from the register's defined offset (0x100).
> /// let boot0 = BOOT_0::read(&bar);
> /// pr_info!("chip revision: {}.{}", boot0.major_revision(), boot0.minor_revision());
> @@ -61,7 +62,7 @@ pub(crate) trait RegisterBase<T> {
> /// 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
> +/// ```ignore
> /// register!(SCRATCH @ 0x00000200, "Scratch register" {
> /// 31:0 value as u32, "Raw value";
> /// });
> @@ -111,7 +112,7 @@ pub(crate) trait RegisterBase<T> {
> /// this register needs to implement `RegisterBase<Base>`. Here is the above example translated
> /// into code:
> ///
> -/// ```no_run
> +/// ```ignore
> /// // Type used to identify the base.
> /// pub(crate) struct CpuCtlBase;
> ///
> @@ -162,7 +163,7 @@ pub(crate) trait RegisterBase<T> {
> /// compile-time or runtime bound checking. Simply define their address as `Address[Size]`, and add
> /// an `idx` parameter to their `read`, `write` and `alter` methods:
> ///
> -/// ```no_run
> +/// ```ignore
> /// # fn no_run() -> Result<(), Error> {
> /// # fn get_scratch_idx() -> usize {
> /// # 0x15
> @@ -211,7 +212,7 @@ pub(crate) trait RegisterBase<T> {
> /// Combining the two features described in the sections above, arrays of registers accessible from
> /// a base can also be defined:
> ///
> -/// ```no_run
> +/// ```ignore
> /// # fn no_run() -> Result<(), Error> {
> /// # fn get_scratch_idx() -> usize {
> /// # 0x15
> @@ -273,28 +274,29 @@ pub(crate) trait RegisterBase<T> {
> /// # Ok(())
> /// # }
> /// ```
> +#[macro_export]
> 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)* } );
> + ::kernel::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)* } );
> + ::kernel::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)* } );
> + ::kernel::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)* } );
> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
> register!(@io_relative $name @ $base [ $alias::OFFSET ]);
> };
>
> @@ -305,7 +307,7 @@ macro_rules! register {
> }
> ) => {
> static_assert!(::core::mem::size_of::<u32>() <= $stride);
> - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
> register!(@io_array $name @ $offset [ $size ; $stride ]);
> };
>
> @@ -326,7 +328,7 @@ macro_rules! register {
> $(, $comment:literal)? { $($fields:tt)* }
> ) => {
> static_assert!(::core::mem::size_of::<u32>() <= $stride);
> - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
> register!(@io_relative_array $name @ $base [ $offset [ $size ; $stride ] ]);
> };
>
> @@ -348,7 +350,7 @@ macro_rules! register {
> }
> ) => {
> static_assert!($idx < $alias::SIZE);
> - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
> register!(@io_relative $name @ $base [ $alias::OFFSET + $idx * $alias::STRIDE ] );
> };
>
> @@ -357,7 +359,7 @@ macro_rules! register {
> // 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)* } );
> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
> register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE );
> };
>
> @@ -414,12 +416,12 @@ pub(crate) fn read<const SIZE: usize, T, B>(
> base: &B,
> ) -> Self where
> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> - B: crate::regs::macros::RegisterBase<$base>,
> + B: ::kernel::io::register::RegisterBase<$base>,
> {
> const OFFSET: usize = $name::OFFSET;
>
> let value = io.read32(
> - <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
> + <B as ::kernel::io::register::RegisterBase<$base>>::BASE + OFFSET
> );
>
> Self(value)
> @@ -435,13 +437,13 @@ pub(crate) fn write<const SIZE: usize, T, B>(
> base: &B,
> ) where
> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> - B: crate::regs::macros::RegisterBase<$base>,
> + B: ::kernel::io::register::RegisterBase<$base>,
> {
> const OFFSET: usize = $name::OFFSET;
>
> io.write32(
> self.0,
> - <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
> + <B as ::kernel::io::register::RegisterBase<$base>>::BASE + OFFSET
> );
> }
>
> @@ -455,7 +457,7 @@ pub(crate) fn alter<const SIZE: usize, T, B, F>(
> f: F,
> ) where
> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> - B: crate::regs::macros::RegisterBase<$base>,
> + B: ::kernel::io::register::RegisterBase<$base>,
> F: ::core::ops::FnOnce(Self) -> Self,
> {
> let reg = f(Self::read(io, base));
> @@ -600,11 +602,11 @@ pub(crate) fn read<const SIZE: usize, T, B>(
> idx: usize,
> ) -> Self where
> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> - B: crate::regs::macros::RegisterBase<$base>,
> + B: ::kernel::io::register::RegisterBase<$base>,
> {
> build_assert!(idx < Self::SIZE);
>
> - let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +
> + let offset = <B as ::kernel::io::register::RegisterBase<$base>>::BASE +
> Self::OFFSET + (idx * Self::STRIDE);
> let value = io.read32(offset);
>
> @@ -622,11 +624,11 @@ pub(crate) fn write<const SIZE: usize, T, B>(
> idx: usize
> ) where
> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> - B: crate::regs::macros::RegisterBase<$base>,
> + B: ::kernel::io::register::RegisterBase<$base>,
> {
> build_assert!(idx < Self::SIZE);
>
> - let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +
> + let offset = <B as ::kernel::io::register::RegisterBase<$base>>::BASE +
> Self::OFFSET + (idx * Self::STRIDE);
>
> io.write32(self.0, offset);
> @@ -643,7 +645,7 @@ pub(crate) fn alter<const SIZE: usize, T, B, F>(
> f: F,
> ) where
> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> - B: crate::regs::macros::RegisterBase<$base>,
> + B: ::kernel::io::register::RegisterBase<$base>,
> F: ::core::ops::FnOnce(Self) -> Self,
> {
> let reg = f(Self::read(io, base, idx));
> @@ -662,7 +664,7 @@ pub(crate) fn try_read<const SIZE: usize, T, B>(
> idx: usize,
> ) -> ::kernel::error::Result<Self> where
> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> - B: crate::regs::macros::RegisterBase<$base>,
> + B: ::kernel::io::register::RegisterBase<$base>,
> {
> if idx < Self::SIZE {
> Ok(Self::read(io, base, idx))
> @@ -684,7 +686,7 @@ pub(crate) fn try_write<const SIZE: usize, T, B>(
> idx: usize,
> ) -> ::kernel::error::Result where
> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> - B: crate::regs::macros::RegisterBase<$base>,
> + B: ::kernel::io::register::RegisterBase<$base>,
> {
> if idx < Self::SIZE {
> Ok(self.write(io, base, idx))
> @@ -707,7 +709,7 @@ pub(crate) fn try_alter<const SIZE: usize, T, B, F>(
> f: F,
> ) -> ::kernel::error::Result where
> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> - B: crate::regs::macros::RegisterBase<$base>,
> + B: ::kernel::io::register::RegisterBase<$base>,
> F: ::core::ops::FnOnce(Self) -> Self,
> {
> if idx < Self::SIZE {
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index fcffc3988a90..8f8260090c02 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -63,6 +63,7 @@
> pub mod alloc;
> #[cfg(CONFIG_AUXILIARY_BUS)]
> pub mod auxiliary;
> +pub mod bitfield;
> pub mod bits;
> #[cfg(CONFIG_BLOCK)]
> pub mod block;
> --
> 2.34.1
>
>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova
2025-10-22 18:40 ` Beata Michalska
@ 2025-10-22 19:37 ` Joel Fernandes
2025-10-23 13:55 ` Beata Michalska
0 siblings, 1 reply; 41+ messages in thread
From: Joel Fernandes @ 2025-10-22 19:37 UTC (permalink / raw)
To: Beata Michalska
Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
dri-devel@lists.freedesktop.org, dakr@kernel.org,
Alexandre Courbot, Alistair Popple, Miguel Ojeda, Alex Gaynor,
Boqun Feng, Gary Guo, bjorn3_gh@protonmail.com, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, John Hubbard, Timur Tabi,
joel@joelfernandes.org, Elle Rhumsaa, Yury Norov, Daniel Almeida,
Andrea Righi, nouveau@lists.freedesktop.org
Hi Beata,
> On Oct 22, 2025, at 2:41 PM, Beata Michalska <beata.michalska@arm.com> wrote:
>
> Hi Joel,
>
> I know I'm chiming in a bit late, so apologies for that.
No problem.
>
> The register! macro does seem to be a solid foundation for MMIO register
> definitions, thought there are few points that could be potentially
> [re]considered.
I agree. Just to clarify, Alexandre is the main developer of the register macro. I just
attempted to move the code and made some improvements :). I replied below:
>
> The current design assumes a fixed, compile-time-known MMIO region size.
> It does not cover cases when the region size is known only at runtime.
> I do appreciate that in cases like that, we are loosing all the deliberate
> compile-time checks but it might be necessary to provide support for those as
> well (at some point at least).
Sure that could be useful if you have a use case.
>
> On the (potential) improvement side:
>
> Allowing offsets to be expressions rather than literals would make the macro
> easier to use for regions defined at a fixed base offset, where subsequent
> offsets are derived from that base, i.e:
>
> REG_1_BASE -> 0x100
> REG_1_STATUS -> REG_1_BASE + 0x0
> REG_1_CONTROL -> REG_1_BASE + 0x04
This is already possible with the register macro using relative-registers (RegisterBase) right?
> ...
>
> The alias mechanism is a nice touch. It might be worth allowing arrays of
> registers with explicit aliases to be defined in a single macro invocation,
> instead of repeating similar definitions, smth along the lines of:
>
> register!(
> REG_STATUS @ 0x300[8; STRIDE] {
> 0:0 enabled as bool;
> 3:1 mode as u8;
> 7:4 flags as u8;
> }
> aliases {
> REG_STATUS_ENABLED[0] {
> 0:0 enabled as bool;
> }
> REG_STATUS_MODE[0] {
> 3:1 mode as u8;
> }
> REG_STATUS_FLAGS[4] {
> 7:4 flags as u8;
> }
> }
The aliasing might be better do embed as syntax in the Bitfield itself,
instead of additional aliases{} blocks.
By the way, array of registers is also supported already as you may know.
> );
>
>
> Finally, for runtime values such as indexes, it could be useful to verify once
> and then allow infallible reads/writes through some kind access token.
Why? The verification is already done at compile-time AFAICS.
> That might make runtime-safe access patterns simpler and more efficient.
Because it is compile-time, it is already runtime efficient :)
> I'm still pondering on how that could look like though (implementation-wise)
Patches welcomed! For now this still lives in nova-core and Alex is working
on adding support for BoundedInt after which we can move it out.
Thanks,
- Joel
> ---
> BR
> Beata
>
>> On Fri, Oct 03, 2025 at 11:47:47AM -0400, Joel Fernandes wrote:
>> Out of broad need for the register and bitfield macros in Rust, move
>> them out of nova into the kernel crate. Several usecases need them (Nova
>> is already using these and Tyr developers said they need them).
>>
>> bitfield moved into kernel crate - defines bitfields in Rust.
>> register moved into io module - defines hardware registers and accessors.
>>
>> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
>> Reviewed-by: Elle Rhumsaa <elle@weathered-steel.dev>
>> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
>> ---
>> drivers/gpu/nova-core/falcon.rs | 2 +-
>> drivers/gpu/nova-core/falcon/gsp.rs | 4 +-
>> drivers/gpu/nova-core/falcon/sec2.rs | 2 +-
>> drivers/gpu/nova-core/nova_core.rs | 3 -
>> drivers/gpu/nova-core/regs.rs | 6 +-
>> .../gpu/nova-core => rust/kernel}/bitfield.rs | 27 ++++-----
>> rust/kernel/io.rs | 1 +
>> .../macros.rs => rust/kernel/io/register.rs | 58 ++++++++++---------
>> rust/kernel/lib.rs | 1 +
>> 9 files changed, 54 insertions(+), 50 deletions(-)
>> rename {drivers/gpu/nova-core => rust/kernel}/bitfield.rs (91%)
>> rename drivers/gpu/nova-core/regs/macros.rs => rust/kernel/io/register.rs (93%)
>>
>> diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
>> index 37e6298195e4..a15fa98c8614 100644
>> --- a/drivers/gpu/nova-core/falcon.rs
>> +++ b/drivers/gpu/nova-core/falcon.rs
>> @@ -6,6 +6,7 @@
>> use hal::FalconHal;
>> use kernel::device;
>> use kernel::dma::DmaAddress;
>> +use kernel::io::register::RegisterBase;
>> use kernel::prelude::*;
>> use kernel::sync::aref::ARef;
>> use kernel::time::Delta;
>> @@ -14,7 +15,6 @@
>> use crate::driver::Bar0;
>> use crate::gpu::Chipset;
>> use crate::regs;
>> -use crate::regs::macros::RegisterBase;
>> use crate::util;
>>
>> pub(crate) mod gsp;
>> diff --git a/drivers/gpu/nova-core/falcon/gsp.rs b/drivers/gpu/nova-core/falcon/gsp.rs
>> index f17599cb49fa..cd4960e997c8 100644
>> --- a/drivers/gpu/nova-core/falcon/gsp.rs
>> +++ b/drivers/gpu/nova-core/falcon/gsp.rs
>> @@ -1,9 +1,11 @@
>> // SPDX-License-Identifier: GPL-2.0
>>
>> +use kernel::io::register::RegisterBase;
>> +
>> use crate::{
>> driver::Bar0,
>> falcon::{Falcon, FalconEngine, PFalcon2Base, PFalconBase},
>> - regs::{self, macros::RegisterBase},
>> + regs::self,
>> };
>>
>> /// Type specifying the `Gsp` falcon engine. Cannot be instantiated.
>> diff --git a/drivers/gpu/nova-core/falcon/sec2.rs b/drivers/gpu/nova-core/falcon/sec2.rs
>> index 815786c8480d..81717868a8a8 100644
>> --- a/drivers/gpu/nova-core/falcon/sec2.rs
>> +++ b/drivers/gpu/nova-core/falcon/sec2.rs
>> @@ -1,7 +1,7 @@
>> // SPDX-License-Identifier: GPL-2.0
>>
>> use crate::falcon::{FalconEngine, PFalcon2Base, PFalconBase};
>> -use crate::regs::macros::RegisterBase;
>> +use kernel::io::register::RegisterBase;
>>
>> /// Type specifying the `Sec2` falcon engine. Cannot be instantiated.
>> pub(crate) struct Sec2(());
>> diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
>> index 112277c7921e..fffcaee2249f 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 206dab2e1335..1f08e6d4045a 100644
>> --- a/drivers/gpu/nova-core/regs.rs
>> +++ b/drivers/gpu/nova-core/regs.rs
>> @@ -4,15 +4,13 @@
>> // but are mapped to types.
>> #![allow(non_camel_case_types)]
>>
>> -#[macro_use]
>> -pub(crate) mod macros;
>> -
>> use crate::falcon::{
>> DmaTrfCmdSize, FalconCoreRev, FalconCoreRevSubversion, FalconFbifMemType, FalconFbifTarget,
>> FalconModSelAlgo, FalconSecurityModel, PFalcon2Base, PFalconBase, PeregrineCoreSelect,
>> };
>> use crate::gpu::{Architecture, Chipset};
>> use kernel::prelude::*;
>> +use kernel::register;
>>
>> // PMC
>>
>> @@ -331,6 +329,7 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
>>
>> pub(crate) mod gm107 {
>> // FUSE
>> + use kernel::register;
>>
>> register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00021c04 {
>> 0:0 display_disabled as bool;
>> @@ -339,6 +338,7 @@ pub(crate) mod gm107 {
>>
>> pub(crate) mod ga100 {
>> // FUSE
>> + use kernel::register;
>>
>> register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00820c04 {
>> 0:0 display_disabled as bool;
>> diff --git a/drivers/gpu/nova-core/bitfield.rs b/rust/kernel/bitfield.rs
>> similarity index 91%
>> rename from drivers/gpu/nova-core/bitfield.rs
>> rename to rust/kernel/bitfield.rs
>> index cbedbb0078f6..09cd5741598c 100644
>> --- a/drivers/gpu/nova-core/bitfield.rs
>> +++ b/rust/kernel/bitfield.rs
>> @@ -9,7 +9,7 @@
>> /// # Syntax
>> ///
>> /// ```rust
>> -/// use nova_core::bitfield;
>> +/// use kernel::bitfield;
>> ///
>> /// #[derive(Debug, Clone, Copy, Default)]
>> /// enum Mode {
>> @@ -82,10 +82,11 @@
>> /// 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_export]
>> 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)* });
>> + ::kernel::bitfield!(@core $vis $name $storage $(, $comment)? { $($fields)* });
>> };
>>
>> // All rules below are helpers.
>> @@ -114,7 +115,7 @@ fn from(val: $name) -> $storage {
>> }
>> }
>>
>> - bitfield!(@fields_dispatcher $vis $name $storage { $($fields)* });
>> + ::kernel::bitfield!(@fields_dispatcher $vis $name $storage { $($fields)* });
>> };
>>
>> // Captures the fields and passes them to all the implementers that require field information.
>> @@ -130,7 +131,7 @@ fn from(val: $name) -> $storage {
>> )*
>> }
>> ) => {
>> - bitfield!(@field_accessors $vis $name $storage {
>> + ::kernel::bitfield!(@field_accessors $vis $name $storage {
>> $(
>> $hi:$lo $field as $type
>> $(?=> $try_into_type)?
>> @@ -139,8 +140,8 @@ fn from(val: $name) -> $storage {
>> ;
>> )*
>> });
>> - bitfield!(@debug $name { $($field;)* });
>> - bitfield!(@default $name { $($field;)* });
>> + ::kernel::bitfield!(@debug $name { $($field;)* });
>> + ::kernel::bitfield!(@default $name { $($field;)* });
>> };
>>
>> // Defines all the field getter/setter methods for `$name`.
>> @@ -155,13 +156,13 @@ fn from(val: $name) -> $storage {
>> }
>> ) => {
>> $(
>> - bitfield!(@check_field_bounds $hi:$lo $field as $type);
>> + ::kernel::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
>> + ::kernel::bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field as $type
>> $(?=> $try_into_type)?
>> $(=> $into_type)?
>> $(, $comment)?
>> @@ -198,7 +199,7 @@ impl $name {
>> @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
>> $(, $comment:literal)?;
>> ) => {
>> - bitfield!(
>> + ::kernel::bitfield!(
>> @leaf_accessor $vis $name $storage, $hi:$lo $field
>> { |f| <$into_type>::from(if f != 0 { true } else { false }) }
>> $into_type => $into_type $(, $comment)?;
>> @@ -209,7 +210,7 @@ impl $name {
>> (
>> @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)?;);
>> + ::kernel::bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field as bool => bool $(, $comment)?;);
>> };
>>
>> // Catches the `?=>` syntax for non-boolean fields.
>> @@ -217,7 +218,7 @@ impl $name {
>> @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
>> + ::kernel::bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field
>> { |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>
>> ::core::result::Result<
>> $try_into_type,
>> @@ -231,7 +232,7 @@ impl $name {
>> @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
>> + ::kernel::bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field
>> { |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
>> };
>>
>> @@ -240,7 +241,7 @@ impl $name {
>> @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)?;);
>> + ::kernel::bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field as $type => $type $(, $comment)?;);
>> };
>>
>> // Generates the accessor methods for a single field.
>> diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
>> index 03b467722b86..a79b603604b1 100644
>> --- a/rust/kernel/io.rs
>> +++ b/rust/kernel/io.rs
>> @@ -8,6 +8,7 @@
>> use crate::{bindings, build_assert, ffi::c_void};
>>
>> pub mod mem;
>> +pub mod register;
>> pub mod resource;
>>
>> pub use resource::Resource;
>> diff --git a/drivers/gpu/nova-core/regs/macros.rs b/rust/kernel/io/register.rs
>> similarity index 93%
>> rename from drivers/gpu/nova-core/regs/macros.rs
>> rename to rust/kernel/io/register.rs
>> index c0a5194e8d97..c24d956f122f 100644
>> --- a/drivers/gpu/nova-core/regs/macros.rs
>> +++ b/rust/kernel/io/register.rs
>> @@ -17,7 +17,8 @@
>> /// 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> {
>> +pub trait RegisterBase<T> {
>> + /// The base address for the register.
>> const BASE: usize;
>> }
>>
>> @@ -26,7 +27,7 @@ pub(crate) trait RegisterBase<T> {
>> ///
>> /// Example:
>> ///
>> -/// ```no_run
>> +/// ```ignore
>> /// 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";
>> @@ -39,7 +40,7 @@ pub(crate) trait RegisterBase<T> {
>> /// significant bits of the register. Each field can be accessed and modified using accessor
>> /// methods:
>> ///
>> -/// ```no_run
>> +/// ```ignore
>> /// // Read from the register's defined offset (0x100).
>> /// let boot0 = BOOT_0::read(&bar);
>> /// pr_info!("chip revision: {}.{}", boot0.major_revision(), boot0.minor_revision());
>> @@ -61,7 +62,7 @@ pub(crate) trait RegisterBase<T> {
>> /// 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
>> +/// ```ignore
>> /// register!(SCRATCH @ 0x00000200, "Scratch register" {
>> /// 31:0 value as u32, "Raw value";
>> /// });
>> @@ -111,7 +112,7 @@ pub(crate) trait RegisterBase<T> {
>> /// this register needs to implement `RegisterBase<Base>`. Here is the above example translated
>> /// into code:
>> ///
>> -/// ```no_run
>> +/// ```ignore
>> /// // Type used to identify the base.
>> /// pub(crate) struct CpuCtlBase;
>> ///
>> @@ -162,7 +163,7 @@ pub(crate) trait RegisterBase<T> {
>> /// compile-time or runtime bound checking. Simply define their address as `Address[Size]`, and add
>> /// an `idx` parameter to their `read`, `write` and `alter` methods:
>> ///
>> -/// ```no_run
>> +/// ```ignore
>> /// # fn no_run() -> Result<(), Error> {
>> /// # fn get_scratch_idx() -> usize {
>> /// # 0x15
>> @@ -211,7 +212,7 @@ pub(crate) trait RegisterBase<T> {
>> /// Combining the two features described in the sections above, arrays of registers accessible from
>> /// a base can also be defined:
>> ///
>> -/// ```no_run
>> +/// ```ignore
>> /// # fn no_run() -> Result<(), Error> {
>> /// # fn get_scratch_idx() -> usize {
>> /// # 0x15
>> @@ -273,28 +274,29 @@ pub(crate) trait RegisterBase<T> {
>> /// # Ok(())
>> /// # }
>> /// ```
>> +#[macro_export]
>> 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)* } );
>> + ::kernel::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)* } );
>> + ::kernel::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)* } );
>> + ::kernel::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)* } );
>> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
>> register!(@io_relative $name @ $base [ $alias::OFFSET ]);
>> };
>>
>> @@ -305,7 +307,7 @@ macro_rules! register {
>> }
>> ) => {
>> static_assert!(::core::mem::size_of::<u32>() <= $stride);
>> - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
>> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
>> register!(@io_array $name @ $offset [ $size ; $stride ]);
>> };
>>
>> @@ -326,7 +328,7 @@ macro_rules! register {
>> $(, $comment:literal)? { $($fields:tt)* }
>> ) => {
>> static_assert!(::core::mem::size_of::<u32>() <= $stride);
>> - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
>> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
>> register!(@io_relative_array $name @ $base [ $offset [ $size ; $stride ] ]);
>> };
>>
>> @@ -348,7 +350,7 @@ macro_rules! register {
>> }
>> ) => {
>> static_assert!($idx < $alias::SIZE);
>> - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
>> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
>> register!(@io_relative $name @ $base [ $alias::OFFSET + $idx * $alias::STRIDE ] );
>> };
>>
>> @@ -357,7 +359,7 @@ macro_rules! register {
>> // 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)* } );
>> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
>> register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE );
>> };
>>
>> @@ -414,12 +416,12 @@ pub(crate) fn read<const SIZE: usize, T, B>(
>> base: &B,
>> ) -> Self where
>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
>> - B: crate::regs::macros::RegisterBase<$base>,
>> + B: ::kernel::io::register::RegisterBase<$base>,
>> {
>> const OFFSET: usize = $name::OFFSET;
>>
>> let value = io.read32(
>> - <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
>> + <B as ::kernel::io::register::RegisterBase<$base>>::BASE + OFFSET
>> );
>>
>> Self(value)
>> @@ -435,13 +437,13 @@ pub(crate) fn write<const SIZE: usize, T, B>(
>> base: &B,
>> ) where
>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
>> - B: crate::regs::macros::RegisterBase<$base>,
>> + B: ::kernel::io::register::RegisterBase<$base>,
>> {
>> const OFFSET: usize = $name::OFFSET;
>>
>> io.write32(
>> self.0,
>> - <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
>> + <B as ::kernel::io::register::RegisterBase<$base>>::BASE + OFFSET
>> );
>> }
>>
>> @@ -455,7 +457,7 @@ pub(crate) fn alter<const SIZE: usize, T, B, F>(
>> f: F,
>> ) where
>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
>> - B: crate::regs::macros::RegisterBase<$base>,
>> + B: ::kernel::io::register::RegisterBase<$base>,
>> F: ::core::ops::FnOnce(Self) -> Self,
>> {
>> let reg = f(Self::read(io, base));
>> @@ -600,11 +602,11 @@ pub(crate) fn read<const SIZE: usize, T, B>(
>> idx: usize,
>> ) -> Self where
>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
>> - B: crate::regs::macros::RegisterBase<$base>,
>> + B: ::kernel::io::register::RegisterBase<$base>,
>> {
>> build_assert!(idx < Self::SIZE);
>>
>> - let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +
>> + let offset = <B as ::kernel::io::register::RegisterBase<$base>>::BASE +
>> Self::OFFSET + (idx * Self::STRIDE);
>> let value = io.read32(offset);
>>
>> @@ -622,11 +624,11 @@ pub(crate) fn write<const SIZE: usize, T, B>(
>> idx: usize
>> ) where
>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
>> - B: crate::regs::macros::RegisterBase<$base>,
>> + B: ::kernel::io::register::RegisterBase<$base>,
>> {
>> build_assert!(idx < Self::SIZE);
>>
>> - let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +
>> + let offset = <B as ::kernel::io::register::RegisterBase<$base>>::BASE +
>> Self::OFFSET + (idx * Self::STRIDE);
>>
>> io.write32(self.0, offset);
>> @@ -643,7 +645,7 @@ pub(crate) fn alter<const SIZE: usize, T, B, F>(
>> f: F,
>> ) where
>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
>> - B: crate::regs::macros::RegisterBase<$base>,
>> + B: ::kernel::io::register::RegisterBase<$base>,
>> F: ::core::ops::FnOnce(Self) -> Self,
>> {
>> let reg = f(Self::read(io, base, idx));
>> @@ -662,7 +664,7 @@ pub(crate) fn try_read<const SIZE: usize, T, B>(
>> idx: usize,
>> ) -> ::kernel::error::Result<Self> where
>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
>> - B: crate::regs::macros::RegisterBase<$base>,
>> + B: ::kernel::io::register::RegisterBase<$base>,
>> {
>> if idx < Self::SIZE {
>> Ok(Self::read(io, base, idx))
>> @@ -684,7 +686,7 @@ pub(crate) fn try_write<const SIZE: usize, T, B>(
>> idx: usize,
>> ) -> ::kernel::error::Result where
>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
>> - B: crate::regs::macros::RegisterBase<$base>,
>> + B: ::kernel::io::register::RegisterBase<$base>,
>> {
>> if idx < Self::SIZE {
>> Ok(self.write(io, base, idx))
>> @@ -707,7 +709,7 @@ pub(crate) fn try_alter<const SIZE: usize, T, B, F>(
>> f: F,
>> ) -> ::kernel::error::Result where
>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
>> - B: crate::regs::macros::RegisterBase<$base>,
>> + B: ::kernel::io::register::RegisterBase<$base>,
>> F: ::core::ops::FnOnce(Self) -> Self,
>> {
>> if idx < Self::SIZE {
>> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
>> index fcffc3988a90..8f8260090c02 100644
>> --- a/rust/kernel/lib.rs
>> +++ b/rust/kernel/lib.rs
>> @@ -63,6 +63,7 @@
>> pub mod alloc;
>> #[cfg(CONFIG_AUXILIARY_BUS)]
>> pub mod auxiliary;
>> +pub mod bitfield;
>> pub mod bits;
>> #[cfg(CONFIG_BLOCK)]
>> pub mod block;
>> --
>> 2.34.1
>>
>>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova
2025-10-22 19:37 ` Joel Fernandes
@ 2025-10-23 13:55 ` Beata Michalska
2025-10-23 14:07 ` Danilo Krummrich
2025-10-23 21:47 ` Joel Fernandes
0 siblings, 2 replies; 41+ messages in thread
From: Beata Michalska @ 2025-10-23 13:55 UTC (permalink / raw)
To: Joel Fernandes
Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
dri-devel@lists.freedesktop.org, dakr@kernel.org,
Alexandre Courbot, Alistair Popple, Miguel Ojeda, Alex Gaynor,
Boqun Feng, Gary Guo, bjorn3_gh@protonmail.com, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, John Hubbard, Timur Tabi,
joel@joelfernandes.org, Elle Rhumsaa, Yury Norov, Daniel Almeida,
Andrea Righi, nouveau@lists.freedesktop.org
On Wed, Oct 22, 2025 at 07:37:55PM +0000, Joel Fernandes wrote:
> Hi Beata,
>
> > On Oct 22, 2025, at 2:41 PM, Beata Michalska <beata.michalska@arm.com> wrote:
> >
> > Hi Joel,
> >
> > I know I'm chiming in a bit late, so apologies for that.
>
> No problem.
>
> >
> > The register! macro does seem to be a solid foundation for MMIO register
> > definitions, thought there are few points that could be potentially
> > [re]considered.
>
> I agree. Just to clarify, Alexandre is the main developer of the register macro. I just
> attempted to move the code and made some improvements :). I replied below:
>
> >
> > The current design assumes a fixed, compile-time-known MMIO region size.
> > It does not cover cases when the region size is known only at runtime.
> > I do appreciate that in cases like that, we are loosing all the deliberate
> > compile-time checks but it might be necessary to provide support for those as
> > well (at some point at least).
>
> Sure that could be useful if you have a use case.
I guess everything that would use IoRequest::iomap(self), which generates
Io<SIZE=0> which is a game over for the macro.
>
> >
> > On the (potential) improvement side:
> >
> > Allowing offsets to be expressions rather than literals would make the macro
> > easier to use for regions defined at a fixed base offset, where subsequent
> > offsets are derived from that base, i.e:
> >
> > REG_1_BASE -> 0x100
> > REG_1_STATUS -> REG_1_BASE + 0x0
> > REG_1_CONTROL -> REG_1_BASE + 0x04
>
> This is already possible with the register macro using relative-registers (RegisterBase) right?
Probably though the use case I had in mind is relative array of registers.
It's fine to use the macro as is for few registers, having a significant number
of those gets cumbersome though. Unless I am misreading things.
>
> > ...
> >
> > The alias mechanism is a nice touch. It might be worth allowing arrays of
> > registers with explicit aliases to be defined in a single macro invocation,
> > instead of repeating similar definitions, smth along the lines of:
> >
> > register!(
> > REG_STATUS @ 0x300[8; STRIDE] {
> > 0:0 enabled as bool;
> > 3:1 mode as u8;
> > 7:4 flags as u8;
> > }
> > aliases {
> > REG_STATUS_ENABLED[0] {
> > 0:0 enabled as bool;
> > }
> > REG_STATUS_MODE[0] {
> > 3:1 mode as u8;
> > }
> > REG_STATUS_FLAGS[4] {
> > 7:4 flags as u8;
> > }
> > }
>
> The aliasing might be better do embed as syntax in the Bitfield itself,
> instead of additional aliases{} blocks.
> By the way, array of registers is also supported already as you may know.
I was referring to aliasing having in mind array of registers.
>
> > );
> >
> >
> > Finally, for runtime values such as indexes, it could be useful to verify once
> > and then allow infallible reads/writes through some kind access token.
>
> Why? The verification is already done at compile-time AFAICS.
Well, that's the point. Those are runtime values, and as of now, the only
support for those is for arrays of registers when one, when using try_xxx
methods, ends up with check being performed each time the method is called.
---
BR
Beata
>
> > That might make runtime-safe access patterns simpler and more efficient.
>
> Because it is compile-time, it is already runtime efficient :)
>
> > I'm still pondering on how that could look like though (implementation-wise)
>
> Patches welcomed! For now this still lives in nova-core and Alex is working
> on adding support for BoundedInt after which we can move it out.
>
> Thanks,
>
> - Joel
>
>
> > ---
> > BR
> > Beata
> >
> >> On Fri, Oct 03, 2025 at 11:47:47AM -0400, Joel Fernandes wrote:
> >> Out of broad need for the register and bitfield macros in Rust, move
> >> them out of nova into the kernel crate. Several usecases need them (Nova
> >> is already using these and Tyr developers said they need them).
> >>
> >> bitfield moved into kernel crate - defines bitfields in Rust.
> >> register moved into io module - defines hardware registers and accessors.
> >>
> >> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
> >> Reviewed-by: Elle Rhumsaa <elle@weathered-steel.dev>
> >> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
> >> ---
> >> drivers/gpu/nova-core/falcon.rs | 2 +-
> >> drivers/gpu/nova-core/falcon/gsp.rs | 4 +-
> >> drivers/gpu/nova-core/falcon/sec2.rs | 2 +-
> >> drivers/gpu/nova-core/nova_core.rs | 3 -
> >> drivers/gpu/nova-core/regs.rs | 6 +-
> >> .../gpu/nova-core => rust/kernel}/bitfield.rs | 27 ++++-----
> >> rust/kernel/io.rs | 1 +
> >> .../macros.rs => rust/kernel/io/register.rs | 58 ++++++++++---------
> >> rust/kernel/lib.rs | 1 +
> >> 9 files changed, 54 insertions(+), 50 deletions(-)
> >> rename {drivers/gpu/nova-core => rust/kernel}/bitfield.rs (91%)
> >> rename drivers/gpu/nova-core/regs/macros.rs => rust/kernel/io/register.rs (93%)
> >>
> >> diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
> >> index 37e6298195e4..a15fa98c8614 100644
> >> --- a/drivers/gpu/nova-core/falcon.rs
> >> +++ b/drivers/gpu/nova-core/falcon.rs
> >> @@ -6,6 +6,7 @@
> >> use hal::FalconHal;
> >> use kernel::device;
> >> use kernel::dma::DmaAddress;
> >> +use kernel::io::register::RegisterBase;
> >> use kernel::prelude::*;
> >> use kernel::sync::aref::ARef;
> >> use kernel::time::Delta;
> >> @@ -14,7 +15,6 @@
> >> use crate::driver::Bar0;
> >> use crate::gpu::Chipset;
> >> use crate::regs;
> >> -use crate::regs::macros::RegisterBase;
> >> use crate::util;
> >>
> >> pub(crate) mod gsp;
> >> diff --git a/drivers/gpu/nova-core/falcon/gsp.rs b/drivers/gpu/nova-core/falcon/gsp.rs
> >> index f17599cb49fa..cd4960e997c8 100644
> >> --- a/drivers/gpu/nova-core/falcon/gsp.rs
> >> +++ b/drivers/gpu/nova-core/falcon/gsp.rs
> >> @@ -1,9 +1,11 @@
> >> // SPDX-License-Identifier: GPL-2.0
> >>
> >> +use kernel::io::register::RegisterBase;
> >> +
> >> use crate::{
> >> driver::Bar0,
> >> falcon::{Falcon, FalconEngine, PFalcon2Base, PFalconBase},
> >> - regs::{self, macros::RegisterBase},
> >> + regs::self,
> >> };
> >>
> >> /// Type specifying the `Gsp` falcon engine. Cannot be instantiated.
> >> diff --git a/drivers/gpu/nova-core/falcon/sec2.rs b/drivers/gpu/nova-core/falcon/sec2.rs
> >> index 815786c8480d..81717868a8a8 100644
> >> --- a/drivers/gpu/nova-core/falcon/sec2.rs
> >> +++ b/drivers/gpu/nova-core/falcon/sec2.rs
> >> @@ -1,7 +1,7 @@
> >> // SPDX-License-Identifier: GPL-2.0
> >>
> >> use crate::falcon::{FalconEngine, PFalcon2Base, PFalconBase};
> >> -use crate::regs::macros::RegisterBase;
> >> +use kernel::io::register::RegisterBase;
> >>
> >> /// Type specifying the `Sec2` falcon engine. Cannot be instantiated.
> >> pub(crate) struct Sec2(());
> >> diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
> >> index 112277c7921e..fffcaee2249f 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 206dab2e1335..1f08e6d4045a 100644
> >> --- a/drivers/gpu/nova-core/regs.rs
> >> +++ b/drivers/gpu/nova-core/regs.rs
> >> @@ -4,15 +4,13 @@
> >> // but are mapped to types.
> >> #![allow(non_camel_case_types)]
> >>
> >> -#[macro_use]
> >> -pub(crate) mod macros;
> >> -
> >> use crate::falcon::{
> >> DmaTrfCmdSize, FalconCoreRev, FalconCoreRevSubversion, FalconFbifMemType, FalconFbifTarget,
> >> FalconModSelAlgo, FalconSecurityModel, PFalcon2Base, PFalconBase, PeregrineCoreSelect,
> >> };
> >> use crate::gpu::{Architecture, Chipset};
> >> use kernel::prelude::*;
> >> +use kernel::register;
> >>
> >> // PMC
> >>
> >> @@ -331,6 +329,7 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
> >>
> >> pub(crate) mod gm107 {
> >> // FUSE
> >> + use kernel::register;
> >>
> >> register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00021c04 {
> >> 0:0 display_disabled as bool;
> >> @@ -339,6 +338,7 @@ pub(crate) mod gm107 {
> >>
> >> pub(crate) mod ga100 {
> >> // FUSE
> >> + use kernel::register;
> >>
> >> register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00820c04 {
> >> 0:0 display_disabled as bool;
> >> diff --git a/drivers/gpu/nova-core/bitfield.rs b/rust/kernel/bitfield.rs
> >> similarity index 91%
> >> rename from drivers/gpu/nova-core/bitfield.rs
> >> rename to rust/kernel/bitfield.rs
> >> index cbedbb0078f6..09cd5741598c 100644
> >> --- a/drivers/gpu/nova-core/bitfield.rs
> >> +++ b/rust/kernel/bitfield.rs
> >> @@ -9,7 +9,7 @@
> >> /// # Syntax
> >> ///
> >> /// ```rust
> >> -/// use nova_core::bitfield;
> >> +/// use kernel::bitfield;
> >> ///
> >> /// #[derive(Debug, Clone, Copy, Default)]
> >> /// enum Mode {
> >> @@ -82,10 +82,11 @@
> >> /// 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_export]
> >> 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)* });
> >> + ::kernel::bitfield!(@core $vis $name $storage $(, $comment)? { $($fields)* });
> >> };
> >>
> >> // All rules below are helpers.
> >> @@ -114,7 +115,7 @@ fn from(val: $name) -> $storage {
> >> }
> >> }
> >>
> >> - bitfield!(@fields_dispatcher $vis $name $storage { $($fields)* });
> >> + ::kernel::bitfield!(@fields_dispatcher $vis $name $storage { $($fields)* });
> >> };
> >>
> >> // Captures the fields and passes them to all the implementers that require field information.
> >> @@ -130,7 +131,7 @@ fn from(val: $name) -> $storage {
> >> )*
> >> }
> >> ) => {
> >> - bitfield!(@field_accessors $vis $name $storage {
> >> + ::kernel::bitfield!(@field_accessors $vis $name $storage {
> >> $(
> >> $hi:$lo $field as $type
> >> $(?=> $try_into_type)?
> >> @@ -139,8 +140,8 @@ fn from(val: $name) -> $storage {
> >> ;
> >> )*
> >> });
> >> - bitfield!(@debug $name { $($field;)* });
> >> - bitfield!(@default $name { $($field;)* });
> >> + ::kernel::bitfield!(@debug $name { $($field;)* });
> >> + ::kernel::bitfield!(@default $name { $($field;)* });
> >> };
> >>
> >> // Defines all the field getter/setter methods for `$name`.
> >> @@ -155,13 +156,13 @@ fn from(val: $name) -> $storage {
> >> }
> >> ) => {
> >> $(
> >> - bitfield!(@check_field_bounds $hi:$lo $field as $type);
> >> + ::kernel::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
> >> + ::kernel::bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field as $type
> >> $(?=> $try_into_type)?
> >> $(=> $into_type)?
> >> $(, $comment)?
> >> @@ -198,7 +199,7 @@ impl $name {
> >> @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
> >> $(, $comment:literal)?;
> >> ) => {
> >> - bitfield!(
> >> + ::kernel::bitfield!(
> >> @leaf_accessor $vis $name $storage, $hi:$lo $field
> >> { |f| <$into_type>::from(if f != 0 { true } else { false }) }
> >> $into_type => $into_type $(, $comment)?;
> >> @@ -209,7 +210,7 @@ impl $name {
> >> (
> >> @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)?;);
> >> + ::kernel::bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field as bool => bool $(, $comment)?;);
> >> };
> >>
> >> // Catches the `?=>` syntax for non-boolean fields.
> >> @@ -217,7 +218,7 @@ impl $name {
> >> @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
> >> + ::kernel::bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field
> >> { |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>
> >> ::core::result::Result<
> >> $try_into_type,
> >> @@ -231,7 +232,7 @@ impl $name {
> >> @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
> >> + ::kernel::bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field
> >> { |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
> >> };
> >>
> >> @@ -240,7 +241,7 @@ impl $name {
> >> @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)?;);
> >> + ::kernel::bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field as $type => $type $(, $comment)?;);
> >> };
> >>
> >> // Generates the accessor methods for a single field.
> >> diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
> >> index 03b467722b86..a79b603604b1 100644
> >> --- a/rust/kernel/io.rs
> >> +++ b/rust/kernel/io.rs
> >> @@ -8,6 +8,7 @@
> >> use crate::{bindings, build_assert, ffi::c_void};
> >>
> >> pub mod mem;
> >> +pub mod register;
> >> pub mod resource;
> >>
> >> pub use resource::Resource;
> >> diff --git a/drivers/gpu/nova-core/regs/macros.rs b/rust/kernel/io/register.rs
> >> similarity index 93%
> >> rename from drivers/gpu/nova-core/regs/macros.rs
> >> rename to rust/kernel/io/register.rs
> >> index c0a5194e8d97..c24d956f122f 100644
> >> --- a/drivers/gpu/nova-core/regs/macros.rs
> >> +++ b/rust/kernel/io/register.rs
> >> @@ -17,7 +17,8 @@
> >> /// 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> {
> >> +pub trait RegisterBase<T> {
> >> + /// The base address for the register.
> >> const BASE: usize;
> >> }
> >>
> >> @@ -26,7 +27,7 @@ pub(crate) trait RegisterBase<T> {
> >> ///
> >> /// Example:
> >> ///
> >> -/// ```no_run
> >> +/// ```ignore
> >> /// 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";
> >> @@ -39,7 +40,7 @@ pub(crate) trait RegisterBase<T> {
> >> /// significant bits of the register. Each field can be accessed and modified using accessor
> >> /// methods:
> >> ///
> >> -/// ```no_run
> >> +/// ```ignore
> >> /// // Read from the register's defined offset (0x100).
> >> /// let boot0 = BOOT_0::read(&bar);
> >> /// pr_info!("chip revision: {}.{}", boot0.major_revision(), boot0.minor_revision());
> >> @@ -61,7 +62,7 @@ pub(crate) trait RegisterBase<T> {
> >> /// 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
> >> +/// ```ignore
> >> /// register!(SCRATCH @ 0x00000200, "Scratch register" {
> >> /// 31:0 value as u32, "Raw value";
> >> /// });
> >> @@ -111,7 +112,7 @@ pub(crate) trait RegisterBase<T> {
> >> /// this register needs to implement `RegisterBase<Base>`. Here is the above example translated
> >> /// into code:
> >> ///
> >> -/// ```no_run
> >> +/// ```ignore
> >> /// // Type used to identify the base.
> >> /// pub(crate) struct CpuCtlBase;
> >> ///
> >> @@ -162,7 +163,7 @@ pub(crate) trait RegisterBase<T> {
> >> /// compile-time or runtime bound checking. Simply define their address as `Address[Size]`, and add
> >> /// an `idx` parameter to their `read`, `write` and `alter` methods:
> >> ///
> >> -/// ```no_run
> >> +/// ```ignore
> >> /// # fn no_run() -> Result<(), Error> {
> >> /// # fn get_scratch_idx() -> usize {
> >> /// # 0x15
> >> @@ -211,7 +212,7 @@ pub(crate) trait RegisterBase<T> {
> >> /// Combining the two features described in the sections above, arrays of registers accessible from
> >> /// a base can also be defined:
> >> ///
> >> -/// ```no_run
> >> +/// ```ignore
> >> /// # fn no_run() -> Result<(), Error> {
> >> /// # fn get_scratch_idx() -> usize {
> >> /// # 0x15
> >> @@ -273,28 +274,29 @@ pub(crate) trait RegisterBase<T> {
> >> /// # Ok(())
> >> /// # }
> >> /// ```
> >> +#[macro_export]
> >> 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)* } );
> >> + ::kernel::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)* } );
> >> + ::kernel::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)* } );
> >> + ::kernel::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)* } );
> >> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
> >> register!(@io_relative $name @ $base [ $alias::OFFSET ]);
> >> };
> >>
> >> @@ -305,7 +307,7 @@ macro_rules! register {
> >> }
> >> ) => {
> >> static_assert!(::core::mem::size_of::<u32>() <= $stride);
> >> - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
> >> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
> >> register!(@io_array $name @ $offset [ $size ; $stride ]);
> >> };
> >>
> >> @@ -326,7 +328,7 @@ macro_rules! register {
> >> $(, $comment:literal)? { $($fields:tt)* }
> >> ) => {
> >> static_assert!(::core::mem::size_of::<u32>() <= $stride);
> >> - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
> >> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
> >> register!(@io_relative_array $name @ $base [ $offset [ $size ; $stride ] ]);
> >> };
> >>
> >> @@ -348,7 +350,7 @@ macro_rules! register {
> >> }
> >> ) => {
> >> static_assert!($idx < $alias::SIZE);
> >> - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
> >> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
> >> register!(@io_relative $name @ $base [ $alias::OFFSET + $idx * $alias::STRIDE ] );
> >> };
> >>
> >> @@ -357,7 +359,7 @@ macro_rules! register {
> >> // 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)* } );
> >> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
> >> register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE );
> >> };
> >>
> >> @@ -414,12 +416,12 @@ pub(crate) fn read<const SIZE: usize, T, B>(
> >> base: &B,
> >> ) -> Self where
> >> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> >> - B: crate::regs::macros::RegisterBase<$base>,
> >> + B: ::kernel::io::register::RegisterBase<$base>,
> >> {
> >> const OFFSET: usize = $name::OFFSET;
> >>
> >> let value = io.read32(
> >> - <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
> >> + <B as ::kernel::io::register::RegisterBase<$base>>::BASE + OFFSET
> >> );
> >>
> >> Self(value)
> >> @@ -435,13 +437,13 @@ pub(crate) fn write<const SIZE: usize, T, B>(
> >> base: &B,
> >> ) where
> >> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> >> - B: crate::regs::macros::RegisterBase<$base>,
> >> + B: ::kernel::io::register::RegisterBase<$base>,
> >> {
> >> const OFFSET: usize = $name::OFFSET;
> >>
> >> io.write32(
> >> self.0,
> >> - <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
> >> + <B as ::kernel::io::register::RegisterBase<$base>>::BASE + OFFSET
> >> );
> >> }
> >>
> >> @@ -455,7 +457,7 @@ pub(crate) fn alter<const SIZE: usize, T, B, F>(
> >> f: F,
> >> ) where
> >> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> >> - B: crate::regs::macros::RegisterBase<$base>,
> >> + B: ::kernel::io::register::RegisterBase<$base>,
> >> F: ::core::ops::FnOnce(Self) -> Self,
> >> {
> >> let reg = f(Self::read(io, base));
> >> @@ -600,11 +602,11 @@ pub(crate) fn read<const SIZE: usize, T, B>(
> >> idx: usize,
> >> ) -> Self where
> >> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> >> - B: crate::regs::macros::RegisterBase<$base>,
> >> + B: ::kernel::io::register::RegisterBase<$base>,
> >> {
> >> build_assert!(idx < Self::SIZE);
> >>
> >> - let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +
> >> + let offset = <B as ::kernel::io::register::RegisterBase<$base>>::BASE +
> >> Self::OFFSET + (idx * Self::STRIDE);
> >> let value = io.read32(offset);
> >>
> >> @@ -622,11 +624,11 @@ pub(crate) fn write<const SIZE: usize, T, B>(
> >> idx: usize
> >> ) where
> >> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> >> - B: crate::regs::macros::RegisterBase<$base>,
> >> + B: ::kernel::io::register::RegisterBase<$base>,
> >> {
> >> build_assert!(idx < Self::SIZE);
> >>
> >> - let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +
> >> + let offset = <B as ::kernel::io::register::RegisterBase<$base>>::BASE +
> >> Self::OFFSET + (idx * Self::STRIDE);
> >>
> >> io.write32(self.0, offset);
> >> @@ -643,7 +645,7 @@ pub(crate) fn alter<const SIZE: usize, T, B, F>(
> >> f: F,
> >> ) where
> >> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> >> - B: crate::regs::macros::RegisterBase<$base>,
> >> + B: ::kernel::io::register::RegisterBase<$base>,
> >> F: ::core::ops::FnOnce(Self) -> Self,
> >> {
> >> let reg = f(Self::read(io, base, idx));
> >> @@ -662,7 +664,7 @@ pub(crate) fn try_read<const SIZE: usize, T, B>(
> >> idx: usize,
> >> ) -> ::kernel::error::Result<Self> where
> >> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> >> - B: crate::regs::macros::RegisterBase<$base>,
> >> + B: ::kernel::io::register::RegisterBase<$base>,
> >> {
> >> if idx < Self::SIZE {
> >> Ok(Self::read(io, base, idx))
> >> @@ -684,7 +686,7 @@ pub(crate) fn try_write<const SIZE: usize, T, B>(
> >> idx: usize,
> >> ) -> ::kernel::error::Result where
> >> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> >> - B: crate::regs::macros::RegisterBase<$base>,
> >> + B: ::kernel::io::register::RegisterBase<$base>,
> >> {
> >> if idx < Self::SIZE {
> >> Ok(self.write(io, base, idx))
> >> @@ -707,7 +709,7 @@ pub(crate) fn try_alter<const SIZE: usize, T, B, F>(
> >> f: F,
> >> ) -> ::kernel::error::Result where
> >> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> >> - B: crate::regs::macros::RegisterBase<$base>,
> >> + B: ::kernel::io::register::RegisterBase<$base>,
> >> F: ::core::ops::FnOnce(Self) -> Self,
> >> {
> >> if idx < Self::SIZE {
> >> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> >> index fcffc3988a90..8f8260090c02 100644
> >> --- a/rust/kernel/lib.rs
> >> +++ b/rust/kernel/lib.rs
> >> @@ -63,6 +63,7 @@
> >> pub mod alloc;
> >> #[cfg(CONFIG_AUXILIARY_BUS)]
> >> pub mod auxiliary;
> >> +pub mod bitfield;
> >> pub mod bits;
> >> #[cfg(CONFIG_BLOCK)]
> >> pub mod block;
> >> --
> >> 2.34.1
> >>
> >>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova
2025-10-23 13:55 ` Beata Michalska
@ 2025-10-23 14:07 ` Danilo Krummrich
2025-10-23 21:47 ` Joel Fernandes
1 sibling, 0 replies; 41+ messages in thread
From: Danilo Krummrich @ 2025-10-23 14:07 UTC (permalink / raw)
To: Beata Michalska
Cc: Joel Fernandes, linux-kernel@vger.kernel.org,
rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org,
Alexandre Courbot, Alistair Popple, Miguel Ojeda, Alex Gaynor,
Boqun Feng, Gary Guo, bjorn3_gh@protonmail.com, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, John Hubbard, Timur Tabi,
joel@joelfernandes.org, Elle Rhumsaa, Yury Norov, Daniel Almeida,
Andrea Righi, nouveau@lists.freedesktop.org
On Thu Oct 23, 2025 at 3:55 PM CEST, Beata Michalska wrote:
> I guess everything that would use IoRequest::iomap(self), which generates
> Io<SIZE=0> which is a game over for the macro.
To be honest, we may just want to remove this API. A driver that requires a zero
sized MMIO region with only optional runtime derived access offsets seems a bit
too exotic.
That doesn't mean that occasionally we'll have drivers needing runtime offsets,
so eventually we want to support that with the register!() macro, but it's not
*that* urgent.
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova
2025-10-23 13:55 ` Beata Michalska
2025-10-23 14:07 ` Danilo Krummrich
@ 2025-10-23 21:47 ` Joel Fernandes
2025-10-23 21:50 ` Joel Fernandes
2025-10-27 9:06 ` Beata Michalska
1 sibling, 2 replies; 41+ messages in thread
From: Joel Fernandes @ 2025-10-23 21:47 UTC (permalink / raw)
To: Beata Michalska
Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
dri-devel@lists.freedesktop.org, dakr@kernel.org,
Alexandre Courbot, Alistair Popple, Miguel Ojeda, Alex Gaynor,
Boqun Feng, Gary Guo, bjorn3_gh@protonmail.com, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, John Hubbard, Timur Tabi,
joel@joelfernandes.org, Elle Rhumsaa, Yury Norov, Daniel Almeida,
Andrea Righi, nouveau@lists.freedesktop.org
Hi Beata,
> On Oct 23, 2025, at 9:55 AM, Beata Michalska <beata.michalska@arm.com> wrote:
[...]
>>>
>>> The current design assumes a fixed, compile-time-known MMIO region size.
>>> It does not cover cases when the region size is known only at runtime.
>>> I do appreciate that in cases like that, we are loosing all the deliberate
>>> compile-time checks but it might be necessary to provide support for those
>>> as well (at some point at least).
>>
>> Sure that could be useful if you have a use case.
> I guess everything that would use IoRequest::iomap(self), which generates
> Io<SIZE=0> which is a game over for the macro.
I am curious what is your usecase for this such that size of the IO region
cannot be know until runtime, can you share? It should also be possible at
runtime to use the correct type, based on the IO region size IMO. The correct
type can encode the required size.
>>
>>>
>>> On the (potential) improvement side:
>>>
>>> Allowing offsets to be expressions rather than literals would make the macro
>>> easier to use for regions defined at a fixed base offset, where subsequent
>>> offsets are derived from that base, i.e:
>>>
>>> REG_1_BASE -> 0x100
>>> REG_1_STATUS -> REG_1_BASE + 0x0
>>> REG_1_CONTROL -> REG_1_BASE + 0x04
>>
>> This is already possible with the register macro using relative-registers
>> (RegisterBase) right?
>
> Probably though the use case I had in mind is relative array of registers.
> It's fine to use the macro as is for few registers, having a significant
> number of those gets cumbersome though. Unless I am misreading things.
I am not sure it is cumbersome. The relative register syntax should be able to
support a larger number of registers. Could you share an example to describe the
issue with RegisterBase vs with your proposed syntax?
>>> The alias mechanism is a nice touch. It might be worth allowing arrays of
>>> registers with explicit aliases to be defined in a single macro invocation,
>>> instead of repeating similar definitions, smth along the lines of:
>>>
>>> register!(
>>> REG_STATUS @ 0x300[8; STRIDE] {
>>> 0:0 enabled as bool;
>>> 3:1 mode as u8;
>>> 7:4 flags as u8;
>>> }
>>> aliases {
>>> REG_STATUS_ENABLED[0] {
>>> 0:0 enabled as bool;
>>> }
>>> REG_STATUS_MODE[0] {
>>> 3:1 mode as u8;
>>> }
>>> REG_STATUS_FLAGS[4] {
>>> 7:4 flags as u8;
>>> }
>>> }
>>
>> The aliasing might be better do embed as syntax in the Bitfield itself,
>> instead of additional aliases{} blocks.
>> By the way, array of registers is also supported already as you may know.
> I was referring to aliasing having in mind array of registers.
>
AFAICS, either way you will still have a large number of alias definitions.
It might be better to invoke register macro explicitly for each alias IMO.
By the way, I do not follow your example because the names of the aliases in it
have the names of specific fields embedded in them. Whereas the register macro
aliases feature aliases the whole register, not specific fields, right?
>>> Finally, for runtime values such as indexes, it could be useful to verify
>>> once and then allow infallible reads/writes through some kind access token.
>>
>> Why? The verification is already done at compile-time AFAICS.
>
> Well, that's the point. Those are runtime values, and as of now, the only
> support for those is for arrays of registers when one, when using try_xxx
> methods, ends up with check being performed each time the method is called.
Ah for this part of your email, you are referring to try accessors. For the
fixed sizes regions at least, to avoid the runtime check, it will be possible to
accept BoundedInt [1] in the future. That type actually came up for the exact
same reason (keeping the checking light). This cleverly moves the checking to
the caller side which could be done in a slow path. If the size of the IO region
is fixed, then you don’t need to use try accessors at all if you use BoundedInt
whenever we have it.
thanks,
- Joel
[1] https://lore.kernel.org/all/20251009-bounded_ints-v2-0-ff3d7fee3ffd@nvidia.com/
>
> ---
> BR
> Beata
>>
>>> That might make runtime-safe access patterns simpler and more efficient.
>>
>> Because it is compile-time, it is already runtime efficient :)
>>
>>> I'm still pondering on how that could look like though (implementation-wise)
>>
>> Patches welcomed! For now this still lives in nova-core and Alex is working
>> on adding support for BoundedInt after which we can move it out.
>>
>> Thanks,
>>
>> - Joel
>>
>>
>>> ---
>>> BR
>>> Beata
>>>
>>>> On Fri, Oct 03, 2025 at 11:47:47AM -0400, Joel Fernandes wrote:
>>>> Out of broad need for the register and bitfield macros in Rust, move
>>>> them out of nova into the kernel crate. Several usecases need them (Nova
>>>> is already using these and Tyr developers said they need them).
>>>>
>>>> bitfield moved into kernel crate - defines bitfields in Rust.
>>>> register moved into io module - defines hardware registers and accessors.
>>>>
>>>> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
>>>> Reviewed-by: Elle Rhumsaa <elle@weathered-steel.dev>
>>>> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
>>>> ---
>>>> drivers/gpu/nova-core/falcon.rs | 2 +-
>>>> drivers/gpu/nova-core/falcon/gsp.rs | 4 +-
>>>> drivers/gpu/nova-core/falcon/sec2.rs | 2 +-
>>>> drivers/gpu/nova-core/nova_core.rs | 3 -
>>>> drivers/gpu/nova-core/regs.rs | 6 +-
>>>> .../gpu/nova-core => rust/kernel}/bitfield.rs | 27 ++++-----
>>>> rust/kernel/io.rs | 1 +
>>>> .../macros.rs => rust/kernel/io/register.rs | 58 ++++++++++---------
>>>> rust/kernel/lib.rs | 1 +
>>>> 9 files changed, 54 insertions(+), 50 deletions(-)
>>>> rename {drivers/gpu/nova-core => rust/kernel}/bitfield.rs (91%)
>>>> rename drivers/gpu/nova-core/regs/macros.rs => rust/kernel/io/register.rs (93%)
>>>>
>>>> diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
>>>> index 37e6298195e4..a15fa98c8614 100644
>>>> --- a/drivers/gpu/nova-core/falcon.rs
>>>> +++ b/drivers/gpu/nova-core/falcon.rs
>>>> @@ -6,6 +6,7 @@
>>>> use hal::FalconHal;
>>>> use kernel::device;
>>>> use kernel::dma::DmaAddress;
>>>> +use kernel::io::register::RegisterBase;
>>>> use kernel::prelude::*;
>>>> use kernel::sync::aref::ARef;
>>>> use kernel::time::Delta;
>>>> @@ -14,7 +15,6 @@
>>>> use crate::driver::Bar0;
>>>> use crate::gpu::Chipset;
>>>> use crate::regs;
>>>> -use crate::regs::macros::RegisterBase;
>>>> use crate::util;
>>>>
>>>> pub(crate) mod gsp;
>>>> diff --git a/drivers/gpu/nova-core/falcon/gsp.rs
b/drivers/gpu/nova-core/falcon/gsp.rs
>>>> index f17599cb49fa..cd4960e997c8 100644
>>>> --- a/drivers/gpu/nova-core/falcon/gsp.rs
>>>> +++ b/drivers/gpu/nova-core/falcon/gsp.rs
>>>> @@ -1,9 +1,11 @@
>>>> // SPDX-License-Identifier: GPL-2.0
>>>>
>>>> +use kernel::io::register::RegisterBase;
>>>> +
>>>> use crate::{
>>>> driver::Bar0,
>>>> falcon::{Falcon, FalconEngine, PFalcon2Base, PFalconBase},
>>>> - regs::{self, macros::RegisterBase},
>>>> + regs::self,
>>>> };
>>>>
>>>> /// Type specifying the `Gsp` falcon engine. Cannot be instantiated.
>>>> diff --git a/drivers/gpu/nova-core/falcon/sec2.rs
b/drivers/gpu/nova-core/falcon/sec2.rs
>>>> index 815786c8480d..81717868a8a8 100644
>>>> --- a/drivers/gpu/nova-core/falcon/sec2.rs
>>>> +++ b/drivers/gpu/nova-core/falcon/sec2.rs
>>>> @@ -1,7 +1,7 @@
>>>> // SPDX-License-Identifier: GPL-2.0
>>>>
>>>> use crate::falcon::{FalconEngine, PFalcon2Base, PFalconBase};
>>>> -use crate::regs::macros::RegisterBase;
>>>> +use kernel::io::register::RegisterBase;
>>>>
>>>> /// Type specifying the `Sec2` falcon engine. Cannot be instantiated.
>>>> pub(crate) struct Sec2(());
>>>> diff --git a/drivers/gpu/nova-core/nova_core.rs
b/drivers/gpu/nova-core/nova_core.rs
>>>> index 112277c7921e..fffcaee2249f 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 206dab2e1335..1f08e6d4045a 100644
>>>> --- a/drivers/gpu/nova-core/regs.rs
>>>> +++ b/drivers/gpu/nova-core/regs.rs
>>>> @@ -4,15 +4,13 @@
>>>> // but are mapped to types.
>>>> #![allow(non_camel_case_types)]
>>>>
>>>> -#[macro_use]
>>>> -pub(crate) mod macros;
>>>> -
>>>> use crate::falcon::{
>>>> DmaTrfCmdSize, FalconCoreRev, FalconCoreRevSubversion,
FalconFbifMemType, FalconFbifTarget,
>>>> FalconModSelAlgo, FalconSecurityModel, PFalcon2Base, PFalconBase,
PeregrineCoreSelect,
>>>> };
>>>> use crate::gpu::{Architecture, Chipset};
>>>> use kernel::prelude::*;
>>>> +use kernel::register;
>>>>
>>>> // PMC
>>>>
>>>> @@ -331,6 +329,7 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
>>>>
>>>> pub(crate) mod gm107 {
>>>> // FUSE
>>>> + use kernel::register;
>>>>
>>>> register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00021c04 {
>>>> 0:0 display_disabled as bool;
>>>> @@ -339,6 +338,7 @@ pub(crate) mod gm107 {
>>>>
>>>> pub(crate) mod ga100 {
>>>> // FUSE
>>>> + use kernel::register;
>>>>
>>>> register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00820c04 {
>>>> 0:0 display_disabled as bool;
>>>> diff --git a/drivers/gpu/nova-core/bitfield.rs b/rust/kernel/bitfield.rs
>>>> similarity index 91%
>>>> rename from drivers/gpu/nova-core/bitfield.rs
>>>> rename to rust/kernel/bitfield.rs
>>>> index cbedbb0078f6..09cd5741598c 100644
>>>> --- a/drivers/gpu/nova-core/bitfield.rs
>>>> +++ b/rust/kernel/bitfield.rs
>>>> @@ -9,7 +9,7 @@
>>>> /// # Syntax
>>>> ///
>>>> /// ```rust
>>>> -/// use nova_core::bitfield;
>>>> +/// use kernel::bitfield;
>>>> ///
>>>> /// #[derive(Debug, Clone, Copy, Default)]
>>>> /// enum Mode {
>>>> @@ -82,10 +82,11 @@
>>>> /// 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_export]
>>>> 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)* });
>>>> + ::kernel::bitfield!(@core $vis $name $storage $(, $comment)? {
$($fields)* });
>>>> };
>>>>
>>>> // All rules below are helpers.
>>>> @@ -114,7 +115,7 @@ fn from(val: $name) -> $storage {
>>>> }
>>>> }
>>>>
>>>> - bitfield!(@fields_dispatcher $vis $name $storage { $($fields)* });
>>>> + ::kernel::bitfield!(@fields_dispatcher $vis $name $storage {
$($fields)* });
>>>> };
>>>>
>>>> // Captures the fields and passes them to all the implementers that
require field information.
>>>> @@ -130,7 +131,7 @@ fn from(val: $name) -> $storage {
>>>> )*
>>>> }
>>>> ) => {
>>>> - bitfield!(@field_accessors $vis $name $storage {
>>>> + ::kernel::bitfield!(@field_accessors $vis $name $storage {
>>>> $(
>>>> $hi:$lo $field as $type
>>>> $(?=> $try_into_type)?
>>>> @@ -139,8 +140,8 @@ fn from(val: $name) -> $storage {
>>>> ;
>>>> )*
>>>> });
>>>> - bitfield!(@debug $name { $($field;)* });
>>>> - bitfield!(@default $name { $($field;)* });
>>>> + ::kernel::bitfield!(@debug $name { $($field;)* });
>>>> + ::kernel::bitfield!(@default $name { $($field;)* });
>>>> };
>>>>
>>>> // Defines all the field getter/setter methods for `$name`.
>>>> @@ -155,13 +156,13 @@ fn from(val: $name) -> $storage {
>>>> }
>>>> ) => {
>>>> $(
>>>> - bitfield!(@check_field_bounds $hi:$lo $field as $type);
>>>> + ::kernel::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
>>>> + ::kernel::bitfield!(@field_accessor $vis $name $storage,
$hi:$lo $field as $type
>>>> $(?=> $try_into_type)?
>>>> $(=> $into_type)?
>>>> $(, $comment)?
>>>> @@ -198,7 +199,7 @@ impl $name {
>>>> @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt
$field:ident as bool => $into_type:ty
>>>> $(, $comment:literal)?;
>>>> ) => {
>>>> - bitfield!(
>>>> + ::kernel::bitfield!(
>>>> @leaf_accessor $vis $name $storage, $hi:$lo $field
>>>> { |f| <$into_type>::from(if f != 0 { true } else { false }) }
>>>> $into_type => $into_type $(, $comment)?;
>>>> @@ -209,7 +210,7 @@ impl $name {
>>>> (
>>>> @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)?;);
>>>> + ::kernel::bitfield!(@field_accessor $vis $name $storage, $hi:$lo
$field as bool => bool $(, $comment)?;);
>>>> };
>>>>
>>>> // Catches the `?=>` syntax for non-boolean fields.
>>>> @@ -217,7 +218,7 @@ impl $name {
>>>> @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
>>>> + ::kernel::bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field
>>>> { |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>
>>>> ::core::result::Result<
>>>> $try_into_type,
>>>> @@ -231,7 +232,7 @@ impl $name {
>>>> @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
>>>> + ::kernel::bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field
>>>> { |f| <$into_type>::from(f as $type) } $into_type => $into_type
$(, $comment)?;);
>>>> };
>>>>
>>>> @@ -240,7 +241,7 @@ impl $name {
>>>> @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)?;);
>>>> + ::kernel::bitfield!(@field_accessor $vis $name $storage, $hi:$lo
$field as $type => $type $(, $comment)?;);
>>>> };
>>>>
>>>> // Generates the accessor methods for a single field.
>>>> diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
>>>> index 03b467722b86..a79b603604b1 100644
>>>> --- a/rust/kernel/io.rs
>>>> +++ b/rust/kernel/io.rs
>>>> @@ -8,6 +8,7 @@
>>>> use crate::{bindings, build_assert, ffi::c_void};
>>>>
>>>> pub mod mem;
>>>> +pub mod register;
>>>> pub mod resource;
>>>>
>>>> pub use resource::Resource;
>>>> diff --git a/drivers/gpu/nova-core/regs/macros.rs b/rust/kernel/io/register.rs
>>>> similarity index 93%
>>>> rename from drivers/gpu/nova-core/regs/macros.rs
>>>> rename to rust/kernel/io/register.rs
>>>> index c0a5194e8d97..c24d956f122f 100644
>>>> --- a/drivers/gpu/nova-core/regs/macros.rs
>>>> +++ b/rust/kernel/io/register.rs
>>>> @@ -17,7 +17,8 @@
>>>> /// 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> {
>>>> +pub trait RegisterBase<T> {
>>>> + /// The base address for the register.
>>>> const BASE: usize;
>>>> }
>>>>
>>>> @@ -26,7 +27,7 @@ pub(crate) trait RegisterBase<T> {
>>>> ///
>>>> /// Example:
>>>> ///
>>>> -/// ```no_run
>>>> +/// ```ignore
>>>> /// 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";
>>>> @@ -39,7 +40,7 @@ pub(crate) trait RegisterBase<T> {
>>>> /// significant bits of the register. Each field can be accessed and
modified using accessor
>>>> /// methods:
>>>> ///
>>>> -/// ```no_run
>>>> +/// ```ignore
>>>> /// // Read from the register's defined offset (0x100).
>>>> /// let boot0 = BOOT_0::read(&bar);
>>>> /// pr_info!("chip revision: {}.{}", boot0.major_revision(),
boot0.minor_revision());
>>>> @@ -61,7 +62,7 @@ pub(crate) trait RegisterBase<T> {
>>>> /// 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
>>>> +/// ```ignore
>>>> /// register!(SCRATCH @ 0x00000200, "Scratch register" {
>>>> /// 31:0 value as u32, "Raw value";
>>>> /// });
>>>> @@ -111,7 +112,7 @@ pub(crate) trait RegisterBase<T> {
>>>> /// this register needs to implement `RegisterBase<Base>`. Here is the
above example translated
>>>> /// into code:
>>>> ///
>>>> -/// ```no_run
>>>> +/// ```ignore
>>>> /// // Type used to identify the base.
>>>> /// pub(crate) struct CpuCtlBase;
>>>> ///
>>>> @@ -162,7 +163,7 @@ pub(crate) trait RegisterBase<T> {
>>>> /// compile-time or runtime bound checking. Simply define their address as
`Address[Size]`, and add
>>>> /// an `idx` parameter to their `read`, `write` and `alter` methods:
>>>> ///
>>>> -/// ```no_run
>>>> +/// ```ignore
>>>> /// # fn no_run() -> Result<(), Error> {
>>>> /// # fn get_scratch_idx() -> usize {
>>>> /// # 0x15
>>>> @@ -211,7 +212,7 @@ pub(crate) trait RegisterBase<T> {
>>>> /// Combining the two features described in the sections above, arrays of
registers accessible from
>>>> /// a base can also be defined:
>>>> ///
>>>> -/// ```no_run
>>>> +/// ```ignore
>>>> /// # fn no_run() -> Result<(), Error> {
>>>> /// # fn get_scratch_idx() -> usize {
>>>> /// # 0x15
>>>> @@ -273,28 +274,29 @@ pub(crate) trait RegisterBase<T> {
>>>> /// # Ok(())
>>>> /// # }
>>>> /// ```
>>>> +#[macro_export]
>>>> 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)* } );
>>>> + ::kernel::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)* } );
>>>> + ::kernel::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)* } );
>>>> + ::kernel::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)* } );
>>>> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? {
$($fields)* } );
>>>> register!(@io_relative $name @ $base [ $alias::OFFSET ]);
>>>> };
>>>>
>>>> @@ -305,7 +307,7 @@ macro_rules! register {
>>>> }
>>>> ) => {
>>>> static_assert!(::core::mem::size_of::<u32>() <= $stride);
>>>> - bitfield!(pub(crate) struct $name(u32) $(, $comment)? {
$($fields)* } );
>>>> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? {
$($fields)* } );
>>>> register!(@io_array $name @ $offset [ $size ; $stride ]);
>>>> };
>>>>
>>>> @@ -326,7 +328,7 @@ macro_rules! register {
>>>> $(, $comment:literal)? { $($fields:tt)* }
>>>> ) => {
>>>> static_assert!(::core::mem::size_of::<u32>() <= $stride);
>>>> - bitfield!(pub(crate) struct $name(u32) $(, $comment)? {
$($fields)* } );
>>>> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? {
$($fields)* } );
>>>> register!(@io_relative_array $name @ $base [ $offset [ $size ;
$stride ] ]);
>>>> };
>>>>
>>>> @@ -348,7 +350,7 @@ macro_rules! register {
>>>> }
>>>> ) => {
>>>> static_assert!($idx < $alias::SIZE);
>>>> - bitfield!(pub(crate) struct $name(u32) $(, $comment)? {
$($fields)* } );
>>>> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? {
$($fields)* } );
>>>> register!(@io_relative $name @ $base [ $alias::OFFSET + $idx *
$alias::STRIDE ] );
>>>> };
>>>>
>>>> @@ -357,7 +359,7 @@ macro_rules! register {
>>>> // 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)* } );
>>>> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? {
$($fields)* } );
>>>> register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE );
>>>> };
>>>>
>>>> @@ -414,12 +416,12 @@ pub(crate) fn read<const SIZE: usize, T, B>(
>>>> base: &B,
>>>> ) -> Self where
>>>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
>>>> - B: crate::regs::macros::RegisterBase<$base>,
>>>> + B: ::kernel::io::register::RegisterBase<$base>,
>>>> {
>>>> const OFFSET: usize = $name::OFFSET;
>>>>
>>>> let value = io.read32(
>>>> - <B as crate::regs::macros::RegisterBase<$base>>::BASE
+ OFFSET
>>>> + <B as
::kernel::io::register::RegisterBase<$base>>::BASE + OFFSET
>>>> );
>>>>
>>>> Self(value)
>>>> @@ -435,13 +437,13 @@ pub(crate) fn write<const SIZE: usize, T, B>(
>>>> base: &B,
>>>> ) where
>>>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
>>>> - B: crate::regs::macros::RegisterBase<$base>,
>>>> + B: ::kernel::io::register::RegisterBase<$base>,
>>>> {
>>>> const OFFSET: usize = $name::OFFSET;
>>>>
>>>> io.write32(
>>>> self.0,
>>>> - <B as crate::regs::macros::RegisterBase<$base>>::BASE
+ OFFSET
>>>> + <B as
::kernel::io::register::RegisterBase<$base>>::BASE + OFFSET
>>>> );
>>>> }
>>>>
>>>> @@ -455,7 +457,7 @@ pub(crate) fn alter<const SIZE: usize, T, B, F>(
>>>> f: F,
>>>> ) where
>>>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
>>>> - B: crate::regs::macros::RegisterBase<$base>,
>>>> + B: ::kernel::io::register::RegisterBase<$base>,
>>>> F: ::core::ops::FnOnce(Self) -> Self,
>>>> {
>>>> let reg = f(Self::read(io, base));
>>>> @@ -600,11 +602,11 @@ pub(crate) fn read<const SIZE: usize, T, B>(
>>>> idx: usize,
>>>> ) -> Self where
>>>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
>>>> - B: crate::regs::macros::RegisterBase<$base>,
>>>> + B: ::kernel::io::register::RegisterBase<$base>,
>>>> {
>>>> build_assert!(idx < Self::SIZE);
>>>>
>>>> - let offset = <B as
crate::regs::macros::RegisterBase<$base>>::BASE +
>>>> + let offset = <B as
::kernel::io::register::RegisterBase<$base>>::BASE +
>>>> Self::OFFSET + (idx * Self::STRIDE);
>>>> let value = io.read32(offset);
>>>>
>>>> @@ -622,11 +624,11 @@ pub(crate) fn write<const SIZE: usize, T, B>(
>>>> idx: usize
>>>> ) where
>>>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
>>>> - B: crate::regs::macros::RegisterBase<$base>,
>>>> + B: ::kernel::io::register::RegisterBase<$base>,
>>>> {
>>>> build_assert!(idx < Self::SIZE);
>>>>
>>>> - let offset = <B as
crate::regs::macros::RegisterBase<$base>>::BASE +
>>>> + let offset = <B as
::kernel::io::register::RegisterBase<$base>>::BASE +
>>>> Self::OFFSET + (idx * Self::STRIDE);
>>>>
>>>> io.write32(self.0, offset);
>>>> @@ -643,7 +645,7 @@ pub(crate) fn alter<const SIZE: usize, T, B, F>(
>>>> f: F,
>>>> ) where
>>>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
>>>> - B: crate::regs::macros::RegisterBase<$base>,
>>>> + B: ::kernel::io::register::RegisterBase<$base>,
>>>> F: ::core::ops::FnOnce(Self) -> Self,
>>>> {
>>>> let reg = f(Self::read(io, base, idx));
>>>> @@ -662,7 +664,7 @@ pub(crate) fn try_read<const SIZE: usize, T, B>(
>>>> idx: usize,
>>>> ) -> ::kernel::error::Result<Self> where
>>>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
>>>> - B: crate::regs::macros::RegisterBase<$base>,
>>>> + B: ::kernel::io::register::RegisterBase<$base>,
>>>> {
>>>> if idx < Self::SIZE {
>>>> Ok(Self::read(io, base, idx))
>>>> @@ -684,7 +686,7 @@ pub(crate) fn try_write<const SIZE: usize, T, B>(
>>>> idx: usize,
>>>> ) -> ::kernel::error::Result where
>>>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
>>>> - B: crate::regs::macros::RegisterBase<$base>,
>>>> + B: ::kernel::io::register::RegisterBase<$base>,
>>>> {
>>>> if idx < Self::SIZE {
>>>> Ok(self.write(io, base, idx))
>>>> @@ -707,7 +709,7 @@ pub(crate) fn try_alter<const SIZE: usize, T, B, F>(
>>>> f: F,
>>>> ) -> ::kernel::error::Result where
>>>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
>>>> - B: crate::regs::macros::RegisterBase<$base>,
>>>> + B: ::kernel::io::register::RegisterBase<$base>,
>>>> F: ::core::ops::FnOnce(Self) -> Self,
>>>> {
>>>> if idx < Self::SIZE {
>>>> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
>>>> index fcffc3988a90..8f8260090c02 100644
>>>> --- a/rust/kernel/lib.rs
>>>> +++ b/rust/kernel/lib.rs
>>>> @@ -63,6 +63,7 @@
>>>> pub mod alloc;
>>>> #[cfg(CONFIG_AUXILIARY_BUS)]
>>>> pub mod auxiliary;
>>>> +pub mod bitfield;
>>>> pub mod bits;
>>>> #[cfg(CONFIG_BLOCK)]
>>>> pub mod block;
>>>> --
>>>> 2.34.1
>>>>
>>>>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova
2025-10-23 21:47 ` Joel Fernandes
@ 2025-10-23 21:50 ` Joel Fernandes
2025-10-27 9:06 ` Beata Michalska
1 sibling, 0 replies; 41+ messages in thread
From: Joel Fernandes @ 2025-10-23 21:50 UTC (permalink / raw)
To: Beata Michalska
Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
dri-devel@lists.freedesktop.org, dakr@kernel.org,
Alexandre Courbot, Alistair Popple, Miguel Ojeda, Alex Gaynor,
Boqun Feng, Gary Guo, bjorn3_gh@protonmail.com, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, John Hubbard, Timur Tabi,
joel@joelfernandes.org, Elle Rhumsaa, Yury Norov, Daniel Almeida,
Andrea Righi, nouveau@lists.freedesktop.org
On 10/23/2025 5:47 PM, Joel Fernandes wrote:
>>>> Finally, for runtime values such as indexes, it could be useful to verify
>>>> once and then allow infallible reads/writes through some kind access token.
>>> Why? The verification is already done at compile-time AFAICS.
>> Well, that's the point. Those are runtime values, and as of now, the only
>> support for those is for arrays of registers when one, when using try_xxx
>> methods, ends up with check being performed each time the method is called.
>> Ah for this part of your email, you are referring to try accessors. For the
> fixed sizes regions at least, to avoid the runtime check, it will be possible to
> accept BoundedInt [1] in the future. That type actually came up for the exact
> same reason (keeping the checking light). This cleverly moves the checking to
> the caller side which could be done in a slow path. If the size of the IO region
> is fixed, then you don’t need to use try accessors at all if you use BoundedInt
> whenever we have it.
To clarify, BoundedInt is supposed to bound the values passed to the APIs, not
the address. Perhaps we can add additional types for the offsets as well.
thanks,
- Joel
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova
2025-10-23 21:47 ` Joel Fernandes
2025-10-23 21:50 ` Joel Fernandes
@ 2025-10-27 9:06 ` Beata Michalska
2025-10-27 9:56 ` Danilo Krummrich
1 sibling, 1 reply; 41+ messages in thread
From: Beata Michalska @ 2025-10-27 9:06 UTC (permalink / raw)
To: Joel Fernandes
Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
dri-devel@lists.freedesktop.org, dakr@kernel.org,
Alexandre Courbot, Alistair Popple, Miguel Ojeda, Alex Gaynor,
Boqun Feng, Gary Guo, bjorn3_gh@protonmail.com, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, John Hubbard, Timur Tabi,
joel@joelfernandes.org, Elle Rhumsaa, Yury Norov, Daniel Almeida,
Andrea Righi, nouveau@lists.freedesktop.org
On Thu, Oct 23, 2025 at 05:47:11PM -0400, Joel Fernandes wrote:
> Hi Beata,
>
> > On Oct 23, 2025, at 9:55 AM, Beata Michalska <beata.michalska@arm.com> wrote:
> [...]
> >>>
> >>> The current design assumes a fixed, compile-time-known MMIO region size.
> >>> It does not cover cases when the region size is known only at runtime.
> >>> I do appreciate that in cases like that, we are loosing all the deliberate
> >>> compile-time checks but it might be necessary to provide support for those
> >>> as well (at some point at least).
> >>
> >> Sure that could be useful if you have a use case.
> > I guess everything that would use IoRequest::iomap(self), which generates
> > Io<SIZE=0> which is a game over for the macro.
>
> I am curious what is your usecase for this such that size of the IO region
> cannot be know until runtime, can you share? It should also be possible at
> runtime to use the correct type, based on the IO region size IMO. The correct
> type can encode the required size.
It's more theoretical at this point, but there are drivers that do rely on
information from either DT or ACPI tables for the base address and size of the
MMIO region: anything that uses devm_platform_ioremap_resource() or
devm_platform_ioremap_resource_byname() I guess.
I'm not sure I follow your reference to the 'correct type' though.
Anyways, as it has been stated [1], this should not be a blocker for
getting the changes in.
>
> >>
> >>>
> >>> On the (potential) improvement side:
> >>>
> >>> Allowing offsets to be expressions rather than literals would make the macro
> >>> easier to use for regions defined at a fixed base offset, where subsequent
> >>> offsets are derived from that base, i.e:
> >>>
> >>> REG_1_BASE -> 0x100
> >>> REG_1_STATUS -> REG_1_BASE + 0x0
> >>> REG_1_CONTROL -> REG_1_BASE + 0x04
> >>
> >> This is already possible with the register macro using relative-registers
> >> (RegisterBase) right?
> >
> > Probably though the use case I had in mind is relative array of registers.
> > It's fine to use the macro as is for few registers, having a significant
> > number of those gets cumbersome though. Unless I am misreading things.
>
> I am not sure it is cumbersome. The relative register syntax should be able to
> support a larger number of registers. Could you share an example to describe the
> issue with RegisterBase vs with your proposed syntax?
Let's assume there is an array of arrays of registers:
+-------------------------------+
| ... |
| |
+-------------------------------+ -> SECTION_1 = BASE
| ENTRY_1 |
+-------------------------------+
| ENTRY_2 |
+-------------------------------+
| ... |
+-------------------------------+
| ENTRY_N |
+-------------------------------+ -> SECTION_2 = BASE + SIZEOF(SECTION)
| ENTRY_1 |
+-------------------------------+
| ENTRY_2 |
+-------------------------------+
| ... |
+-------------------------------+
| ENTRY_N |
+-------------------------------+ -> SECTION_M = BASE + SIZEOF(SECTION) *(M-1)
| ENTRY_1 |
+-------------------------------+
| ENTRY_2 |
+-------------------------------+
| ... |
+-------------------------------+
| ENTRY_N |
+-------------------------------+
Each section has the same layout and is composed on N entries.
Access pattern for thise is: get ENTRY_x for a section ID, where the ID is
provided at runtime, entries are fixed.
The most straightforward approach (for me at least) was to use register! macro
for each ENTRY, making each of them an array whose stride skips from one section
to the same filed in the next section. With the stride being the full section
size, index section walks through all sections for that entry, i.e.:
register!(ENTRY_1 @ (BASE + 0x00) [SECTION_COUNT; SECTION_SIZE] {
31:0 value as u32;
});
register!(ENTRY_2 @ (BASE + SECTION_SIZE) [SECTION_COUNT; SECTION_SIZE] {
31:0 value as u32;
});
...
Pretty straightforward and here is where the expression of the macro's offset
could be handy.
Now, with the relative registers approach ..., unless I am not mistaken,
it would look like:
pub(crate) struct SectionBase;
pub(crate) struct Section<const ID: usize>;
impl<const ID: usize> RegisterBase<SetionBase> for Section<ID> {
const BASE: usize = BASE + ID + SECTION_SIZE;
}
or have a type per each Section explicitely.
register!(SECTION @ BASE[SECTION_COUNT; SECTION_SIZE] {
31:0 value as u32;
});
register!(ENTRY @ SectionBase[0x0000[ENTRY_COUNT; ENTRY_SIZE]] {
31:0 value as u32;
});
Now alias per each entry: mapping ENTRY_n => ENTRY_name so that things are more
descriptive and it's easier to code accessing functional regs by name rather
than by index:
register!(ENTRY_1 => SectionBase[ENTRY[0]] { 31:0 value as u32; });
...
Reading say ENTRY_4 in Sction 18:
ENTRY::try_read(io, &Section::<18>, 4)?;
So far so good (though still more verbose approach).
Because Section is being indexed based on a runtime value this will not work
as we need compile-time BASE for relative registers.
This would require mapping between runtime idx and Section::<n>:
not ideal I would say.
Please correct me if I am wrong.
>
> >>> The alias mechanism is a nice touch. It might be worth allowing arrays of
> >>> registers with explicit aliases to be defined in a single macro invocation,
> >>> instead of repeating similar definitions, smth along the lines of:
> >>>
> >>> register!(
> >>> REG_STATUS @ 0x300[8; STRIDE] {
> >>> 0:0 enabled as bool;
> >>> 3:1 mode as u8;
> >>> 7:4 flags as u8;
> >>> }
> >>> aliases {
> >>> REG_STATUS_ENABLED[0] {
> >>> 0:0 enabled as bool;
> >>> }
> >>> REG_STATUS_MODE[0] {
> >>> 3:1 mode as u8;
> >>> }
> >>> REG_STATUS_FLAGS[4] {
> >>> 7:4 flags as u8;
> >>> }
> >>> }
> >>
> >> The aliasing might be better do embed as syntax in the Bitfield itself,
> >> instead of additional aliases{} blocks.
> >> By the way, array of registers is also supported already as you may know.
> > I was referring to aliasing having in mind array of registers.
> >
>
> AFAICS, either way you will still have a large number of alias definitions.
> It might be better to invoke register macro explicitly for each alias IMO.
For each alias you do have to have an entry: whether that would be embedded in
the macro syntax or explicitly defined via separate register! calls.
I just think it's easier to have it embedded otherwise you need to repeat the
same pattern for each alias , as of:
register!(ALIAS_1 => BASE_REG ...);
register!(ALIAS_2 => BASE_REG ...);
register!(ALIAS_3 => BASE_REG ...);
Call me lazy (which I am) , but I find it redundant.
Anyways it's nothing major, was only a suggestion for potential improvement.
>
> By the way, I do not follow your example because the names of the aliases in it
> have the names of specific fields embedded in them. Whereas the register macro
> aliases feature aliases the whole register, not specific fields, right?
Actually you can alias a register as whole or with explicit fields.
It just gives you a different view of the same register (memory).
>
> >>> Finally, for runtime values such as indexes, it could be useful to verify
> >>> once and then allow infallible reads/writes through some kind access token.
> >>
> >> Why? The verification is already done at compile-time AFAICS.
> >
> > Well, that's the point. Those are runtime values, and as of now, the only
> > support for those is for arrays of registers when one, when using try_xxx
> > methods, ends up with check being performed each time the method is called.
>
> Ah for this part of your email, you are referring to try accessors. For the
> fixed sizes regions at least, to avoid the runtime check, it will be possible to
> accept BoundedInt [1] in the future. That type actually came up for the exact
> same reason (keeping the checking light). This cleverly moves the checking to
> the caller side which could be done in a slow path. If the size of the IO region
> is fixed, then you don’t need to use try accessors at all if you use BoundedInt
> whenever we have it.
>
All right - my bad. Guess I need to have a look at BoundedInt then.
Thanks for the info.
---
[1] https://lore.kernel.org/rust-for-linux/DDPRDKFEK3H3.2CE3YMXRTLGTI@kernel.org/
---
BR
Beata
> thanks,
>
> - Joel
>
> [1] https://lore.kernel.org/all/20251009-bounded_ints-v2-0-ff3d7fee3ffd@nvidia.com/
>
> >
> > ---
> > BR
> > Beata
> >>
> >>> That might make runtime-safe access patterns simpler and more efficient.
> >>
> >> Because it is compile-time, it is already runtime efficient :)
> >>
> >>> I'm still pondering on how that could look like though (implementation-wise)
> >>
> >> Patches welcomed! For now this still lives in nova-core and Alex is working
> >> on adding support for BoundedInt after which we can move it out.
> >>
> >> Thanks,
> >>
> >> - Joel
> >>
> >>
> >>> ---
> >>> BR
> >>> Beata
> >>>
> >>>> On Fri, Oct 03, 2025 at 11:47:47AM -0400, Joel Fernandes wrote:
> >>>> Out of broad need for the register and bitfield macros in Rust, move
> >>>> them out of nova into the kernel crate. Several usecases need them (Nova
> >>>> is already using these and Tyr developers said they need them).
> >>>>
> >>>> bitfield moved into kernel crate - defines bitfields in Rust.
> >>>> register moved into io module - defines hardware registers and accessors.
> >>>>
> >>>> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
> >>>> Reviewed-by: Elle Rhumsaa <elle@weathered-steel.dev>
> >>>> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
> >>>> ---
> >>>> drivers/gpu/nova-core/falcon.rs | 2 +-
> >>>> drivers/gpu/nova-core/falcon/gsp.rs | 4 +-
> >>>> drivers/gpu/nova-core/falcon/sec2.rs | 2 +-
> >>>> drivers/gpu/nova-core/nova_core.rs | 3 -
> >>>> drivers/gpu/nova-core/regs.rs | 6 +-
> >>>> .../gpu/nova-core => rust/kernel}/bitfield.rs | 27 ++++-----
> >>>> rust/kernel/io.rs | 1 +
> >>>> .../macros.rs => rust/kernel/io/register.rs | 58 ++++++++++---------
> >>>> rust/kernel/lib.rs | 1 +
> >>>> 9 files changed, 54 insertions(+), 50 deletions(-)
> >>>> rename {drivers/gpu/nova-core => rust/kernel}/bitfield.rs (91%)
> >>>> rename drivers/gpu/nova-core/regs/macros.rs => rust/kernel/io/register.rs (93%)
> >>>>
> >>>> diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
> >>>> index 37e6298195e4..a15fa98c8614 100644
> >>>> --- a/drivers/gpu/nova-core/falcon.rs
> >>>> +++ b/drivers/gpu/nova-core/falcon.rs
> >>>> @@ -6,6 +6,7 @@
> >>>> use hal::FalconHal;
> >>>> use kernel::device;
> >>>> use kernel::dma::DmaAddress;
> >>>> +use kernel::io::register::RegisterBase;
> >>>> use kernel::prelude::*;
> >>>> use kernel::sync::aref::ARef;
> >>>> use kernel::time::Delta;
> >>>> @@ -14,7 +15,6 @@
> >>>> use crate::driver::Bar0;
> >>>> use crate::gpu::Chipset;
> >>>> use crate::regs;
> >>>> -use crate::regs::macros::RegisterBase;
> >>>> use crate::util;
> >>>>
> >>>> pub(crate) mod gsp;
> >>>> diff --git a/drivers/gpu/nova-core/falcon/gsp.rs
> b/drivers/gpu/nova-core/falcon/gsp.rs
> >>>> index f17599cb49fa..cd4960e997c8 100644
> >>>> --- a/drivers/gpu/nova-core/falcon/gsp.rs
> >>>> +++ b/drivers/gpu/nova-core/falcon/gsp.rs
> >>>> @@ -1,9 +1,11 @@
> >>>> // SPDX-License-Identifier: GPL-2.0
> >>>>
> >>>> +use kernel::io::register::RegisterBase;
> >>>> +
> >>>> use crate::{
> >>>> driver::Bar0,
> >>>> falcon::{Falcon, FalconEngine, PFalcon2Base, PFalconBase},
> >>>> - regs::{self, macros::RegisterBase},
> >>>> + regs::self,
> >>>> };
> >>>>
> >>>> /// Type specifying the `Gsp` falcon engine. Cannot be instantiated.
> >>>> diff --git a/drivers/gpu/nova-core/falcon/sec2.rs
> b/drivers/gpu/nova-core/falcon/sec2.rs
> >>>> index 815786c8480d..81717868a8a8 100644
> >>>> --- a/drivers/gpu/nova-core/falcon/sec2.rs
> >>>> +++ b/drivers/gpu/nova-core/falcon/sec2.rs
> >>>> @@ -1,7 +1,7 @@
> >>>> // SPDX-License-Identifier: GPL-2.0
> >>>>
> >>>> use crate::falcon::{FalconEngine, PFalcon2Base, PFalconBase};
> >>>> -use crate::regs::macros::RegisterBase;
> >>>> +use kernel::io::register::RegisterBase;
> >>>>
> >>>> /// Type specifying the `Sec2` falcon engine. Cannot be instantiated.
> >>>> pub(crate) struct Sec2(());
> >>>> diff --git a/drivers/gpu/nova-core/nova_core.rs
> b/drivers/gpu/nova-core/nova_core.rs
> >>>> index 112277c7921e..fffcaee2249f 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 206dab2e1335..1f08e6d4045a 100644
> >>>> --- a/drivers/gpu/nova-core/regs.rs
> >>>> +++ b/drivers/gpu/nova-core/regs.rs
> >>>> @@ -4,15 +4,13 @@
> >>>> // but are mapped to types.
> >>>> #![allow(non_camel_case_types)]
> >>>>
> >>>> -#[macro_use]
> >>>> -pub(crate) mod macros;
> >>>> -
> >>>> use crate::falcon::{
> >>>> DmaTrfCmdSize, FalconCoreRev, FalconCoreRevSubversion,
> FalconFbifMemType, FalconFbifTarget,
> >>>> FalconModSelAlgo, FalconSecurityModel, PFalcon2Base, PFalconBase,
> PeregrineCoreSelect,
> >>>> };
> >>>> use crate::gpu::{Architecture, Chipset};
> >>>> use kernel::prelude::*;
> >>>> +use kernel::register;
> >>>>
> >>>> // PMC
> >>>>
> >>>> @@ -331,6 +329,7 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
> >>>>
> >>>> pub(crate) mod gm107 {
> >>>> // FUSE
> >>>> + use kernel::register;
> >>>>
> >>>> register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00021c04 {
> >>>> 0:0 display_disabled as bool;
> >>>> @@ -339,6 +338,7 @@ pub(crate) mod gm107 {
> >>>>
> >>>> pub(crate) mod ga100 {
> >>>> // FUSE
> >>>> + use kernel::register;
> >>>>
> >>>> register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00820c04 {
> >>>> 0:0 display_disabled as bool;
> >>>> diff --git a/drivers/gpu/nova-core/bitfield.rs b/rust/kernel/bitfield.rs
> >>>> similarity index 91%
> >>>> rename from drivers/gpu/nova-core/bitfield.rs
> >>>> rename to rust/kernel/bitfield.rs
> >>>> index cbedbb0078f6..09cd5741598c 100644
> >>>> --- a/drivers/gpu/nova-core/bitfield.rs
> >>>> +++ b/rust/kernel/bitfield.rs
> >>>> @@ -9,7 +9,7 @@
> >>>> /// # Syntax
> >>>> ///
> >>>> /// ```rust
> >>>> -/// use nova_core::bitfield;
> >>>> +/// use kernel::bitfield;
> >>>> ///
> >>>> /// #[derive(Debug, Clone, Copy, Default)]
> >>>> /// enum Mode {
> >>>> @@ -82,10 +82,11 @@
> >>>> /// 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_export]
> >>>> 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)* });
> >>>> + ::kernel::bitfield!(@core $vis $name $storage $(, $comment)? {
> $($fields)* });
> >>>> };
> >>>>
> >>>> // All rules below are helpers.
> >>>> @@ -114,7 +115,7 @@ fn from(val: $name) -> $storage {
> >>>> }
> >>>> }
> >>>>
> >>>> - bitfield!(@fields_dispatcher $vis $name $storage { $($fields)* });
> >>>> + ::kernel::bitfield!(@fields_dispatcher $vis $name $storage {
> $($fields)* });
> >>>> };
> >>>>
> >>>> // Captures the fields and passes them to all the implementers that
> require field information.
> >>>> @@ -130,7 +131,7 @@ fn from(val: $name) -> $storage {
> >>>> )*
> >>>> }
> >>>> ) => {
> >>>> - bitfield!(@field_accessors $vis $name $storage {
> >>>> + ::kernel::bitfield!(@field_accessors $vis $name $storage {
> >>>> $(
> >>>> $hi:$lo $field as $type
> >>>> $(?=> $try_into_type)?
> >>>> @@ -139,8 +140,8 @@ fn from(val: $name) -> $storage {
> >>>> ;
> >>>> )*
> >>>> });
> >>>> - bitfield!(@debug $name { $($field;)* });
> >>>> - bitfield!(@default $name { $($field;)* });
> >>>> + ::kernel::bitfield!(@debug $name { $($field;)* });
> >>>> + ::kernel::bitfield!(@default $name { $($field;)* });
> >>>> };
> >>>>
> >>>> // Defines all the field getter/setter methods for `$name`.
> >>>> @@ -155,13 +156,13 @@ fn from(val: $name) -> $storage {
> >>>> }
> >>>> ) => {
> >>>> $(
> >>>> - bitfield!(@check_field_bounds $hi:$lo $field as $type);
> >>>> + ::kernel::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
> >>>> + ::kernel::bitfield!(@field_accessor $vis $name $storage,
> $hi:$lo $field as $type
> >>>> $(?=> $try_into_type)?
> >>>> $(=> $into_type)?
> >>>> $(, $comment)?
> >>>> @@ -198,7 +199,7 @@ impl $name {
> >>>> @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt
> $field:ident as bool => $into_type:ty
> >>>> $(, $comment:literal)?;
> >>>> ) => {
> >>>> - bitfield!(
> >>>> + ::kernel::bitfield!(
> >>>> @leaf_accessor $vis $name $storage, $hi:$lo $field
> >>>> { |f| <$into_type>::from(if f != 0 { true } else { false }) }
> >>>> $into_type => $into_type $(, $comment)?;
> >>>> @@ -209,7 +210,7 @@ impl $name {
> >>>> (
> >>>> @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)?;);
> >>>> + ::kernel::bitfield!(@field_accessor $vis $name $storage, $hi:$lo
> $field as bool => bool $(, $comment)?;);
> >>>> };
> >>>>
> >>>> // Catches the `?=>` syntax for non-boolean fields.
> >>>> @@ -217,7 +218,7 @@ impl $name {
> >>>> @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
> >>>> + ::kernel::bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field
> >>>> { |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>
> >>>> ::core::result::Result<
> >>>> $try_into_type,
> >>>> @@ -231,7 +232,7 @@ impl $name {
> >>>> @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
> >>>> + ::kernel::bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field
> >>>> { |f| <$into_type>::from(f as $type) } $into_type => $into_type
> $(, $comment)?;);
> >>>> };
> >>>>
> >>>> @@ -240,7 +241,7 @@ impl $name {
> >>>> @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)?;);
> >>>> + ::kernel::bitfield!(@field_accessor $vis $name $storage, $hi:$lo
> $field as $type => $type $(, $comment)?;);
> >>>> };
> >>>>
> >>>> // Generates the accessor methods for a single field.
> >>>> diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
> >>>> index 03b467722b86..a79b603604b1 100644
> >>>> --- a/rust/kernel/io.rs
> >>>> +++ b/rust/kernel/io.rs
> >>>> @@ -8,6 +8,7 @@
> >>>> use crate::{bindings, build_assert, ffi::c_void};
> >>>>
> >>>> pub mod mem;
> >>>> +pub mod register;
> >>>> pub mod resource;
> >>>>
> >>>> pub use resource::Resource;
> >>>> diff --git a/drivers/gpu/nova-core/regs/macros.rs b/rust/kernel/io/register.rs
> >>>> similarity index 93%
> >>>> rename from drivers/gpu/nova-core/regs/macros.rs
> >>>> rename to rust/kernel/io/register.rs
> >>>> index c0a5194e8d97..c24d956f122f 100644
> >>>> --- a/drivers/gpu/nova-core/regs/macros.rs
> >>>> +++ b/rust/kernel/io/register.rs
> >>>> @@ -17,7 +17,8 @@
> >>>> /// 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> {
> >>>> +pub trait RegisterBase<T> {
> >>>> + /// The base address for the register.
> >>>> const BASE: usize;
> >>>> }
> >>>>
> >>>> @@ -26,7 +27,7 @@ pub(crate) trait RegisterBase<T> {
> >>>> ///
> >>>> /// Example:
> >>>> ///
> >>>> -/// ```no_run
> >>>> +/// ```ignore
> >>>> /// 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";
> >>>> @@ -39,7 +40,7 @@ pub(crate) trait RegisterBase<T> {
> >>>> /// significant bits of the register. Each field can be accessed and
> modified using accessor
> >>>> /// methods:
> >>>> ///
> >>>> -/// ```no_run
> >>>> +/// ```ignore
> >>>> /// // Read from the register's defined offset (0x100).
> >>>> /// let boot0 = BOOT_0::read(&bar);
> >>>> /// pr_info!("chip revision: {}.{}", boot0.major_revision(),
> boot0.minor_revision());
> >>>> @@ -61,7 +62,7 @@ pub(crate) trait RegisterBase<T> {
> >>>> /// 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
> >>>> +/// ```ignore
> >>>> /// register!(SCRATCH @ 0x00000200, "Scratch register" {
> >>>> /// 31:0 value as u32, "Raw value";
> >>>> /// });
> >>>> @@ -111,7 +112,7 @@ pub(crate) trait RegisterBase<T> {
> >>>> /// this register needs to implement `RegisterBase<Base>`. Here is the
> above example translated
> >>>> /// into code:
> >>>> ///
> >>>> -/// ```no_run
> >>>> +/// ```ignore
> >>>> /// // Type used to identify the base.
> >>>> /// pub(crate) struct CpuCtlBase;
> >>>> ///
> >>>> @@ -162,7 +163,7 @@ pub(crate) trait RegisterBase<T> {
> >>>> /// compile-time or runtime bound checking. Simply define their address as
> `Address[Size]`, and add
> >>>> /// an `idx` parameter to their `read`, `write` and `alter` methods:
> >>>> ///
> >>>> -/// ```no_run
> >>>> +/// ```ignore
> >>>> /// # fn no_run() -> Result<(), Error> {
> >>>> /// # fn get_scratch_idx() -> usize {
> >>>> /// # 0x15
> >>>> @@ -211,7 +212,7 @@ pub(crate) trait RegisterBase<T> {
> >>>> /// Combining the two features described in the sections above, arrays of
> registers accessible from
> >>>> /// a base can also be defined:
> >>>> ///
> >>>> -/// ```no_run
> >>>> +/// ```ignore
> >>>> /// # fn no_run() -> Result<(), Error> {
> >>>> /// # fn get_scratch_idx() -> usize {
> >>>> /// # 0x15
> >>>> @@ -273,28 +274,29 @@ pub(crate) trait RegisterBase<T> {
> >>>> /// # Ok(())
> >>>> /// # }
> >>>> /// ```
> >>>> +#[macro_export]
> >>>> 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)* } );
> >>>> + ::kernel::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)* } );
> >>>> + ::kernel::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)* } );
> >>>> + ::kernel::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)* } );
> >>>> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? {
> $($fields)* } );
> >>>> register!(@io_relative $name @ $base [ $alias::OFFSET ]);
> >>>> };
> >>>>
> >>>> @@ -305,7 +307,7 @@ macro_rules! register {
> >>>> }
> >>>> ) => {
> >>>> static_assert!(::core::mem::size_of::<u32>() <= $stride);
> >>>> - bitfield!(pub(crate) struct $name(u32) $(, $comment)? {
> $($fields)* } );
> >>>> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? {
> $($fields)* } );
> >>>> register!(@io_array $name @ $offset [ $size ; $stride ]);
> >>>> };
> >>>>
> >>>> @@ -326,7 +328,7 @@ macro_rules! register {
> >>>> $(, $comment:literal)? { $($fields:tt)* }
> >>>> ) => {
> >>>> static_assert!(::core::mem::size_of::<u32>() <= $stride);
> >>>> - bitfield!(pub(crate) struct $name(u32) $(, $comment)? {
> $($fields)* } );
> >>>> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? {
> $($fields)* } );
> >>>> register!(@io_relative_array $name @ $base [ $offset [ $size ;
> $stride ] ]);
> >>>> };
> >>>>
> >>>> @@ -348,7 +350,7 @@ macro_rules! register {
> >>>> }
> >>>> ) => {
> >>>> static_assert!($idx < $alias::SIZE);
> >>>> - bitfield!(pub(crate) struct $name(u32) $(, $comment)? {
> $($fields)* } );
> >>>> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? {
> $($fields)* } );
> >>>> register!(@io_relative $name @ $base [ $alias::OFFSET + $idx *
> $alias::STRIDE ] );
> >>>> };
> >>>>
> >>>> @@ -357,7 +359,7 @@ macro_rules! register {
> >>>> // 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)* } );
> >>>> + ::kernel::bitfield!(pub(crate) struct $name(u32) $(, $comment)? {
> $($fields)* } );
> >>>> register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE );
> >>>> };
> >>>>
> >>>> @@ -414,12 +416,12 @@ pub(crate) fn read<const SIZE: usize, T, B>(
> >>>> base: &B,
> >>>> ) -> Self where
> >>>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> >>>> - B: crate::regs::macros::RegisterBase<$base>,
> >>>> + B: ::kernel::io::register::RegisterBase<$base>,
> >>>> {
> >>>> const OFFSET: usize = $name::OFFSET;
> >>>>
> >>>> let value = io.read32(
> >>>> - <B as crate::regs::macros::RegisterBase<$base>>::BASE
> + OFFSET
> >>>> + <B as
> ::kernel::io::register::RegisterBase<$base>>::BASE + OFFSET
> >>>> );
> >>>>
> >>>> Self(value)
> >>>> @@ -435,13 +437,13 @@ pub(crate) fn write<const SIZE: usize, T, B>(
> >>>> base: &B,
> >>>> ) where
> >>>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> >>>> - B: crate::regs::macros::RegisterBase<$base>,
> >>>> + B: ::kernel::io::register::RegisterBase<$base>,
> >>>> {
> >>>> const OFFSET: usize = $name::OFFSET;
> >>>>
> >>>> io.write32(
> >>>> self.0,
> >>>> - <B as crate::regs::macros::RegisterBase<$base>>::BASE
> + OFFSET
> >>>> + <B as
> ::kernel::io::register::RegisterBase<$base>>::BASE + OFFSET
> >>>> );
> >>>> }
> >>>>
> >>>> @@ -455,7 +457,7 @@ pub(crate) fn alter<const SIZE: usize, T, B, F>(
> >>>> f: F,
> >>>> ) where
> >>>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> >>>> - B: crate::regs::macros::RegisterBase<$base>,
> >>>> + B: ::kernel::io::register::RegisterBase<$base>,
> >>>> F: ::core::ops::FnOnce(Self) -> Self,
> >>>> {
> >>>> let reg = f(Self::read(io, base));
> >>>> @@ -600,11 +602,11 @@ pub(crate) fn read<const SIZE: usize, T, B>(
> >>>> idx: usize,
> >>>> ) -> Self where
> >>>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> >>>> - B: crate::regs::macros::RegisterBase<$base>,
> >>>> + B: ::kernel::io::register::RegisterBase<$base>,
> >>>> {
> >>>> build_assert!(idx < Self::SIZE);
> >>>>
> >>>> - let offset = <B as
> crate::regs::macros::RegisterBase<$base>>::BASE +
> >>>> + let offset = <B as
> ::kernel::io::register::RegisterBase<$base>>::BASE +
> >>>> Self::OFFSET + (idx * Self::STRIDE);
> >>>> let value = io.read32(offset);
> >>>>
> >>>> @@ -622,11 +624,11 @@ pub(crate) fn write<const SIZE: usize, T, B>(
> >>>> idx: usize
> >>>> ) where
> >>>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> >>>> - B: crate::regs::macros::RegisterBase<$base>,
> >>>> + B: ::kernel::io::register::RegisterBase<$base>,
> >>>> {
> >>>> build_assert!(idx < Self::SIZE);
> >>>>
> >>>> - let offset = <B as
> crate::regs::macros::RegisterBase<$base>>::BASE +
> >>>> + let offset = <B as
> ::kernel::io::register::RegisterBase<$base>>::BASE +
> >>>> Self::OFFSET + (idx * Self::STRIDE);
> >>>>
> >>>> io.write32(self.0, offset);
> >>>> @@ -643,7 +645,7 @@ pub(crate) fn alter<const SIZE: usize, T, B, F>(
> >>>> f: F,
> >>>> ) where
> >>>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> >>>> - B: crate::regs::macros::RegisterBase<$base>,
> >>>> + B: ::kernel::io::register::RegisterBase<$base>,
> >>>> F: ::core::ops::FnOnce(Self) -> Self,
> >>>> {
> >>>> let reg = f(Self::read(io, base, idx));
> >>>> @@ -662,7 +664,7 @@ pub(crate) fn try_read<const SIZE: usize, T, B>(
> >>>> idx: usize,
> >>>> ) -> ::kernel::error::Result<Self> where
> >>>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> >>>> - B: crate::regs::macros::RegisterBase<$base>,
> >>>> + B: ::kernel::io::register::RegisterBase<$base>,
> >>>> {
> >>>> if idx < Self::SIZE {
> >>>> Ok(Self::read(io, base, idx))
> >>>> @@ -684,7 +686,7 @@ pub(crate) fn try_write<const SIZE: usize, T, B>(
> >>>> idx: usize,
> >>>> ) -> ::kernel::error::Result where
> >>>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> >>>> - B: crate::regs::macros::RegisterBase<$base>,
> >>>> + B: ::kernel::io::register::RegisterBase<$base>,
> >>>> {
> >>>> if idx < Self::SIZE {
> >>>> Ok(self.write(io, base, idx))
> >>>> @@ -707,7 +709,7 @@ pub(crate) fn try_alter<const SIZE: usize, T, B, F>(
> >>>> f: F,
> >>>> ) -> ::kernel::error::Result where
> >>>> T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
> >>>> - B: crate::regs::macros::RegisterBase<$base>,
> >>>> + B: ::kernel::io::register::RegisterBase<$base>,
> >>>> F: ::core::ops::FnOnce(Self) -> Self,
> >>>> {
> >>>> if idx < Self::SIZE {
> >>>> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> >>>> index fcffc3988a90..8f8260090c02 100644
> >>>> --- a/rust/kernel/lib.rs
> >>>> +++ b/rust/kernel/lib.rs
> >>>> @@ -63,6 +63,7 @@
> >>>> pub mod alloc;
> >>>> #[cfg(CONFIG_AUXILIARY_BUS)]
> >>>> pub mod auxiliary;
> >>>> +pub mod bitfield;
> >>>> pub mod bits;
> >>>> #[cfg(CONFIG_BLOCK)]
> >>>> pub mod block;
> >>>> --
> >>>> 2.34.1
> >>>>
> >>>>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova
2025-10-27 9:06 ` Beata Michalska
@ 2025-10-27 9:56 ` Danilo Krummrich
2025-10-27 15:05 ` Beata Michalska
0 siblings, 1 reply; 41+ messages in thread
From: Danilo Krummrich @ 2025-10-27 9:56 UTC (permalink / raw)
To: Beata Michalska
Cc: Joel Fernandes, linux-kernel@vger.kernel.org,
rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org,
Alexandre Courbot, Alistair Popple, Miguel Ojeda, Alex Gaynor,
Boqun Feng, Gary Guo, bjorn3_gh@protonmail.com, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, John Hubbard, Timur Tabi,
joel@joelfernandes.org, Elle Rhumsaa, Yury Norov, Daniel Almeida,
Andrea Righi, nouveau@lists.freedesktop.org
On Mon Oct 27, 2025 at 10:06 AM CET, Beata Michalska wrote:
> It's more theoretical at this point, but there are drivers that do rely on
> information from either DT or ACPI tables for the base address and size of the
> MMIO region: anything that uses devm_platform_ioremap_resource() or
> devm_platform_ioremap_resource_byname() I guess.
Don't get confused, those are two different things: The size of the MMIO region
(or a PCI BAR) and the const SIZE generic in Io<SIZE> are two different things.
The former is the actual size of an MMIO region, whereas the latter is the
minimum size requested by a driver for proper operation.
For instance, let's assume your driver requests ten contiguous 32-bit registers
starting at offset zero of an MMIO region.
In this case you can call req.iomap_sized<0x28>(), because you know that your
driver is not able to properly work without an MMIO region with at least a width
of 0x28 bytes.
The actual size of the MMIO region returned by req.iomap_sized<0x28>() may
indeed be smaller or larger than that, depending on what is defined in the DT,
ACPI or PCI BAR.
If smaller than the const SIZE generic, the call to req.iomap_sized<0x28>() will
fail, otherwise it will be successful. The actual size of the MMIO region is not
influenced by the const SIZE generic.
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova
2025-10-27 9:56 ` Danilo Krummrich
@ 2025-10-27 15:05 ` Beata Michalska
0 siblings, 0 replies; 41+ messages in thread
From: Beata Michalska @ 2025-10-27 15:05 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Joel Fernandes, linux-kernel@vger.kernel.org,
rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org,
Alexandre Courbot, Alistair Popple, Miguel Ojeda, Alex Gaynor,
Boqun Feng, Gary Guo, bjorn3_gh@protonmail.com, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, John Hubbard, Timur Tabi,
joel@joelfernandes.org, Elle Rhumsaa, Yury Norov, Daniel Almeida,
Andrea Righi, nouveau@lists.freedesktop.org
On Mon, Oct 27, 2025 at 10:56:41AM +0100, Danilo Krummrich wrote:
> On Mon Oct 27, 2025 at 10:06 AM CET, Beata Michalska wrote:
> > It's more theoretical at this point, but there are drivers that do rely on
> > information from either DT or ACPI tables for the base address and size of the
> > MMIO region: anything that uses devm_platform_ioremap_resource() or
> > devm_platform_ioremap_resource_byname() I guess.
>
> Don't get confused, those are two different things: The size of the MMIO region
> (or a PCI BAR) and the const SIZE generic in Io<SIZE> are two different things.
>
> The former is the actual size of an MMIO region, whereas the latter is the
> minimum size requested by a driver for proper operation.
>
> For instance, let's assume your driver requests ten contiguous 32-bit registers
> starting at offset zero of an MMIO region.
>
> In this case you can call req.iomap_sized<0x28>(), because you know that your
> driver is not able to properly work without an MMIO region with at least a width
> of 0x28 bytes.
>
> The actual size of the MMIO region returned by req.iomap_sized<0x28>() may
> indeed be smaller or larger than that, depending on what is defined in the DT,
> ACPI or PCI BAR.
>
> If smaller than the const SIZE generic, the call to req.iomap_sized<0x28>() will
> fail, otherwise it will be successful. The actual size of the MMIO region is not
> influenced by the const SIZE generic.
I appreciate the explanation.
I think my confusion here comes from the fact that I was assuming there is an
intent to accommodate different MMIO regions sizes for various device revisions,
and not expecting all drivers to explicitly call the iomap_sized in all cases.
My bad then.
Again, thanks for clarifying that.
---
Best Regards
Beata
^ permalink raw reply [flat|nested] 41+ messages in thread
end of thread, other threads:[~2025-10-27 15:05 UTC | newest]
Thread overview: 41+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-03 15:47 [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/ Joel Fernandes
2025-10-03 15:47 ` [PATCH v6 1/5] nova-core: bitfield: Move bitfield-specific code from register! into new macro Joel Fernandes
2025-10-06 17:56 ` Edwin Peer
2025-10-07 6:49 ` Alexandre Courbot
2025-10-03 15:47 ` [PATCH v6 2/5] nova-core: bitfield: Add support for different storage widths Joel Fernandes
2025-10-03 15:47 ` [PATCH v6 3/5] nova-core: bitfield: Add support for custom visiblity Joel Fernandes
2025-10-03 15:47 ` [PATCH v6 4/5] rust: Move register and bitfield macros out of Nova Joel Fernandes
2025-10-06 10:38 ` Alexandre Courbot
2025-10-09 6:59 ` Dirk Behme
2025-10-09 11:16 ` Danilo Krummrich
2025-10-09 11:28 ` Alexandre Courbot
2025-10-09 12:54 ` Danilo Krummrich
2025-10-10 7:28 ` Dirk Behme
2025-10-22 18:40 ` Beata Michalska
2025-10-22 19:37 ` Joel Fernandes
2025-10-23 13:55 ` Beata Michalska
2025-10-23 14:07 ` Danilo Krummrich
2025-10-23 21:47 ` Joel Fernandes
2025-10-23 21:50 ` Joel Fernandes
2025-10-27 9:06 ` Beata Michalska
2025-10-27 9:56 ` Danilo Krummrich
2025-10-27 15:05 ` Beata Michalska
2025-10-03 15:47 ` [PATCH v6 5/5] rust: bitfield: Add KUNIT tests for bitfield Joel Fernandes
2025-10-06 10:37 ` Alexandre Courbot
2025-10-06 19:38 ` Joel Fernandes
2025-10-06 20:36 ` [PATCH v7] " Joel Fernandes
2025-10-06 18:05 ` [PATCH v6 0/5] Introduce bitfield and move register macro to rust/kernel/ Edwin Peer
2025-10-06 22:29 ` Yury Norov
2025-10-07 10:36 ` Alexandre Courbot
2025-10-07 10:42 ` Miguel Ojeda
2025-10-07 13:20 ` Alexandre Courbot
2025-10-07 16:06 ` Yury Norov
2025-10-07 16:12 ` Miguel Ojeda
2025-10-07 13:16 ` Danilo Krummrich
2025-10-07 21:08 ` Joel Fernandes
2025-10-07 22:08 ` Danilo Krummrich
2025-10-08 14:28 ` Yury Norov
2025-10-08 15:00 ` Danilo Krummrich
2025-10-07 15:41 ` Yury Norov
2025-10-07 21:41 ` Daniel Almeida
2025-10-08 15:49 ` Yury Norov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).