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::<_>()