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: <DG14TFQG2C13.2BFREQR1DV4I@garyguo.net>
Date: Thu, 29 Jan 2026 14:10:36 +0000
From: "Gary Guo" <gary@...yguo.net>
To: "Alexandre Courbot" <acourbot@...dia.com>, "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 8:00 AM GMT, Alexandre Courbot wrote:
> 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.

The issue with `Deref` bound here is that now you *require* a level of
indirection. If something implements `Io` directly, you cannot use it with the
method. While `&Bar` is accepted, `&Mmio` is not because `Mmio` is a direct
implementor of `Io` and not deref to it.

A `Deref` bound also does not help if, say, a type is `Arc<Bar>` which needs two
level of dereffing before it is Io. For consistency I think it's best to avoid
`Deref` call all together

>
> 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?).

It'll always resolve to the correct crate.

In theory, a crate can do

    extern crate kernel as shell;
    extern crate foobar as kernel;

and now ::kernel:: would resolve to the wrong crate. Of course, people doing
this are just shooting their own foot so it's not something to worry about.

Using `$crate::` also allow re-exporting of the macro without adding a direct
dependency (as indirect dependencies are not visible with ::crate_name).

Neither are issue of the `kernel` crate, but in general it's advisable to use
`$crate` where possible.

>
>>
>>> +            @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.

I'll look into this.

>
>>
>>> +        $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).

`pin_init:::zeroed()` is a const function.

Best,
Gary

>
>>
>>> +
>>> +            /// 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