From nobody Mon Oct 6 21:00:45 2025 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F14FD2ED84D; Wed, 16 Jul 2025 21:45:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752702340; cv=none; b=evqfu7d6x3xMpMh8tnIYhzB4nus6D3zmpetYpuBjZVoiOY0xhi+cZUYNvL48t0zy5idK2mbqkSFk09mYvHrlPDMJOwUnbkNfkCxvTGbsKwnEi/LuVd4moISgN7vGeR3xwD4GZP1/r3lPmuiZI6dDEoBu1MdPZM6pHosUUtIE4/c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752702340; c=relaxed/simple; bh=NVHP2L7I+F80S0iSXz7Ayio5SqCeQC1UYDi+yY/64nc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=J6dYQz4z18W9WSd/kJYSR9ruS/VYba3GfZob6jShDFoQ9sKsdP6BYyqtyLLxZX9ehB6xUZrPRmMeCdgFhmjVbqxFaTfmyCDI6XCB3Lxw2/Uz+jUdjOWym7ed53viPLZW0jy7pOa1Jx0W3IiqVvfzjM97jU54R7NYIIjvKfl2U4o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=Hz2DBHig; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="Hz2DBHig" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1752702328; bh=NVHP2L7I+F80S0iSXz7Ayio5SqCeQC1UYDi+yY/64nc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Hz2DBHig2iA/huelh7HWjMNM9giutkfWS3ES2rK+8afcDcxTzmrYst9FeOBeA0nc0 JDX7NTSwiCVQM4+Y8FPXG4BWNkUmdTOlRI8txI9NqsaI/om3b7EoidfPYs3OPO9USF eg+SS5hbOiw6qIM1lZ6h1/D1DkgbP+PDr5TCYo+EDIscw9D9qr2vzu0tJ1ldHsMVbU e2/I+BXAbSAVEW7pCvZrkItyCOoiGAaREFqlOGSPM34hITfE1He3R8pi7MKmIILEQT VjEoZseZ2uNOL86okc5kjyB5W+HsTb8ufTXOjF08jdSHt3rutuXjd4iGvgL3sQo1lM bgA8ytbk/cPwQ== Received: from [192.168.0.2] (unknown [IPv6:2804:14d:72b4:82f6:67c:16ff:fe57:b5a3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: dwlsalmeida) by bali.collaboradmins.com (Postfix) with ESMTPSA id 6F24E17E10D0; Wed, 16 Jul 2025 23:45:26 +0200 (CEST) From: Daniel Almeida Date: Wed, 16 Jul 2025 18:45:13 -0300 Subject: [PATCH v14 1/3] rust: io: add resource abstraction Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250716-topics-tyr-platform_iomem-v14-1-2c2709135cb2@collabora.com> References: <20250716-topics-tyr-platform_iomem-v14-0-2c2709135cb2@collabora.com> In-Reply-To: <20250716-topics-tyr-platform_iomem-v14-0-2c2709135cb2@collabora.com> To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Greg Kroah-Hartman , "Rafael J. Wysocki" Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, Fiona Behrens , Daniel Almeida X-Mailer: b4 0.14.2 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 Reviewed-by: Alice Ryhl Co-developed-by: Fiona Behrens Signed-off-by: Fiona Behrens Signed-off-by: Daniel Almeida --- rust/bindings/bindings_helper.h | 1 + rust/helpers/io.c | 36 +++++++ rust/kernel/io.rs | 4 + rust/kernel/io/resource.rs | 229 ++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 270 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 7e8f2285064797d5bbac5583990ff823b76c6bdc..5f795e60e889b9fc887013743c8= 1b1cf92a52adb 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/io.c b/rust/helpers/io.c index 15ea187c5466256effd07efe6f6995a1dd95a967..404776cf6717c8570c7600a2471= 2ce6e4623d3c6 100644 --- a/rust/helpers/io.c +++ b/rust/helpers/io.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 =20 #include +#include =20 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); } #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); +} diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 72d80a6f131e3e826ecd9d2c3bcf54e89aa60cc3..7b70d5b5477e57d6d0f24bcd26b= d8b0071721bc0 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -7,6 +7,10 @@ use crate::error::{code::EINVAL, Result}; use crate::{bindings, build_assert}; =20 +pub mod resource; + +pub use resource::Resource; + /// Raw representation of an MMIO region. /// /// By itself, the existence of an instance of this structure does not pro= vide any guarantees that diff --git a/rust/kernel/io/resource.rs b/rust/kernel/io/resource.rs new file mode 100644 index 0000000000000000000000000000000000000000..ec07af06effbb00d3db186513a0= 683b491df020b --- /dev/null +++ b/rust/kernel/io/resource.rs @@ -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 archite= ctures. +pub type ResourceSize =3D bindings::phys_addr_t; + +/// A region allocated from a parent [`Resource`]. +/// +/// # Invariants +/// +/// - `self.0` points to a valid `bindings::resource` that was obtained th= rough +/// `bindings::__request_region`. +pub struct Region { + /// The resource returned when the region was requested. + resource: NonNull, + /// 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 =3D 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) =3D { + let res =3D &**self; + (res.flags(), res.start(), res.size()) + }; + + let release_fn =3D 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 i= s safe to be used from +// any thread. +unsafe impl Send for Region {} + +// SAFETY: `Region` only holds a pointer to a C `struct resource`, referen= ces 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::resour= ce`. +#[repr(transparent)] +pub struct Resource(Opaque); + +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 wi= ll + /// 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`. + unsafe { &*ptr.cast() } + } + + /// Requests a resource region. + /// + /// Exclusive access will be given and the region will be marked as bu= sy. + /// 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 { + // SAFETY: + // - Safe as per the invariant of `Resource`. + // - `__request_region` will store a reference to the name, but th= at is + // safe as we own it and it will not be dropped until the `Region`= is + // dropped. + let region =3D 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 =3D 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 =3D 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 =3D self.0.get(); + + // SAFETY: safe as per the invariants of `Resource` + let name =3D 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 th= is + // 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 =3D self.0.get(); + // SAFETY: safe as per the invariants of `Resource` + let flags =3D 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`, refer= ences +// 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) =3D=3D flags + } +} + +impl core::ops::BitOr for Flags { + type Output =3D Self; + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } +} + +impl core::ops::BitAnd for Flags { + type Output =3D Self; + fn bitand(self, rhs: Self) -> Self::Output { + Self(self.0 & rhs.0) + } +} + +impl core::ops::Not for Flags { + type Output =3D Self; + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +impl Flags { + /// PCI/ISA I/O ports. + pub const IORESOURCE_IO: Flags =3D Flags::new(bindings::IORESOURCE_IO); + + /// Resource is software muxed. + pub const IORESOURCE_MUXED: Flags =3D Flags::new(bindings::IORESOURCE_= MUXED); + + /// Resource represents a memory region. + pub const IORESOURCE_MEM: Flags =3D Flags::new(bindings::IORESOURCE_ME= M); + + /// Resource represents a memory region that must be ioremaped using `= ioremap_np`. + pub const IORESOURCE_MEM_NONPOSTED: Flags =3D Flags::new(bindings::IOR= ESOURCE_MEM_NONPOSTED); + + const fn new(value: u32) -> Self { + crate::build_assert!(value as u64 <=3D c_ulong::MAX as u64); + Flags(value as c_ulong) + } +} --=20 2.50.0 From nobody Mon Oct 6 21:00:45 2025 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 18C6B2ED87D; Wed, 16 Jul 2025 21:45:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752702342; cv=none; b=KN2MJzr/oXyg2a/xiBqaF77n+ckH211MkUZvqDLSWRkWSmnGBqf3baxiJewwrDvR05vKNQvTvxSbArZriPilI4aSJCm+iFnORkzXfzosSkUMdreWRp+iZUG1buKRe6aaeue+JdEas8Q3mUUGeh+phjX7drUBCTQEXolpoTIBvlk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752702342; c=relaxed/simple; bh=wv/Vm8YrvCD/3HksOSprMnMXMEdtKRgjkN9k/T3BkU8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=k9WsiOXn/w0+TPrxPcRfimS/WBRnmFO1ngh7KE3G7GJ/BRllexGHyPX0rE49HGJ77J24P/HV8lPnZEYqhOgmtR5TccP7scklIwJ38CQEalqMWCpUZfnF7PuohSR/k08UHLsvOV+OeLCzAvfSQAbJiovjenhyRCl80yoPGQn01cw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=XR1vVKey; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="XR1vVKey" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1752702331; bh=wv/Vm8YrvCD/3HksOSprMnMXMEdtKRgjkN9k/T3BkU8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=XR1vVKey9aJ9aN4LAu/9OEOKFnPI+OB8Cwo4kzKo2q1AMFrsrjROCqxL9Wp9CPeyj Oqz8YdVOhX1NobxYnTJNETV73nUwPBWTbk7q6FrC/faEAu0GHYXq8WN9y2Mc+HpLo4 7FhkIkSqstkZbZD0HdMwUYRbsrlapamGZUjeemBmOFtOHaIbmW/VJTYYpt553y3tPa 33Y+/h4k0LeEpHUxMG4ce6fN2XeTox/YnK/AJ0qqqMjhdwoE0SRFfAX4gilzYVXZCC 1iGHphKMi3vCob5fXEP5Ea+I73+GFqUVI6j6hojKaibmI2yOWZS6ieRQiwqvXxxPPh sOQ66ClE3ATBA== Received: from [192.168.0.2] (unknown [IPv6:2804:14d:72b4:82f6:67c:16ff:fe57:b5a3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: dwlsalmeida) by bali.collaboradmins.com (Postfix) with ESMTPSA id 36FAC17E11ED; Wed, 16 Jul 2025 23:45:29 +0200 (CEST) From: Daniel Almeida Date: Wed, 16 Jul 2025 18:45:14 -0300 Subject: [PATCH v14 2/3] rust: io: mem: add a generic iomem abstraction Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250716-topics-tyr-platform_iomem-v14-2-2c2709135cb2@collabora.com> References: <20250716-topics-tyr-platform_iomem-v14-0-2c2709135cb2@collabora.com> In-Reply-To: <20250716-topics-tyr-platform_iomem-v14-0-2c2709135cb2@collabora.com> To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Greg Kroah-Hartman , "Rafael J. Wysocki" Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, Daniel Almeida X-Mailer: b4 0.14.2 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 Reviewed-by: Alice Ryhl Signed-off-by: Daniel Almeida --- rust/helpers/io.c | 5 + rust/kernel/io.rs | 1 + rust/kernel/io/mem.rs | 282 ++++++++++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 288 insertions(+) diff --git a/rust/helpers/io.c b/rust/helpers/io.c index 404776cf6717c8570c7600a24712ce6e4623d3c6..c475913c69e647b1042e8e7d66b= 9148d892947a1 100644 --- a/rust/helpers/io.c +++ b/rust/helpers/io.c @@ -8,6 +8,11 @@ void __iomem *rust_helper_ioremap(phys_addr_t offset, size= _t size) return ioremap(offset, size); } =20 +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) { iounmap(addr); diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 7b70d5b5477e57d6d0f24bcd26bd8b0071721bc0..b7fc759f8b5d3c3ac6f33f5a66e= 9f619c58b7405 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -7,6 +7,7 @@ use crate::error::{code::EINVAL, Result}; use crate::{bindings, build_assert}; =20 +pub mod mem; pub mod resource; =20 pub use resource::Resource; diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs new file mode 100644 index 0000000000000000000000000000000000000000..9534f8ae42b2555ca79ec6317e8= 74d93fc60c04f --- /dev/null +++ b/rust/kernel/io/mem.rs @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Generic memory-mapped IO. + +use core::ops::Deref; + +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, + resource: &'a Resource, +} + +impl<'a> IoRequest<'a> { + /// Creates a new [`IoRequest`] instance. + /// + /// # Safety + /// + /// Callers must ensure that `resource` is valid for `device` during t= he + /// lifetime `'a`. + pub(crate) unsafe fn new(device: &'a Device, resource: &'a Reso= urce) -> 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#g= etting-access-to-the-device + /// + /// # Examples + /// + /// The following example uses a [`platform::Device`] for illustration + /// purposes. + /// + /// ```no_run + /// use kernel::{bindings, c_str, platform, of, device::Core}; + /// struct SampleDriver; + /// + /// impl platform::Driver for SampleDriver { + /// # type IdInfo =3D (); + /// # const OF_ID_TABLE: Option> =3D None; + /// + /// fn probe( + /// pdev: &platform::Device, + /// info: Option<&Self::IdInfo>, + /// ) -> Result>> { + /// let offset =3D 0; // Some offset. + /// + /// // If the size is known at compile time, use [`Self::iomap_s= ized`]. + /// // + /// // No runtime checks will apply when reading and writing. + /// let request =3D pdev.request_io_by_index(0).ok_or(ENODEV)?; + /// let iomem =3D request.iomap_sized::<42>(); + /// let iomem =3D KBox::pin_init(iomem, GFP_KERNEL)?; + /// + /// let io =3D iomem.access(pdev.as_ref())?; + /// + /// // Read and write a 32-bit value at `offset`. + /// let data =3D io.read32_relaxed(offset); + /// + /// io.write32_relaxed(data, offset); + /// + /// # Ok(KBox::new(SampleDriver, GFP_KERNEL)?.into()) + /// } + /// } + /// ``` + pub fn iomap_sized(self) -> impl PinInit>, 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#g= etting-access-to-the-device + pub fn iomap_exclusive_sized( + self, + ) -> impl PinInit>, 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#g= etting-access-to-the-device + /// + /// # Examples + /// + /// The following example uses a [`platform::Device`] for illustration + /// purposes. + /// + /// ```no_run + /// use kernel::{bindings, c_str, platform, of, device::Core}; + /// struct SampleDriver; + /// + /// impl platform::Driver for SampleDriver { + /// # type IdInfo =3D (); + /// # const OF_ID_TABLE: Option> =3D None; + /// + /// fn probe( + /// pdev: &platform::Device, + /// info: Option<&Self::IdInfo>, + /// ) -> Result>> { + /// let offset =3D 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 ch= ecks on every + /// // access. + /// let request =3D pdev.request_io_by_index(0).ok_or(ENODEV)?; + /// let iomem =3D request.iomap(); + /// let iomem =3D KBox::pin_init(iomem, GFP_KERNEL)?; + /// + /// let io =3D iomem.access(pdev.as_ref())?; + /// + /// let data =3D io.try_read32_relaxed(offset)?; + /// + /// io.try_write32_relaxed(data, offset)?; + /// + /// # Ok(KBox::new(SampleDriver, GFP_KERNEL)?.into()) + /// } + /// } + /// ``` + pub fn iomap(self) -> impl PinInit>, 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>= , 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 { + /// The underlying `IoMem` instance. + iomem: IoMem, + + /// 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 ExclusiveIoMem { + /// Creates a new `ExclusiveIoMem` instance. + fn ioremap(resource: &Resource) -> Result { + let start =3D resource.start(); + let size =3D resource.size(); + let name =3D resource.name().ok_or(EINVAL)?; + + let region =3D resource + .request_region( + start, + size, + name.to_cstring()?, + io::resource::Flags::IORESOURCE_MEM, + ) + .ok_or(EBUSY)?; + + let iomem =3D IoMem::ioremap(resource)?; + + let iomem =3D 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= , Error> + 'a { + let dev =3D io_request.device; + let res =3D io_request.resource; + + Devres::new(dev, Self::ioremap(res)) + } +} + +impl Deref for ExclusiveIoMem { + type Target =3D Io; + + fn deref(&self) -> &Self::Target { + &self.iomem + } +} + +/// A generic memory-mapped IO region. +/// +/// Accesses to the underlying region is checked either at compile time, i= f the +/// region's size is known at that point, or at runtime otherwise. +/// +/// # Invariants +/// +/// [`IoMem`] always holds an [`IoRaw`] instance that holds a valid pointe= r to the +/// start of the I/O memory mapped region. +pub struct IoMem { + io: IoRaw, +} + +impl IoMem { + fn ioremap(resource: &Resource) -> Result { + // Note: It looks like there aren't any 32-bit architectures that = define + // ioremap_np. This means that sometimes this conversion will fail= . If + // we performed a lossy cast, i.e., using `as`, then `bindings::io= remap` + // would return NULL anyway. + // + // TODO: Properly address this in the C code to avoid this `try_in= to`. + let size =3D resource.size().try_into()?; + if size =3D=3D 0 { + return Err(EINVAL); + } + + let res_start =3D resource.start(); + + let addr =3D 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 =3D IoRaw::new(addr as usize, size)?; + let io =3D IoMem { io }; + + Ok(io) + } + + /// Creates a new `IoMem` instance from a previously acquired [`IoRequ= est`]. + pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit= , Error> + 'a { + let dev =3D io_request.device; + let res =3D io_request.resource; + + Devres::new(dev, Self::ioremap(res)) + } +} + +impl Drop for IoMem { + fn drop(&mut self) { + // SAFETY: Safe as by the invariant of `Io`. + unsafe { bindings::iounmap(self.io.addr() as *mut c_void) } + } +} + +impl Deref for IoMem { + type Target =3D Io; + + fn deref(&self) -> &Self::Target { + // SAFETY: Safe as by the invariant of `IoMem`. + unsafe { Io::from_raw(&self.io) } + } +} --=20 2.50.0 From nobody Mon Oct 6 21:00:45 2025 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DD0C91C5D5A; Wed, 16 Jul 2025 21:45:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752702338; cv=none; b=kxKJqwy2/cMhYH0X0Fpn8MLsBkuvptnmeOBUQpyQ66OrqvbeTUIvEe4EzABgVCZvfJNtsqnM4tv03lBLXCoPQxE4jg78HsGfiim34yVtj2lRntrqdXOUK2KsCH4abrl1CX6CLQDUAjn/wTVG8cFyrShy7xCzD3t7aAJem1cLAq0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752702338; c=relaxed/simple; bh=31AqHo3GIvu3IzySTWWesuZK0J2sD7GkvCbF0fO1rZY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=sMT8PKe3eZt5KinwQOuhl9oAcAEnt76z4U7kIQkicBkL0jMktSS9iI3rIhAveGqQ+WKuY+iDyIxfIGSuaENKce2fOmNMRx+bwRnqFLl7mCCoc5flHIPM0Uu/HW6TO1nHaYOFV4DFdP0eqUjLtJuGHTzL5dyaYCvv4/upf3zwFUk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=G4Szcu1L; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="G4Szcu1L" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1752702334; bh=31AqHo3GIvu3IzySTWWesuZK0J2sD7GkvCbF0fO1rZY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=G4Szcu1LofR5wX/87OEmXAmPhGGVzib+JrdT98ONSXtxbnyxjdydkBNpSyzzkmZ5d 6hLzGZ6nZjxqYnK3JdgY9evHhGlxFb2BjCh4AOIj7h2Lx6/ewvp4B5LfsvmMFpM60x p+AcHTmSOmfAuh1G6U/16rVtz85+TDRhVs9YDsfrrjlfFxVICjYuyABAI2lBKzp4yD zBBn56YvusRWYHobYtSF0MXxR5+T2agtvcW7tzmvoL06lyfKFJRzakZshby42+O074 SJ+csne4k/Ba8co+PFMrJ+XV79y6umLrI7+/dtr0yFk6TUMp2Oi0VaeftrlbOXSAyK V8vx05iOYsNVA== Received: from [192.168.0.2] (unknown [IPv6:2804:14d:72b4:82f6:67c:16ff:fe57:b5a3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: dwlsalmeida) by bali.collaboradmins.com (Postfix) with ESMTPSA id D04DE17E1298; Wed, 16 Jul 2025 23:45:31 +0200 (CEST) From: Daniel Almeida Date: Wed, 16 Jul 2025 18:45:15 -0300 Subject: [PATCH v14 3/3] rust: platform: add resource accessors Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250716-topics-tyr-platform_iomem-v14-3-2c2709135cb2@collabora.com> References: <20250716-topics-tyr-platform_iomem-v14-0-2c2709135cb2@collabora.com> In-Reply-To: <20250716-topics-tyr-platform_iomem-v14-0-2c2709135cb2@collabora.com> To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Greg Kroah-Hartman , "Rafael J. Wysocki" Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, Daniel Almeida X-Mailer: b4 0.14.2 The previous patches have added the abstractions for Resources and the ability to map them and therefore read and write the underlying memory . The only thing missing to make this accessible for platform devices is to provide accessors that return instances of IoRequest<'a>. These ensure that the resource are valid only for the lifetime of the platform device, and that the platform device is in the Bound state. Therefore, add these accessors. Also make it possible to retrieve resources from platform devices in Rust using either a name or an index. Acked-by: Miguel Ojeda Signed-off-by: Daniel Almeida --- rust/kernel/platform.rs | 60 +++++++++++++++++++++++++++++++++++++++++++++= +++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 86f9d73c64b38ffe067be329a77b2fc04564c7fe..57f9964bb736cf5749ec3954def= 03c0d02873642 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -5,8 +5,11 @@ //! C header: [`include/linux/platform_device.h`](srctree/include/linux/pl= atform_device.h) =20 use crate::{ - acpi, bindings, container_of, device, driver, + acpi, bindings, container_of, + device::{self, Bound}, + driver, error::{to_result, Result}, + io::{mem::IoRequest, Resource}, of, prelude::*, str::CStr, @@ -211,6 +214,61 @@ impl Device { fn as_raw(&self) -> *mut bindings::platform_device { self.0.get() } + + /// Returns the resource at `index`, if any. + pub fn resource_by_index(&self, index: u32) -> Option<&Resource> { + // SAFETY: `self.as_raw()` returns a valid pointer to a `struct pl= atform_device`. + let resource =3D unsafe { + bindings::platform_get_resource(self.as_raw(), bindings::IORES= OURCE_MEM, index) + }; + + if resource.is_null() { + return None; + } + + // SAFETY: `resource` is a valid pointer to a `struct resource` as + // returned by `platform_get_resource`. + Some(unsafe { Resource::from_raw(resource) }) + } + + /// Returns the resource with a given `name`, if any. + pub fn resource_by_name(&self, name: &CStr) -> Option<&Resource> { + // SAFETY: `self.as_raw()` returns a valid pointer to a `struct + // platform_device` and `name` points to a valid C string. + let resource =3D unsafe { + bindings::platform_get_resource_byname( + self.as_raw(), + bindings::IORESOURCE_MEM, + name.as_char_ptr(), + ) + }; + + if resource.is_null() { + return None; + } + + // SAFETY: `resource` is a valid pointer to a `struct resource` as + // returned by `platform_get_resource`. + Some(unsafe { Resource::from_raw(resource) }) + } +} + +impl Device { + /// Returns an `IoRequest` for the resource at `index`, if any. + pub fn io_request_by_index(&self, index: u32) -> Option>= { + // SAFETY: `resource` is a valid resource for `&self` during the + // lifetime of the `IoRequest`. + self.resource_by_index(index) + .map(|resource| unsafe { IoRequest::new(self.as_ref(), resourc= e) }) + } + + /// Returns an `IoRequest` for the resource with a given `name`, if an= y. + pub fn io_request_by_name(&self, name: &CStr) -> Option>= { + // SAFETY: `resource` is a valid resource for `&self` during the + // lifetime of the `IoRequest`. + self.resource_by_name(name) + .map(|resource| unsafe { IoRequest::new(self.as_ref(), resourc= e) }) + } } =20 // SAFETY: `Device` is a transparent wrapper of a type that doesn't depend= on `Device`'s generic --=20 2.50.0