Re: [PATCH v6 13/20] rust: io: add projection macro and methods

Daniel Almeida <[email protected]>
Newsgroups dev.linux.lists.nova-gpu,dev.linux.lists.driver-core,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci,org.kernel.vger.linux-pwm,org.kernel.vger.rust-for-linux
Message-ID <[email protected]>

> On 6 Jul 2026, at 09:44, Gary Guo <[email protected]> wrote:
> 
> Add an `io_project!()` macro to allow projection from `Io` to a subview of
> it, using the pointer projection mechanism to perform compile-time checks.
> 
> For cases where type-casting is required, the `try_cast()` function may be
> used where the size and alignment checks are performed at runtime.
> 
> Reviewed-by: Alexandre Courbot <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
> ---
> rust/kernel/io.rs | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 128 insertions(+)
> 
> diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
> index ab970e8b2e78..62643b23e904 100644
> --- a/rust/kernel/io.rs
> +++ b/rust/kernel/io.rs
> @@ -49,6 +49,7 @@
> /// - Size of the region is at least as large as the `SIZE` generic parameter.
> /// - Size of the region is multiple of 4.
> #[repr(C, align(4))]
> +#[derive(FromBytes)]
> pub struct Region<const SIZE: usize = 0> {
>     inner: [u8],
> }
> @@ -90,6 +91,19 @@ fn size(p: *const Self) -> usize {
>     }
> }
> 
> +// SAFETY:
> +// - Values read from I/O are always treated as initialized.
> +// - Per type invariant the size is multiple of 4 and the type is 4-byte aligned, so it is padding
> +//   free.
> +//
> +// This cannot be derived as `derive(IntoBytes)` as the padding free property comes from type
> +// invariant which the macro does not know.
> +unsafe impl<const SIZE: usize> IntoBytes for Region<SIZE> {
> +    #[inline]
> +    #[allow(unused)] // Rust 1.87+ stops requiring this and will emit unused warnings.
> +    fn only_derive_is_allowed_to_implement_this_trait() {}
> +}
> +
> /// Raw representation of an MMIO region.
> ///
> /// `MmioRaw<T>` is equivalent to `T __iomem *` in C.
> @@ -339,6 +353,51 @@ fn size(self) -> usize {
>         KnownSize::size(Self::Backend::as_ptr(self.as_view()))
>     }
> 
> +    /// Try to convert into a different typed I/O view.
> +    ///
> +    /// A runtime check is performed to ensure that the target type is of same or smaller size to
> +    /// current type, and the current view is properly aligned for the target type. Returns
> +    /// `Err(EINVAL)` if the runtime check fails.
> +    ///
> +    /// # Examples
> +    ///
> +    /// ```no_run
> +    /// use kernel::io::{
> +    ///     io_project,
> +    ///     Mmio,
> +    ///     Io,
> +    ///     Region,
> +    /// };
> +    /// #[derive(FromBytes, IntoBytes)]
> +    /// #[repr(C)]
> +    /// struct MyStruct { field: u32, }
> +    ///
> +    /// # fn test(mmio: &Mmio<'_, Region>) -> Result {
> +    /// // let mmio: Mmio<'_, Region>;
> +    /// let whole: Mmio<'_, MyStruct> = mmio.try_cast()?;
> +    /// # Ok::<(), Error>(()) }
> +    /// ```
> +    #[inline]
> +    fn try_cast<U>(self) -> Result<<Self::Backend as IoBackend>::View<'a, U>>
> +    where
> +        Self::Target: FromBytes + IntoBytes,
> +        U: FromBytes + IntoBytes,
> +    {
> +        let view = self.as_view();
> +        let ptr = Self::Backend::as_ptr(view);
> +
> +        if size_of::<U>() > KnownSize::size(ptr) {
> +            return Err(EINVAL);
> +        }
> +
> +        if ptr.addr() % align_of::<U>() != 0 {
> +            return Err(EINVAL);
> +        }
> +
> +        // SAFETY: We have checked bounds and alignment, so this is a valid projection.
> +        Ok(unsafe { Self::Backend::project_view(view, ptr.cast()) })
> +    }
> +
>     /// Fallible 8-bit read with runtime bounds check.
>     #[inline(always)]
>     fn try_read8(self, offset: usize) -> Result<u8>
> @@ -979,3 +1038,72 @@ pub fn relaxed(self) -> RelaxedMmio<'a, T> {
> // MMIO regions on 64-bit systems also support 64-bit accesses.
> #[cfg(CONFIG_64BIT)]
> impl_mmio_io_capable!(RelaxedMmioBackend, u64, readq_relaxed, writeq_relaxed);
> +
> +// This helper turns associated functions to methods so it can be invoked in macro.
> +// Used by `io_project!()` only.
> +#[doc(hidden)]
> +#[derive(Clone, Copy)]
> +pub struct ProjectHelper<T>(pub T);
> +
> +impl<'a, T> ProjectHelper<T>
> +where
> +    T: Io<'a, Backend: IoBackend<View<'a, T::Target> = T>>,
> +{
> +    // These helper methods must not have symbols present in the binary to avoid confusion.
> +    #[inline(always)]
> +    pub fn as_ptr(self) -> *mut T::Target {
> +        T::Backend::as_ptr(self.0)
> +    }
> +
> +    /// # Safety
> +    ///
> +    /// Same as `IoBackend::project_view`
> +    #[inline(always)]
> +    pub unsafe fn project_view<U: ?Sized + KnownSize>(
> +        self,
> +        ptr: *mut U,
> +    ) -> <T::Backend as IoBackend>::View<'a, U> {
> +        // SAFETY: Per safety requirement.
> +        unsafe { T::Backend::project_view::<T::Target, _>(self.0, ptr) }
> +    }
> +}
> +
> +/// Project an I/O type to a subview of it.
> +///
> +/// 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!).
> +///
> +/// # Examples
> +///
> +/// ```
> +/// use kernel::io::{
> +///     io_project,
> +///     Mmio,
> +/// };
> +/// #[repr(C)]
> +/// struct MyStruct { field: u32, }
> +///
> +/// # 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);
> +/// # Ok::<(), Error>(()) }
> +/// ```
> +#[macro_export]
> +#[doc(hidden)]
> +macro_rules! io_project {
> +    ($io:expr, $($proj:tt)*) => {{
> +        #[allow(unused)]
> +        use $crate::io::IoBase as _;
> +        let view = $crate::io::ProjectHelper($io.as_view());
> +        let ptr = $crate::ptr::project!(
> +            mut view.as_ptr(), $($proj)*
> +        );
> +        #[allow(unused_unsafe)]
> +        // SAFETY: `ptr` is a projection.
> +        unsafe { view.project_view(ptr) }
> +    }};
> +}
> +#[doc(inline)]
> +pub use crate::io_project;
> 
> -- 
> 2.54.0
> 

Reviewed-by: Daniel Almeida <[email protected]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.