> On 6 Jul 2026, at 09:44, Gary Guo <[email protected]> wrote:
> 
> Currently, `io_read` and `io_write` methods require the exact type of `Io`
> plus an address. This means that they need to be monomorphized for each
> different `Io` instance. This also means that multiple I/O implementors for
> the same I/O kind needs to duplicate implementation (e.g. `Mmio` and
> `MmioOwned`).
> 
> Create a new `IoBackend` trait and define these operations on it instead.
> The operations are just going to receive a view type and operate on them.
> This has the additional advantage that the invariants can be moved from the
> trait (and guaranteed via `unsafe`) to type invariants on the canonical
> view types of the backends, so `io_read` and `io_write` can be safe.
> 
> Note that view type is needed; addresses are insufficient in this

nit: missing article: “a view type is needed” or “view types are needed”.

> design, as they do not carry sufficient information. For example,
> `ConfigSpace` needs `&pci::Device` in addition to the address.
> 
> `io_addr_assert` and `io_addr` are renamed to `io_view*` to reflect
> that they operate on views now, and make them standalone functions so
> they cannot be used by users to cast types outside io.rs.
> 
> Reviewed-by: Alexandre Courbot <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
> ---
> rust/kernel/io.rs     | 382 ++++++++++++++++++++++++++------------------------
> rust/kernel/pci/io.rs |  70 +++++----
> 2 files changed, 246 insertions(+), 206 deletions(-)
> 
> diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
> index 8110b49aa430..89039b3defd5 100644
> --- a/rust/kernel/io.rs
> +++ b/rust/kernel/io.rs
> @@ -243,6 +243,81 @@ const fn offset_valid<U>(base: usize, offset: usize, 
> size: usize) -> bool {
>     }
> }
> 
> +/// Returns a view for a given `offset`, performing compile-time bound 
> checks.
> +// Always inline to optimize out error path of `build_assert`.

You mean to avoid that cryptic “rust_build_error” when the assertion fails?

> +#[inline(always)]
> +fn io_view_assert<'a, IO: Io<'a>, U>(
> +    this: IO,
> +    offset: usize,
> +) -> <IO::Backend as IoBackend>::View<'a, U> {
> +    // We cannot check alignment with `offset_valid` using `ptr.addr()`. So 
> set 0 for it and
> +    // ensure alignment by checking that the alignment of `U` is smaller or 
> equal to the
> +    // alignment of `IO::Target`.
> +    const_assert!(Alignment::of::<U>().as_usize() <= 
> IO::Target::MIN_ALIGN.as_usize());
> +    build_assert!(offset_valid::<U>(0, offset, IO::Target::MIN_SIZE));
> +
> +    let view = this.as_view();
> +    let ptr = IO::Backend::as_ptr(view);
> +    let projected_ptr = ptr.cast::<U>().wrapping_byte_add(offset);
> +    // SAFETY: `offset_valid` checks for size and alignment and therefore 
> `projected_ptr` is a
> +    // valid projection.
> +    unsafe { IO::Backend::project_view(view, projected_ptr) }
> +}
> +
> +/// Returns a view for a given `offset`, performing runtime bound checks.
> +#[inline]
> +fn io_view<'a, IO: Io<'a>, U>(
> +    this: IO,
> +    offset: usize,
> +) -> Result<<IO::Backend as IoBackend>::View<'a, U>> {
> +    let view = this.as_view();
> +    let ptr = IO::Backend::as_ptr(view);
> +
> +    if !offset_valid::<U>(ptr.addr(), offset, KnownSize::size(ptr)) {
> +        return Err(EINVAL);
> +    }
> +
> +    let projected_ptr = ptr.cast::<U>().wrapping_byte_add(offset);
> +    // SAFETY: `offset_valid` checks for size and alignment and therefore 
> `projected_ptr` is a
> +    // valid projection.
> +    Ok(unsafe { IO::Backend::project_view(view, projected_ptr) })
> +}
> +
> +/// I/O backends.
> +///
> +/// This is an abstract representation to be implemented by arbitrary I/O
> +/// backends (e.g. MMIO, PCI config space, etc.).
> +///
> +/// The base trait only defines the projection operations; which I/O methods 
> are available depends
> +/// on which [`IoCapable<T>`] traits are implemented for the type. For 
> example, for MMIO regions,
> +/// all widths (u8, u16, u32, and u64 on 64-bit systems) are typically 
> supported. For PCI
> +/// configuration space, u8, u16, and u32 are supported but u64 is not.
> +///
> +/// This trait is separate from the `Io` trait as multiple different I/O 
> types may share the same
> +/// operation.
> +pub trait IoBackend {
> +    /// View type for this I/O backend.
> +    type View<'a, T: ?Sized + KnownSize>: Io<'a, Backend = Self, Target = T>;
> +
> +    /// Convert a `view` to a raw pointer for projection.
> +    ///
> +    /// The returned pointer is private implementation detail of the 
> backend; it is likely not
> +    /// valid. It should not be dereferenced.
> +    fn as_ptr<'a, T: ?Sized + KnownSize>(view: Self::View<'a, T>) -> *mut T;
> +
> +    /// Project `view` to its subregion indicated by `ptr`.
> +    ///
> +    /// If input `view` is valid, returned view must also be valid.
> +    ///
> +    /// # Safety
> +    ///
> +    /// `ptr` must be a projection of `Self::as_ptr(view)`.
> +    unsafe fn project_view<'a, T: ?Sized + KnownSize, U: ?Sized + KnownSize>(
> +        view: Self::View<'a, T>,
> +        ptr: *mut U,
> +    ) -> Self::View<'a, U>;
> +}
> +
> /// Trait indicating that an I/O backend supports operations of a certain 
> type and providing an
> /// implementation for these operations.
> ///
> @@ -251,22 +326,12 @@ const fn offset_valid<U>(base: usize, offset: usize, 
> size: usize) -> bool {
> /// For example, a PCI configuration space may implement `IoCapable<u8>`, 
> `IoCapable<u16>`,
> /// and `IoCapable<u32>`, but not `IoCapable<u64>`, while an MMIO region on a 
> 64-bit
> /// system might implement all four.
> -pub trait IoCapable<T> {
> -    /// Performs an I/O read of type `T` at `address` and returns the result.
> -    ///
> -    /// # Safety
> -    ///
> -    /// - The range `[address..address + size_of::<T>()]` must be within the 
> bounds of `Self`.
> -    /// - `address` must be aligned.
> -    unsafe fn io_read(self, address: usize) -> T;
> +pub trait IoCapable<T>: IoBackend {
> +    /// Performs an I/O read of type `T` at `view` and returns the result.
> +    fn io_read<'a>(view: Self::View<'a, T>) -> T;
> 
> -    /// Performs an I/O write of `value` at `address`.
> -    ///
> -    /// # Safety
> -    ///
> -    /// - The range `[address..address + size_of::<T>()]` must be within the 
> bounds of `Self`.
> -    /// - `address` must be aligned.
> -    unsafe fn io_write(self, value: T, address: usize);
> +    /// Performs an I/O write of `value` at `view`.
> +    fn io_write<'a>(view: Self::View<'a, T>, value: T);
> }
> 
> /// Describes a given I/O location: its offset, width, and type to convert 
> the raw value from and
> @@ -318,66 +383,30 @@ fn offset(self) -> usize {
> /// Types implementing this trait (e.g. MMIO BARs or PCI config regions)
> /// can perform I/O operations on regions of memory.
> ///
> -/// This is an abstract representation to be implemented by arbitrary I/O
> -/// backends (e.g. MMIO, PCI config space, etc.).
> -///
> /// The [`Io`] trait provides:
> -/// - Base address and size information
> +/// - Method to convert into [`IoBackend::View`].
> /// - Helper methods for offset validation and address calculation
> /// - Fallible (runtime checked) accessors for different data widths
> ///
> -/// Which I/O methods are available depends on which [`IoCapable<T>`] traits
> -/// are implemented for the type.
> +/// Which I/O methods are available depends on the associated [`IoBackend`] 
> implementation.
> ///
> /// This should be implemented on cheaply copyable handles, such as 
> references or view types.
> -///
> -/// # Examples
> -///
> -/// For MMIO regions, all widths (u8, u16, u32, and u64 on 64-bit systems) 
> are typically
> -/// supported. For PCI configuration space, u8, u16, and u32 are supported 
> but u64 is not.
> -pub trait Io: Copy {
> +pub trait Io<'a>: Copy {
> +    /// Type that defines all I/O operations.
> +    type Backend: IoBackend;
> +
>     /// Type of this I/O region. For untyped regions, [`Region`] can be used.
>     type Target: ?Sized + KnownSize;
> 
> -    /// Returns the base address of this mapping.
> -    fn addr(self) -> usize;
> -
> -    /// Returns the maximum size of this mapping.
> -    fn maxsize(self) -> usize;
> -
> -    /// Returns the absolute I/O address for a given `offset`,
> -    /// performing compile-time bound checks.
> -    // Always inline to optimize out error path of `build_assert`.
> -    #[inline(always)]
> -    fn io_addr_assert<U>(self, offset: usize) -> usize {
> -        // We cannot check alignment with `offset_valid` using 
> `self.addr()`. So set 0 for it and
> -        // ensure alignment by checking that the alignment of `U` is smaller 
> or equal to the
> -        // alignment of `Self::Target`.
> -        const_assert!(Alignment::of::<U>().as_usize() <= 
> Self::Target::MIN_ALIGN.as_usize());
> -        build_assert!(offset_valid::<U>(0, offset, Self::Target::MIN_SIZE));
> -
> -        self.addr() + offset
> -    }
> -
> -    /// Returns the absolute I/O address for a given `offset`,
> -    /// performing runtime bound checks.
> -    #[inline]
> -    fn io_addr<U>(self, offset: usize) -> Result<usize> {
> -        if !offset_valid::<U>(self.addr(), offset, self.maxsize()) {
> -            return Err(EINVAL);
> -        }
> -
> -        // Probably no need to check, since the safety requirements of 
> `Self::new` guarantee that
> -        // this can't overflow.
> -        self.addr().checked_add(offset).ok_or(EINVAL)
> -    }
> +    /// Return a view that covers the full region.
> +    fn as_view(self) -> <Self::Backend as IoBackend>::View<'a, Self::Target>;
> 
>     /// Fallible 8-bit read with runtime bounds check.
>     #[inline(always)]
>     fn try_read8(self, offset: usize) -> Result<u8>
>     where
>         usize: IoLoc<Self::Target, u8, IoType = u8>,
> -        Self: IoCapable<u8>,
> +        Self::Backend: IoCapable<u8>,
>     {
>         self.try_read(offset)
>     }
> @@ -387,7 +416,7 @@ fn try_read8(self, offset: usize) -> Result<u8>
>     fn try_read16(self, offset: usize) -> Result<u16>
>     where
>         usize: IoLoc<Self::Target, u16, IoType = u16>,
> -        Self: IoCapable<u16>,
> +        Self::Backend: IoCapable<u16>,
>     {
>         self.try_read(offset)
>     }
> @@ -397,7 +426,7 @@ fn try_read16(self, offset: usize) -> Result<u16>
>     fn try_read32(self, offset: usize) -> Result<u32>
>     where
>         usize: IoLoc<Self::Target, u32, IoType = u32>,
> -        Self: IoCapable<u32>,
> +        Self::Backend: IoCapable<u32>,
>     {
>         self.try_read(offset)
>     }
> @@ -407,7 +436,7 @@ fn try_read32(self, offset: usize) -> Result<u32>
>     fn try_read64(self, offset: usize) -> Result<u64>
>     where
>         usize: IoLoc<Self::Target, u64, IoType = u64>,
> -        Self: IoCapable<u64>,
> +        Self::Backend: IoCapable<u64>,
>     {
>         self.try_read(offset)
>     }
> @@ -417,7 +446,7 @@ fn try_read64(self, offset: usize) -> Result<u64>
>     fn try_write8(self, value: u8, offset: usize) -> Result
>     where
>         usize: IoLoc<Self::Target, u8, IoType = u8>,
> -        Self: IoCapable<u8>,
> +        Self::Backend: IoCapable<u8>,
>     {
>         self.try_write(offset, value)
>     }
> @@ -427,7 +456,7 @@ fn try_write8(self, value: u8, offset: usize) -> Result
>     fn try_write16(self, value: u16, offset: usize) -> Result
>     where
>         usize: IoLoc<Self::Target, u16, IoType = u16>,
> -        Self: IoCapable<u16>,
> +        Self::Backend: IoCapable<u16>,
>     {
>         self.try_write(offset, value)
>     }
> @@ -437,7 +466,7 @@ fn try_write16(self, value: u16, offset: usize) -> Result
>     fn try_write32(self, value: u32, offset: usize) -> Result
>     where
>         usize: IoLoc<Self::Target, u32, IoType = u32>,
> -        Self: IoCapable<u32>,
> +        Self::Backend: IoCapable<u32>,
>     {
>         self.try_write(offset, value)
>     }
> @@ -447,7 +476,7 @@ fn try_write32(self, value: u32, offset: usize) -> Result
>     fn try_write64(self, value: u64, offset: usize) -> Result
>     where
>         usize: IoLoc<Self::Target, u64, IoType = u64>,
> -        Self: IoCapable<u64>,
> +        Self::Backend: IoCapable<u64>,
>     {
>         self.try_write(offset, value)
>     }
> @@ -457,7 +486,7 @@ fn try_write64(self, value: u64, offset: usize) -> Result
>     fn read8(self, offset: usize) -> u8
>     where
>         usize: IoLoc<Self::Target, u8, IoType = u8>,
> -        Self: IoCapable<u8>,
> +        Self::Backend: IoCapable<u8>,
>     {
>         self.read(offset)
>     }
> @@ -467,7 +496,7 @@ fn read8(self, offset: usize) -> u8
>     fn read16(self, offset: usize) -> u16
>     where
>         usize: IoLoc<Self::Target, u16, IoType = u16>,
> -        Self: IoCapable<u16>,
> +        Self::Backend: IoCapable<u16>,
>     {
>         self.read(offset)
>     }
> @@ -477,7 +506,7 @@ fn read16(self, offset: usize) -> u16
>     fn read32(self, offset: usize) -> u32
>     where
>         usize: IoLoc<Self::Target, u32, IoType = u32>,
> -        Self: IoCapable<u32>,
> +        Self::Backend: IoCapable<u32>,
>     {
>         self.read(offset)
>     }
> @@ -487,7 +516,7 @@ fn read32(self, offset: usize) -> u32
>     fn read64(self, offset: usize) -> u64
>     where
>         usize: IoLoc<Self::Target, u64, IoType = u64>,
> -        Self: IoCapable<u64>,
> +        Self::Backend: IoCapable<u64>,
>     {
>         self.read(offset)
>     }
> @@ -497,7 +526,7 @@ fn read64(self, offset: usize) -> u64
>     fn write8(self, value: u8, offset: usize)
>     where
>         usize: IoLoc<Self::Target, u8, IoType = u8>,
> -        Self: IoCapable<u8>,
> +        Self::Backend: IoCapable<u8>,
>     {
>         self.write(offset, value)
>     }
> @@ -507,7 +536,7 @@ fn write8(self, value: u8, offset: usize)
>     fn write16(self, value: u16, offset: usize)
>     where
>         usize: IoLoc<Self::Target, u16, IoType = u16>,
> -        Self: IoCapable<u16>,
> +        Self::Backend: IoCapable<u16>,
>     {
>         self.write(offset, value)
>     }
> @@ -517,7 +546,7 @@ fn write16(self, value: u16, offset: usize)
>     fn write32(self, value: u32, offset: usize)
>     where
>         usize: IoLoc<Self::Target, u32, IoType = u32>,
> -        Self: IoCapable<u32>,
> +        Self::Backend: IoCapable<u32>,
>     {
>         self.write(offset, value)
>     }
> @@ -527,7 +556,7 @@ fn write32(self, value: u32, offset: usize)
>     fn write64(self, value: u64, offset: usize)
>     where
>         usize: IoLoc<Self::Target, u64, IoType = u64>,
> -        Self: IoCapable<u64>,
> +        Self::Backend: IoCapable<u64>,
>     {
>         self.write(offset, value)
>     }
> @@ -559,12 +588,10 @@ fn write64(self, value: u64, offset: usize)
>     fn try_read<T, L>(self, location: L) -> Result<T>
>     where
>         L: IoLoc<Self::Target, T>,
> -        Self: IoCapable<L::IoType>,
> +        Self::Backend: IoCapable<L::IoType>,
>     {
> -        let address = self.io_addr::<L::IoType>(location.offset())?;
> -
> -        // SAFETY: `address` has been validated by `io_addr`.
> -        Ok(unsafe { self.io_read(address) }.into())
> +        let view = io_view::<Self, L::IoType>(self, location.offset())?;
> +        Ok(Self::Backend::io_read(view).into())
>     }
> 
>     /// Generic fallible write with runtime bounds check.
> @@ -594,14 +621,11 @@ fn try_read<T, L>(self, location: L) -> Result<T>
>     fn try_write<T, L>(self, location: L, value: T) -> Result
>     where
>         L: IoLoc<Self::Target, T>,
> -        Self: IoCapable<L::IoType>,
> +        Self::Backend: IoCapable<L::IoType>,
>     {
> -        let address = self.io_addr::<L::IoType>(location.offset())?;
> +        let view = io_view::<Self, L::IoType>(self, location.offset())?;
>         let io_value = value.into();
> -
> -        // SAFETY: `address` has been validated by `io_addr`.
> -        unsafe { self.io_write(io_value, address) }
> -
> +        Self::Backend::io_write(view, io_value);
>         Ok(())
>     }
> 
> @@ -642,7 +666,7 @@ fn try_write_reg<T, L, V>(self, value: V) -> Result
>     where
>         L: IoLoc<Self::Target, T>,
>         V: LocatedRegister<Self::Target, Location = L, Value = T>,
> -        Self: IoCapable<L::IoType>,
> +        Self::Backend: IoCapable<L::IoType>,
>     {
>         let (location, value) = value.into_io_op();
> 
> @@ -675,17 +699,14 @@ fn try_write_reg<T, L, V>(self, value: V) -> Result
>     fn try_update<T, L, F>(self, location: L, f: F) -> Result
>     where
>         L: IoLoc<Self::Target, T>,
> -        Self: IoCapable<L::IoType>,
> +        Self::Backend: IoCapable<L::IoType>,
>         F: FnOnce(T) -> T,
>     {
> -        let address = self.io_addr::<L::IoType>(location.offset())?;
> +        let view = io_view::<Self, L::IoType>(self, location.offset())?;
> 
> -        // SAFETY: `address` has been validated by `io_addr`.
> -        let value: T = unsafe { self.io_read(address) }.into();
> +        let value: T = Self::Backend::io_read(view).into();
>         let io_value = f(value).into();
> -
> -        // SAFETY: `address` has been validated by `io_addr`.
> -        unsafe { self.io_write(io_value, address) }
> +        Self::Backend::io_write(view, io_value);
> 
>         Ok(())
>     }
> @@ -715,12 +736,10 @@ fn try_update<T, L, F>(self, location: L, f: F) -> 
> Result
>     fn read<T, L>(self, location: L) -> T
>     where
>         L: IoLoc<Self::Target, T>,
> -        Self: IoCapable<L::IoType>,
> +        Self::Backend: IoCapable<L::IoType>,
>     {
> -        let address = self.io_addr_assert::<L::IoType>(location.offset());
> -
> -        // SAFETY: `address` has been validated by `io_addr_assert`.
> -        unsafe { self.io_read(address) }.into()
> +        let view = io_view_assert::<Self, L::IoType>(self, 
> location.offset());
> +        Self::Backend::io_read(view).into()
>     }
> 
>     /// Generic infallible write with compile-time bounds check.
> @@ -748,13 +767,11 @@ fn read<T, L>(self, location: L) -> T
>     fn write<T, L>(self, location: L, value: T)
>     where
>         L: IoLoc<Self::Target, T>,
> -        Self: IoCapable<L::IoType>,
> +        Self::Backend: IoCapable<L::IoType>,
>     {
> -        let address = self.io_addr_assert::<L::IoType>(location.offset());
> +        let view = io_view_assert::<Self, L::IoType>(self, 
> location.offset());
>         let io_value = value.into();
> -
> -        // SAFETY: `address` has been validated by `io_addr_assert`.
> -        unsafe { self.io_write(io_value, address) }
> +        Self::Backend::io_write(view, io_value);
>     }
> 
>     /// Generic infallible write of a fully-located register value.
> @@ -793,7 +810,7 @@ fn write_reg<T, L, V>(self, value: V)
>     where
>         L: IoLoc<Self::Target, T>,
>         V: LocatedRegister<Self::Target, Location = L, Value = T>,
> -        Self: IoCapable<L::IoType>,
> +        Self::Backend: IoCapable<L::IoType>,
>     {
>         let (location, value) = value.into_io_op();
> 
> @@ -826,17 +843,13 @@ fn write_reg<T, L, V>(self, value: V)
>     fn update<T, L, F>(self, location: L, f: F)
>     where
>         L: IoLoc<Self::Target, T>,
> -        Self: IoCapable<L::IoType>,
> +        Self::Backend: IoCapable<L::IoType>,
>         F: FnOnce(T) -> T,
>     {
> -        let address = self.io_addr_assert::<L::IoType>(location.offset());
> -
> -        // SAFETY: `address` has been validated by `io_addr_assert`.
> -        let value: T = unsafe { self.io_read(address) }.into();
> +        let view = io_view_assert::<Self, L::IoType>(self, 
> location.offset());
> +        let value: T = Self::Backend::io_read(view).into();
>         let io_value = f(value).into();
> -
> -        // SAFETY: `address` has been validated by `io_addr_assert`.
> -        unsafe { self.io_write(io_value, address) }
> +        Self::Backend::io_write(view, io_value);
>     }
> }
> 
> @@ -880,78 +893,78 @@ unsafe impl<T: ?Sized + Sync> Send for Mmio<'_, T> {}
> // SAFETY: `Mmio<'_, T>` is conceptually `&T` but in I/O memory.
> unsafe impl<T: ?Sized + Sync> Sync for Mmio<'_, T> {}
> 
> -impl<T: ?Sized + KnownSize> Io for Mmio<'_, T> {
> +impl<'a, T: ?Sized + KnownSize> Io<'a> for Mmio<'a, T> {
> +    type Backend = MmioBackend;
>     type Target = T;
> 
>     #[inline]
> -    fn addr(self) -> usize {
> -        self.ptr.addr()
> +    fn as_view(self) -> Mmio<'a, T> {
> +        self
>     }
> +}
> +
> +/// I/O Backend for memory-mapped I/O.
> +pub struct MmioBackend;
> +
> +impl IoBackend for MmioBackend {
> +    type View<'a, T: ?Sized + KnownSize> = Mmio<'a, T>;
> 
>     #[inline]
> -    fn maxsize(self) -> usize {
> -        KnownSize::size(self.ptr)
> +    fn as_ptr<'a, T: ?Sized + KnownSize>(view: Self::View<'a, T>) -> *mut T {
> +        view.ptr
> +    }
> +
> +    #[inline]
> +    unsafe fn project_view<'a, T: ?Sized + KnownSize, U: ?Sized + KnownSize>(
> +        _view: Self::View<'a, T>,
> +        ptr: *mut U,
> +    ) -> Self::View<'a, U> {
> +        // INVARIANT: Per safety requirement, `ptr` is projection from 
> `view`, so it is also a valid
> +        // memory-mapped I/O region.
> +        Mmio {
> +            ptr,
> +            phantom: PhantomData,
> +        }
>     }
> }
> 
> -/// Implements [`IoCapable`] on `$mmio` for `$ty` using `$read_fn` and 
> `$write_fn`.
> +/// Implements [`IoCapable`] on `$backend` for `$ty` using `$read_fn` and 
> `$write_fn`.
> macro_rules! impl_mmio_io_capable {
> -    ($mmio:ident, $(#[$attr:meta])* $ty:ty, $read_fn:ident, $write_fn:ident) 
> => {
> -        $(#[$attr])*
> -        impl<T: ?Sized> IoCapable<$ty> for $mmio<'_, T> {
> +    ($backend: ident, $ty:ty, $read_fn:ident, $write_fn:ident) => {
> +        impl IoCapable<$ty> for $backend {
>             #[inline]
> -            unsafe fn io_read(self, address: usize) -> $ty {
> -                // SAFETY: By the trait invariant `address` is a valid 
> address for MMIO operations.
> -                unsafe { bindings::$read_fn(address as *const c_void) }
> +            fn io_read(view: <$backend as IoBackend>::View<'_, $ty>) -> $ty {
> +                // SAFETY: `$backend::as_ptr(view)` is a valid pointer for 
> MMIO operations for both
> +                // `MmioBackend` and `RelaxedMmioBackend`.
> +                unsafe { 
> bindings::$read_fn($backend::as_ptr(view).cast_const().cast()) }
>             }
> 
>             #[inline]
> -            unsafe fn io_write(self, value: $ty, address: usize) {
> -                // SAFETY: By the trait invariant `address` is a valid 
> address for MMIO operations.
> -                unsafe { bindings::$write_fn(value, address as *mut c_void) }
> +            fn io_write(view: <$backend as IoBackend>::View<'_, $ty>, value: 
> $ty) {
> +                // SAFETY: `$backend::as_ptr(view)` is a valid pointer for 
> MMIO operations for both
> +                // `MmioBackend` and `RelaxedMmioBackend`.
> +                unsafe { bindings::$write_fn(value, 
> $backend::as_ptr(view).cast()) }
>             }
>         }
>     };
> }
> 
> // MMIO regions support 8, 16, and 32-bit accesses.
> -impl_mmio_io_capable!(Mmio, u8, readb, writeb);
> -impl_mmio_io_capable!(Mmio, u16, readw, writew);
> -impl_mmio_io_capable!(Mmio, u32, readl, writel);
> +impl_mmio_io_capable!(MmioBackend, u8, readb, writeb);
> +impl_mmio_io_capable!(MmioBackend, u16, readw, writew);
> +impl_mmio_io_capable!(MmioBackend, u32, readl, writel);
> // MMIO regions on 64-bit systems also support 64-bit accesses.
> #[cfg(CONFIG_64BIT)]
> -impl_mmio_io_capable!(Mmio, u64, readq, writeq);
> +impl_mmio_io_capable!(MmioBackend, u64, readq, writeq);
> 
> -impl<'a, const SIZE: usize> Io for &'a MmioOwned<SIZE> {
> +impl<'a, const SIZE: usize> Io<'a> for &'a MmioOwned<SIZE> {
> +    type Backend = MmioBackend;
>     type Target = Region<SIZE>;
> 
> -    /// Returns the base address of this mapping.
>     #[inline]
> -    fn addr(self) -> usize {
> -        self.0.addr()
> -    }
> -
> -    /// Returns the maximum size of this mapping.
> -    #[inline]
> -    fn maxsize(self) -> usize {
> -        self.0.size()
> -    }
> -}
> -
> -impl<'a, const SIZE: usize, T> IoCapable<T> for &'a MmioOwned<SIZE>
> -where
> -    Mmio<'a, Region<SIZE>>: IoCapable<T>,
> -{
> -    #[inline]
> -    unsafe fn io_read(self, address: usize) -> T {
> -        // SAFETY: Per safety requirement.
> -        unsafe { self.as_view().io_read(address) }
> -    }
> -
> -    #[inline]
> -    unsafe fn io_write(self, value: T, address: usize) {
> -        // SAFETY: Per safety requirement.
> -        unsafe { self.as_view().io_write(value, address) }
> +    fn as_view(self) -> Mmio<'a, Self::Target> {
> +        // SAFETY: `Mmio` has same invariant as `MmioOwned`
> +        unsafe { Mmio::from_raw(self.0) }
>     }
> }
> 
> @@ -967,13 +980,6 @@ pub unsafe fn from_raw(raw: &MmioRaw<Region<SIZE>>) -> 
> &Self {
>         // SAFETY: `MmioOwned` is a transparent wrapper around `MmioRaw`.
>         unsafe { &*core::ptr::from_ref(raw).cast() }
>     }
> -
> -    /// Return a view that covers the full region.
> -    #[inline]
> -    pub fn as_view(&self) -> Mmio<'_, Region<SIZE>> {
> -        // SAFETY: `Mmio` has same invariant as `MmioOwned`.
> -        unsafe { Mmio::from_raw(self.0) }
> -    }
> }
> 
> /// [`Mmio`] but using relaxed accessors.
> @@ -992,17 +998,34 @@ fn clone(&self) -> Self {
>     }
> }
> 
> -impl<T: ?Sized + KnownSize> Io for RelaxedMmio<'_, T> {
> -    type Target = T;
> +/// I/O Backend for memory-mapped I/O, with relaxed access semantics.
> +pub struct RelaxedMmioBackend;
> +
> +impl IoBackend for RelaxedMmioBackend {
> +    type View<'a, T: ?Sized + KnownSize> = RelaxedMmio<'a, T>;
> 
>     #[inline]
> -    fn addr(self) -> usize {
> -        self.0.addr()
> +    fn as_ptr<'a, T: ?Sized + KnownSize>(view: Self::View<'a, T>) -> *mut T {
> +        MmioBackend::as_ptr(view.0)
>     }
> 
>     #[inline]
> -    fn maxsize(self) -> usize {
> -        self.0.maxsize()
> +    unsafe fn project_view<'a, T: ?Sized + KnownSize, U: ?Sized + KnownSize>(
> +        view: Self::View<'a, T>,
> +        ptr: *mut U,
> +    ) -> Self::View<'a, U> {
> +        // SAFETY: Per safety requirement.
> +        RelaxedMmio(unsafe { MmioBackend::project_view(view.0, ptr) })
> +    }
> +}
> +
> +impl<'a, T: ?Sized + KnownSize> Io<'a> for RelaxedMmio<'a, T> {
> +    type Backend = RelaxedMmioBackend;
> +    type Target = T;
> +
> +    #[inline]
> +    fn as_view(self) -> RelaxedMmio<'a, T> {
> +        self
>     }
> }
> 
> @@ -1035,14 +1058,9 @@ pub fn relaxed(self) -> RelaxedMmio<'a, T> {
> }
> 
> // MMIO regions support 8, 16, and 32-bit accesses.
> -impl_mmio_io_capable!(RelaxedMmio, u8, readb_relaxed, writeb_relaxed);
> -impl_mmio_io_capable!(RelaxedMmio, u16, readw_relaxed, writew_relaxed);
> -impl_mmio_io_capable!(RelaxedMmio, u32, readl_relaxed, writel_relaxed);
> +impl_mmio_io_capable!(RelaxedMmioBackend, u8, readb_relaxed, writeb_relaxed);
> +impl_mmio_io_capable!(RelaxedMmioBackend, u16, readw_relaxed, 
> writew_relaxed);
> +impl_mmio_io_capable!(RelaxedMmioBackend, u32, readl_relaxed, 
> writel_relaxed);
> // MMIO regions on 64-bit systems also support 64-bit accesses.
> -impl_mmio_io_capable!(
> -    RelaxedMmio,
> -    #[cfg(CONFIG_64BIT)]
> -    u64,
> -    readq_relaxed,
> -    writeq_relaxed
> -);
> +#[cfg(CONFIG_64BIT)]
> +impl_mmio_io_capable!(RelaxedMmioBackend, u64, readq_relaxed, 
> writeq_relaxed);
> diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs
> index 89f4bb483a7f..e67c1e3694fb 100644
> --- a/rust/kernel/pci/io.rs
> +++ b/rust/kernel/pci/io.rs
> @@ -9,6 +9,7 @@
>     devres::Devres,
>     io::{
>         Io,
> +        IoBackend,
>         IoCapable,
>         MmioOwned,
>         MmioRaw,
> @@ -84,32 +85,57 @@ unsafe impl<T: ?Sized + Sync> Send for ConfigSpace<'_, T> 
> {}
> // SAFETY: `ConfigSpace<'_, T>` is conceptually `&T` but in I/O memory.
> unsafe impl<T: ?Sized + Sync> Sync for ConfigSpace<'_, T> {}
> 
> +/// I/O Backend for PCI configuration space.
> +pub struct ConfigSpaceBackend;
> +
> +impl IoBackend for ConfigSpaceBackend {
> +    type View<'a, T: ?Sized + KnownSize> = ConfigSpace<'a, T>;
> +
> +    #[inline]
> +    fn as_ptr<'a, T: ?Sized + KnownSize>(view: ConfigSpace<'a, T>) -> *mut T 
> {
> +        view.ptr
> +    }
> +
> +    #[inline]
> +    unsafe fn project_view<'a, T: ?Sized + KnownSize, U: ?Sized + KnownSize>(
> +        view: Self::View<'a, T>,
> +        ptr: *mut U,
> +    ) -> Self::View<'a, U> {
> +        // INVARIANT: Per safety requirement.
> +        ConfigSpace {
> +            pdev: view.pdev,
> +            ptr,
> +        }
> +    }
> +}
> +
> /// Implements [`IoCapable`] on [`ConfigSpace`] for `$ty` using `$read_fn` 
> and `$write_fn`.
> macro_rules! impl_config_space_io_capable {
>     ($ty:ty, $read_fn:ident, $write_fn:ident) => {
> -        impl<'a, T: ?Sized> IoCapable<$ty> for ConfigSpace<'a, T> {
> -            unsafe fn io_read(self, address: usize) -> $ty {
> +        impl IoCapable<$ty> for ConfigSpaceBackend {
> +            fn io_read(view: ConfigSpace<'_, $ty>) -> $ty {
> +                // CAST: The offset is cast to `i32` because the C functions 
> expect a 32-bit
> +                // signed offset parameter. PCI configuration space size is 
> at most 4096 bytes,
> +                // so the value always fits within `i32` without truncation 
> or sign change.
> +                let addr = view.ptr.addr() as i32;
> +
>                 let mut val: $ty = 0;
> 
>                 // Return value from C function is ignored in infallible 
> accessors.
> -                let _ret =
> -                    // SAFETY: By the type invariant `self.pdev` is a valid 
> address.
> -                    // CAST: The offset is cast to `i32` because the C 
> functions expect a 32-bit
> -                    // signed offset parameter. PCI configuration space size 
> is at most 4096 bytes,
> -                    // so the value always fits within `i32` without 
> truncation or sign change.
> -                    unsafe { bindings::$read_fn(self.pdev.as_raw(), address 
> as i32, &mut val) };
> -
> +                // SAFETY: By the type invariant `pdev` is a valid address.
> +                let _ = unsafe { bindings::$read_fn(view.pdev.as_raw(), 
> addr, &mut val) };
>                 val
>             }
> 
> -            unsafe fn io_write(self, value: $ty, address: usize) {
> +            fn io_write(view: ConfigSpace<'_, $ty>, value: $ty) {
> +                // CAST: The offset is cast to `i32` because the C functions 
> expect a 32-bit
> +                // signed offset parameter. PCI configuration space size is 
> at most 4096 bytes,
> +                // so the value always fits within `i32` without truncation 
> or sign change.
> +                let addr = view.ptr.addr() as i32;
> +
>                 // Return value from C function is ignored in infallible 
> accessors.
> -                let _ret =
> -                    // SAFETY: By the type invariant `self.pdev` is a valid 
> address.
> -                    // CAST: The offset is cast to `i32` because the C 
> functions expect a 32-bit
> -                    // signed offset parameter. PCI configuration space size 
> is at most 4096 bytes,
> -                    // so the value always fits within `i32` without 
> truncation or sign change.
> -                    unsafe { bindings::$write_fn(self.pdev.as_raw(), address 
> as i32, value) };
> +                // SAFETY: By the type invariant `pdev` is a valid address.
> +                let _ = unsafe { bindings::$write_fn(view.pdev.as_raw(), 
> addr, value) };
>             }
>         }
>     };
> @@ -120,17 +146,13 @@ unsafe fn io_write(self, value: $ty, address: usize) {
> impl_config_space_io_capable!(u16, pci_read_config_word, 
> pci_write_config_word);
> impl_config_space_io_capable!(u32, pci_read_config_dword, 
> pci_write_config_dword);
> 
> -impl<'a, T: ?Sized + KnownSize> Io for ConfigSpace<'a, T> {
> +impl<'a, T: ?Sized + KnownSize> Io<'a> for ConfigSpace<'a, T> {
> +    type Backend = ConfigSpaceBackend;
>     type Target = T;
> 
>     #[inline]
> -    fn addr(self) -> usize {
> -        self.ptr.addr()
> -    }
> -
> -    #[inline]
> -    fn maxsize(self) -> usize {
> -        KnownSize::size(self.ptr)
> +    fn as_view(self) -> ConfigSpace<'a, T> {
> +        self
>     }
> }
> 
> 
> -- 
> 2.54.0
> 

Reviewed-by: Daniel Almeida <[email protected]>

Reply via email to