[PATCH v7 3/3] rust: platform: allow ioremap of platform resources

Daniel Almeida posted 3 patches 2 weeks, 2 days ago
[PATCH v7 3/3] rust: platform: allow ioremap of platform resources
Posted by Daniel Almeida 2 weeks, 2 days ago
The preceding patches added support for resources, and for a general
IoMem abstraction, but thus far there is no way to access said IoMem
from drivers, as its creation is unsafe and depends on a resource that
must be acquired from some device first.

Now, allow the ioremap of platform resources themselves, thereby making
the IoMem available to platform drivers.

Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
---
 rust/kernel/platform.rs | 123 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 122 insertions(+), 1 deletion(-)

diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index 1297f5292ba9b7ca9784f84979efbeccb0768bd3..56f3d7c0d536d77082d7f8d2407de17ee3e95ffa 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -5,8 +5,14 @@
 //! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h)
 
 use crate::{
-    bindings, container_of, device, driver,
+    bindings, container_of, device,
+    devres::Devres,
+    driver,
     error::{to_result, Result},
+    io::{
+        mem::{ExclusiveIoMem, IoMem},
+        resource::Resource,
+    },
     of,
     prelude::*,
     str::CStr,
@@ -191,6 +197,121 @@ fn as_raw(&self) -> *mut bindings::platform_device {
         // embedded in `struct platform_device`.
         unsafe { container_of!(self.0.as_raw(), bindings::platform_device, dev) }.cast_mut()
     }
+
+    /// Maps a platform resource through ioremap() where the size is known at
+    /// compile time.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// use kernel::{bindings, c_str, platform};
+    ///
+    /// fn probe(pdev: &mut platform::Device, /* ... */) -> Result<()> {
+    ///     let offset = 0; // Some offset.
+    ///
+    ///     // If the size is known at compile time, use `ioremap_resource_sized`.
+    ///     // No runtime checks will apply when reading and writing.
+    ///     let resource = pdev.resource(0).ok_or(ENODEV)?;
+    ///     let iomem = pdev.ioremap_resource_sized::<42>(&resource)?;
+    ///
+    ///     // Read and write a 32-bit value at `offset`. Calling `try_access()` on
+    ///     // the `Devres` makes sure that the resource is still valid.
+    ///     let data = iomem.try_access().ok_or(ENODEV)?.readl(offset);
+    ///
+    ///     iomem.try_access().ok_or(ENODEV)?.writel(data, offset);
+    ///
+    ///     # Ok::<(), Error>(())
+    /// }
+    /// ```
+    pub fn ioremap_resource_sized<const SIZE: usize>(
+        &self,
+        resource: &Resource,
+    ) -> Result<Devres<IoMem<SIZE>>> {
+        IoMem::new(resource, self.as_ref())
+    }
+
+    /// Same as [`Self::ioremap_resource_sized`] but with exclusive access to the
+    /// underlying region.
+    pub fn ioremap_resource_exclusive_sized<const SIZE: usize>(
+        &self,
+        resource: &Resource,
+    ) -> Result<Devres<ExclusiveIoMem<SIZE>>> {
+        ExclusiveIoMem::new(resource, self.as_ref())
+    }
+
+    /// Maps a platform resource through ioremap().
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # use kernel::{bindings, c_str, platform};
+    ///
+    /// fn probe(pdev: &mut platform::Device, /* ... */) -> Result<()> {
+    ///     let offset = 0; // Some offset.
+    ///
+    ///     // Unlike `ioremap_resource_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 resource = pdev.resource(0).ok_or(ENODEV)?;
+    ///     let iomem = pdev.ioremap_resource(&resource)?;
+    ///
+    ///     let data = iomem.try_access().ok_or(ENODEV)?.try_readl(offset)?;
+    ///
+    ///     iomem.try_access().ok_or(ENODEV)?.try_writel(data, offset)?;
+    ///
+    ///     # Ok::<(), Error>(())
+    /// }
+    /// ```
+    pub fn ioremap_resource(&self, resource: &Resource) -> Result<Devres<IoMem<0>>> {
+        self.ioremap_resource_sized::<0>(resource)
+    }
+
+    /// Same as [`Self::ioremap_resource`] but with exclusive access to the underlying
+    /// region.
+    pub fn ioremap_resource_exclusive(
+        &self,
+        resource: &Resource,
+    ) -> Result<Devres<ExclusiveIoMem<0>>> {
+        self.ioremap_resource_exclusive_sized::<0>(resource)
+    }
+
+    /// Returns the resource at `index`, if any.
+    pub fn resource(&self, index: u32) -> Option<&Resource> {
+        // SAFETY: `self.as_raw()` returns a valid pointer to a `struct platform_device`.
+        let resource = unsafe {
+            bindings::platform_get_resource(self.as_raw(), bindings::IORESOURCE_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_ptr(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 = 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_ptr(resource) })
+    }
 }
 
 impl AsRef<device::Device> for Device {

-- 
2.48.1
Re: [PATCH v7 3/3] rust: platform: allow ioremap of platform resources
Posted by Danilo Krummrich 2 weeks, 2 days ago
Hi Daniel,

On Tue, Mar 18, 2025 at 02:20:43PM -0300, Daniel Almeida wrote:
> The preceding patches added support for resources, and for a general
> IoMem abstraction, but thus far there is no way to access said IoMem
> from drivers, as its creation is unsafe and depends on a resource that
> must be acquired from some device first.
> 
> Now, allow the ioremap of platform resources themselves, thereby making
> the IoMem available to platform drivers.
> 
> Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
> ---
>  rust/kernel/platform.rs | 123 +++++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 122 insertions(+), 1 deletion(-)

You need to rebase this onto driver-core-next.

> 
> diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
> index 1297f5292ba9b7ca9784f84979efbeccb0768bd3..56f3d7c0d536d77082d7f8d2407de17ee3e95ffa 100644
> --- a/rust/kernel/platform.rs
> +++ b/rust/kernel/platform.rs
> @@ -5,8 +5,14 @@
>  //! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h)
>  
>  use crate::{
> -    bindings, container_of, device, driver,
> +    bindings, container_of, device,
> +    devres::Devres,
> +    driver,
>      error::{to_result, Result},
> +    io::{
> +        mem::{ExclusiveIoMem, IoMem},
> +        resource::Resource,
> +    },
>      of,
>      prelude::*,
>      str::CStr,
> @@ -191,6 +197,121 @@ fn as_raw(&self) -> *mut bindings::platform_device {
>          // embedded in `struct platform_device`.
>          unsafe { container_of!(self.0.as_raw(), bindings::platform_device, dev) }.cast_mut()
>      }
> +
> +    /// Maps a platform resource through ioremap() where the size is known at
> +    /// compile time.
> +    ///
> +    /// # Examples
> +    ///
> +    /// ```no_run
> +    /// use kernel::{bindings, c_str, platform};
> +    ///
> +    /// fn probe(pdev: &mut platform::Device, /* ... */) -> Result<()> {
> +    ///     let offset = 0; // Some offset.
> +    ///
> +    ///     // If the size is known at compile time, use `ioremap_resource_sized`.
> +    ///     // No runtime checks will apply when reading and writing.
> +    ///     let resource = pdev.resource(0).ok_or(ENODEV)?;
> +    ///     let iomem = pdev.ioremap_resource_sized::<42>(&resource)?;
> +    ///
> +    ///     // Read and write a 32-bit value at `offset`. Calling `try_access()` on
> +    ///     // the `Devres` makes sure that the resource is still valid.
> +    ///     let data = iomem.try_access().ok_or(ENODEV)?.readl(offset);
> +    ///
> +    ///     iomem.try_access().ok_or(ENODEV)?.writel(data, offset);

I'd probably write this as

	|| -> Result {
		let iomem = iomem.try_access().ok_or(ENODEV)?;

		iomem.read32(offset);
		iomem.write32(data, offset);

		Ok(())
	}()?;

There's also a patch [1] in progress that makes this more convenient.

[1] https://lore.kernel.org/rust-for-linux/20250313-try_with-v1-1-adcae7ed98a9@nvidia.com/
Re: [PATCH v7 3/3] rust: platform: allow ioremap of platform resources
Posted by Daniel Almeida 2 weeks, 2 days ago
Hi Danilo,

> On 18 Mar 2025, at 14:43, Danilo Krummrich <dakr@kernel.org> wrote:
> 
> Hi Daniel,
> 
> On Tue, Mar 18, 2025 at 02:20:43PM -0300, Daniel Almeida wrote:
>> The preceding patches added support for resources, and for a general
>> IoMem abstraction, but thus far there is no way to access said IoMem
>> from drivers, as its creation is unsafe and depends on a resource that
>> must be acquired from some device first.
>> 
>> Now, allow the ioremap of platform resources themselves, thereby making
>> the IoMem available to platform drivers.
>> 
>> Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
>> ---
>> rust/kernel/platform.rs | 123 +++++++++++++++++++++++++++++++++++++++++++++++-
>> 1 file changed, 122 insertions(+), 1 deletion(-)
> 
> You need to rebase this onto driver-core-next.

Right, I totally forgot about this.

> 
>> 
>> diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
>> index 1297f5292ba9b7ca9784f84979efbeccb0768bd3..56f3d7c0d536d77082d7f8d2407de17ee3e95ffa 100644
>> --- a/rust/kernel/platform.rs
>> +++ b/rust/kernel/platform.rs
>> @@ -5,8 +5,14 @@
>> //! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h)
>> 
>> use crate::{
>> -    bindings, container_of, device, driver,
>> +    bindings, container_of, device,
>> +    devres::Devres,
>> +    driver,
>>     error::{to_result, Result},
>> +    io::{
>> +        mem::{ExclusiveIoMem, IoMem},
>> +        resource::Resource,
>> +    },
>>     of,
>>     prelude::*,
>>     str::CStr,
>> @@ -191,6 +197,121 @@ fn as_raw(&self) -> *mut bindings::platform_device {
>>         // embedded in `struct platform_device`.
>>         unsafe { container_of!(self.0.as_raw(), bindings::platform_device, dev) }.cast_mut()
>>     }
>> +
>> +    /// Maps a platform resource through ioremap() where the size is known at
>> +    /// compile time.
>> +    ///
>> +    /// # Examples
>> +    ///
>> +    /// ```no_run
>> +    /// use kernel::{bindings, c_str, platform};
>> +    ///
>> +    /// fn probe(pdev: &mut platform::Device, /* ... */) -> Result<()> {
>> +    ///     let offset = 0; // Some offset.
>> +    ///
>> +    ///     // If the size is known at compile time, use `ioremap_resource_sized`.
>> +    ///     // No runtime checks will apply when reading and writing.
>> +    ///     let resource = pdev.resource(0).ok_or(ENODEV)?;
>> +    ///     let iomem = pdev.ioremap_resource_sized::<42>(&resource)?;
>> +    ///
>> +    ///     // Read and write a 32-bit value at `offset`. Calling `try_access()` on
>> +    ///     // the `Devres` makes sure that the resource is still valid.
>> +    ///     let data = iomem.try_access().ok_or(ENODEV)?.readl(offset);
>> +    ///
>> +    ///     iomem.try_access().ok_or(ENODEV)?.writel(data, offset);
> 
> I'd probably write this as
> 
> || -> Result {
> let iomem = iomem.try_access().ok_or(ENODEV)?;
> 
> iomem.read32(offset);
> iomem.write32(data, offset);
> 
> Ok(())
> }()?;
> 
> There's also a patch [1] in progress that makes this more convenient.
> 
> [1] https://lore.kernel.org/rust-for-linux/20250313-try_with-v1-1-adcae7ed98a9@nvidia.com/

Thanks!

— Daniel
Re: [PATCH v7 3/3] rust: platform: allow ioremap of platform resources
Posted by Benno Lossin 2 weeks, 1 day ago
On Tue Mar 18, 2025 at 7:22 PM CET, Daniel Almeida wrote:
> On 18 Mar 2025, at 14:43, Danilo Krummrich <dakr@kernel.org> wrote:
>> On Tue, Mar 18, 2025 at 02:20:43PM -0300, Daniel Almeida wrote:
>>> +    ///     // Read and write a 32-bit value at `offset`. Calling `try_access()` on
>>> +    ///     // the `Devres` makes sure that the resource is still valid.
>>> +    ///     let data = iomem.try_access().ok_or(ENODEV)?.readl(offset);
>>> +    ///
>>> +    ///     iomem.try_access().ok_or(ENODEV)?.writel(data, offset);
>> 
>> I'd probably write this as
>> 
>> || -> Result {
>> let iomem = iomem.try_access().ok_or(ENODEV)?;
>> 
>> iomem.read32(offset);
>> iomem.write32(data, offset);
>> 
>> Ok(())
>> }()?;

Why use a closure here?

---
Cheers,
Benno
Re: [PATCH v7 3/3] rust: platform: allow ioremap of platform resources
Posted by Danilo Krummrich 2 weeks, 1 day ago
On Wed, Mar 19, 2025 at 12:48:00AM +0000, Benno Lossin wrote:
> On Tue Mar 18, 2025 at 7:22 PM CET, Daniel Almeida wrote:
> > On 18 Mar 2025, at 14:43, Danilo Krummrich <dakr@kernel.org> wrote:
> >> On Tue, Mar 18, 2025 at 02:20:43PM -0300, Daniel Almeida wrote:
> >>> +    ///     // Read and write a 32-bit value at `offset`. Calling `try_access()` on
> >>> +    ///     // the `Devres` makes sure that the resource is still valid.
> >>> +    ///     let data = iomem.try_access().ok_or(ENODEV)?.readl(offset);
> >>> +    ///
> >>> +    ///     iomem.try_access().ok_or(ENODEV)?.writel(data, offset);
> >> 
> >> I'd probably write this as
> >> 
> >> || -> Result {
> >> let iomem = iomem.try_access().ok_or(ENODEV)?;
> >> 
> >> iomem.read32(offset);
> >> iomem.write32(data, offset);
> >> 
> >> Ok(())
> >> }()?;
> 
> Why use a closure here?

Calling try_access() only once and not having the closure is fine too.

But I also think it would be good practice for an example to explicitly limit
the scope of the corresponding guard.

Ideally, it uses [1], once available.

[1] https://lore.kernel.org/rust-for-linux/20250313-try_with-v1-1-adcae7ed98a9@nvidia.com/
Re: [PATCH v7 3/3] rust: platform: allow ioremap of platform resources
Posted by Benno Lossin 2 weeks, 1 day ago
On Wed Mar 19, 2025 at 12:22 PM CET, Danilo Krummrich wrote:
> On Wed, Mar 19, 2025 at 12:48:00AM +0000, Benno Lossin wrote:
>> On Tue Mar 18, 2025 at 7:22 PM CET, Daniel Almeida wrote:
>> > On 18 Mar 2025, at 14:43, Danilo Krummrich <dakr@kernel.org> wrote:
>> >> On Tue, Mar 18, 2025 at 02:20:43PM -0300, Daniel Almeida wrote:
>> >>> +    ///     // Read and write a 32-bit value at `offset`. Calling `try_access()` on
>> >>> +    ///     // the `Devres` makes sure that the resource is still valid.
>> >>> +    ///     let data = iomem.try_access().ok_or(ENODEV)?.readl(offset);
>> >>> +    ///
>> >>> +    ///     iomem.try_access().ok_or(ENODEV)?.writel(data, offset);
>> >> 
>> >> I'd probably write this as
>> >> 
>> >> || -> Result {
>> >> let iomem = iomem.try_access().ok_or(ENODEV)?;
>> >> 
>> >> iomem.read32(offset);
>> >> iomem.write32(data, offset);
>> >> 
>> >> Ok(())
>> >> }()?;
>> 
>> Why use a closure here?
>
> Calling try_access() only once and not having the closure is fine too.
>
> But I also think it would be good practice for an example to explicitly limit
> the scope of the corresponding guard.

Ah you're using the closure to limit the lifetime of the guard. You
don't need a closure, braces suffice.

> Ideally, it uses [1], once available.
>
> [1] https://lore.kernel.org/rust-for-linux/20250313-try_with-v1-1-adcae7ed98a9@nvidia.com/

Yeah that sounds best.

---
Cheers,
Benno