On Fri, Oct 25, 2024 at 4:12 PM Alex Gaynor <alex.gaynor@xxxxxxxxx> wrote: > > On Fri, Oct 25, 2024 at 5:06 PM Rob Herring (Arm) <robh@xxxxxxxxxx> wrote: > > > > The device property API is a firmware agnostic API for reading > > properties from firmware (DT/ACPI) devices nodes and swnodes. > > > > While the C API takes a pointer to a caller allocated variable/buffer, > > the rust API is designed to return a value and can be used in struct > > initialization. Rust generics are also utilized to support different > > sizes of properties (e.g. u8, u16, u32). > > > > Signed-off-by: Rob Herring (Arm) <robh@xxxxxxxxxx> > > --- > > Not sure if we need the KVec variant, but I kept it as that was my first > > pass attempt. Most callers are filling in some value in a driver data > > struct. Sometimes the number of elements is not known, so the caller > > calls to get the array size, allocs the correct size buffer, and then > > reads the property again to fill in the buffer. > > > > I have not implemented a wrapper for device_property_read_string(_array) > > because that API is problematic for dynamic DT nodes. The API just > > returns pointer(s) into the raw DT data. We probably need to return a > > copy of the string(s) instead for rust. > > > > After property accessors, next up is child node accessors/iterators. > > --- > > rust/bindings/bindings_helper.h | 1 + > > rust/kernel/device.rs | 145 +++++++++++++++++++++++++++++++++++++++- > > 2 files changed, 145 insertions(+), 1 deletion(-) > > > > diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h > > index 217c776615b9..65717cc20a23 100644 > > --- a/rust/bindings/bindings_helper.h > > +++ b/rust/bindings/bindings_helper.h > > @@ -19,6 +19,7 @@ > > #include <linux/pci.h> > > #include <linux/phy.h> > > #include <linux/platform_device.h> > > +#include <linux/property.h> > > #include <linux/refcount.h> > > #include <linux/sched.h> > > #include <linux/slab.h> > > diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs > > index 0c28b1e6b004..bb66a28df890 100644 > > --- a/rust/kernel/device.rs > > +++ b/rust/kernel/device.rs > > @@ -5,10 +5,14 @@ > > //! C header: [`include/linux/device.h`](srctree/include/linux/device.h) > > > > use crate::{ > > + alloc::KVec, > > bindings, > > + error::{to_result, Result}, > > + prelude::*, > > + str::CStr, > > types::{ARef, Opaque}, > > }; > > -use core::{fmt, ptr}; > > +use core::{fmt, mem::size_of, ptr}; > > > > #[cfg(CONFIG_PRINTK)] > > use crate::c_str; > > @@ -189,6 +193,145 @@ unsafe fn printk(&self, klevel: &[u8], msg: fmt::Arguments<'_>) { > > ) > > }; > > } > > + > > + /// Returns if a firmware property `name` is true or false > > + pub fn property_read_bool(&self, name: &CStr) -> bool { > > + unsafe { bindings::device_property_present(self.as_raw(), name.as_ptr() as *const i8) } > > + } > > + > > + /// Returns if a firmware string property `name` has match for `match_str` > > + pub fn property_match_string(&self, name: &CStr, match_str: &CStr) -> Result<usize> { > > + let ret = unsafe { > > + bindings::device_property_match_string( > > + self.as_raw(), > > + name.as_ptr() as *const i8, > > + match_str.as_ptr() as *const i8, > > + ) > > + }; > > + to_result(ret)?; > > + Ok(ret as usize) > > + } > > + > > + /// Returns firmware property `name` scalar value > > + /// > > + /// Valid types are i8, u8, i16, u16, i32, u32, i64, u64 > > + pub fn property_read<T: Copy>(&self, name: &CStr) -> Result<T> { > > + let mut val: [T; 1] = unsafe { core::mem::zeroed() }; > > + > > + Self::_property_read_array(&self, name, &mut val)?; > > + Ok(val[0]) > > + } > > + > > This, and several of the other methods are unsound, because they can > be used to construct arbitrary types for which may not allow all bit > patterns. You can use: > https://rust.docs.kernel.org/kernel/types/trait.FromBytes.html as the > bound to ensure only valid types are used. > > Also, instead of using mem::zeroed(), you should use MaybeUnininit > (https://doc.rust-lang.org/stable/core/mem/union.MaybeUninit.html) > which allows you to avoid needing to zero initialize. Something like this what you had in mind?: pub fn property_read_array<T, const N: usize>(&self, name: &CStr) -> Result<[T; N]> { let mut val: [MaybeUninit<T>; N] = [const { MaybeUninit::uninit() }; N]; Self::_property_read_array(self, name, &mut val)?; // SAFETY: On success, _property_read_array has filled in the array let val = unsafe { mem::transmute_copy(&val) }; Ok(val) }