[PATCH v5 6/9] rust: device: Implement accessors for firmware properties

Remo Senekowitsch posted 9 patches 7 months ago
There is a newer version of this series
[PATCH v5 6/9] rust: device: Implement accessors for firmware properties
Posted by Remo Senekowitsch 7 months ago
Add methods to FwNode for reading several firmware property types like
strings, integers and arrays.

Most types are read with the generic `property_read` method. There are
two exceptions:

* `property_read_bool` cannot fail, so the fallible function signature
  of `property_read` would not make sense for reading booleans.

* `property_read_array_vec` can fail because of a dynamic memory
  allocation. This error must be handled separately, leading to a
  different function signature than `property_read`.

The traits `Property` and `PropertyInt` drive the generic behavior
of `property_read`. `PropertyInt` is necessary to associate
specific integer types with the C functions to read them. While
there is a C function to read integers of generic sizes called
`fwnode_property_read_int_array`, it was preferred not to make this
public.

Co-developed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Remo Senekowitsch <remo@buenzli.dev>
---
 rust/kernel/device/property.rs | 253 ++++++++++++++++++++++++++++++++-
 1 file changed, 251 insertions(+), 2 deletions(-)

diff --git a/rust/kernel/device/property.rs b/rust/kernel/device/property.rs
index 59c61e2493831..5e70db30adacf 100644
--- a/rust/kernel/device/property.rs
+++ b/rust/kernel/device/property.rs
@@ -4,9 +4,17 @@
 //!
 //! C header: [`include/linux/property.h`](srctree/include/linux/property.h)
 
-use core::ptr;
+use core::{mem::MaybeUninit, ptr};
 
-use crate::{bindings, str::CStr, types::Opaque};
+use crate::{
+    alloc::KVec,
+    bindings,
+    error::{to_result, Result},
+    prelude::*,
+    private::Sealed,
+    str::{CStr, CString},
+    types::Opaque,
+};
 
 /// A reference-counted fwnode_handle.
 ///
@@ -109,6 +117,103 @@ pub fn property_present(&self, name: &CStr) -> bool {
         // SAFETY: By the invariant of `CStr`, `name` is null-terminated.
         unsafe { bindings::fwnode_property_present(self.as_raw().cast_const(), name.as_char_ptr()) }
     }
+
+    /// Returns firmware property `name` boolean value
+    pub fn property_read_bool(&self, name: &CStr) -> bool {
+        // SAFETY: `name` is non-null and null-terminated. `self.as_raw()` is valid
+        // because `self` is valid.
+        unsafe { bindings::fwnode_property_read_bool(self.as_raw(), name.as_char_ptr()) }
+    }
+
+    /// Returns the index of matching string `match_str` for firmware string property `name`
+    pub fn property_match_string(&self, name: &CStr, match_str: &CStr) -> Result<usize> {
+        // SAFETY: `name` and `match_str` are non-null and null-terminated. `self.as_raw` is
+        // valid because `self` is valid.
+        let ret = unsafe {
+            bindings::fwnode_property_match_string(
+                self.as_raw(),
+                name.as_char_ptr(),
+                match_str.as_char_ptr(),
+            )
+        };
+        to_result(ret)?;
+        Ok(ret as usize)
+    }
+
+    /// Returns firmware property `name` integer array values in a KVec
+    pub fn property_read_array_vec<'fwnode, 'name, T: PropertyInt>(
+        &'fwnode self,
+        name: &'name CStr,
+        len: usize,
+    ) -> Result<PropertyGuard<'fwnode, 'name, KVec<T>>> {
+        let mut val: KVec<T> = KVec::with_capacity(len, GFP_KERNEL)?;
+
+        let res = T::read_array_from_fwnode_property(self, name, val.spare_capacity_mut());
+        let res = match res {
+            Ok(_) => {
+                // SAFETY:
+                // - `len` is equal to `val.capacity - val.len`, because
+                //   `val.capacity` is `len` and `val.len` is zero.
+                // - All elements within the interval [`0`, `len`) were initialized
+                //   by `read_array_from_fwnode_property`.
+                unsafe { val.inc_len(len) }
+                Ok(val)
+            }
+            Err(e) => Err(e),
+        };
+        Ok(PropertyGuard {
+            inner: res,
+            fwnode: self,
+            name,
+        })
+    }
+
+    /// Returns integer array length for firmware property `name`
+    pub fn property_count_elem<T: PropertyInt>(&self, name: &CStr) -> Result<usize> {
+        T::read_array_len_from_fwnode_property(self, name)
+    }
+
+    /// Returns the value of firmware property `name`.
+    ///
+    /// This method is generic over the type of value to read. Informally,
+    /// the types that can be read are booleans, strings, unsigned integers and
+    /// arrays of unsigned integers.
+    ///
+    /// Reading a [`KVec`] of integers is done with the separate
+    /// method [`Self::property_read_array_vec`], because it takes an
+    /// additional `len` argument.
+    ///
+    /// When reading a boolean, this method never fails. A missing property
+    /// is interpreted as `false`, whereas a present property is interpreted
+    /// as `true`.
+    ///
+    /// For more precise documentation about what types can be read, see
+    /// the [implementors of Property][Property#implementors] and [its
+    /// implementations on foreign types][Property#foreign-impls].
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use kernel::{c_str, device::{Device, property::FwNode}, str::CString};
+    /// fn examples(dev: &Device) -> Result {
+    ///     let fwnode = dev.fwnode().ok_or(ENOENT)?;
+    ///     let b: u32 = fwnode.property_read(c_str!("some-number")).required_by(dev)?;
+    ///     if let Some(s) = fwnode.property_read::<CString>(c_str!("some-str")).optional() {
+    ///         // ...
+    ///     }
+    ///     Ok(())
+    /// }
+    /// ```
+    pub fn property_read<'fwnode, 'name, T: Property>(
+        &'fwnode self,
+        name: &'name CStr,
+    ) -> PropertyGuard<'fwnode, 'name, T> {
+        PropertyGuard {
+            inner: T::read_from_fwnode_property(self, name),
+            fwnode: self,
+            name,
+        }
+    }
 }
 
 // SAFETY: Instances of `FwNode` are always reference-counted.
@@ -124,6 +229,150 @@ unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
     }
 }
 
+/// Implemented for types that can be read as properties.
+///
+/// This is implemented for strings, integers and arrays of integers. It's used
+/// to make [`FwNode::property_read`] generic over the type of property being
+/// read. There are also two dedicated methods to read other types, because they
+/// require more specialized function signatures:
+/// - [`property_read_bool`](FwNode::property_read_bool)
+/// - [`property_read_array_vec`](FwNode::property_read_array_vec)
+///
+/// It must be public, because it appears in the signatures of other public
+/// functions, but its methods shouldn't be used outside the kernel crate.
+pub trait Property: Sized + Sealed {
+    /// Used to make [`FwNode::property_read`] generic.
+    fn read_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result<Self>;
+}
+
+impl Sealed for CString {}
+
+impl Property for CString {
+    fn read_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result<Self> {
+        let mut str: *mut u8 = ptr::null_mut();
+        let pstr: *mut _ = &mut str;
+
+        // SAFETY: `name` is non-null and null-terminated. `fwnode.as_raw` is
+        // valid because `fwnode` is valid.
+        let ret = unsafe {
+            bindings::fwnode_property_read_string(fwnode.as_raw(), name.as_char_ptr(), pstr.cast())
+        };
+        to_result(ret)?;
+
+        // SAFETY: `pstr` is a valid pointer to a NUL-terminated C string. It
+        // is valid for at least as long as `fwnode`, but it's only used within
+        // the current function. The memory it points to is not mutated during
+        // that time.
+        let str = unsafe { CStr::from_char_ptr(*pstr) };
+        Ok(str.try_into()?)
+    }
+}
+
+/// Implemented for all integers that can be read as properties.
+///
+/// This helper trait is needed on top of the existing [`Property`]
+/// trait to associate the integer types of various sizes with their
+/// corresponding `fwnode_property_read_*_array` functions.
+///
+/// It must be public, because it appears in the signatures of other public
+/// functions, but its methods shouldn't be used outside the kernel crate.
+pub trait PropertyInt: Copy + Sealed {
+    /// Reads a property array.
+    fn read_array_from_fwnode_property<'a>(
+        fwnode: &FwNode,
+        name: &CStr,
+        out: &'a mut [MaybeUninit<Self>],
+    ) -> Result<&'a mut [Self]>;
+
+    /// Reads the length of a property array.
+    fn read_array_len_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result<usize>;
+}
+// This macro generates implementations of the traits `Property` and
+// `PropertyInt` for integers of various sizes. Its input is a list
+// of pairs separated by commas. The first element of the pair is the
+// type of the integer, the second one is the name of its corresponding
+// `fwnode_property_read_*_array` function.
+macro_rules! impl_property_for_int {
+    ($($int:ty: $f:ident),* $(,)?) => { $(
+        impl Sealed for $int {}
+        impl<const N: usize> Sealed for [$int; N] {}
+
+        impl PropertyInt for $int {
+            fn read_array_from_fwnode_property<'a>(
+                fwnode: &FwNode,
+                name: &CStr,
+                out: &'a mut [MaybeUninit<Self>],
+            ) -> Result<&'a mut [Self]> {
+                // SAFETY: `fwnode`, `name` and `out` are all valid by their type
+                // invariants. `out.len()` is a valid bound for the memory pointed
+                // to by `out.as_mut_ptr()`.
+                // CAST: It's ok to cast from `*mut MaybeUninit<$int>` to a
+                // `*mut $int` because they have the same memory layout.
+                let ret = unsafe {
+                    bindings::$f(
+                        fwnode.as_raw(),
+                        name.as_char_ptr(),
+                        out.as_mut_ptr().cast(),
+                        out.len(),
+                    )
+                };
+                to_result(ret)?;
+                // SAFETY: Transmuting from `&'a mut [MaybeUninit<Self>]` to
+                // `&'a mut [Self]` is sound, because the previous call to a
+                // `fwnode_property_read_*_array` function (which didn't fail)
+                // fully initialized the slice.
+                Ok(unsafe { core::mem::transmute(out) })
+            }
+
+            fn read_array_len_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result<usize> {
+                // SAFETY: `fwnode` and `name` are valid by their type invariants.
+                // It's ok to pass a null pointer to the
+                // `fwnode_property_read_*_array` functions if `nval` is zero.
+                // This will return the length of the array.
+                let ret = unsafe {
+                    bindings::$f(
+                        fwnode.as_raw(),
+                        name.as_char_ptr(),
+                        ptr::null_mut(),
+                        0,
+                    )
+                };
+                to_result(ret)?;
+                Ok(ret as usize)
+            }
+        }
+
+        impl Property for $int {
+            fn read_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result<Self> {
+                let val: [_; 1] = <[$int; 1]>::read_from_fwnode_property(fwnode, name)?;
+                Ok(val[0])
+            }
+        }
+
+        impl<const N: usize> Property for [$int; N] {
+            fn read_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result<Self> {
+                let mut val: [MaybeUninit<$int>; N] = [const { MaybeUninit::uninit() }; N];
+
+                <$int>::read_array_from_fwnode_property(fwnode, name, &mut val)?;
+
+                // SAFETY: `val` is always initialized when
+                // fwnode_property_read_*_array is successful.
+                Ok(val.map(|v| unsafe { v.assume_init() }))
+            }
+        }
+    )* };
+}
+impl_property_for_int! {
+    u8: fwnode_property_read_u8_array,
+    u16: fwnode_property_read_u16_array,
+    u32: fwnode_property_read_u32_array,
+    u64: fwnode_property_read_u64_array,
+    i8: fwnode_property_read_u8_array,
+    i16: fwnode_property_read_u16_array,
+    i32: fwnode_property_read_u32_array,
+    i64: fwnode_property_read_u64_array,
+}
+
 /// A helper for reading device properties.
 ///
 /// Use [`Self::required_by`] if a missing property is considered a bug and
-- 
2.49.0
Re: [PATCH v5 6/9] rust: device: Implement accessors for firmware properties
Posted by Benno Lossin 7 months ago
On Tue May 20, 2025 at 10:00 PM CEST, Remo Senekowitsch wrote:
> Add methods to FwNode for reading several firmware property types like
> strings, integers and arrays.
>
> Most types are read with the generic `property_read` method. There are
> two exceptions:
>
> * `property_read_bool` cannot fail, so the fallible function signature
>   of `property_read` would not make sense for reading booleans.
>
> * `property_read_array_vec` can fail because of a dynamic memory
>   allocation. This error must be handled separately, leading to a
>   different function signature than `property_read`.
>
> The traits `Property` and `PropertyInt` drive the generic behavior
> of `property_read`. `PropertyInt` is necessary to associate
> specific integer types with the C functions to read them. While
> there is a C function to read integers of generic sizes called
> `fwnode_property_read_int_array`, it was preferred not to make this
> public.
>
> Co-developed-by: Rob Herring (Arm) <robh@kernel.org>
> Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
> Signed-off-by: Remo Senekowitsch <remo@buenzli.dev>
> ---
>  rust/kernel/device/property.rs | 253 ++++++++++++++++++++++++++++++++-
>  1 file changed, 251 insertions(+), 2 deletions(-)

I already like this design much better than your previous version.

I probably won't have the time to review the other patches, but one
thing that I notice was the lack of lists in `SAFETY` comments. If there
are multiple justification in a single `SAFETY` comment, we us a bullet
markdown list. (same for requirements in a `# Safety` section)

---
Cheers,
Benno
Re: [PATCH v5 6/9] rust: device: Implement accessors for firmware properties
Posted by kernel test robot 7 months ago
Hi Remo,

kernel test robot noticed the following build errors:

[auto build test ERROR on rust/rust-next]
[also build test ERROR on driver-core/driver-core-testing driver-core/driver-core-next driver-core/driver-core-linus robh/for-next linus/master v6.15-rc7 next-20250521]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Remo-Senekowitsch/rust-device-Create-FwNode-abstraction-for-accessing-device-properties/20250521-040612
base:   https://github.com/Rust-for-Linux/linux rust-next
patch link:    https://lore.kernel.org/r/20250520200024.268655-7-remo%40buenzli.dev
patch subject: [PATCH v5 6/9] rust: device: Implement accessors for firmware properties
config: x86_64-rhel-9.4-rust (https://download.01.org/0day-ci/archive/20250522/202505220521.EPHBSqQg-lkp@intel.com/config)
compiler: clang version 18.1.8 (https://github.com/llvm/llvm-project 3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff)
rustc: rustc 1.78.0 (9b00956e5 2024-04-29)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250522/202505220521.EPHBSqQg-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202505220521.EPHBSqQg-lkp@intel.com/

All errors (new ones prefixed by >>):

>> error[E0432]: unresolved import `crate::private`
   --> rust/kernel/device/property.rs:14:5
   |
   14 |     private::Sealed,
   |     ^^^^^^^
   |     |
   |     unresolved import
   |     help: a similar path exists: `crate::device::private`

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki