lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <DG0WY7S2QPQM.37S6PROC464QI@nvidia.com>
Date: Thu, 29 Jan 2026 17:00:41 +0900
From: "Alexandre Courbot" <acourbot@...dia.com>
To: "Gary Guo" <gary@...yguo.net>
Cc: "Danilo Krummrich" <dakr@...nel.org>, "Alice Ryhl"
 <aliceryhl@...gle.com>, "Daniel Almeida" <daniel.almeida@...labora.com>,
 "Miguel Ojeda" <ojeda@...nel.org>, "Boqun Feng" <boqun.feng@...il.com>,
 Björn Roy Baron <bjorn3_gh@...tonmail.com>, "Benno Lossin"
 <lossin@...nel.org>, "Andreas Hindborg" <a.hindborg@...nel.org>, "Trevor
 Gross" <tmgross@...ch.edu>, "Yury Norov" <yury.norov@...il.com>, "John
 Hubbard" <jhubbard@...dia.com>, "Alistair Popple" <apopple@...dia.com>,
 "Joel Fernandes" <joelagnelf@...dia.com>, "Timur Tabi" <ttabi@...dia.com>,
 "Edwin Peer" <epeer@...dia.com>, "Eliot Courtney" <ecourtney@...dia.com>,
 "Dirk Behme" <dirk.behme@...bosch.com>, "Steven Price"
 <steven.price@....com>, <rust-for-linux@...r.kernel.org>,
 <linux-kernel@...r.kernel.org>
Subject: Re: [PATCH v4 5/7] rust: io: add `register!` macro

On Thu Jan 29, 2026 at 1:16 AM JST, Gary Guo wrote:
> On Wed Jan 28, 2026 at 2:37 AM GMT, Alexandre Courbot wrote:
>> Add a macro for defining hardware register types with I/O accessors.
>>
>> Each register 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.
>>
>> The address of registers can be direct, relative, or indexed, supporting
>> most of the patterns in which registers are arranged.
>>
>> Tested-by: Dirk Behme <dirk.behme@...bosch.com>
>> Signed-off-by: Alexandre Courbot <acourbot@...dia.com>
>> ---
>>  rust/kernel/io.rs          |    1 +
>>  rust/kernel/io/register.rs | 1287 ++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 1288 insertions(+)
>>
>> diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
>> index 056a3ec71647..112f43ecbf88 100644
>> --- a/rust/kernel/io.rs
>> +++ b/rust/kernel/io.rs
>> @@ -11,6 +11,7 @@
>>  
>>  pub mod mem;
>>  pub mod poll;
>> +pub mod register;
>>  pub mod resource;
>>  
>>  pub use resource::Resource;
>> diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs
>> new file mode 100644
>> index 000000000000..fc85dcd1f09a
>> --- /dev/null
>> +++ b/rust/kernel/io/register.rs
>> @@ -0,0 +1,1287 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +
>> +//! A macro to define register layout and accessors.
>> +//!
>> +//! A single register typically includes several fields, which are accessed through a combination
>> +//! of bit-shift and mask operations that introduce a class of potential mistakes, notably because
>> +//! not all possible field values are necessarily valid.
>> +//!
>> +//! The [`register!`] macro in this module provides an intuitive and readable syntax for defining a
>> +//! dedicated type for each register. Each such type comes with its own field accessors that can
>> +//! return an error if a field's value is invalid.
>> +//!
>> +//! [`register!`]: kernel::register!
>> +
>> +use core::ops::Deref;
>> +
>> +use crate::io::{
>> +    IoCapable,
>> +    IoKnownSize, //
>> +};
>> +
>> +/// Trait providing a base address to be added to the offset of a relative register to obtain
>> +/// its actual offset.
>> +///
>> +/// The `T` generic argument is used to distinguish which base to use, in case a type provides
>> +/// several bases. It is given to the `register!` macro to restrict the use of the register to
>> +/// implementors of this particular variant.
>> +pub trait RegisterBase<T> {
>> +    /// Base address to which register offsets are added.
>> +    const BASE: usize;
>> +}
>> +
>> +/// Trait providing I/O read/write operations for register storage types.
>> +///
>> +/// This trait is implemented for all integer types on which I/O can be performed, allowing the
>> +/// `register!` macro to generate appropriate I/O accessor methods based on the register's storage
>> +/// type.
>> +pub trait RegisterIo: Sized {
>
> Is this trait intended for public usage or just internal detail of `register!()`
> macro?
>
> If it's the former, then we should probably just put the method into `IoCapable`
> and allow generic-read in Io. If it's the latter, let's `#[doc(hidden)]` this so
> it won't get abused.

It is an internal detail of `register!()` and not supposed to be used by anyone
else.

>
>> +    /// Read a value from the given offset in the I/O region.
>> +    fn read<T, I>(io: &T, offset: usize) -> Self
>> +    where
>> +        T: Deref<Target = I>,
>> +        I: IoKnownSize + IoCapable<Self>;
>
> I think generally `Deref` bound shouldn't be exposed to user. What smart
> pointers are involved here, and can we implement forwarding impls of `Io`?

Removing the requirement for `Deref` in the `RegisterIo` trait is simple - we
can just call `Deref` in the register IO accessors.

But I suspect you mean that we should also remove it from the register
accessors themselves? The problem if I remove it there is that things like
Nova's `Bar0` do not implement `IoKnownSize` and `IoCapable` (but rather deref
to something that does), which would require all callers to do the deref op
itself as deref coercion doesn't seem to be usable here.

>
>> +
>> +    /// Write a value to the given offset in the I/O region.
>> +    fn write<T, I>(self, io: &T, offset: usize)
>> +    where
>> +        T: Deref<Target = I>,
>> +        I: IoKnownSize + IoCapable<Self>;
>> +}
>> +
>> +impl RegisterIo for u8 {
>> +    #[inline(always)]
>> +    fn read<T, I>(io: &T, offset: usize) -> Self
>> +    where
>> +        T: Deref<Target = I>,
>> +        I: IoKnownSize + IoCapable<Self>,
>> +    {
>> +        io.read8(offset)
>> +    }
>> +
>> +    #[inline(always)]
>> +    fn write<T, I>(self, io: &T, offset: usize)
>> +    where
>> +        T: Deref<Target = I>,
>> +        I: IoKnownSize + IoCapable<Self>,
>> +    {
>> +        io.write8(self, offset)
>> +    }
>> +}
>> +
>> +impl RegisterIo for u16 {
>> +    #[inline(always)]
>> +    fn read<T, I>(io: &T, offset: usize) -> Self
>> +    where
>> +        T: Deref<Target = I>,
>> +        I: IoKnownSize + IoCapable<Self>,
>> +    {
>> +        io.read16(offset)
>> +    }
>> +
>> +    #[inline(always)]
>> +    fn write<T, I>(self, io: &T, offset: usize)
>> +    where
>> +        T: Deref<Target = I>,
>> +        I: IoKnownSize + IoCapable<Self>,
>> +    {
>> +        io.write16(self, offset)
>> +    }
>> +}
>> +
>> +impl RegisterIo for u32 {
>> +    #[inline(always)]
>> +    fn read<T, I>(io: &T, offset: usize) -> Self
>> +    where
>> +        T: Deref<Target = I>,
>> +        I: IoKnownSize + IoCapable<Self>,
>> +    {
>> +        io.read32(offset)
>> +    }
>> +
>> +    #[inline(always)]
>> +    fn write<T, I>(self, io: &T, offset: usize)
>> +    where
>> +        T: Deref<Target = I>,
>> +        I: IoKnownSize + IoCapable<Self>,
>> +    {
>> +        io.write32(self, offset)
>> +    }
>> +}
>> +
>> +#[cfg(CONFIG_64BIT)]
>
> This cfg should be removed.

Done.

>
>> +impl RegisterIo for u64 {
>> +    #[inline(always)]
>> +    fn read<T, I>(io: &T, offset: usize) -> Self
>> +    where
>> +        T: Deref<Target = I>,
>> +        I: IoKnownSize + IoCapable<Self>,
>> +    {
>> +        io.read64(offset)
>> +    }
>> +
>> +    #[inline(always)]
>> +    fn write<T, I>(self, io: &T, offset: usize)
>> +    where
>> +        T: Deref<Target = I>,
>> +        I: IoKnownSize + IoCapable<Self>,
>> +    {
>> +        io.write64(self, offset)
>> +    }
>> +}
>> +
>> +/// Defines a dedicated type for a register, including getter and setter methods for its fields and
>> +/// methods to read and write it from an [`Io`] region.
>> +///
>> +/// Example:
>> +///
>> +/// ```
>> +/// use kernel::register;
>> +///
>> +/// register! {
>> +///     /// Basic information about the chip.
>> +///     pub BOOT_0(u32) @ 0x00000100 {
>> +///         /// Vendor ID.
>> +///         15:8 vendor_id;
>> +///         /// Major revision of the chip.
>> +///         7:4 major_revision;
>> +///         /// Minor revision of the chip.
>> +///         3:0 minor_revision;
>> +///     }
>> +/// }
>> +/// ```
>> +///
>> +/// This defines a `BOOT_0` type which can be read from or written to offset `0x100` of an `Io`
>> +/// region. For instance, `minor_revision` consists of the 4 least significant bits of the
>> +/// register.
>> +///
>> +/// Fields are instances of [`Bounded`](kernel::num::Bounded) and can be read by calling their
>> +/// getter method, which is named after them. They also have setter methods prefixed with `set_`
>> +/// for runtime values and `with_` for constant values. All setters return the updated register
>> +/// value.
>> +///
>> +/// ```no_run
>> +/// use kernel::register;
>> +/// use kernel::num::Bounded;
>> +///
>> +/// # register! {
>> +/// #     pub BOOT_0(u32) @ 0x00000100 {
>> +/// #         15:8 vendor_id;
>> +/// #         7:4 major_revision;
>> +/// #         3:0 minor_revision;
>> +/// #     }
>> +/// # }
>> +/// # fn test<T: kernel::io::IoKnownSize + kernel::io::IoCapable<u32>>(bar: &T) {
>> +/// # fn obtain_vendor_id() -> u8 { 0xff }
>> +/// // Read from the register's defined offset (0x100).
>> +/// let boot0 = BOOT_0::read(&bar);
>> +/// pr_info!("chip revision: {}.{}", boot0.major_revision().get(), boot0.minor_revision().get());
>> +///
>> +/// // Update some fields and write the new value back.
>> +/// boot0
>> +///     // Constant values.
>> +///     .with_major_revision::<3>()
>> +///     .with_minor_revision::<10>()
>> +///     // Run-time value.
>> +///     .set_vendor_id(obtain_vendor_id())
>> +///     .write(&bar);
>> +///
>> +/// // Or, just read and update the register in a single step.
>> +/// BOOT_0::update(&bar, |r| r
>> +///     .with_major_revision::<3>()
>> +///     .with_minor_revision::<10>()
>> +///     .set_vendor_id(obtain_vendor_id())
>> +/// );
>> +///
>> +/// // Constant values can also be built using the const setters.
>> +/// const V: BOOT_0 = BOOT_0::zeroed()
>> +///     .with_major_revision::<3>()
>> +///     .with_minor_revision::<10>();
>> +/// # }
>> +/// ```
>> +///
>> +/// Fields can also be transparently converted from/to an arbitrary type by using the bitfield `=>`
>> +/// and `?=>` syntaxes.
>> +///
>> +/// If present, doccomments above register or fields definitions are added to the relevant item
>> +/// they document (the register type itself, or the field's setter and getter methods).
>> +///
>> +/// Note that multiple registers can be defined in a single `register!` invocation. This can be
>> +/// useful to group related registers together.
>> +///
>> +/// ```
>> +/// use kernel::register;
>> +///
>> +/// register! {
>> +///     pub BOOT_0(u8) @ 0x00000100 {
>> +///         7:4 major_revision;
>> +///         3:0 minor_revision;
>> +///     }
>> +///
>> +///     pub BOOT_1(u8) @ 0x00000101 {
>> +///         7:5 num_threads;
>> +///         4:0 num_cores;
>> +///     }
>> +/// };
>> +/// ```
>> +///
>> +/// It is possible to create an alias of an existing register with new field definitions by using
>> +/// the `=> ALIAS` syntax. This is useful for cases where a register's interpretation depends on
>> +/// the context:
>> +///
>> +/// ```
>> +/// use kernel::register;
>> +///
>> +/// register! {
>> +///     /// Scratch register.
>> +///     pub SCRATCH(u32) @ 0x00000200 {
>> +///         /// Raw value.
>> +///         31:0 value;
>> +///     }
>> +///
>> +///     /// Boot status of the firmware.
>> +///     pub SCRATCH_BOOT_STATUS(u32) => SCRATCH {
>> +///         /// Whether the firmware has completed booting.
>> +///         0:0 completed;
>> +///     }
>> +/// }
>> +/// ```
>> +///
>> +/// In this example, `SCRATCH_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while also
>> +/// providing its own `completed` field.
>> +///
>> +/// ## Relative registers
>> +///
>> +/// A register can be defined as being accessible from a fixed offset of a provided base. For
>> +/// instance, imagine the following I/O space:
>> +///
>> +/// ```text
>> +///           +-----------------------------+
>> +///           |             ...             |
>> +///           |                             |
>> +///  0x100--->+------------CPU0-------------+
>> +///           |                             |
>> +///  0x110--->+-----------------------------+
>> +///           |           CPU_CTL           |
>> +///           +-----------------------------+
>> +///           |             ...             |
>> +///           |                             |
>> +///           |                             |
>> +///  0x200--->+------------CPU1-------------+
>> +///           |                             |
>> +///  0x210--->+-----------------------------+
>> +///           |           CPU_CTL           |
>> +///           +-----------------------------+
>> +///           |             ...             |
>> +///           +-----------------------------+
>> +/// ```
>> +///
>> +/// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O
>> +/// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define
>> +/// them twice and would prefer a way to select which one to use from a single definition
>> +///
>> +/// This can be done using the `Base + Offset` syntax when specifying the register's address.
>> +///
>> +/// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the
>> +/// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for
>> +/// this register needs to implement `RegisterBase<Base>`. Here is the above example translated
>> +/// into code:
>> +///
>> +/// ```no_run
>> +/// use kernel::register;
>> +/// use kernel::io::register::RegisterBase;
>> +///
>> +/// // Type used to identify the base.
>> +/// pub struct CpuCtlBase;
>> +///
>> +/// // ZST describing `CPU0`.
>> +/// struct Cpu0;
>> +/// impl RegisterBase<CpuCtlBase> for Cpu0 {
>> +///     const BASE: usize = 0x100;
>> +/// }
>> +/// // Singleton of `CPU0` used to identify it.
>> +/// const CPU0: Cpu0 = Cpu0;
>> +///
>> +/// // ZST describing `CPU1`.
>> +/// struct Cpu1;
>> +/// impl RegisterBase<CpuCtlBase> for Cpu1 {
>> +///     const BASE: usize = 0x200;
>> +/// }
>> +/// // Singleton of `CPU1` used to identify it.
>> +/// const CPU1: Cpu1 = Cpu1;
>> +///
>> +/// # fn test<T: kernel::io::IoKnownSize + kernel::io::IoCapable<u32>>(bar: &T) {
>> +/// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`.
>> +/// register! {
>> +///     /// CPU core control.
>> +///     pub CPU_CTL(u32) @ CpuCtlBase + 0x10 {
>> +///         /// Start the CPU core.
>> +///         0:0 start;
>> +///     }
>> +/// }
>> +///
>> +/// // The `read`, `write` and `update` methods of relative registers take an extra `base` argument
>> +/// // that is used to resolve its final address by adding its `BASE` to the offset of the
>> +/// // register.
>> +///
>> +/// // Start `CPU0`.
>> +/// CPU_CTL::update(&bar, &CPU0, |r| r.set_start(true));
>> +///
>> +/// // Start `CPU1`.
>> +/// CPU_CTL::update(&bar, &CPU1, |r| r.set_start(true));
>> +///
>> +/// // Aliases can also be defined for relative register.
>> +/// register! {
>> +///     /// Alias to CPU core control.
>> +///     pub CPU_CTL_ALIAS(u32) => CpuCtlBase + CPU_CTL {
>> +///         /// Start the aliased CPU core.
>> +///         1:1 alias_start;
>> +///     }
>> +/// }
>> +///
>> +/// // Start the aliased `CPU0`.
>> +/// CPU_CTL_ALIAS::update(&bar, &CPU0, |r| r.set_alias_start(true));
>> +/// # }
>> +/// ```
>> +///
>> +/// ## Arrays of registers
>> +///
>> +/// Some I/O areas contain consecutive registers that can be interpreted in the same way. These
>> +/// areas can be defined as an array of identical registers, allowing them to be accessed by index
>> +/// with compile-time or runtime bound checking. Simply specify their size inside `[` and `]`
>> +/// brackets, and add an `idx` parameter to their `read`, `write` and `update` methods:
>> +///
>> +/// ```no_run
>> +/// use kernel::register;
>> +///
>> +/// # fn test<T: kernel::io::IoKnownSize + kernel::io::IoCapable<u32>>(bar: &T)
>> +/// #     -> Result<(), Error>{
>> +/// # fn get_scratch_idx() -> usize {
>> +/// #   0x15
>> +/// # }
>> +/// // Array of 64 consecutive registers with the same layout starting at offset `0x80`.
>> +/// register! {
>> +///     /// Scratch registers.
>> +///     pub SCRATCH(u32)[64] @ 0x00000080 {
>> +///         31:0 value;
>> +///     }
>> +/// }
>> +///
>> +/// // Read scratch register 0, i.e. I/O address `0x80`.
>> +/// let scratch_0 = SCRATCH::read(&bar, 0).value();
>> +/// // Read scratch register 15, i.e. I/O address `0x80 + (15 * 4)`.
>> +/// let scratch_15 = SCRATCH::read(&bar, 15).value();
>> +///
>> +/// // This is out of bounds and won't build.
>> +/// // let scratch_128 = SCRATCH::read(&bar, 128).value();
>> +///
>> +/// // Runtime-obtained array index.
>> +/// let scratch_idx = get_scratch_idx();
>> +/// // Access on a runtime index returns an error if it is out-of-bounds.
>> +/// let some_scratch = SCRATCH::try_read(&bar, scratch_idx)?.value();
>> +///
>> +/// // Alias to a particular register in an array.
>> +/// // Here `SCRATCH[8]` is used to convey the firmware exit code.
>> +/// register! {
>> +///     /// Firmware exit status code.
>> +///     pub FIRMWARE_STATUS(u32) => SCRATCH[8] {
>> +///         7:0 status;
>> +///     }
>> +/// }
>> +/// let status = FIRMWARE_STATUS::read(&bar).status();
>> +///
>> +/// // Non-contiguous register arrays can be defined by adding a stride parameter.
>> +/// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the
>> +/// // registers of the two declarations below are interleaved.
>> +/// register! {
>> +///     /// Scratch registers bank 0.
>> +///     pub SCRATCH_INTERLEAVED_0(u32)[16 ; 8] @ 0x000000c0 {
>> +///         31:0 value;
>> +///     }
>> +///
>> +///     /// Scratch registers bank 1.
>> +///     pub SCRATCH_INTERLEAVED_1(u32)[16 ; 8] @ 0x000000c4 {
>> +///         31:0 value;
>> +///     }
>> +/// }
>> +/// # Ok(())
>> +/// # }
>> +/// ```
>> +///
>> +/// ## Relative arrays of registers
>> +///
>> +/// Combining the two features described in the sections above, arrays of registers accessible from
>> +/// a base can also be defined:
>> +///
>> +/// ```no_run
>> +/// use kernel::register;
>> +/// use kernel::io::register::RegisterBase;
>> +///
>> +/// # fn test<T: kernel::io::IoKnownSize + kernel::io::IoCapable<u32>>(bar: &T)
>> +/// #     -> Result<(), Error>{
>> +/// # fn get_scratch_idx() -> usize {
>> +/// #   0x15
>> +/// # }
>> +/// // Type used as parameter of `RegisterBase` to specify the base.
>> +/// pub struct CpuCtlBase;
>> +///
>> +/// // ZST describing `CPU0`.
>> +/// struct Cpu0;
>> +/// impl RegisterBase<CpuCtlBase> for Cpu0 {
>> +///     const BASE: usize = 0x100;
>> +/// }
>> +/// // Singleton of `CPU0` used to identify it.
>> +/// const CPU0: Cpu0 = Cpu0;
>> +///
>> +/// // ZST describing `CPU1`.
>> +/// struct Cpu1;
>> +/// impl RegisterBase<CpuCtlBase> for Cpu1 {
>> +///     const BASE: usize = 0x200;
>> +/// }
>> +/// // Singleton of `CPU1` used to identify it.
>> +/// const CPU1: Cpu1 = Cpu1;
>> +///
>> +/// // 64 per-cpu scratch registers, arranged as a contiguous array.
>> +/// register! {
>> +///     /// Per-CPU scratch registers.
>> +///     pub CPU_SCRATCH(u32)[64] @ CpuCtlBase + 0x00000080 {
>> +///         31:0 value;
>> +///     }
>> +/// }
>> +///
>> +/// let cpu0_scratch_0 = CPU_SCRATCH::read(&bar, &Cpu0, 0).value();
>> +/// let cpu1_scratch_15 = CPU_SCRATCH::read(&bar, &Cpu1, 15).value();
>> +///
>> +/// // This won't build.
>> +/// // let cpu0_scratch_128 = CPU_SCRATCH::read(&bar, &Cpu0, 128).value();
>> +///
>> +/// // Runtime-obtained array index.
>> +/// let scratch_idx = get_scratch_idx();
>> +/// // Access on a runtime value returns an error if it is out-of-bounds.
>> +/// let cpu0_some_scratch = CPU_SCRATCH::try_read(&bar, &Cpu0, scratch_idx)?.value();
>> +///
>> +/// // `SCRATCH[8]` is used to convey the firmware exit code.
>> +/// register! {
>> +///     /// Per-CPU firmware exit status code.
>> +///     pub CPU_FIRMWARE_STATUS(u32) => CpuCtlBase + CPU_SCRATCH[8] {
>> +///         7:0 status;
>> +///     }
>> +/// }
>> +/// let cpu0_status = CPU_FIRMWARE_STATUS::read(&bar, &Cpu0).status();
>> +///
>> +/// // Non-contiguous register arrays can be defined by adding a stride parameter.
>> +/// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the
>> +/// // registers of the two declarations below are interleaved.
>> +/// register! {
>> +///     /// Scratch registers bank 0.
>> +///     pub CPU_SCRATCH_INTERLEAVED_0(u32)[16 ; 8] @ CpuCtlBase + 0x00000d00 {
>
> I discussed this with Alex off-list and we agree that this'll better be
>
>     pub CPU_SCRATCH_INTERLEAVED_0(u32)[u16, stride = 8] @ CpuCtlBase + 0x00000d00
>
> spelling the full intention out rather than have a special syntax, as this isn't
> the common case.

Implemented it and it indeed looks much better.

>
>> +///         31:0 value;
>> +///     }
>> +///
>> +///     /// Scratch registers bank 1.
>> +///     pub CPU_SCRATCH_INTERLEAVED_1(u32)[16 ; 8] @ CpuCtlBase + 0x00000d04 {
>> +///         31:0 value;
>> +///     }
>> +/// }
>> +/// # Ok(())
>> +/// # }
>> +/// ```
>> +/// [`Io`]: kernel::io::Io
>> +#[macro_export]
>> +macro_rules! register {
>> +    // Entry point for the macro, allowing multiple registers to be defined in one call.
>> +    // It matches all possible register declaration patterns to dispatch them to corresponding
>> +    // `@...` rule that defines a single register.
>> +    (
>> +        $(
>> +            $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
>> +                $([ $size:expr $(; $stride:expr)? ])? $(@ $offset:literal)?
>> +                $(@ $base:ident + $base_offset:literal)?
>
> Does `$(@ $($base:ident +)? $offset:literal)?` not work?

It does! I was trying to match the `+` in the right-side expression and
obviously couldn't, but it didn't occur to me to do it the other way. :)

>
>> +                $(=> $alias:ident $(+ $alias_offset:ident)? $([$alias_idx:expr])? )?
>> +            { $($fields:tt)* }
>> +        )*
>> +    ) => {
>> +        $(
>> +        ::kernel::register!(
>
> $crate::register!()

Updated throughout the file (I am not sure to understand the difference though?).

>
>> +            @reg $(#[$attr])* $vis $name ($storage) $([$size $(; $stride)?])?
>> +                $(@ $offset)?
>> +                $(@ $base + $base_offset)?
>> +                $(=> $alias $(+ $alias_offset)? $([$alias_idx])? )?
>> +            { $($fields)* }
>> +        );
>> +        )*
>> +    };
>> +
>> +    // All the rules below are private helpers.
>> +
>> +    // Creates a register at a fixed offset of the MMIO space.
>> +    (
>> +        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $offset:literal
>> +            { $($fields:tt)* }
>> +    ) => {
>> +        ::kernel::register!(
>> +            @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }
>> +        );
>> +        ::kernel::register!(@io_fixed $name($storage) @ $offset);
>> +    };
>> +
>> +    // Creates an alias register of fixed offset register `alias` with its own fields.
>> +    (
>> +        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident
>> +            { $($fields:tt)* }
>> +    ) => {
>> +        ::kernel::register!(
>> +            @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }
>> +        );
>> +        ::kernel::register!(@io_fixed $name($storage) @ $alias::OFFSET);
>> +    };
>> +
>> +    // Creates a register at a relative offset from a base address provider.
>> +    (
>> +        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $base:ident + $offset:literal
>> +            { $($fields:tt)* }
>> +    ) => {
>> +        ::kernel::register!(
>> +            @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }
>> +        );
>> +        ::kernel::register!(@io_relative $name($storage) @ $base + $offset );
>> +    };
>> +
>> +    // Creates an alias register of relative offset register `alias` with its own fields.
>> +    (
>> +        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $base:ident + $alias:ident
>> +            { $($fields:tt)* }
>> +    ) => {
>> +        ::kernel::register!(
>> +            @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }
>> +        );
>> +        ::kernel::register!(@io_relative $name($storage) @ $base + $alias::OFFSET );
>
> Would this generate error messages if $name and $alias are of different base, or
> would such case be a bug but silently compiles?

You would get an build error when trying to use the alias as the bases
are dedicated types and the types would be incompatible.

>
>> +    };
>> +
>> +    // Creates an array of registers at a fixed offset of the MMIO space.
>> +    (
>> +        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
>> +            [ $size:expr ; $stride:expr ] @ $offset:literal { $($fields:tt)* }
>> +    ) => {
>> +        static_assert!(::core::mem::size_of::<$storage>() <= $stride);
>> +
>> +        ::kernel::register!(
>> +            @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }
>> +        );
>> +        ::kernel::register!(@io_array $name($storage) [ $size ; $stride ] @ $offset);
>> +    };
>> +
>> +    // Shortcut for contiguous array of registers (stride == size of element).
>> +    (
>> +        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ] @ $offset:literal
>> +            { $($fields:tt)* }
>> +    ) => {
>> +        ::kernel::register!(
>> +            $(#[$attr])* $vis $name($storage) [ $size ; ::core::mem::size_of::<$storage>() ]
>> +                @ $offset { $($fields)* }
>> +        );
>> +    };
>> +
>> +    // Creates an alias of register `idx` of array of registers `alias` with its own fields.
>> +    (
>> +        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident [ $idx:expr ]
>> +            { $($fields:tt)* }
>> +    ) => {
>> +        static_assert!($idx < $alias::SIZE);
>> +
>> +        ::kernel::register!(
>> +            @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }
>> +        );
>> +        ::kernel::register!(@io_fixed $name($storage) @ $alias::OFFSET + $idx * $alias::STRIDE);
>> +    };
>> +
>> +    // Creates an array of registers at a relative offset from a base address provider.
>> +    (
>> +        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ; $stride:expr ]
>> +            @ $base:ident + $offset:literal { $($fields:tt)* }
>> +    ) => {
>> +        static_assert!(::core::mem::size_of::<$storage>() <= $stride);
>> +
>> +        ::kernel::register!(
>> +            @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }
>> +        );
>> +        ::kernel::register!(
>> +            @io_relative_array $name($storage) [ $size ; $stride ] @ $base + $offset
>> +        );
>> +    };
>> +
>> +    // Shortcut for contiguous array of relative registers (stride == size of element).
>> +    (
>> +        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ]
>> +            @ $base:ident + $offset:literal { $($fields:tt)* }
>> +    ) => {
>> +        ::kernel::register!(
>> +            $(#[$attr])* $vis $name($storage) [ $size ; ::core::mem::size_of::<$storage>() ]
>> +                @ $base + $offset { $($fields)* }
>> +        );
>> +    };
>> +
>> +    // Creates an alias of register `idx` of relative array of registers `alias` with its own
>> +    // fields.
>> +    (
>> +        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
>> +            => $base:ident + $alias:ident [ $idx:expr ] { $($fields:tt)* }
>> +    ) => {
>> +        static_assert!($idx < $alias::SIZE);
>> +
>> +        ::kernel::register!(
>> +            @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }
>> +        );
>> +        ::kernel::register!(
>> +            @io_relative $name($storage) @ $base + $alias::OFFSET + $idx * $alias::STRIDE
>> +        );
>> +    };
>> +
>> +    // Generates the bitfield for the register.
>> +    //
>> +    // `#[allow(non_camel_case_types)]` is added since register names typically use SCREAMING_CASE.
>> +    (
>> +        @bitfield $(#[$attr:meta])* $vis:vis struct $name:ident($storage:ty) { $($fields:tt)* }
>> +    ) => {
>> +        ::kernel::register!(@bitfield_core
>> +            #[allow(non_camel_case_types)]
>> +            $(#[$attr])* $vis $name $storage
>> +        );
>> +        ::kernel::register!(@bitfield_fields $vis $name $storage { $($fields)* });
>> +    };
>> +
>> +    // Generates the IO accessors for a fixed offset register.
>> +    (@io_fixed $name:ident ($storage:ty) @ $offset:expr) => {
>> +        #[allow(dead_code)]
>> +        impl $name {
>> +            /// Absolute offset of the register.
>> +            pub const OFFSET: usize = $offset;
>> +
>> +            /// Read the register from its address in `io`.
>> +            #[inline(always)]
>> +            pub fn read<T, I>(io: &T) -> Self where
>> +                T: ::core::ops::Deref<Target = I>,
>> +                I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
>> +            {
>> +                Self(<$storage as $crate::io::register::RegisterIo>::read(io, Self::OFFSET))
>> +            }
>> +
>> +            /// Write the value contained in `self` to the register address in `io`.
>> +            #[inline(always)]
>> +            pub fn write<T, I>(self, io: &T) where
>> +                T: ::core::ops::Deref<Target = I>,
>> +                I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
>> +            {
>> +                <$storage as $crate::io::register::RegisterIo>::write(self.0, io, Self::OFFSET)
>> +            }
>> +
>> +            /// Read the register from its address in `io` and run `f` on its value to obtain a new
>> +            /// value to write back.
>> +            ///
>> +            /// Note that this operation is not atomic. In concurrent contexts, external
>> +            /// synchronization may be required to prevent race conditions.
>
> Given the non-atomicity, how much value does it provide compared to having the
> user write read and write themselves? I feel that people reading the code may
> assume the atomicity without reading docs if they see `FOO::update`, while it's
> less likely that they do so if they read
> `FOO::read(io).with_bar(baz).write(io)`.

It is just a convenience method. I cannot pretend it is widely used, but
it allows to turn multiple-step ops into one-liners.

>
>> +            #[inline(always)]
>> +            pub fn update<T, I, F>(
>> +                io: &T,
>> +                f: F,
>> +            ) where
>> +                T: ::core::ops::Deref<Target = I>,
>> +                I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
>> +                F: ::core::ops::FnOnce(Self) -> Self,
>> +            {
>> +                let reg = f(Self::read(io));
>> +                reg.write(io);
>> +            }
>> +        }
>> +    };
>> +
>> +    // Generates the IO accessors for a relative offset register.
>> +    (@io_relative $name:ident ($storage:ty) @ $base:ident + $offset:expr ) => {
>> +        #[allow(dead_code)]
>> +        impl $name {
>> +            /// Relative offset of the register.
>> +            pub const OFFSET: usize = $offset;
>> +
>> +            /// Read the register from `io`, using the base address provided by `base` and adding
>> +            /// the register's offset to it.
>> +            #[inline(always)]
>> +            pub fn read<T, I, B>(
>> +                io: &T,
>> +                #[allow(unused_variables)]
>> +                base: &B,
>> +            ) -> Self where
>> +                T: ::core::ops::Deref<Target = I>,
>> +                I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
>> +                B: $crate::io::register::RegisterBase<$base>,
>> +            {
>> +                let offset = <B as $crate::io::register::RegisterBase<$base>>::BASE + Self::OFFSET;
>> +
>> +                Self(<$storage as $crate::io::register::RegisterIo>::read(io, offset))
>> +            }
>> +
>> +            /// Write the value contained in `self` to `io`, using the base address provided by
>> +            /// `base` and adding the register's offset to it.
>> +            #[inline(always)]
>> +            pub fn write<T, I, B>(
>> +                self,
>> +                io: &T,
>> +                #[allow(unused_variables)]
>> +                base: &B,
>> +            ) where
>> +                T: ::core::ops::Deref<Target = I>,
>> +                I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
>> +                B: $crate::io::register::RegisterBase<$base>,
>> +            {
>> +                let offset = <B as $crate::io::register::RegisterBase<$base>>::BASE + Self::OFFSET;
>> +
>> +                <$storage as $crate::io::register::RegisterIo>::write(self.0, io, offset)
>> +            }
>> +
>> +            /// Read the register from `io`, using the base address provided by `base` and adding
>> +            /// the register's offset to it, then run `f` on its value to obtain a new value to
>> +            /// write back.
>> +            ///
>> +            /// Note that this operation is not atomic. In concurrent contexts, external
>> +            /// synchronization may be required to prevent race conditions.
>> +            #[inline(always)]
>> +            pub fn update<T, I, B, F>(
>> +                io: &T,
>> +                base: &B,
>> +                f: F,
>> +            ) where
>> +                T: ::core::ops::Deref<Target = I>,
>> +                I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
>> +                B: $crate::io::register::RegisterBase<$base>,
>> +                F: ::core::ops::FnOnce(Self) -> Self,
>> +            {
>> +                let reg = f(Self::read(io, base));
>> +                reg.write(io, base);
>> +            }
>> +        }
>> +    };
>> +
>> +    // Generates the IO accessors for an array of registers.
>> +    (@io_array $name:ident ($storage:ty) [ $size:expr ; $stride:expr ] @ $offset:literal) => {
>> +        #[allow(dead_code)]
>> +        impl $name {
>> +            /// Absolute offset of the register array.
>> +            pub const OFFSET: usize = $offset;
>> +            /// Number of elements in the array of registers.
>> +            pub const SIZE: usize = $size;
>> +            /// Number of bytes separating each element of the array of registers.
>> +            pub const STRIDE: usize = $stride;
>> +
>> +            /// Read the array register at index `idx` from its address in `io`.
>> +            #[inline(always)]
>> +            pub fn read<T, I>(
>> +                io: &T,
>> +                idx: usize,
>> +            ) -> Self where
>> +                T: ::core::ops::Deref<Target = I>,
>> +                I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
>> +            {
>> +                build_assert!(idx < Self::SIZE);
>> +
>> +                let offset = Self::OFFSET + (idx * Self::STRIDE);
>> +
>> +                Self(<$storage as $crate::io::register::RegisterIo>::read(io, offset))
>> +            }
>> +
>> +            /// Write the value contained in `self` to the array register with index `idx` in `io`.
>> +            #[inline(always)]
>> +            pub fn write<T, I>(
>> +                self,
>> +                io: &T,
>> +                idx: usize
>> +            ) where
>> +                T: ::core::ops::Deref<Target = I>,
>> +                I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
>> +            {
>> +                build_assert!(idx < Self::SIZE);
>> +
>> +                let offset = Self::OFFSET + (idx * Self::STRIDE);
>> +
>> +                <$storage as $crate::io::register::RegisterIo>::write(self.0, io, offset)
>> +            }
>> +
>> +            /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
>> +            /// new value to write back.
>> +            ///
>> +            /// Note that this operation is not atomic. In concurrent contexts, external
>> +            /// synchronization may be required to prevent race conditions.
>> +            #[inline(always)]
>> +            pub fn update<T, I, F>(
>> +                io: &T,
>> +                idx: usize,
>> +                f: F,
>> +            ) where
>> +                T: ::core::ops::Deref<Target = I>,
>> +                I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
>> +                F: ::core::ops::FnOnce(Self) -> Self,
>> +            {
>> +                let reg = f(Self::read(io, idx));
>> +                reg.write(io, idx);
>> +            }
>> +
>> +            /// Read the array register at index `idx` from its address in `io`.
>> +            ///
>> +            /// The validity of `idx` is checked at run-time, and `EINVAL` is returned if the
>> +            /// access was out-of-bounds.
>> +            #[inline(always)]
>> +            pub fn try_read<T, I>(
>> +                io: &T,
>> +                idx: usize,
>> +            ) -> ::kernel::error::Result<Self> where
>> +                T: ::core::ops::Deref<Target = I>,
>> +                I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
>> +            {
>> +                if idx < Self::SIZE {
>> +                    Ok(Self::read(io, idx))
>> +                } else {
>> +                    Err(::kernel::error::code::EINVAL)
>> +                }
>> +            }
>> +
>> +            /// Write the value contained in `self` to the array register with index `idx` in `io`.
>> +            ///
>> +            /// The validity of `idx` is checked at run-time, and `EINVAL` is returned if the
>> +            /// access was out-of-bounds.
>> +            #[inline(always)]
>> +            pub fn try_write<T, I>(
>> +                self,
>> +                io: &T,
>> +                idx: usize,
>> +            ) -> ::kernel::error::Result where
>> +                T: ::core::ops::Deref<Target = I>,
>> +                I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
>> +            {
>> +                if idx < Self::SIZE {
>> +                    Ok(self.write(io, idx))
>> +                } else {
>> +                    Err(::kernel::error::code::EINVAL)
>> +                }
>> +            }
>> +
>> +            /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
>> +            /// new value to write back.
>> +            ///
>> +            /// The validity of `idx` is checked at run-time, and `EINVAL` is returned if the
>> +            /// access was out-of-bounds.
>> +            ///
>> +            /// Note that this operation is not atomic. In concurrent contexts, external
>> +            /// synchronization may be required to prevent race conditions.
>> +            #[inline(always)]
>> +            pub fn try_update<T, I, F>(
>> +                io: &T,
>> +                idx: usize,
>> +                f: F,
>> +            ) -> ::kernel::error::Result where
>> +                T: ::core::ops::Deref<Target = I>,
>> +                I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
>> +                F: ::core::ops::FnOnce(Self) -> Self,
>> +            {
>> +                if idx < Self::SIZE {
>> +                    Ok(Self::update(io, idx, f))
>> +                } else {
>> +                    Err(::kernel::error::code::EINVAL)
>> +                }
>> +            }
>> +        }
>> +    };
>> +
>> +    // Generates the IO accessors for an array of relative registers.
>> +    (
>> +        @io_relative_array $name:ident ($storage:ty) [ $size:expr ; $stride:expr ]
>> +            @ $base:ident + $offset:literal
>> +    ) => {
>> +        #[allow(dead_code)]
>> +        impl $name {
>> +            /// Relative offset of the register array.
>> +            pub const OFFSET: usize = $offset;
>> +            /// Number of elements in the array of registers.
>> +            pub const SIZE: usize = $size;
>> +            /// Number of bytes separating each element of the array of registers.
>> +            pub const STRIDE: usize = $stride;
>> +
>> +            /// Read the array register at index `idx` from `io`, using the base address provided
>> +            /// by `base` and adding the register's offset to it.
>> +            #[inline(always)]
>> +            pub fn read<T, I, B>(
>> +                io: &T,
>> +                #[allow(unused_variables)]
>> +                base: &B,
>> +                idx: usize,
>> +            ) -> Self where
>> +                T: ::core::ops::Deref<Target = I>,
>> +                I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
>> +                B: $crate::io::register::RegisterBase<$base>,
>> +            {
>> +                build_assert!(idx < Self::SIZE);
>> +
>> +                let offset = <B as $crate::io::register::RegisterBase<$base>>::BASE +
>> +                    Self::OFFSET + (idx * Self::STRIDE);
>> +
>> +                Self(<$storage as $crate::io::register::RegisterIo>::read(io, offset))
>> +            }
>> +
>> +            /// Write the value contained in `self` to `io`, using the base address provided by
>> +            /// `base` and adding the offset of array register `idx` to it.
>> +            #[inline(always)]
>> +            pub fn write<T, I, B>(
>> +                self,
>> +                io: &T,
>> +                #[allow(unused_variables)]
>> +                base: &B,
>> +                idx: usize
>> +            ) where
>> +                T: ::core::ops::Deref<Target = I>,
>> +                I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
>> +                B: $crate::io::register::RegisterBase<$base>,
>> +            {
>> +                build_assert!(idx < Self::SIZE);
>> +
>> +                let offset = <B as $crate::io::register::RegisterBase<$base>>::BASE +
>> +                    Self::OFFSET + (idx * Self::STRIDE);
>> +
>> +                <$storage as $crate::io::register::RegisterIo>::write(self.0, io, offset)
>> +            }
>> +
>> +            /// Read the array register at index `idx` from `io`, using the base address provided
>> +            /// by `base` and adding the register's offset to it, then run `f` on its value to
>> +            /// obtain a new value to write back.
>> +            ///
>> +            /// Note that this operation is not atomic. In concurrent contexts, external
>> +            /// synchronization may be required to prevent race conditions.
>> +            #[inline(always)]
>> +            pub fn update<T, I, B, F>(
>> +                io: &T,
>> +                base: &B,
>> +                idx: usize,
>> +                f: F,
>> +            ) where
>> +                T: ::core::ops::Deref<Target = I>,
>> +                I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
>> +                B: $crate::io::register::RegisterBase<$base>,
>> +                F: ::core::ops::FnOnce(Self) -> Self,
>> +            {
>> +                let reg = f(Self::read(io, base, idx));
>> +                reg.write(io, base, idx);
>> +            }
>> +
>> +            /// Read the array register at index `idx` from `io`, using the base address provided
>> +            /// by `base` and adding the register's offset to it.
>> +            ///
>> +            /// The validity of `idx` is checked at run-time, and `EINVAL` is returned if the
>> +            /// access was out-of-bounds.
>> +            #[inline(always)]
>> +            pub fn try_read<T, I, B>(
>> +                io: &T,
>> +                base: &B,
>> +                idx: usize,
>> +            ) -> ::kernel::error::Result<Self> where
>> +                T: ::core::ops::Deref<Target = I>,
>> +                I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
>> +                B: $crate::io::register::RegisterBase<$base>,
>> +            {
>> +                if idx < Self::SIZE {
>> +                    Ok(Self::read(io, base, idx))
>> +                } else {
>> +                    Err(::kernel::error::code::EINVAL)
>> +                }
>> +            }
>> +
>> +            /// Write the value contained in `self` to `io`, using the base address provided by
>> +            /// `base` and adding the offset of array register `idx` to it.
>> +            ///
>> +            /// The validity of `idx` is checked at run-time, and `EINVAL` is returned if the
>> +            /// access was out-of-bounds.
>> +            #[inline(always)]
>> +            pub fn try_write<T, I, B>(
>> +                self,
>> +                io: &T,
>> +                base: &B,
>> +                idx: usize,
>> +            ) -> ::kernel::error::Result where
>> +                T: ::core::ops::Deref<Target = I>,
>> +                I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
>> +                B: $crate::io::register::RegisterBase<$base>,
>> +            {
>> +                if idx < Self::SIZE {
>> +                    Ok(self.write(io, base, idx))
>> +                } else {
>> +                    Err(::kernel::error::code::EINVAL)
>> +                }
>> +            }
>> +
>> +            /// Read the array register at index `idx` from `io`, using the base address provided
>> +            /// by `base` and adding the register's offset to it, then run `f` on its value to
>> +            /// obtain a new value to write back.
>> +            ///
>> +            /// The validity of `idx` is checked at run-time, and `EINVAL` is returned if the
>> +            /// access was out-of-bounds.
>> +            ///
>> +            /// Note that this operation is not atomic. In concurrent contexts, external
>> +            /// synchronization may be required to prevent race conditions.
>> +            #[inline(always)]
>> +            pub fn try_update<T, I, B, F>(
>> +                io: &T,
>> +                base: &B,
>> +                idx: usize,
>> +                f: F,
>> +            ) -> ::kernel::error::Result where
>> +                T: ::core::ops::Deref<Target = I>,
>> +                I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
>> +                B: $crate::io::register::RegisterBase<$base>,
>> +                F: ::core::ops::FnOnce(Self) -> Self,
>> +            {
>> +                if idx < Self::SIZE {
>> +                    Ok(Self::update(io, base, idx, f))
>> +                } else {
>> +                    Err(::kernel::error::code::EINVAL)
>> +                }
>> +            }
>> +        }
>> +    };
>> +
>> +    // Defines the wrapper `$name` type and its conversions from/to the storage type.
>> +    (@bitfield_core $(#[$attr:meta])* $vis:vis $name:ident $storage:ty) => {
>> +        $(#[$attr])*
>> +        #[repr(transparent)]
>> +        #[derive(Clone, Copy, PartialEq, Eq)]
>
> You can derive `Zeroable` and save you an unsafe impl.

Unfortunately this happens if I try to do it:

    error: no rules expected `(`
		    --> ../samples/rust/rust_driver_pci.rs:74:9
		    |
	    74 | /         ::kernel::register! {
	    75 | |             VENDOR_ID(u16) @ 0x0 {
	    76 | |                 15:0 vendor_id;
    ...    |
	    86 | |         }
		    | |_________^ no rules expected this token in macro call

Not quite sure why to be honest.

>
>> +        $vis struct $name($storage);
>> +
>> +        #[allow(dead_code)]
>> +        impl $name {
>> +            /// Creates a bitfield from a raw value.
>> +            $vis const fn from_raw(value: $storage) -> Self {
>> +                Self(value)
>> +            }
>> +
>> +            /// Creates a zeroed bitfield value.
>> +            ///
>> +            /// This is a const alternative to the `Zeroable::zeroed()` trait method.
>> +            $vis const fn zeroed() -> Self {
>> +                Self(0)
>> +            }
>
> All types that impl `Zeroable` automatically have the `::zeroed()` fn provided
> via the trait.

Yes, but that method from the trait cannot be used in const context, and
`zeroed` is the starting point for building register values from scratch (and
thus constant values).

>
>> +
>> +            /// Returns the raw value of this bitfield.
>> +            ///
>> +            /// This is similar to the [`From`] implementation, but is shorter to invoke in
>> +            /// most cases.
>> +            $vis const fn as_raw(self) -> $storage {
>> +                self.0
>> +            }
>> +        }
>> +
>> +        // SAFETY: `$storage` is `Zeroable` and `$name` is transparent.
>> +        unsafe impl ::pin_init::Zeroable for $name {}
>> +
>> +        impl ::core::convert::From<$name> for $storage {
>> +            fn from(val: $name) -> $storage {
>> +                val.as_raw()
>> +            }
>> +        }
>> +
>> +        impl ::core::convert::From<$storage> for $name {
>> +            fn from(val: $storage) -> $name {
>> +                Self::from_raw(val)
>> +            }
>> +        }
>> +    };
>> +
>> +    // Definitions requiring knowledge of individual fields: private and public field accessors,
>> +    // and `Debug` implementation.
>> +    (@bitfield_fields $vis:vis $name:ident $storage:ty {
>> +        $($(#[doc = $doc:expr])* $hi:literal:$lo:literal $field:ident
>> +            $(?=> $try_into_type:ty)?
>> +            $(=> $into_type:ty)?
>> +        ;
>> +        )*
>> +    }
>> +    ) => {
>> +        #[allow(dead_code)]
>> +        impl $name {
>> +        $(
>> +        ::kernel::register!(@private_field_accessors $vis $name $storage : $hi:$lo $field);
>> +        ::kernel::register!(
>> +            @public_field_accessors $(#[doc = $doc])* $vis $name $storage : $hi:$lo $field
>> +            $(?=> $try_into_type)?
>> +            $(=> $into_type)?
>> +        );
>> +        )*
>> +        }
>> +
>> +        ::kernel::register!(@debug $name { $($field;)* });
>> +    };
>> +
>> +    // Private field accessors working with the correct `Bounded` type for the field.
>> +    (
>> +        @private_field_accessors $vis:vis $name:ident $storage:ty : $hi:tt:$lo:tt $field:ident
>> +    ) => {
>> +        ::kernel::macros::paste!(
>> +        $vis const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
>
> Is this used by anything?

Not yet. This is intended for user code that wants to know which bits are used
by a specific field. I don't mind removing it though.

Thanks for the review and ideas!

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ