mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	rust: io: mem: add a generic iomem abstraction
Add a generic iomem abstraction to safely read and write ioremapped regions. This abstraction requires a previously acquired IoRequest instance. This makes it so that both the resource and the device match, or, in other words, that the resource is indeed a valid resource for a given bound device. A subsequent patch will add the ability to retrieve IoRequest instances from platform devices. The reads and writes are done through IoRaw, and are thus checked either at compile-time, if the size of the region is known at that point, or at runtime otherwise. Non-exclusive access to the underlying memory region is made possible to cater to cases where overlapped regions are unavoidable. Acked-by: Miguel Ojeda <ojeda@kernel.org> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com> Link: https://lore.kernel.org/r/20250717-topics-tyr-platform_iomem-v15-2-beca780b77e3@collabora.com [ Add #[expect(dead_code)] to avoid a temporary warning, remove unnecessary OF_ID_TABLE constants in doc-tests and ignore doc-tests for now to avoid a temporary build failure. - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
This commit is contained in:
		
							parent
							
								
									493fc33ec2
								
							
						
					
					
						commit
						1d0d4b2851
					
				
					 3 changed files with 286 additions and 0 deletions
				
			
		| 
						 | 
					@ -8,6 +8,11 @@ void __iomem *rust_helper_ioremap(phys_addr_t offset, size_t size)
 | 
				
			||||||
	return ioremap(offset, size);
 | 
						return ioremap(offset, size);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void __iomem *rust_helper_ioremap_np(phys_addr_t offset, size_t size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return ioremap_np(offset, size);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void rust_helper_iounmap(void __iomem *addr)
 | 
					void rust_helper_iounmap(void __iomem *addr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	iounmap(addr);
 | 
						iounmap(addr);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@
 | 
				
			||||||
use crate::error::{code::EINVAL, Result};
 | 
					use crate::error::{code::EINVAL, Result};
 | 
				
			||||||
use crate::{bindings, build_assert};
 | 
					use crate::{bindings, build_assert};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub mod mem;
 | 
				
			||||||
pub mod resource;
 | 
					pub mod resource;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use resource::Resource;
 | 
					pub use resource::Resource;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										280
									
								
								rust/kernel/io/mem.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								rust/kernel/io/mem.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,280 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//! Generic memory-mapped IO.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use core::ops::Deref;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::c_str;
 | 
				
			||||||
 | 
					use crate::device::Bound;
 | 
				
			||||||
 | 
					use crate::device::Device;
 | 
				
			||||||
 | 
					use crate::devres::Devres;
 | 
				
			||||||
 | 
					use crate::io;
 | 
				
			||||||
 | 
					use crate::io::resource::Region;
 | 
				
			||||||
 | 
					use crate::io::resource::Resource;
 | 
				
			||||||
 | 
					use crate::io::Io;
 | 
				
			||||||
 | 
					use crate::io::IoRaw;
 | 
				
			||||||
 | 
					use crate::prelude::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// An IO request for a specific device and resource.
 | 
				
			||||||
 | 
					pub struct IoRequest<'a> {
 | 
				
			||||||
 | 
					    device: &'a Device<Bound>,
 | 
				
			||||||
 | 
					    resource: &'a Resource,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a> IoRequest<'a> {
 | 
				
			||||||
 | 
					    /// Creates a new [`IoRequest`] instance.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Safety
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Callers must ensure that `resource` is valid for `device` during the
 | 
				
			||||||
 | 
					    /// lifetime `'a`.
 | 
				
			||||||
 | 
					    #[expect(dead_code)]
 | 
				
			||||||
 | 
					    pub(crate) unsafe fn new(device: &'a Device<Bound>, resource: &'a Resource) -> Self {
 | 
				
			||||||
 | 
					        IoRequest { device, resource }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Maps an [`IoRequest`] where the size is known at compile time.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This uses the [`ioremap()`] C API.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Examples
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// The following example uses a [`platform::Device`] for illustration
 | 
				
			||||||
 | 
					    /// purposes.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// ```ignore
 | 
				
			||||||
 | 
					    /// use kernel::{bindings, c_str, platform, of, device::Core};
 | 
				
			||||||
 | 
					    /// struct SampleDriver;
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// impl platform::Driver for SampleDriver {
 | 
				
			||||||
 | 
					    ///    # type IdInfo = ();
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    ///    fn probe(
 | 
				
			||||||
 | 
					    ///       pdev: &platform::Device<Core>,
 | 
				
			||||||
 | 
					    ///       info: Option<&Self::IdInfo>,
 | 
				
			||||||
 | 
					    ///    ) -> Result<Pin<KBox<Self>>> {
 | 
				
			||||||
 | 
					    ///       let offset = 0; // Some offset.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    ///       // If the size is known at compile time, use [`Self::iomap_sized`].
 | 
				
			||||||
 | 
					    ///       //
 | 
				
			||||||
 | 
					    ///       // No runtime checks will apply when reading and writing.
 | 
				
			||||||
 | 
					    ///       let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
 | 
				
			||||||
 | 
					    ///       let iomem = request.iomap_sized::<42>();
 | 
				
			||||||
 | 
					    ///       let iomem = KBox::pin_init(iomem, GFP_KERNEL)?;
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    ///       let io = iomem.access(pdev.as_ref())?;
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    ///       // Read and write a 32-bit value at `offset`.
 | 
				
			||||||
 | 
					    ///       let data = io.read32_relaxed(offset);
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    ///       io.write32_relaxed(data, offset);
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    ///       # Ok(KBox::new(SampleDriver, GFP_KERNEL)?.into())
 | 
				
			||||||
 | 
					    ///     }
 | 
				
			||||||
 | 
					    /// }
 | 
				
			||||||
 | 
					    /// ```
 | 
				
			||||||
 | 
					    pub fn iomap_sized<const SIZE: usize>(self) -> impl PinInit<Devres<IoMem<SIZE>>, Error> + 'a {
 | 
				
			||||||
 | 
					        IoMem::new(self)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Same as [`Self::iomap_sized`] but with exclusive access to the
 | 
				
			||||||
 | 
					    /// underlying region.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This uses the [`ioremap()`] C API.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device
 | 
				
			||||||
 | 
					    pub fn iomap_exclusive_sized<const SIZE: usize>(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					    ) -> impl PinInit<Devres<ExclusiveIoMem<SIZE>>, Error> + 'a {
 | 
				
			||||||
 | 
					        ExclusiveIoMem::new(self)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Maps an [`IoRequest`] where the size is not known at compile time,
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This uses the [`ioremap()`] C API.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Examples
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// The following example uses a [`platform::Device`] for illustration
 | 
				
			||||||
 | 
					    /// purposes.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// ```ignore
 | 
				
			||||||
 | 
					    /// use kernel::{bindings, c_str, platform, of, device::Core};
 | 
				
			||||||
 | 
					    /// struct SampleDriver;
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// impl platform::Driver for SampleDriver {
 | 
				
			||||||
 | 
					    ///    # type IdInfo = ();
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    ///    fn probe(
 | 
				
			||||||
 | 
					    ///       pdev: &platform::Device<Core>,
 | 
				
			||||||
 | 
					    ///       info: Option<&Self::IdInfo>,
 | 
				
			||||||
 | 
					    ///    ) -> Result<Pin<KBox<Self>>> {
 | 
				
			||||||
 | 
					    ///       let offset = 0; // Some offset.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    ///       // Unlike [`Self::iomap_sized`], here the size of the memory region
 | 
				
			||||||
 | 
					    ///       // is not known at compile time, so only the `try_read*` and `try_write*`
 | 
				
			||||||
 | 
					    ///       // family of functions should be used, leading to runtime checks on every
 | 
				
			||||||
 | 
					    ///       // access.
 | 
				
			||||||
 | 
					    ///       let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
 | 
				
			||||||
 | 
					    ///       let iomem = request.iomap();
 | 
				
			||||||
 | 
					    ///       let iomem = KBox::pin_init(iomem, GFP_KERNEL)?;
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    ///       let io = iomem.access(pdev.as_ref())?;
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    ///       let data = io.try_read32_relaxed(offset)?;
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    ///       io.try_write32_relaxed(data, offset)?;
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    ///       # Ok(KBox::new(SampleDriver, GFP_KERNEL)?.into())
 | 
				
			||||||
 | 
					    ///     }
 | 
				
			||||||
 | 
					    /// }
 | 
				
			||||||
 | 
					    /// ```
 | 
				
			||||||
 | 
					    pub fn iomap(self) -> impl PinInit<Devres<IoMem<0>>, Error> + 'a {
 | 
				
			||||||
 | 
					        Self::iomap_sized::<0>(self)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Same as [`Self::iomap`] but with exclusive access to the underlying
 | 
				
			||||||
 | 
					    /// region.
 | 
				
			||||||
 | 
					    pub fn iomap_exclusive(self) -> impl PinInit<Devres<ExclusiveIoMem<0>>, Error> + 'a {
 | 
				
			||||||
 | 
					        Self::iomap_exclusive_sized::<0>(self)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// An exclusive memory-mapped IO region.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// # Invariants
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`].
 | 
				
			||||||
 | 
					pub struct ExclusiveIoMem<const SIZE: usize> {
 | 
				
			||||||
 | 
					    /// The underlying `IoMem` instance.
 | 
				
			||||||
 | 
					    iomem: IoMem<SIZE>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// The region abstraction. This represents exclusive access to the
 | 
				
			||||||
 | 
					    /// range represented by the underlying `iomem`.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This field is needed for ownership of the region.
 | 
				
			||||||
 | 
					    _region: Region,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<const SIZE: usize> ExclusiveIoMem<SIZE> {
 | 
				
			||||||
 | 
					    /// Creates a new `ExclusiveIoMem` instance.
 | 
				
			||||||
 | 
					    fn ioremap(resource: &Resource) -> Result<Self> {
 | 
				
			||||||
 | 
					        let start = resource.start();
 | 
				
			||||||
 | 
					        let size = resource.size();
 | 
				
			||||||
 | 
					        let name = resource.name().unwrap_or(c_str!(""));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let region = resource
 | 
				
			||||||
 | 
					            .request_region(
 | 
				
			||||||
 | 
					                start,
 | 
				
			||||||
 | 
					                size,
 | 
				
			||||||
 | 
					                name.to_cstring()?,
 | 
				
			||||||
 | 
					                io::resource::Flags::IORESOURCE_MEM,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .ok_or(EBUSY)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let iomem = IoMem::ioremap(resource)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let iomem = ExclusiveIoMem {
 | 
				
			||||||
 | 
					            iomem,
 | 
				
			||||||
 | 
					            _region: region,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(iomem)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Creates a new `ExclusiveIoMem` instance from a previously acquired [`IoRequest`].
 | 
				
			||||||
 | 
					    pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a {
 | 
				
			||||||
 | 
					        let dev = io_request.device;
 | 
				
			||||||
 | 
					        let res = io_request.resource;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Devres::new(dev, Self::ioremap(res))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<const SIZE: usize> Deref for ExclusiveIoMem<SIZE> {
 | 
				
			||||||
 | 
					    type Target = Io<SIZE>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn deref(&self) -> &Self::Target {
 | 
				
			||||||
 | 
					        &self.iomem
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// A generic memory-mapped IO region.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Accesses to the underlying region is checked either at compile time, if the
 | 
				
			||||||
 | 
					/// region's size is known at that point, or at runtime otherwise.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// # Invariants
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// [`IoMem`] always holds an [`IoRaw`] instance that holds a valid pointer to the
 | 
				
			||||||
 | 
					/// start of the I/O memory mapped region.
 | 
				
			||||||
 | 
					pub struct IoMem<const SIZE: usize = 0> {
 | 
				
			||||||
 | 
					    io: IoRaw<SIZE>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<const SIZE: usize> IoMem<SIZE> {
 | 
				
			||||||
 | 
					    fn ioremap(resource: &Resource) -> Result<Self> {
 | 
				
			||||||
 | 
					        // Note: Some ioremap() implementations use types that depend on the CPU
 | 
				
			||||||
 | 
					        // word width rather than the bus address width.
 | 
				
			||||||
 | 
					        //
 | 
				
			||||||
 | 
					        // TODO: Properly address this in the C code to avoid this `try_into`.
 | 
				
			||||||
 | 
					        let size = resource.size().try_into()?;
 | 
				
			||||||
 | 
					        if size == 0 {
 | 
				
			||||||
 | 
					            return Err(EINVAL);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let res_start = resource.start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let addr = if resource
 | 
				
			||||||
 | 
					            .flags()
 | 
				
			||||||
 | 
					            .contains(io::resource::Flags::IORESOURCE_MEM_NONPOSTED)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // SAFETY:
 | 
				
			||||||
 | 
					            // - `res_start` and `size` are read from a presumably valid `struct resource`.
 | 
				
			||||||
 | 
					            // - `size` is known not to be zero at this point.
 | 
				
			||||||
 | 
					            unsafe { bindings::ioremap_np(res_start, size) }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // SAFETY:
 | 
				
			||||||
 | 
					            // - `res_start` and `size` are read from a presumably valid `struct resource`.
 | 
				
			||||||
 | 
					            // - `size` is known not to be zero at this point.
 | 
				
			||||||
 | 
					            unsafe { bindings::ioremap(res_start, size) }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if addr.is_null() {
 | 
				
			||||||
 | 
					            return Err(ENOMEM);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let io = IoRaw::new(addr as usize, size)?;
 | 
				
			||||||
 | 
					        let io = IoMem { io };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(io)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Creates a new `IoMem` instance from a previously acquired [`IoRequest`].
 | 
				
			||||||
 | 
					    pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a {
 | 
				
			||||||
 | 
					        let dev = io_request.device;
 | 
				
			||||||
 | 
					        let res = io_request.resource;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Devres::new(dev, Self::ioremap(res))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<const SIZE: usize> Drop for IoMem<SIZE> {
 | 
				
			||||||
 | 
					    fn drop(&mut self) {
 | 
				
			||||||
 | 
					        // SAFETY: Safe as by the invariant of `Io`.
 | 
				
			||||||
 | 
					        unsafe { bindings::iounmap(self.io.addr() as *mut c_void) }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<const SIZE: usize> Deref for IoMem<SIZE> {
 | 
				
			||||||
 | 
					    type Target = Io<SIZE>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn deref(&self) -> &Self::Target {
 | 
				
			||||||
 | 
					        // SAFETY: Safe as by the invariant of `IoMem`.
 | 
				
			||||||
 | 
					        unsafe { Io::from_raw(&self.io) }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in a new issue