libfdt: Add property iterator

Bug: 277993056
Test: atest liblibfdt.integration_test
Change-Id: I80449de22b2550e3907424c25f1148f52ba7d9bd
diff --git a/libs/libfdt/src/iterators.rs b/libs/libfdt/src/iterators.rs
index 7bffd8c..000f723 100644
--- a/libs/libfdt/src/iterators.rs
+++ b/libs/libfdt/src/iterators.rs
@@ -17,6 +17,7 @@
 use crate::Fdt;
 use crate::FdtError;
 use crate::FdtNode;
+use crate::FdtProperty;
 use crate::{AddrCells, SizeCells};
 use core::ffi::CStr;
 use core::marker::PhantomData;
@@ -321,3 +322,29 @@
         res
     }
 }
+
+/// Iterator over properties
+#[derive(Debug)]
+pub struct PropertyIterator<'a> {
+    prop: Option<FdtProperty<'a>>,
+}
+
+impl<'a> PropertyIterator<'a> {
+    pub(crate) fn new(node: &'a FdtNode) -> Result<Self, FdtError> {
+        let prop = node.first_property()?;
+
+        Ok(Self { prop })
+    }
+}
+
+impl<'a> Iterator for PropertyIterator<'a> {
+    type Item = FdtProperty<'a>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let res = self.prop;
+
+        self.prop = res?.next_property().ok()?;
+
+        res
+    }
+}
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index a6d5739..9eeee60 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -20,8 +20,8 @@
 mod iterators;
 
 pub use iterators::{
-    AddressRange, CellIterator, CompatibleIterator, MemRegIterator, RangesIterator, Reg,
-    RegIterator, SubnodeIterator,
+    AddressRange, CellIterator, CompatibleIterator, MemRegIterator, PropertyIterator,
+    RangesIterator, Reg, RegIterator, SubnodeIterator,
 };
 
 use core::cmp::max;
@@ -194,6 +194,71 @@
     }
 }
 
+/// DT property wrapper to abstract endianess changes
+#[repr(transparent)]
+#[derive(Debug)]
+struct FdtPropertyStruct(libfdt_bindgen::fdt_property);
+
+impl FdtPropertyStruct {
+    fn from_offset(fdt: &Fdt, offset: c_int) -> Result<&Self> {
+        let mut len = 0;
+        let prop =
+            // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+            unsafe { libfdt_bindgen::fdt_get_property_by_offset(fdt.as_ptr(), offset, &mut len) };
+        if prop.is_null() {
+            fdt_err(len)?;
+            return Err(FdtError::Internal); // shouldn't happen.
+        }
+        // SAFETY: prop is only returned when it points to valid libfdt_bindgen.
+        Ok(unsafe { &*prop.cast::<FdtPropertyStruct>() })
+    }
+
+    fn name_offset(&self) -> c_int {
+        u32::from_be(self.0.nameoff).try_into().unwrap()
+    }
+
+    fn data_len(&self) -> usize {
+        u32::from_be(self.0.len).try_into().unwrap()
+    }
+
+    fn data_ptr(&self) -> *const c_void {
+        self.0.data.as_ptr().cast::<_>()
+    }
+}
+
+/// DT property.
+#[derive(Clone, Copy, Debug)]
+pub struct FdtProperty<'a> {
+    fdt: &'a Fdt,
+    offset: c_int,
+    property: &'a FdtPropertyStruct,
+}
+
+impl<'a> FdtProperty<'a> {
+    fn new(fdt: &'a Fdt, offset: c_int) -> Result<Self> {
+        let property = FdtPropertyStruct::from_offset(fdt, offset)?;
+        Ok(Self { fdt, offset, property })
+    }
+
+    /// Returns the property name
+    pub fn name(&self) -> Result<&'a CStr> {
+        self.fdt.string(self.property.name_offset())
+    }
+
+    /// Returns the property value
+    pub fn value(&self) -> Result<&'a [u8]> {
+        self.fdt.get_from_ptr(self.property.data_ptr(), self.property.data_len())
+    }
+
+    fn next_property(&self) -> Result<Option<Self>> {
+        let ret =
+            // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+            unsafe { libfdt_bindgen::fdt_next_property_offset(self.fdt.as_ptr(), self.offset) };
+
+        fdt_err_or_option(ret)?.map(|offset| Self::new(self.fdt, offset)).transpose()
+    }
+}
+
 /// DT node.
 #[derive(Clone, Copy, Debug)]
 pub struct FdtNode<'a> {
@@ -403,6 +468,19 @@
 
         Ok(fdt_err_or_option(ret)?.map(|offset| FdtNode { fdt: self.fdt, offset }))
     }
+
+    /// Returns an iterator of properties
+    pub fn properties(&'a self) -> Result<PropertyIterator<'a>> {
+        PropertyIterator::new(self)
+    }
+
+    fn first_property(&self) -> Result<Option<FdtProperty<'a>>> {
+        let ret =
+            // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+            unsafe { libfdt_bindgen::fdt_first_property_offset(self.fdt.as_ptr(), self.offset) };
+
+        fdt_err_or_option(ret)?.map(|offset| FdtProperty::new(self.fdt, offset)).transpose()
+    }
 }
 
 /// Mutable FDT node.
@@ -828,6 +906,17 @@
         self.buffer.get(offset..(offset + len)).ok_or(FdtError::Internal)
     }
 
+    fn string(&self, offset: c_int) -> Result<&CStr> {
+        // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+        let res = unsafe { libfdt_bindgen::fdt_string(self.as_ptr(), offset) };
+        if res.is_null() {
+            return Err(FdtError::Internal);
+        }
+
+        // SAFETY: Non-null return from fdt_string() is valid null-terminating string within FDT.
+        Ok(unsafe { CStr::from_ptr(res) })
+    }
+
     /// Returns a shared pointer to the device tree.
     pub fn as_ptr(&self) -> *const c_void {
         self.buffer.as_ptr().cast::<_>()