mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	rust: io: add resource abstraction
In preparation for ioremap support, add a Rust abstraction for struct resource. A future commit will introduce the Rust API to ioremap a resource from a platform device. The current abstraction, therefore, adds only the minimum API needed to get that done. Acked-by: Miguel Ojeda <ojeda@kernel.org> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Co-developed-by: Fiona Behrens <me@kloenk.dev> Signed-off-by: Fiona Behrens <me@kloenk.dev> Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com> Link: https://lore.kernel.org/r/20250717-topics-tyr-platform_iomem-v15-1-beca780b77e3@collabora.com [ Capitalize safety comments and end it with a period. - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
This commit is contained in:
		
							parent
							
								
									931d9251e4
								
							
						
					
					
						commit
						493fc33ec2
					
				
					 4 changed files with 270 additions and 0 deletions
				
			
		| 
						 | 
					@ -53,6 +53,7 @@
 | 
				
			||||||
#include <linux/file.h>
 | 
					#include <linux/file.h>
 | 
				
			||||||
#include <linux/firmware.h>
 | 
					#include <linux/firmware.h>
 | 
				
			||||||
#include <linux/fs.h>
 | 
					#include <linux/fs.h>
 | 
				
			||||||
 | 
					#include <linux/ioport.h>
 | 
				
			||||||
#include <linux/jiffies.h>
 | 
					#include <linux/jiffies.h>
 | 
				
			||||||
#include <linux/jump_label.h>
 | 
					#include <linux/jump_label.h>
 | 
				
			||||||
#include <linux/mdio.h>
 | 
					#include <linux/mdio.h>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
// SPDX-License-Identifier: GPL-2.0
 | 
					// SPDX-License-Identifier: GPL-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/io.h>
 | 
					#include <linux/io.h>
 | 
				
			||||||
 | 
					#include <linux/ioport.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void __iomem *rust_helper_ioremap(phys_addr_t offset, size_t size)
 | 
					void __iomem *rust_helper_ioremap(phys_addr_t offset, size_t size)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -99,3 +100,38 @@ void rust_helper_writeq_relaxed(u64 value, void __iomem *addr)
 | 
				
			||||||
	writeq_relaxed(value, addr);
 | 
						writeq_relaxed(value, addr);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					resource_size_t rust_helper_resource_size(struct resource *res)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return resource_size(res);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct resource *rust_helper_request_mem_region(resource_size_t start,
 | 
				
			||||||
 | 
											resource_size_t n,
 | 
				
			||||||
 | 
											const char *name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return request_mem_region(start, n, name);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void rust_helper_release_mem_region(resource_size_t start, resource_size_t n)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						release_mem_region(start, n);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct resource *rust_helper_request_region(resource_size_t start,
 | 
				
			||||||
 | 
										    resource_size_t n, const char *name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return request_region(start, n, name);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct resource *rust_helper_request_muxed_region(resource_size_t start,
 | 
				
			||||||
 | 
											  resource_size_t n,
 | 
				
			||||||
 | 
											  const char *name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return request_muxed_region(start, n, name);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void rust_helper_release_region(resource_size_t start, resource_size_t n)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						release_region(start, n);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,10 @@
 | 
				
			||||||
use crate::error::{code::EINVAL, Result};
 | 
					use crate::error::{code::EINVAL, Result};
 | 
				
			||||||
use crate::{bindings, build_assert};
 | 
					use crate::{bindings, build_assert};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub mod resource;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub use resource::Resource;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Raw representation of an MMIO region.
 | 
					/// Raw representation of an MMIO region.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// By itself, the existence of an instance of this structure does not provide any guarantees that
 | 
					/// By itself, the existence of an instance of this structure does not provide any guarantees that
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										229
									
								
								rust/kernel/io/resource.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								rust/kernel/io/resource.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,229 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//! Abstractions for [system
 | 
				
			||||||
 | 
					//! resources](https://docs.kernel.org/core-api/kernel-api.html#resources-management).
 | 
				
			||||||
 | 
					//!
 | 
				
			||||||
 | 
					//! C header: [`include/linux/ioport.h`](srctree/include/linux/ioport.h)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use core::ops::Deref;
 | 
				
			||||||
 | 
					use core::ptr::NonNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::prelude::*;
 | 
				
			||||||
 | 
					use crate::str::{CStr, CString};
 | 
				
			||||||
 | 
					use crate::types::Opaque;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Resource Size type.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// This is a type alias to either `u32` or `u64` depending on the config option
 | 
				
			||||||
 | 
					/// `CONFIG_PHYS_ADDR_T_64BIT`, and it can be a u64 even on 32-bit architectures.
 | 
				
			||||||
 | 
					pub type ResourceSize = bindings::phys_addr_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// A region allocated from a parent [`Resource`].
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// # Invariants
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// - `self.0` points to a valid `bindings::resource` that was obtained through
 | 
				
			||||||
 | 
					///   `bindings::__request_region`.
 | 
				
			||||||
 | 
					pub struct Region {
 | 
				
			||||||
 | 
					    /// The resource returned when the region was requested.
 | 
				
			||||||
 | 
					    resource: NonNull<bindings::resource>,
 | 
				
			||||||
 | 
					    /// The name that was passed in when the region was requested. We need to
 | 
				
			||||||
 | 
					    /// store it for ownership reasons.
 | 
				
			||||||
 | 
					    _name: CString,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Deref for Region {
 | 
				
			||||||
 | 
					    type Target = Resource;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn deref(&self) -> &Self::Target {
 | 
				
			||||||
 | 
					        // SAFETY: Safe as per the invariant of `Region`.
 | 
				
			||||||
 | 
					        unsafe { Resource::from_raw(self.resource.as_ptr()) }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Drop for Region {
 | 
				
			||||||
 | 
					    fn drop(&mut self) {
 | 
				
			||||||
 | 
					        let (flags, start, size) = {
 | 
				
			||||||
 | 
					            let res = &**self;
 | 
				
			||||||
 | 
					            (res.flags(), res.start(), res.size())
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let release_fn = if flags.contains(Flags::IORESOURCE_MEM) {
 | 
				
			||||||
 | 
					            bindings::release_mem_region
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            bindings::release_region
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // SAFETY: Safe as per the invariant of `Region`.
 | 
				
			||||||
 | 
					        unsafe { release_fn(start, size) };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SAFETY: `Region` only holds a pointer to a C `struct resource`, which is safe to be used from
 | 
				
			||||||
 | 
					// any thread.
 | 
				
			||||||
 | 
					unsafe impl Send for Region {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SAFETY: `Region` only holds a pointer to a C `struct resource`, references to which are
 | 
				
			||||||
 | 
					// safe to be used from any thread.
 | 
				
			||||||
 | 
					unsafe impl Sync for Region {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// A resource abstraction.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// # Invariants
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// [`Resource`] is a transparent wrapper around a valid `bindings::resource`.
 | 
				
			||||||
 | 
					#[repr(transparent)]
 | 
				
			||||||
 | 
					pub struct Resource(Opaque<bindings::resource>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Resource {
 | 
				
			||||||
 | 
					    /// Creates a reference to a [`Resource`] from a valid pointer.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Safety
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// The caller must ensure that for the duration of 'a, the pointer will
 | 
				
			||||||
 | 
					    /// point at a valid `bindings::resource`.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// The caller must also ensure that the [`Resource`] is only accessed via the
 | 
				
			||||||
 | 
					    /// returned reference for the duration of 'a.
 | 
				
			||||||
 | 
					    pub(crate) const unsafe fn from_raw<'a>(ptr: *mut bindings::resource) -> &'a Self {
 | 
				
			||||||
 | 
					        // SAFETY: Self is a transparent wrapper around `Opaque<bindings::resource>`.
 | 
				
			||||||
 | 
					        unsafe { &*ptr.cast() }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Requests a resource region.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Exclusive access will be given and the region will be marked as busy.
 | 
				
			||||||
 | 
					    /// Further calls to [`Self::request_region`] will return [`None`] if
 | 
				
			||||||
 | 
					    /// the region, or a part of it, is already in use.
 | 
				
			||||||
 | 
					    pub fn request_region(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        start: ResourceSize,
 | 
				
			||||||
 | 
					        size: ResourceSize,
 | 
				
			||||||
 | 
					        name: CString,
 | 
				
			||||||
 | 
					        flags: Flags,
 | 
				
			||||||
 | 
					    ) -> Option<Region> {
 | 
				
			||||||
 | 
					        // SAFETY:
 | 
				
			||||||
 | 
					        // - Safe as per the invariant of `Resource`.
 | 
				
			||||||
 | 
					        // - `__request_region` will store a reference to the name, but that is
 | 
				
			||||||
 | 
					        // safe as we own it and it will not be dropped until the `Region` is
 | 
				
			||||||
 | 
					        // dropped.
 | 
				
			||||||
 | 
					        let region = unsafe {
 | 
				
			||||||
 | 
					            bindings::__request_region(
 | 
				
			||||||
 | 
					                self.0.get(),
 | 
				
			||||||
 | 
					                start,
 | 
				
			||||||
 | 
					                size,
 | 
				
			||||||
 | 
					                name.as_char_ptr(),
 | 
				
			||||||
 | 
					                flags.0 as c_int,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Some(Region {
 | 
				
			||||||
 | 
					            resource: NonNull::new(region)?,
 | 
				
			||||||
 | 
					            _name: name,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns the size of the resource.
 | 
				
			||||||
 | 
					    pub fn size(&self) -> ResourceSize {
 | 
				
			||||||
 | 
					        let inner = self.0.get();
 | 
				
			||||||
 | 
					        // SAFETY: Safe as per the invariants of `Resource`.
 | 
				
			||||||
 | 
					        unsafe { bindings::resource_size(inner) }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns the start address of the resource.
 | 
				
			||||||
 | 
					    pub fn start(&self) -> ResourceSize {
 | 
				
			||||||
 | 
					        let inner = self.0.get();
 | 
				
			||||||
 | 
					        // SAFETY: Safe as per the invariants of `Resource`.
 | 
				
			||||||
 | 
					        unsafe { (*inner).start }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns the name of the resource.
 | 
				
			||||||
 | 
					    pub fn name(&self) -> Option<&CStr> {
 | 
				
			||||||
 | 
					        let inner = self.0.get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // SAFETY: Safe as per the invariants of `Resource`.
 | 
				
			||||||
 | 
					        let name = unsafe { (*inner).name };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if name.is_null() {
 | 
				
			||||||
 | 
					            return None;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // SAFETY: In the C code, `resource::name` either contains a null
 | 
				
			||||||
 | 
					        // pointer or points to a valid NUL-terminated C string, and at this
 | 
				
			||||||
 | 
					        // point we know it is not null, so we can safely convert it to a
 | 
				
			||||||
 | 
					        // `CStr`.
 | 
				
			||||||
 | 
					        Some(unsafe { CStr::from_char_ptr(name) })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns the flags associated with the resource.
 | 
				
			||||||
 | 
					    pub fn flags(&self) -> Flags {
 | 
				
			||||||
 | 
					        let inner = self.0.get();
 | 
				
			||||||
 | 
					        // SAFETY: Safe as per the invariants of `Resource`.
 | 
				
			||||||
 | 
					        let flags = unsafe { (*inner).flags };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Flags(flags)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SAFETY: `Resource` only holds a pointer to a C `struct resource`, which is
 | 
				
			||||||
 | 
					// safe to be used from any thread.
 | 
				
			||||||
 | 
					unsafe impl Send for Resource {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SAFETY: `Resource` only holds a pointer to a C `struct resource`, references
 | 
				
			||||||
 | 
					// to which are safe to be used from any thread.
 | 
				
			||||||
 | 
					unsafe impl Sync for Resource {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Resource flags as stored in the C `struct resource::flags` field.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// They can be combined with the operators `|`, `&`, and `!`.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Values can be used from the [`flags`] module.
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, PartialEq)]
 | 
				
			||||||
 | 
					pub struct Flags(c_ulong);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Flags {
 | 
				
			||||||
 | 
					    /// Check whether `flags` is contained in `self`.
 | 
				
			||||||
 | 
					    pub fn contains(self, flags: Flags) -> bool {
 | 
				
			||||||
 | 
					        (self & flags) == flags
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl core::ops::BitOr for Flags {
 | 
				
			||||||
 | 
					    type Output = Self;
 | 
				
			||||||
 | 
					    fn bitor(self, rhs: Self) -> Self::Output {
 | 
				
			||||||
 | 
					        Self(self.0 | rhs.0)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl core::ops::BitAnd for Flags {
 | 
				
			||||||
 | 
					    type Output = Self;
 | 
				
			||||||
 | 
					    fn bitand(self, rhs: Self) -> Self::Output {
 | 
				
			||||||
 | 
					        Self(self.0 & rhs.0)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl core::ops::Not for Flags {
 | 
				
			||||||
 | 
					    type Output = Self;
 | 
				
			||||||
 | 
					    fn not(self) -> Self::Output {
 | 
				
			||||||
 | 
					        Self(!self.0)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Flags {
 | 
				
			||||||
 | 
					    /// PCI/ISA I/O ports.
 | 
				
			||||||
 | 
					    pub const IORESOURCE_IO: Flags = Flags::new(bindings::IORESOURCE_IO);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Resource is software muxed.
 | 
				
			||||||
 | 
					    pub const IORESOURCE_MUXED: Flags = Flags::new(bindings::IORESOURCE_MUXED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Resource represents a memory region.
 | 
				
			||||||
 | 
					    pub const IORESOURCE_MEM: Flags = Flags::new(bindings::IORESOURCE_MEM);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Resource represents a memory region that must be ioremaped using `ioremap_np`.
 | 
				
			||||||
 | 
					    pub const IORESOURCE_MEM_NONPOSTED: Flags = Flags::new(bindings::IORESOURCE_MEM_NONPOSTED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const fn new(value: u32) -> Self {
 | 
				
			||||||
 | 
					        crate::build_assert!(value as u64 <= c_ulong::MAX as u64);
 | 
				
			||||||
 | 
					        Flags(value as c_ulong)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in a new issue