Re: [PATCH v2 1/3] rust: extract `bitfield!` macro from `register!`

From: Eliot Courtney

Date: Sun Apr 12 2026 - 22:30:17 EST


On Thu Apr 9, 2026 at 11:58 PM JST, Alexandre Courbot wrote:
> Extract the bitfield-defining part of the `register!` macro into an
> independent macro used to define bitfield types with bounds-checked
> accessors.
>
> Each field is represented as a `Bounded` of the appropriate bit width,
> ensuring field values are never silently truncated.
>
> Fields can optionally be converted to/from custom types, either fallibly
> or infallibly.
>
> Appropriate documentation is also added, and a MAINTAINERS entry created
> for the new module.
>
> Signed-off-by: Alexandre Courbot <acourbot@xxxxxxxxxx>
> ---
> MAINTAINERS | 8 +
> rust/kernel/bitfield.rs | 491 +++++++++++++++++++++++++++++++++++++++++++++
> rust/kernel/io/register.rs | 246 +----------------------
> rust/kernel/lib.rs | 1 +
> 4 files changed, 502 insertions(+), 244 deletions(-)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index b01791963e25..77f2617ade5d 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -23186,6 +23186,14 @@ F: scripts/*rust*
> F: tools/testing/selftests/rust/
> K: \b(?i:rust)\b
>
> +RUST [BITFIELD]
> +M: Alexandre Courbot <acourbot@xxxxxxxxxx>
> +M: Joel Fernandes <joelagnelf@xxxxxxxxxx>
> +R: Yury Norov <yury.norov@xxxxxxxxx>
> +L: rust-for-linux@xxxxxxxxxxxxxxx
> +S: Maintained
> +F: rust/kernel/bitfield.rs
> +
> RUST [ALLOC]
> M: Danilo Krummrich <dakr@xxxxxxxxxx>
> R: Lorenzo Stoakes <ljs@xxxxxxxxxx>

nit: Should this be kept in alphabetical order (e.g. with ALLOC here
below?).

> diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
> new file mode 100644
> index 000000000000..f5948eec8a76
> --- /dev/null
> +++ b/rust/kernel/bitfield.rs
> @@ -0,0 +1,491 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Support for defining bitfields as Rust structures.
> +//!
> +//! The [`bitfield!`](kernel::bitfield!) macro declares integer types that are split into distinct
> +//! bit fields of arbitrary length. Each field is typed using [`Bounded`](kernel::num::Bounded) to
> +//! ensure values are properly validated and to avoid implicit data loss.
> +//!
> +//! # Example
> +//!
> +//! ```rust
> +//! use kernel::bitfield;
> +//! use kernel::num::Bounded;
> +//!
> +//! bitfield! {
> +//! pub struct Rgb(u16) {
> +//! 15:11 blue;
> +//! 10:5 green;
> +//! 4:0 red;
> +//! }
> +//! }
> +//!
> +//! // Valid value for the `blue` field.
> +//! let blue = Bounded::<u16, 5>::new::<0x18>();
> +//!
> +//! // Setters can be chained. Values ranges are checked at compile-time.
> +//! let color = Rgb::zeroed()
> +//! // Compile-time bounds check of constant value.
> +//! .with_const_red::<0x10>()
> +//! .with_const_green::<0x1f>()
> +//! // A `Bounded` can also be passed.
> +//! .with_blue(blue);
> +//!
> +//! assert_eq!(color.red(), 0x10);
> +//! assert_eq!(color.green(), 0x1f);
> +//! assert_eq!(color.blue(), 0x18);
> +//! assert_eq!(
> +//! color.into_raw(),
> +//! (0x18 << Rgb::BLUE_SHIFT) + (0x1f << Rgb::GREEN_SHIFT) + 0x10,
> +//! );
> +//!
> +//! // Convert to/from the backing storage type.
> +//! let raw: u16 = color.into();
> +//! assert_eq!(Rgb::from(raw), color);
> +//! ```
> +//!
> +//! # Syntax
> +//!
> +//! ```text
> +//! bitfield! {
> +//! #[attributes]
> +//! // Documentation for `Name`.
> +//! pub struct Name(storage_type) {
> +//! // `field_1` documentation.
> +//! hi:lo field_1;
> +//! // `field_2` documentation.
> +//! hi:lo field_2 => ConvertedType;
> +//! // `field_3` documentation.
> +//! hi:lo field_3 ?=> ConvertedType;
> +//! ...
> +//! }
> +//! }
> +//! ```
> +//!
> +//! - `storage_type`: The underlying integer type (`u8`, `u16`, `u32`, `u64`).
> +//! - `hi:lo`: Bit range (inclusive), where `hi >= lo`.
> +//! - `=> Type`: Optional infallible conversion (see [below](#infallible-conversion-)).
> +//! - `?=> Type`: Optional fallible conversion (see [below](#fallible-conversion-)).
> +//! - Documentation strings and attributes are optional.
> +//!
> +//! # Generated code
> +//!
> +//! Each field is internally represented as a [`Bounded`] parameterized by its bit width. Field
> +//! values can either be set/retrieved directly, or converted from/to another type.
> +//!
> +//! The use of `Bounded` for each field enforces bounds-checking (at build time or runtime) of every
> +//! value assigned to a field. This ensures that data is never accidentally truncated.
> +//!
> +//! The macro generates the bitfield type, [`From`] and [`Into`] implementations for its storage
> +//! type, as well as [`Debug`] and [`Zeroable`](pin_init::Zeroable) implementations.
> +//!
> +//! For each field, it also generates:
> +//!
> +//! - `with_field(value)` — infallible setter; the argument type must be statically known to fit
> +//! the field width.
> +//! - `with_const_field::<VALUE>()` — const setter; the value is validated at compile time.
> +//! Usually shorter to use than `with_field` for constant values as it doesn't require
> +//! constructing a `Bounded`.
> +//! - `try_with_field(value)` — fallible setter. Returns an error if the value is out of range.
> +//! - `FIELD_MASK`, `FIELD_SHIFT`, `FIELD_RANGE` - constants for manual bit manipulation.
> +//!
> +//! # Implicit conversions
> +//!
> +//! Types that fit entirely within a field's bit width can be used directly with setters. For
> +//! example, `bool` works with single-bit fields, and `u8` works with 8-bit fields:
> +//!
> +//! ```rust
> +//! use kernel::bitfield;
> +//!
> +//! bitfield! {
> +//! pub struct Flags(u32) {
> +//! 15:8 byte_field;
> +//! 0:0 flag;
> +//! }
> +//! }
> +//!
> +//! let flags = Flags::zeroed()
> +//! .with_byte_field(0x42_u8)
> +//! .with_flag(true);
> +//!
> +//! assert_eq!(flags.into_raw(), (0x42 << Flags::BYTE_FIELD_SHIFT) | 1);
> +//! ```
> +//!
> +//! # Runtime bounds checking
> +//!
> +//! When a value is not known at compile time, use `try_with_field()` to check bounds at runtime:
> +//!
> +//! ```rust
> +//! use kernel::bitfield;
> +//!
> +//! bitfield! {
> +//! pub struct Config(u8) {
> +//! 3:0 nibble;
> +//! }
> +//! }
> +//!
> +//! fn set_nibble(config: Config, value: u8) -> Result<Config, Error> {
> +//! // Returns `EOVERFLOW` if `value > 0xf`.
> +//! config.try_with_nibble(value)
> +//! }
> +//! # Ok::<(), Error>(())
> +//! ```
> +//!
> +//! # Type conversion
> +//!
> +//! Fields can be automatically converted to/from a custom type using `=>` (infallible) or `?=>`
> +//! (fallible). The custom type must implement the appropriate `From` or `TryFrom` traits with
> +//! `Bounded`.
> +//!
> +//! ## Infallible conversion (`=>`)
> +//!
> +//! Use this when all possible bit patterns of a field map to valid values:
> +//!
> +//! ```rust
> +//! use kernel::bitfield;
> +//! use kernel::num::Bounded;
> +//!
> +//! #[derive(Debug, Clone, Copy, PartialEq)]
> +//! enum Power {
> +//! Off,
> +//! On,
> +//! }
> +//!
> +//! impl From<Bounded<u32, 1>> for Power {
> +//! fn from(v: Bounded<u32, 1>) -> Self {
> +//! match *v {
> +//! 0 => Power::Off,
> +//! _ => Power::On,
> +//! }
> +//! }
> +//! }
> +//!
> +//! impl From<Power> for Bounded<u32, 1> {
> +//! fn from(p: Power) -> Self {
> +//! (p as u32 != 0).into()
> +//! }
> +//! }
> +//!
> +//! bitfield! {
> +//! pub struct Control(u32) {
> +//! 0:0 power => Power;
> +//! }
> +//! }
> +//!
> +//! let ctrl = Control::zeroed().with_power(Power::On);
> +//! assert_eq!(ctrl.power(), Power::On);
> +//! ```
> +//!
> +//! ## Fallible conversion (`?=>`)
> +//!
> +//! Use this when some bit patterns of a field are invalid. The getter returns a [`Result`]:
> +//!
> +//! ```rust
> +//! use kernel::bitfield;
> +//! use kernel::num::Bounded;
> +//!
> +//! #[derive(Debug, Clone, Copy, PartialEq)]
> +//! enum Mode {
> +//! Low = 0,
> +//! High = 1,
> +//! Auto = 2,
> +//! // 3 is invalid
> +//! }
> +//!
> +//! impl TryFrom<Bounded<u32, 2>> for Mode {
> +//! type Error = u32;
> +//!
> +//! fn try_from(v: Bounded<u32, 2>) -> Result<Self, u32> {
> +//! match *v {
> +//! 0 => Ok(Mode::Low),
> +//! 1 => Ok(Mode::High),
> +//! 2 => Ok(Mode::Auto),
> +//! n => Err(n),
> +//! }
> +//! }
> +//! }
> +//!
> +//! impl From<Mode> for Bounded<u32, 2> {
> +//! fn from(m: Mode) -> Self {
> +//! match m {
> +//! Mode::Low => Bounded::<u32, _>::new::<0>(),
> +//! Mode::High => Bounded::<u32, _>::new::<1>(),
> +//! Mode::Auto => Bounded::<u32, _>::new::<2>(),
> +//! }
> +//! }
> +//! }
> +//!
> +//! bitfield! {
> +//! pub struct Config(u32) {
> +//! 1:0 mode ?=> Mode;
> +//! }
> +//! }
> +//!
> +//! let cfg = Config::zeroed().with_mode(Mode::Auto);
> +//! assert_eq!(cfg.mode(), Ok(Mode::Auto));
> +//!
> +//! // Invalid bit pattern returns an error.
> +//! assert_eq!(Config::from(0b11).mode(), Err(3));
> +//! ```
> +//!
> +//! [`Bounded`]: kernel::num::Bounded

In the nova version of bitfield we had @check_field_bounds. If we put
in the bit numbers the wrong way around, this patch gives a compile
error like:

```
attempt to compute `4_u32 - 7_u32`, which would overflow
```

The original nova version looks like it used build_assert, but I think
we can do it with const assert!, so we should be able to get a better
build error message for this case:

```
const _: () = assert!($hi >= $lo, "bitfield: hi bit must be >= lo bit");
```

With just that we get an extra build error, although it still spams the
confusing build error. We could also consider adding a function like:

```
pub const fn bitfield_width(hi: u32, lo: u32) -> u32 {
assert!(hi >= lo, "bitfield: hi bit must be >= lo bit");
hi + 1 - lo
}
```

Using this instead gets rid of some confusing build errors since we can
also use it in type bounds. But to get rid of all of them we would need
to do it for the mask etc and others.

WDYT?