From ff067ace5fbeada7e629d4aaccfa1d04da3eeebc Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 1 Sep 2026 17:50:28 +0100 Subject: rust: io: perform conversions using `AsRepr` For types that are layout-compatible with an I/O capable type, we would want the ability to use them directly for I/O operations. E.g. bitfield! { pub struct Foo(u32) { ... } } #[repr(C)] struct Bar { foo: Foo, } let mmio: Mmio<'_, Bar> = ...; io_read!(mmio, .foo) Currently this feature is available from `register!()` macro but not otherwise available with `io_read!`, `io_write!`. Support this by performing conversions to I/O primitives via the `AsRepr`/`AsReprMut` trait. This makes the `IoLoc::IoType` and `Register::Storage` redundant; thus remove them; also convert register methods to use the `read_val` and `write_val` instead. Reviewed-by: Alexandre Courbot Signed-off-by: Gary Guo Tested-by: Alexandre Courbot Link: https://patch.msgid.link/20260901-typed_register-v4-4-5552b1d59525@garyguo.net Signed-off-by: Danilo Krummrich --- rust/kernel/bitfield.rs | 9 ++++ rust/kernel/io.rs | 128 ++++++++++++++++++++++++--------------------- rust/kernel/io/register.rs | 15 ------ rust/macros/io/register.rs | 2 - 4 files changed, 78 insertions(+), 76 deletions(-) diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs index a0d089423f21..15c78790e151 100644 --- a/rust/kernel/bitfield.rs +++ b/rust/kernel/bitfield.rs @@ -346,6 +346,15 @@ macro_rules! bitfield { Self::from_raw(val) } } + + // SAFETY: `$name` is transparent over `$storage` and `$storage` has no interior mutability. + unsafe impl $crate::mem::AsRepr for $name { + // Normalize `$storage` to the canonical repr type in case it is signed. + type Repr = <$storage as $crate::mem::AsRepr>::Repr; + } + + // SAFETY: `$name` is transparent over `$storage`. + unsafe impl $crate::mem::AsReprMut for $name {} }; // Definitions requiring knowledge of individual fields: private and public field accessors, diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 5ce9fd129068..18c71bd765b2 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -11,6 +11,10 @@ use core::{ use crate::{ bindings, + mem::{ + AsRepr, + AsReprMut, // + }, prelude::*, ptr::{ Alignment, @@ -226,6 +230,17 @@ fn io_view<'a, IO: Io<'a>, U>( Ok(unsafe { IO::Backend::project_view(view, projected_ptr) }) } +/// Returns the primitive view of a I/O view. +#[inline] +fn io_view_as_repr<'a, IO: Io<'a, Target = T>, T: AsRepr>( + this: IO, +) -> ::View<'a, T::Repr> { + let view = this.as_view(); + + // SAFETY: `AsRepr` guarantees layout compatibility. + unsafe { IO::Backend::project_view(view, IO::Backend::as_ptr(view).cast::()) } +} + /// I/O backends. /// /// This is an abstract representation to be implemented by arbitrary I/O @@ -353,15 +368,12 @@ pub trait IoCopyable: IoBackend { /// /// - The valid `Base` to operate on. For most registers, this should be [`Region`]. /// - The offset to access (returned by [`IoLoc::offset`]), -/// - The width of the access (determined by [`IoLoc::IoType`]), -/// - The type `T` in which the raw data is returned or provided. +/// - The type `T` in which the data is returned or provided. /// -/// `T` and `IoLoc::IoType` may differ: for instance, a typed register has `T` = the register type -/// with its bitfields, and `IoType` = its backing primitive (e.g. `u32`). +/// `T` is not necessarily the type for underlying I/O operation. Methods that take `IoLoc` have `T: +/// AsRepr` bound and the `::Repr` type would be used to perform I/O and converted to +/// `T` instead. pub trait IoLoc { - /// Size ([`u8`], [`u16`], etc) of the I/O performed on the returned [`offset`](IoLoc::offset). - type IoType: Into + From; - /// Consumes `self` and returns the offset of this location. fn offset(self) -> usize; } @@ -372,8 +384,6 @@ macro_rules! impl_usize_ioloc { ($($ty:ty),*) => { $( impl IoLoc, $ty> for usize { - type IoType = $ty; - #[inline(always)] fn offset(self) -> usize { self @@ -498,10 +508,10 @@ pub trait Io<'a>: IoBase<'a> { #[inline] fn read_val(self) -> Self::Target where - Self::Backend: IoCapable, - Self::Target: Sized, + Self::Target: AsReprMut, + Self::Backend: IoCapable<::Repr>, { - Self::Backend::io_read(self.as_view()) + Self::Target::from_repr(Self::Backend::io_read(io_view_as_repr(self))) } /// Write a value to I/O. @@ -520,10 +530,10 @@ pub trait Io<'a>: IoBase<'a> { #[inline] fn write_val(self, value: Self::Target) where - Self::Backend: IoCapable, - Self::Target: Sized, + Self::Target: AsRepr, + Self::Backend: IoCapable<::Repr>, { - Self::Backend::io_write(self.as_view(), value) + Self::Backend::io_write(io_view_as_repr(self), Self::Target::into_repr(value)) } /// Copy-read from I/O memory. @@ -645,7 +655,7 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn try_read8(self, offset: usize) -> Result where - usize: IoLoc, + usize: IoLoc, Self::Backend: IoCapable, { self.try_read(offset) @@ -655,7 +665,7 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn try_read16(self, offset: usize) -> Result where - usize: IoLoc, + usize: IoLoc, Self::Backend: IoCapable, { self.try_read(offset) @@ -665,7 +675,7 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn try_read32(self, offset: usize) -> Result where - usize: IoLoc, + usize: IoLoc, Self::Backend: IoCapable, { self.try_read(offset) @@ -675,7 +685,7 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn try_read64(self, offset: usize) -> Result where - usize: IoLoc, + usize: IoLoc, Self::Backend: IoCapable, { self.try_read(offset) @@ -685,7 +695,7 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn try_write8(self, value: u8, offset: usize) -> Result where - usize: IoLoc, + usize: IoLoc, Self::Backend: IoCapable, { self.try_write(offset, value) @@ -695,7 +705,7 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn try_write16(self, value: u16, offset: usize) -> Result where - usize: IoLoc, + usize: IoLoc, Self::Backend: IoCapable, { self.try_write(offset, value) @@ -705,7 +715,7 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn try_write32(self, value: u32, offset: usize) -> Result where - usize: IoLoc, + usize: IoLoc, Self::Backend: IoCapable, { self.try_write(offset, value) @@ -715,7 +725,7 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn try_write64(self, value: u64, offset: usize) -> Result where - usize: IoLoc, + usize: IoLoc, Self::Backend: IoCapable, { self.try_write(offset, value) @@ -727,7 +737,7 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn read8(self, offset: usize) -> u8 where - usize: IoLoc, + usize: IoLoc, Self::Backend: IoCapable, { self.read(offset) @@ -739,7 +749,7 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn read16(self, offset: usize) -> u16 where - usize: IoLoc, + usize: IoLoc, Self::Backend: IoCapable, { self.read(offset) @@ -751,7 +761,7 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn read32(self, offset: usize) -> u32 where - usize: IoLoc, + usize: IoLoc, Self::Backend: IoCapable, { self.read(offset) @@ -763,7 +773,7 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn read64(self, offset: usize) -> u64 where - usize: IoLoc, + usize: IoLoc, Self::Backend: IoCapable, { self.read(offset) @@ -775,7 +785,7 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn write8(self, value: u8, offset: usize) where - usize: IoLoc, + usize: IoLoc, Self::Backend: IoCapable, { self.write(offset, value) @@ -787,7 +797,7 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn write16(self, value: u16, offset: usize) where - usize: IoLoc, + usize: IoLoc, Self::Backend: IoCapable, { self.write(offset, value) @@ -799,7 +809,7 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn write32(self, value: u32, offset: usize) where - usize: IoLoc, + usize: IoLoc, Self::Backend: IoCapable, { self.write(offset, value) @@ -811,7 +821,7 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn write64(self, value: u64, offset: usize) where - usize: IoLoc, + usize: IoLoc, Self::Backend: IoCapable, { self.write(offset, value) @@ -843,11 +853,12 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn try_read(self, location: L) -> Result where + T: AsReprMut, L: IoLoc, - Self::Backend: IoCapable, + Self::Backend: IoCapable<::Repr>, { - let view = io_view::(self, location.offset())?; - Ok(Self::Backend::io_read(view).into()) + let view = io_view::(self, location.offset())?; + Ok(view.read_val()) } /// Generic fallible write with runtime bounds check. @@ -876,12 +887,12 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn try_write(self, location: L, value: T) -> Result where + T: AsRepr, L: IoLoc, - Self::Backend: IoCapable, + Self::Backend: IoCapable<::Repr>, { - let view = io_view::(self, location.offset())?; - let io_value = value.into(); - Self::Backend::io_write(view, io_value); + let view = io_view::(self, location.offset())?; + view.write_val(value); Ok(()) } @@ -920,9 +931,10 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn try_write_reg(self, value: V) -> Result where + T: AsRepr, L: IoLoc, V: LocatedRegister, - Self::Backend: IoCapable, + Self::Backend: IoCapable<::Repr>, { let (location, value) = value.into_io_op(); @@ -954,16 +966,13 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn try_update(self, location: L, f: F) -> Result where + T: AsReprMut, L: IoLoc, - Self::Backend: IoCapable, + Self::Backend: IoCapable<::Repr>, F: FnOnce(T) -> T, { - let view = io_view::(self, location.offset())?; - - let value: T = Self::Backend::io_read(view).into(); - let io_value = f(value).into(); - Self::Backend::io_write(view, io_value); - + let view = io_view::(self, location.offset())?; + view.write_val(f(view.read_val())); Ok(()) } @@ -991,11 +1000,12 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn read(self, location: L) -> T where + T: AsReprMut, L: IoLoc, - Self::Backend: IoCapable, + Self::Backend: IoCapable<::Repr>, { - let view = io_view_assert::(self, location.offset()); - Self::Backend::io_read(view).into() + let view = io_view_assert::(self, location.offset()); + view.read_val() } /// Generic infallible write with compile-time bounds check. @@ -1022,12 +1032,12 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn write(self, location: L, value: T) where + T: AsRepr, L: IoLoc, - Self::Backend: IoCapable, + Self::Backend: IoCapable<::Repr>, { - let view = io_view_assert::(self, location.offset()); - let io_value = value.into(); - Self::Backend::io_write(view, io_value); + let view = io_view_assert::(self, location.offset()); + view.write_val(value) } /// Generic infallible write of a fully-located register value. @@ -1064,9 +1074,10 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn write_reg(self, value: V) where + T: AsRepr, L: IoLoc, V: LocatedRegister, - Self::Backend: IoCapable, + Self::Backend: IoCapable<::Repr>, { let (location, value) = value.into_io_op(); @@ -1098,14 +1109,13 @@ pub trait Io<'a>: IoBase<'a> { #[inline(always)] fn update(self, location: L, f: F) where + T: AsReprMut, L: IoLoc, - Self::Backend: IoCapable, + Self::Backend: IoCapable<::Repr>, F: FnOnce(T) -> T, { - let view = io_view_assert::(self, location.offset()); - let value: T = Self::Backend::io_read(view).into(); - let io_value = f(value).into(); - Self::Backend::io_write(view, io_value); + let view = io_view_assert::(self, location.offset()); + view.write_val(f(view.read_val())); } } diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs index 6a19552ffb95..45d2e73a92e7 100644 --- a/rust/kernel/io/register.rs +++ b/rust/kernel/io/register.rs @@ -117,9 +117,6 @@ use super::Region; /// Trait implemented by all registers. pub trait Register: Sized { - /// Backing primitive type of the register. - type Storage: Into + From; - /// Start offset of the register. /// /// The interpretation of this offset depends on the type of the register. @@ -135,8 +132,6 @@ impl IoLoc, T> for () where T: FixedRegister, { - type IoType = T::Storage; - #[inline(always)] fn offset(self) -> usize { T::OFFSET @@ -149,8 +144,6 @@ impl IoLoc, T> for T where T: FixedRegister, { - type IoType = T::Storage; - #[inline(always)] fn offset(self) -> usize { T::OFFSET @@ -174,8 +167,6 @@ impl IoLoc, T> for FixedRegisterLoc where T: FixedRegister, { - type IoType = T::Storage; - #[inline(always)] fn offset(self) -> usize { T::OFFSET @@ -246,8 +237,6 @@ where T: RelativeRegister, B: RegisterBase + ?Sized, { - type IoType = T::Storage; - #[inline(always)] fn offset(self) -> usize { RelativeRegisterLoc::offset(self) @@ -289,8 +278,6 @@ impl IoLoc, T> for RegisterArrayLoc where T: RegisterArray, { - type IoType = T::Storage; - #[inline(always)] fn offset(self) -> usize { T::OFFSET + self.0 * T::STRIDE @@ -377,8 +364,6 @@ where T: RelativeRegisterArray, B: RegisterBase + ?Sized, { - type IoType = T::Storage; - #[inline(always)] fn offset(self) -> usize { self.0.offset() + self.1 * T::STRIDE diff --git a/rust/macros/io/register.rs b/rust/macros/io/register.rs index cc5bcad31ae8..2e4cdabb2795 100644 --- a/rust/macros/io/register.rs +++ b/rust/macros/io/register.rs @@ -213,8 +213,6 @@ pub(crate) fn register(def: RegDef) -> Result { ); impl ::kernel::io::register::Register for #name { - type Storage = #storage; - const OFFSET: usize = #offset; } )); -- cgit v1.2.3