On Mon, Oct 28, 2024 at 6:24 PM Rob Herring <robh@xxxxxxxxxx> wrote: > > 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) > } Yes, that's right. Ideally you could use https://doc.rust-lang.org/stable/core/mem/union.MaybeUninit.html#method.array_assume_init instead of transmute, but it's not yet stable. Alex -- All that is necessary for evil to succeed is for good people to do nothing.