All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alexandre Courbot <acourbot@nvidia.com>
To: "Danilo Krummrich" <dakr@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Daniel Almeida" <daniel.almeida@collabora.com>,
	"Miguel Ojeda" <ojeda@kernel.org>, "Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <lossin@kernel.org>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Boqun Feng" <boqun@kernel.org>
Cc: Yury Norov <yury.norov@gmail.com>,
	John Hubbard <jhubbard@nvidia.com>,
	 Alistair Popple <apopple@nvidia.com>,
	 Joel Fernandes <joelagnelf@nvidia.com>,
	Timur Tabi <ttabi@nvidia.com>,  Edwin Peer <epeer@nvidia.com>,
	Eliot Courtney <ecourtney@nvidia.com>,
	 Dirk Behme <dirk.behme@de.bosch.com>,
	Steven Price <steven.price@arm.com>,
	 rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org,
	 Alexandre Courbot <acourbot@nvidia.com>
Subject: [PATCH v7 05/10] rust: io: add IoLoc and IoWrite types
Date: Tue, 24 Feb 2026 23:21:43 +0900	[thread overview]
Message-ID: <20260224-register-v7-5-aad44f760f33@nvidia.com> (raw)
In-Reply-To: <20260224-register-v7-0-aad44f760f33@nvidia.com>

I/O accesses are defined by the following properties:

- For reads, a start address, a width, and a type to interpret the read
  value as,
- For writes, the same as above, and a value to write.

Introduce the `IoLoc` trait, which allows implementing types to specify
the address a type expects to be accessed at, as well as the width of
the access, and the user-facing type used to perform the access.

This allows read operations to be made generic with the `read` method
over an `IoLoc` argument.

Write operations need a value to write on top of the `IoLoc`: fulfill
that purpose with the `IoWrite` type, which is the combination of an
`IoLoc` and a value of the type it expects. This allows write operations
to be made generic with the `write` method over a single `IoWrite`
argument.

The main purpose of these new entities is to allow register types to be
written using these generic `read` and `write` methods of `Io`.

Co-developed-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
 rust/kernel/io.rs | 241 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 241 insertions(+)

diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index b150743ffa4f..fdd2549d8e13 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -173,6 +173,158 @@ pub trait IoCapable<T> {
     unsafe fn io_write(&self, value: T, address: usize);
 }
 
+/// Describes a given I/O location: its offset, width, and return type.
+///
+/// This trait is the key abstraction allowing [`Io::read`], [`Io::write`], and [`Io::update`]
+/// to work uniformly with both raw `usize` offsets (for primitive types like `u32`) and typed
+/// ones.
+///
+/// An `IoLoc<T>` carries three pieces of information:
+///
+/// - The offset to access (returned by [`IoLoc::offset`]),
+/// - The width of the access (determined by [`IoLoc::IoType`]),
+/// - The type `T` in which data is returned or provided.
+///
+/// `T` and `IoType` may differ: for instance, a typed register has `T` = the register type with
+/// its bitfields, and `IoType` = its backing primitive (e.g. `u32`), with `Into` conversions
+/// between them.
+///
+/// An `IoLoc` can be passed directly to [`Io::read`] or [`Io::try_read`] to obtain a value, or
+/// turned into an [`IoWrite`] via [`IoLoc::set`] to be passed to [`Io::write`] or
+/// [`Io::try_write`].
+pub trait IoLoc<T>: Copy
+where
+    T: Into<Self::IoType>,
+    Self::IoType: Into<T>,
+{
+    /// Size (`u8`, `u16`, etc) of the I/O performed on the returned [`offset`](IoLoc::offset).
+    type IoType;
+
+    /// Returns the offset of this location.
+    fn offset(self) -> usize;
+
+    /// Turns this location into an [`IoWrite`] with the initial `value`.
+    fn set(self, value: T) -> IoWrite<T, Self> {
+        IoWrite { value, loc: self }
+    }
+
+    /// Turns this location into an [`IoWrite`] with the initial value `0`.
+    fn zeroed(self) -> IoWrite<T, Self>
+    where
+        T: Zeroable,
+    {
+        self.set(pin_init::zeroed())
+    }
+
+    /// Turns this location into an [`IoWrite`] with the initial [`Default`] value of `T`.
+    fn default(self) -> IoWrite<T, Self>
+    where
+        T: Default,
+    {
+        self.set(Default::default())
+    }
+
+    /// Turns this location into an [`IoWrite`] initialized from `0` and transformed by `f`.
+    ///
+    /// This is a shortcut for `self.zeroed().update(f)`.
+    fn init<F>(self, f: F) -> IoWrite<T, Self>
+    where
+        T: Zeroable,
+        F: FnOnce(T) -> T,
+    {
+        self.zeroed().update(f)
+    }
+
+    /// Turns this location into an [`IoWrite`] initialized from `0` and transformed by `f`.
+    ///
+    /// `f` is expected to return a [`Result<T>`].
+    ///
+    /// This is a shortcut for `self.zeroed().try_update(f)`.
+    fn try_init<F, E>(self, f: F) -> Result<IoWrite<T, Self>, E>
+    where
+        T: Zeroable,
+        F: FnOnce(T) -> Result<T, E>,
+    {
+        self.zeroed().try_update(f)
+    }
+
+    /// Turns this location into an [`IoWrite`] initialized from [`Default`] and transformed
+    /// by `f`.
+    ///
+    /// This is a shortcut for `self.default().update(f)`.
+    fn init_default<F>(self, f: F) -> IoWrite<T, Self>
+    where
+        T: Default,
+        F: FnOnce(T) -> T,
+    {
+        self.default().update(f)
+    }
+
+    /// Turns this location into an [`IoWrite`] initialized from [`Default`] and transformed by
+    /// `f`.
+    ///
+    /// `f` is expected to return a [`Result<T>`].
+    ///
+    /// This is a shortcut for `self.default().try_update(f)`.
+    fn try_init_default<F, E>(self, f: F) -> Result<IoWrite<T, Self>, E>
+    where
+        T: Default,
+        F: FnOnce(T) -> Result<T, E>,
+    {
+        self.default().try_update(f)
+    }
+}
+
+/// A pending I/O write operation, bundling a value with the [`IoLoc`] it should be written to.
+///
+/// Created by [`IoLoc::set`], [`IoLoc::zeroed`], [`IoLoc::default`], [`IoLoc::init`], or
+/// [`IoLoc::init_default`], and consumed by [`Io::write`] or [`Io::try_write`] to perform the
+/// actual write.
+///
+/// The value can be modified before writing using [`IoWrite::update`] or [`IoWrite::try_update`],
+/// enabling a builder pattern:
+///
+/// ```ignore
+/// io.write(REGISTER.init(|v| v.with_field(x)));
+/// ```
+pub struct IoWrite<T, R>
+where
+    R: IoLoc<T>,
+    T: Into<R::IoType>,
+{
+    value: T,
+    loc: R,
+}
+
+impl<R, T> IoWrite<T, R>
+where
+    R: IoLoc<T>,
+    T: Into<R::IoType>,
+{
+    /// Transforms the value to be written by applying `f`, returning the modified [`IoWrite`].
+    #[inline(always)]
+    pub fn update<F>(mut self, f: F) -> Self
+    where
+        F: FnOnce(T) -> T,
+    {
+        self.value = f(self.value);
+
+        self
+    }
+
+    /// Transforms the value to be written by applying `f`, returning the modified [`IoWrite`] on
+    /// success.
+    #[inline(always)]
+    pub fn try_update<F, E>(mut self, f: F) -> Result<Self, E>
+    where
+        F: FnOnce(T) -> Result<T, E>,
+    {
+        self.value = f(self.value)?;
+
+        Ok(self)
+    }
+}
+
 /// Types implementing this trait (e.g. MMIO BARs or PCI config regions)
 /// can perform I/O operations on regions of memory.
 ///
@@ -406,6 +558,95 @@ fn write64(&self, value: u64, offset: usize)
         // SAFETY: `address` has been validated by `io_addr_assert`.
         unsafe { self.io_write(value, address) }
     }
+
+    /// Generic fallible read with runtime bounds check.
+    #[inline(always)]
+    fn try_read<T, R>(&self, r: R) -> Result<T>
+    where
+        R: IoLoc<T>,
+        T: Into<R::IoType>,
+        Self: IoCapable<R::IoType>,
+    {
+        let address = self.io_addr::<R::IoType>(r.offset())?;
+
+        // SAFETY: `address` has been validated by `io_addr`.
+        Ok(unsafe { self.io_read(address) }.into())
+    }
+
+    /// Generic fallible write with runtime bounds check.
+    #[inline(always)]
+    fn try_write<T, R>(&self, op: IoWrite<T, R>) -> Result
+    where
+        R: IoLoc<T>,
+        T: Into<R::IoType>,
+        Self: IoCapable<R::IoType>,
+    {
+        let address = self.io_addr::<R::IoType>(op.loc.offset())?;
+
+        // SAFETY: `address` has been validated by `io_addr`.
+        unsafe { self.io_write(op.value.into(), address) };
+        Ok(())
+    }
+
+    /// Generic fallible update with runtime bounds check.
+    ///
+    /// Caution: this does not perform any synchronization. Race conditions can occur in case of
+    /// concurrent access.
+    #[inline(always)]
+    fn try_update<T, R, F>(&self, r: R, f: F) -> Result
+    where
+        R: IoLoc<T>,
+        T: Into<R::IoType>,
+        Self: IoCapable<R::IoType>,
+        F: FnOnce(T) -> T,
+    {
+        let v = self.try_read(r)?;
+        self.try_write(r.set(f(v)))
+    }
+
+    /// Generic infallible read with compile-time bounds check.
+    #[inline(always)]
+    fn read<T, R>(&self, r: R) -> T
+    where
+        R: IoLoc<T>,
+        T: Into<R::IoType>,
+        Self: IoKnownSize + IoCapable<R::IoType>,
+    {
+        let address = self.io_addr_assert::<R::IoType>(r.offset());
+
+        // SAFETY: `address` has been validated by `io_addr_assert`.
+        unsafe { self.io_read(address) }.into()
+    }
+
+    /// Generic infallible write with compile-time bounds check.
+    #[inline(always)]
+    fn write<T, R>(&self, op: IoWrite<T, R>)
+    where
+        R: IoLoc<T>,
+        T: Into<R::IoType>,
+        Self: IoKnownSize + IoCapable<R::IoType>,
+    {
+        let address = self.io_addr_assert::<R::IoType>(op.loc.offset());
+
+        // SAFETY: `address` has been validated by `io_addr_assert`.
+        unsafe { self.io_write(op.value.into(), address) }
+    }
+
+    /// Generic infallible update with compile-time bounds check.
+    ///
+    /// Caution: this does not perform any synchronization. Race conditions can occur in case of
+    /// concurrent access.
+    #[inline(always)]
+    fn update<T, R, F>(&self, r: R, f: F)
+    where
+        R: IoLoc<T>,
+        T: Into<R::IoType>,
+        Self: IoKnownSize + IoCapable<R::IoType> + Sized,
+        F: FnOnce(T) -> T,
+    {
+        let v = self.read(r);
+        self.write(r.set(f(v)));
+    }
 }
 
 /// Trait for types with a known size at compile time.

-- 
2.53.0


  parent reply	other threads:[~2026-02-24 14:22 UTC|newest]

Thread overview: 64+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-02-24 14:21 [PATCH v7 00/10] rust: add `register!` macro Alexandre Courbot
2026-02-24 14:21 ` [PATCH v7 01/10] rust: enable the `generic_arg_infer` feature Alexandre Courbot
2026-02-24 14:21 ` [PATCH v7 02/10] rust: num: add `shr` and `shl` methods to `Bounded` Alexandre Courbot
2026-02-24 14:21 ` [PATCH v7 03/10] rust: num: add `into_bool` method " Alexandre Courbot
2026-02-24 14:21 ` [PATCH v7 04/10] rust: num: make Bounded::get const Alexandre Courbot
2026-02-27 12:33   ` Gary Guo
2026-02-24 14:21 ` Alexandre Courbot [this message]
2026-02-27 18:02   ` [PATCH v7 05/10] rust: io: add IoLoc and IoWrite types Gary Guo
2026-02-27 18:16     ` Danilo Krummrich
2026-02-28  0:33     ` Alexandre Courbot
2026-03-01 15:11       ` Gary Guo
2026-03-02  1:44         ` Alexandre Courbot
2026-03-02 12:53           ` Gary Guo
2026-03-02 13:12             ` Danilo Krummrich
2026-03-02 13:39               ` Gary Guo
2026-03-03  8:14                 ` Alexandre Courbot
2026-03-03  8:31                   ` Alexandre Courbot
2026-03-03 14:55                     ` Alexandre Courbot
2026-03-03 15:05                       ` Gary Guo
2026-03-04 16:18                       ` Danilo Krummrich
2026-03-04 18:39                         ` Gary Guo
2026-03-04 18:58                           ` Gary Guo
2026-03-04 19:19                             ` John Hubbard
2026-03-04 19:53                               ` Danilo Krummrich
2026-03-04 19:57                                 ` John Hubbard
2026-03-04 20:05                                 ` Gary Guo
2026-03-04 19:38                             ` Danilo Krummrich
2026-03-04 19:48                               ` Gary Guo
2026-03-04 20:37                                 ` Danilo Krummrich
2026-03-04 21:13                                   ` Gary Guo
2026-03-04 21:38                                     ` Danilo Krummrich
2026-03-04 21:42                                       ` Danilo Krummrich
2026-03-04 22:15                                       ` Gary Guo
2026-03-04 22:22                                         ` Danilo Krummrich
2026-03-06  5:37                                         ` Alexandre Courbot
2026-03-06  7:47                                           ` Alexandre Courbot
2026-03-06 10:42                                           ` Gary Guo
2026-03-06 11:10                                             ` Alexandre Courbot
2026-03-06 11:35                                               ` Gary Guo
2026-03-06 12:50                                                 ` Alexandre Courbot
2026-03-06 13:20                                                   ` Gary Guo
2026-03-06 14:32                                                     ` Alexandre Courbot
2026-03-06 14:52                                                       ` Alexandre Courbot
2026-03-06 15:10                                                       ` Alexandre Courbot
2026-03-06 15:35                                                         ` Alexandre Courbot
2026-03-06 15:35                                                       ` Gary Guo
2026-03-07  0:05                                                         ` Alexandre Courbot
2026-03-07 21:10                                                           ` Gary Guo
2026-03-07 21:40                                                             ` Danilo Krummrich
2026-03-08 11:43                                                               ` Alexandre Courbot
2026-03-08 11:35                                                             ` Alexandre Courbot
2026-03-04 18:53                         ` Gary Guo
2026-03-04 22:19   ` Gary Guo
2026-03-05 11:02     ` Alexandre Courbot
2026-02-24 14:21 ` [PATCH v7 06/10] rust: io: use generic read/write accessors for primitive accesses Alexandre Courbot
2026-02-27 18:04   ` Gary Guo
2026-02-24 14:21 ` [PATCH v7 07/10] rust: io: add `register!` macro Alexandre Courbot
2026-02-24 14:21 ` [PATCH v7 08/10] sample: rust: pci: use " Alexandre Courbot
2026-02-24 14:21 ` [PATCH FOR REFERENCE v7 09/10] gpu: nova-core: use the kernel " Alexandre Courbot
2026-02-24 14:21 ` [PATCH v7 10/10] RFC: rust: io: allow fixed register values directly in `write` Alexandre Courbot
2026-02-25 11:58 ` [PATCH v7 00/10] rust: add `register!` macro Dirk Behme
2026-02-25 13:50   ` Alexandre Courbot
2026-02-26 12:01     ` Dirk Behme
2026-02-27 23:30       ` Alexandre Courbot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260224-register-v7-5-aad44f760f33@nvidia.com \
    --to=acourbot@nvidia.com \
    --cc=a.hindborg@kernel.org \
    --cc=aliceryhl@google.com \
    --cc=apopple@nvidia.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun@kernel.org \
    --cc=dakr@kernel.org \
    --cc=daniel.almeida@collabora.com \
    --cc=dirk.behme@de.bosch.com \
    --cc=ecourtney@nvidia.com \
    --cc=epeer@nvidia.com \
    --cc=gary@garyguo.net \
    --cc=jhubbard@nvidia.com \
    --cc=joelagnelf@nvidia.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=steven.price@arm.com \
    --cc=tmgross@umich.edu \
    --cc=ttabi@nvidia.com \
    --cc=yury.norov@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.