[PATCH v3 10/16] rust: io: register: make register have a typed base

From: Gary Guo

Date: Wed Aug 19 2026 - 07:10:41 EST


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@xxxxxxxxxxx>
---
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