Rust for Linux List
 help / color / mirror / Atom feed
From: Gary Guo <gary@garyguo.net>
To: "Danilo Krummrich" <dakr@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Daniel Almeida" <daniel.almeida@collabora.com>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Boqun Feng" <boqun@kernel.org>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <lossin@kernel.org>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Tamir Duberstein" <tamird@kernel.org>,
	"Alexandre Courbot" <acourbot@nvidia.com>,
	"Onur Özkan" <work@onurozkan.dev>,
	"David Airlie" <airlied@gmail.com>,
	"Simona Vetter" <simona@ffwll.ch>,
	"Bjorn Helgaas" <bhelgaas@google.com>,
	"Krzysztof Wilczyński" <kwilczynski@kernel.org>
Cc: driver-core@lists.linux.dev, rust-for-linux@vger.kernel.org,
	 linux-kernel@vger.kernel.org, nova-gpu@lists.linux.dev,
	 dri-devel@lists.freedesktop.org, linux-pci@vger.kernel.org,
	 Gary Guo <gary@garyguo.net>
Subject: [PATCH v3 10/16] rust: io: register: make register have a typed base
Date: Wed, 19 Aug 2026 12:09:18 +0100	[thread overview]
Message-ID: <20260819-typed_register-v3-10-3699a75fadf1@garyguo.net> (raw)
In-Reply-To: <20260819-typed_register-v3-0-3699a75fadf1@garyguo.net>

Previously `register!` defined registers can be used on any untyped I/O
regions. With all users specifying their desired register type now,
propagate the specified type and restrict I/O access only when type
matches.

Also, add an `io_project!` example which is enabled by this change.

Signed-off-by: Gary Guo <gary@garyguo.net>
---
 rust/kernel/io.rs          | 13 +++++++++++
 rust/kernel/io/register.rs | 55 ++++++++++++++++++++++++++++++++--------------
 rust/macros/io/register.rs | 34 ++++++++++++++--------------
 3 files changed, 68 insertions(+), 34 deletions(-)

diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index 84dd876b3407..4542187d6b91 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -1692,21 +1692,34 @@ pub fn project_loc<U, L>(self, location: L) -> <T::Backend as IoBackend>::View<'
 /// The syntax is of form `io_project!(io, proj)` where `io` is an expression to a type that
 /// implements [`Io`] and `proj` is a [projection specification](kernel::ptr::project!).
 ///
+/// `io_project!` can also project to subview of registers defined with [`register!`] macro.
+/// Register projection has syntax `io_project!(io, try: REGISTER)` for fallible projection and
+/// `io_project!(io, build: REGISTER)` for infallible projection.
+///
 /// # Examples
 ///
 /// ```
 /// use kernel::io::{
 ///     io_project,
+///     register,
 ///     Mmio,
 /// };
 /// #[repr(C)]
 /// struct MyStruct { field: u32, }
 ///
+/// register! {
+///     base: MyStruct;
+///     FIELD(u32) @ 0 {
+///         31:0 val;
+///     }
+/// }
+///
 /// # fn test(mmio: Mmio<'_, [MyStruct]>) -> Result {
 /// // let mmio: Mmio<[MyStruct]>;
 /// let field: Mmio<'_, u32> = io_project!(mmio, [try: 1].field);
 /// let whole: Mmio<'_, MyStruct> = io_project!(mmio, [try: 2]);
 /// let nested: Mmio<'_, u32> = io_project!(whole, .field);
+/// let reg: Mmio<'_, FIELD> = io_project!(whole, build: FIELD);
 /// # Ok::<(), Error>(()) }
 /// ```
 #[macro_export]
diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs
index 49a5fe069e47..f622057f7346 100644
--- a/rust/kernel/io/register.rs
+++ b/rust/kernel/io/register.rs
@@ -121,10 +121,11 @@
     io::IoLoc, //
 };
 
-use super::Region;
-
 /// Trait implemented by all registers.
 pub trait Register: Sized {
+    /// Base type for this register.
+    type Base: ?Sized;
+
     /// Start offset of the register.
     ///
     /// The interpretation of this offset depends on the type of the register.
@@ -136,9 +137,9 @@ pub trait FixedRegister: Register {}
 
 /// Allows `()` to be used as the `location` parameter of [`Io::write`](super::Io::write) when
 /// passing a [`FixedRegister`] value.
-impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for ()
+impl<Base: ?Sized, T> IoLoc<Base, T> for ()
 where
-    T: FixedRegister,
+    T: FixedRegister<Base = Base>,
 {
     #[inline(always)]
     fn offset(self) -> usize {
@@ -148,9 +149,9 @@ fn offset(self) -> usize {
 
 /// A [`FixedRegister`] carries its location in its type. Thus `FixedRegister` values can be used
 /// as an [`IoLoc`].
-impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for T
+impl<Base: ?Sized, T> IoLoc<Base, T> for T
 where
-    T: FixedRegister,
+    T: FixedRegister<Base = Base>,
 {
     #[inline(always)]
     fn offset(self) -> usize {
@@ -171,9 +172,9 @@ pub const fn new() -> Self {
     }
 }
 
-impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for FixedRegisterLoc<T>
+impl<Base: ?Sized, T> IoLoc<Base, T> for FixedRegisterLoc<T>
 where
-    T: FixedRegister,
+    T: FixedRegister<Base = Base>,
 {
     #[inline(always)]
     fn offset(self) -> usize {
@@ -240,9 +241,9 @@ const fn offset(self) -> usize {
     }
 }
 
-impl<const SIZE: usize, T, B> IoLoc<Region<SIZE>, T> for RelativeRegisterLoc<T, B>
+impl<SuperBase: ?Sized, T, B> IoLoc<SuperBase, T> for RelativeRegisterLoc<T, B>
 where
-    T: RelativeRegister,
+    T: RelativeRegister<Base = SuperBase>,
     B: RegisterBase<T::BaseFamily> + ?Sized,
 {
     #[inline(always)]
@@ -282,9 +283,9 @@ pub fn try_new(idx: usize) -> Option<Self> {
     }
 }
 
-impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for RegisterArrayLoc<T>
+impl<Base: ?Sized, T> IoLoc<Base, T> for RegisterArrayLoc<T>
 where
-    T: RegisterArray,
+    T: RegisterArray<Base = Base>,
 {
     #[inline(always)]
     fn offset(self) -> usize {
@@ -367,9 +368,9 @@ pub fn try_at(self, idx: usize) -> Option<RelativeRegisterArrayLoc<T, B>> {
     }
 }
 
-impl<const SIZE: usize, T, B> IoLoc<Region<SIZE>, T> for RelativeRegisterArrayLoc<T, B>
+impl<SuperBase: ?Sized, T, B> IoLoc<SuperBase, T> for RelativeRegisterArrayLoc<T, B>
 where
-    T: RelativeRegisterArray,
+    T: RelativeRegisterArray<Base = SuperBase>,
     B: RegisterBase<T::BaseFamily> + ?Sized,
 {
     #[inline(always)]
@@ -393,9 +394,9 @@ pub trait LocatedRegister<Base: ?Sized> {
     fn into_io_op(self) -> (Self::Location, Self::Value);
 }
 
-impl<const SIZE: usize, T> LocatedRegister<Region<SIZE>> for T
+impl<Base: ?Sized, T> LocatedRegister<Base> for T
 where
-    T: FixedRegister,
+    T: FixedRegister<Base = Base>,
 {
     type Location = FixedRegisterLoc<Self::Value>;
     type Value = T;
@@ -406,6 +407,28 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
     }
 }
 
+/// Helper function for register alias implementation.
+///
+/// This is used to enforce base matching. Only called during const eval.
+#[doc(hidden)]
+#[inline(always)]
+pub const fn alias_offset<Base: ?Sized, Alias: Register<Base = Base>>() -> usize {
+    Alias::OFFSET
+}
+
+/// Helper function for register element alias implementation.
+///
+/// This is used to enforce base matching and provide bounds checking. Only called during const
+/// eval.
+#[doc(hidden)]
+#[inline(always)]
+pub const fn element_alias_offset<Base: ?Sized, Alias: RegisterArray<Base = Base>>(
+    idx: usize,
+) -> usize {
+    build_assert!(idx < Alias::SIZE);
+    Alias::OFFSET + idx * Alias::STRIDE
+}
+
 /// 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`](kernel::io::Io) region.
 ///
diff --git a/rust/macros/io/register.rs b/rust/macros/io/register.rs
index 2fb48e1be82d..cb02e850b23f 100644
--- a/rust/macros/io/register.rs
+++ b/rust/macros/io/register.rs
@@ -140,21 +140,23 @@ fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
 }
 
 pub(crate) struct RegDef {
-    base: Option<Type>,
+    base: Type,
     regs: Vec<Reg>,
 }
 
 impl Parse for RegDef {
     fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
-        let base = if input.peek(kw::base) {
-            let _: kw::base = input.parse()?;
-            let _: Token![:] = input.parse()?;
-            let base = input.parse()?;
-            let _: Token![;] = input.parse()?;
-            Some(base)
-        } else {
-            None
-        };
+        if !input.peek(kw::base) {
+            Err(input.error(
+                "a base type needs to be specified for `register!` invocation with `base: ty;`",
+            ))?;
+        }
+
+        let _: kw::base = input.parse()?;
+        let _: Token![:] = input.parse()?;
+        let base = input.parse()?;
+        let _: Token![;] = input.parse()?;
+
         let mut regs = Vec::new();
         while !input.is_empty() {
             regs.push(input.parse()?);
@@ -166,6 +168,7 @@ fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
 pub(crate) fn register(def: RegDef) -> Result<TokenStream> {
     let mut outputs = TokenStream::new();
 
+    let base = &def.base;
     for reg in def.regs {
         let Reg {
             attrs,
@@ -186,18 +189,12 @@ pub(crate) fn register(def: RegDef) -> Result<TokenStream> {
             RegOffset::Fixed { offset } => quote!(#offset),
             RegOffset::Alias { alias } => {
                 quote_spanned!(alias.span().resolved_at(span) =>
-                    <#alias as ::kernel::io::register::Register>::OFFSET
+                    ::kernel::io::register::alias_offset::<#base, #alias>()
                 )
             }
             RegOffset::ElementAlias { alias, idx } => {
-                outputs.extend(quote_spanned!(idx.span().resolved_at(span) =>
-                    ::kernel::build_assert::static_assert!(
-                        #idx < <#alias as ::kernel::io::register::RegisterArray>::SIZE
-                    );
-                ));
                 quote_spanned!(alias.span().resolved_at(span) =>
-                    <#alias as ::kernel::io::register::Register>::OFFSET
-                        + #idx * <#alias as ::kernel::io::register::RegisterArray>::STRIDE
+                    ::kernel::io::register::element_alias_offset::<#base, #alias>(#idx)
                 )
             }
         };
@@ -211,6 +208,7 @@ pub(crate) fn register(def: RegDef) -> Result<TokenStream> {
             );
 
             impl ::kernel::io::register::Register for #name {
+                type Base = #base;
                 const OFFSET: usize = #offset;
             }
         ));

-- 
2.54.0


  parent reply	other threads:[~2026-08-19 11:10 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-19 11:09 [PATCH v3 00/16] rust: io: support register projections and remove relative registers Gary Guo
2026-08-19 11:09 ` [PATCH v3 01/16] rust: io: register: reimplement as proc macro Gary Guo
2026-08-27  6:16   ` Alexandre Courbot
2026-08-27 14:23     ` Gary Guo
2026-08-19 11:09 ` [PATCH v3 02/16] rust: mem: add `transmute` with deferred size check Gary Guo
2026-08-27  6:24   ` Alexandre Courbot
2026-08-27 14:26     ` Gary Guo
2026-08-19 11:09 ` [PATCH v3 03/16] rust: mem: add `AsRepr` and `AsReprMut` Gary Guo
2026-08-26 11:25   ` Alexandre Courbot
2026-08-26 11:50     ` Gary Guo
2026-08-26 13:30       ` Alexandre Courbot
2026-08-19 11:09 ` [PATCH v3 04/16] rust: io: perform conversions using `AsRepr` Gary Guo
2026-08-27  6:53   ` Alexandre Courbot
2026-08-27 14:21     ` Gary Guo
2026-08-27 14:39       ` Alexandre Courbot
2026-08-19 11:09 ` [PATCH v3 05/16] rust: io: support register projections Gary Guo
2026-08-27  7:01   ` Alexandre Courbot
2026-08-19 11:09 ` [PATCH v3 06/16] rust: io: register: allow explicit base type specification Gary Guo
2026-08-27  7:17   ` Alexandre Courbot
2026-08-19 11:09 ` [PATCH v3 07/16] gpu: nova-core: specify base type for registers Gary Guo
2026-08-27 11:20   ` Alexandre Courbot
2026-08-19 11:09 ` [PATCH v3 08/16] drm/tyr: " Gary Guo
2026-08-19 11:09 ` [PATCH v3 09/16] samples: rust: pci: " Gary Guo
2026-08-27 11:29   ` Alexandre Courbot
2026-08-19 11:09 ` Gary Guo [this message]
2026-08-27 13:42   ` [PATCH v3 10/16] rust: io: register: make register have a typed base Alexandre Courbot
2026-08-27 14:32     ` Gary Guo
2026-08-28  0:01       ` Alexandre Courbot
2026-08-19 11:09 ` [PATCH v3 11/16] rust: io: register: support fixed offset register without bitfield Gary Guo
2026-08-28  0:38   ` Alexandre Courbot
2026-08-19 11:09 ` [PATCH v3 12/16] gpu: nova-core: use projection for PFALCON and PFALCON2 registers Gary Guo
2026-08-28  1:04   ` Alexandre Courbot
2026-08-28  1:24     ` Alexandre Courbot
2026-08-19 11:09 ` [PATCH v3 13/16] gpu: nova-core: convert hshub0 from relative register to projection Gary Guo
2026-08-28  1:29   ` Alexandre Courbot
2026-08-19 11:09 ` [PATCH v3 14/16] rust: io: register: remove relative registers Gary Guo
2026-08-28  1:33   ` Alexandre Courbot
2026-08-19 11:09 ` [PATCH v3 15/16] rust: io: register: remove `Register` trait and cleanup macro Gary Guo
2026-08-28  2:07   ` Alexandre Courbot
2026-08-19 11:09 ` [PATCH v3 16/16] rust: io: register: unify handling of register with/without bitfields Gary Guo
2026-08-28  2:07   ` 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=20260819-typed_register-v3-10-3699a75fadf1@garyguo.net \
    --to=gary@garyguo.net \
    --cc=a.hindborg@kernel.org \
    --cc=acourbot@nvidia.com \
    --cc=airlied@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=bhelgaas@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun@kernel.org \
    --cc=dakr@kernel.org \
    --cc=daniel.almeida@collabora.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=driver-core@lists.linux.dev \
    --cc=kwilczynski@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=nova-gpu@lists.linux.dev \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=simona@ffwll.ch \
    --cc=tamird@kernel.org \
    --cc=tmgross@umich.edu \
    --cc=work@onurozkan.dev \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox