libfdt: Map FDT_ERR_NOTFOUND to Option::None
Update the API to make use of Option when retrieving a node or property
from a DT, to ensure that client code treats NOTFOUND (a return value
which might not be a hard error) differently from serious error values.
Bug: 255521657
Test: atest vmbase_example.integration_test
Change-Id: I1404cab01f2065b0fed07330b8c1be54bb993680
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index c38381b..9201243 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -16,6 +16,7 @@
//! to a bare-metal environment.
#![no_std]
+#![feature(let_else)] // Stabilized in 1.65.0
use core::ffi::{c_int, c_void, CStr};
use core::fmt;
@@ -136,6 +137,14 @@
}
}
+fn fdt_err_or_option(val: c_int) -> Result<Option<c_int>> {
+ match fdt_err(val) {
+ Ok(val) => Ok(Some(val)),
+ Err(FdtError::NotFound) => Ok(None),
+ Err(e) => Err(e),
+ }
+}
+
/// Value of a #address-cells property.
#[derive(Copy, Clone, Debug)]
enum AddrCells {
@@ -251,8 +260,8 @@
}
impl<'a> MemRegIterator<'a> {
- fn new(reg: RegIterator<'a>) -> Result<Self> {
- Ok(Self { reg })
+ fn new(reg: RegIterator<'a>) -> Self {
+ Self { reg }
}
}
@@ -285,45 +294,67 @@
}
/// Retrieve the standard (deprecated) device_type <string> property.
- pub fn device_type(&self) -> Result<&CStr> {
+ pub fn device_type(&self) -> Result<Option<&CStr>> {
self.getprop_str(CStr::from_bytes_with_nul(b"device_type\0").unwrap())
}
/// Retrieve the standard reg <prop-encoded-array> property.
- pub fn reg(&self) -> Result<RegIterator<'a>> {
- let parent = self.parent()?;
+ pub fn reg(&self) -> Result<Option<RegIterator<'a>>> {
+ let reg = CStr::from_bytes_with_nul(b"reg\0").unwrap();
- let addr_cells = parent.address_cells()?;
- let size_cells = parent.size_cells()?;
- let cells = self.getprop_cells(CStr::from_bytes_with_nul(b"reg\0").unwrap())?;
+ if let Some(cells) = self.getprop_cells(reg)? {
+ let parent = self.parent()?;
- Ok(RegIterator::new(cells, addr_cells, size_cells))
+ let addr_cells = parent.address_cells()?;
+ let size_cells = parent.size_cells()?;
+
+ Ok(Some(RegIterator::new(cells, addr_cells, size_cells)))
+ } else {
+ Ok(None)
+ }
}
/// Retrieve the value of a given <string> property.
- pub fn getprop_str(&self, name: &CStr) -> Result<&CStr> {
- CStr::from_bytes_with_nul(self.getprop(name)?).map_err(|_| FdtError::BadValue)
+ pub fn getprop_str(&self, name: &CStr) -> Result<Option<&CStr>> {
+ let value = if let Some(bytes) = self.getprop(name)? {
+ Some(CStr::from_bytes_with_nul(bytes).map_err(|_| FdtError::BadValue)?)
+ } else {
+ None
+ };
+ Ok(value)
}
/// Retrieve the value of a given property as an array of cells.
- pub fn getprop_cells(&self, name: &CStr) -> Result<CellIterator<'a>> {
- Ok(CellIterator::new(self.getprop(name)?))
+ pub fn getprop_cells(&self, name: &CStr) -> Result<Option<CellIterator<'a>>> {
+ if let Some(cells) = self.getprop(name)? {
+ Ok(Some(CellIterator::new(cells)))
+ } else {
+ Ok(None)
+ }
}
/// Retrieve the value of a given <u32> property.
- pub fn getprop_u32(&self, name: &CStr) -> Result<u32> {
- let prop = self.getprop(name)?.try_into().map_err(|_| FdtError::BadValue)?;
- Ok(u32::from_be_bytes(prop))
+ pub fn getprop_u32(&self, name: &CStr) -> Result<Option<u32>> {
+ let value = if let Some(bytes) = self.getprop(name)? {
+ Some(u32::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))
+ } else {
+ None
+ };
+ Ok(value)
}
/// Retrieve the value of a given <u64> property.
- pub fn getprop_u64(&self, name: &CStr) -> Result<u64> {
- let prop = self.getprop(name)?.try_into().map_err(|_| FdtError::BadValue)?;
- Ok(u64::from_be_bytes(prop))
+ pub fn getprop_u64(&self, name: &CStr) -> Result<Option<u64>> {
+ let value = if let Some(bytes) = self.getprop(name)? {
+ Some(u64::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))
+ } else {
+ None
+ };
+ Ok(value)
}
/// Retrieve the value of a given property.
- pub fn getprop(&self, name: &CStr) -> Result<&'a [u8]> {
+ pub fn getprop(&self, name: &CStr) -> Result<Option<&'a [u8]>> {
let mut len: i32 = 0;
// SAFETY - Accesses are constrained to the DT totalsize (validated by ctor) and the
// function respects the passed number of characters.
@@ -337,14 +368,21 @@
&mut len as *mut i32,
)
} as *const u8;
+
+ let Some(len) = fdt_err_or_option(len)? else {
+ return Ok(None); // Property was not found.
+ };
+ let len = usize::try_from(len).map_err(|_| FdtError::Internal)?;
+
if prop.is_null() {
- return fdt_err(len).and(Err(FdtError::Internal));
+ // We expected an error code in len but still received a valid value?!
+ return Err(FdtError::Internal);
}
- let len = usize::try_from(fdt_err(len)?).map_err(|_| FdtError::Internal)?;
- let base =
+
+ let offset =
(prop as usize).checked_sub(self.fdt.as_ptr() as usize).ok_or(FdtError::Internal)?;
- self.fdt.bytes.get(base..(base + len)).ok_or(FdtError::Internal)
+ Ok(Some(self.fdt.bytes.get(offset..(offset + len)).ok_or(FdtError::Internal)?))
}
/// Get reference to the containing device tree.
@@ -362,11 +400,7 @@
)
};
- match fdt_err(ret) {
- Ok(offset) => Ok(Some(Self { fdt: self.fdt, offset })),
- Err(FdtError::NotFound) => Ok(None),
- Err(e) => Err(e),
- }
+ Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
}
fn address_cells(&self) -> Result<AddrCells> {
@@ -546,32 +580,35 @@
/// Return an iterator of memory banks specified the "/memory" node.
///
/// NOTE: This does not support individual "/memory@XXXX" banks.
- pub fn memory(&self) -> Result<MemRegIterator> {
+ pub fn memory(&self) -> Result<Option<MemRegIterator>> {
let memory = CStr::from_bytes_with_nul(b"/memory\0").unwrap();
let device_type = CStr::from_bytes_with_nul(b"memory\0").unwrap();
- let node = self.node(memory)?;
- if node.device_type()? != device_type {
- return Err(FdtError::BadValue);
- }
+ if let Some(node) = self.node(memory)? {
+ if node.device_type()? != Some(device_type) {
+ return Err(FdtError::BadValue);
+ }
+ let reg = node.reg()?.ok_or(FdtError::BadValue)?;
- MemRegIterator::new(node.reg()?)
+ Ok(Some(MemRegIterator::new(reg)))
+ } else {
+ Ok(None)
+ }
}
/// Retrieve the standard /chosen node.
- pub fn chosen(&self) -> Result<FdtNode> {
+ pub fn chosen(&self) -> Result<Option<FdtNode>> {
self.node(CStr::from_bytes_with_nul(b"/chosen\0").unwrap())
}
/// Get the root node of the tree.
pub fn root(&self) -> Result<FdtNode> {
- self.node(CStr::from_bytes_with_nul(b"/\0").unwrap())
+ self.node(CStr::from_bytes_with_nul(b"/\0").unwrap())?.ok_or(FdtError::Internal)
}
/// Find a tree node by its full path.
- pub fn node(&self, path: &CStr) -> Result<FdtNode> {
- let offset = self.path_offset(path)?;
- Ok(FdtNode { fdt: self, offset })
+ pub fn node(&self, path: &CStr) -> Result<Option<FdtNode>> {
+ Ok(self.path_offset(path)?.map(|offset| FdtNode { fdt: self, offset }))
}
/// Iterate over nodes with a given compatible string.
@@ -585,12 +622,11 @@
}
/// Find a mutable tree node by its full path.
- pub fn node_mut(&mut self, path: &CStr) -> Result<FdtNodeMut> {
- let offset = self.path_offset(path)?;
- Ok(FdtNodeMut { fdt: self, offset })
+ pub fn node_mut(&mut self, path: &CStr) -> Result<Option<FdtNodeMut>> {
+ Ok(self.path_offset(path)?.map(|offset| FdtNodeMut { fdt: self, offset }))
}
- fn path_offset(&self, path: &CStr) -> Result<c_int> {
+ fn path_offset(&self, path: &CStr) -> Result<Option<c_int>> {
let len = path.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?;
// SAFETY - Accesses are constrained to the DT totalsize (validated by ctor) and the
// function respects the passed number of characters.
@@ -599,7 +635,7 @@
libfdt_bindgen::fdt_path_offset_namelen(self.as_ptr(), path.as_ptr(), len)
};
- fdt_err(ret)
+ fdt_err_or_option(ret)
}
fn check_full(&self) -> Result<()> {
diff --git a/vmbase/example/src/main.rs b/vmbase/example/src/main.rs
index b305559..bb64651 100644
--- a/vmbase/example/src/main.rs
+++ b/vmbase/example/src/main.rs
@@ -151,14 +151,14 @@
let reader = Fdt::from_slice(fdt).unwrap();
info!("FDT passed verification.");
- for reg in reader.memory().unwrap() {
+ for reg in reader.memory().unwrap().unwrap() {
info!("memory @ {reg:#x?}");
}
let compatible = CStr::from_bytes_with_nul(b"ns16550a\0").unwrap();
for c in reader.compatible_nodes(compatible).unwrap() {
- let reg = c.reg().unwrap().next().unwrap();
+ let reg = c.reg().unwrap().unwrap().next().unwrap();
info!("node compatible with '{}' at {reg:?}", compatible.to_str().unwrap());
}
@@ -167,7 +167,7 @@
info!("FDT successfully unpacked.");
let path = CStr::from_bytes_with_nul(b"/memory\0").unwrap();
- let mut node = writer.node_mut(path).unwrap();
+ let mut node = writer.node_mut(path).unwrap().unwrap();
let name = CStr::from_bytes_with_nul(b"child\0").unwrap();
let mut child = node.add_subnode(name).unwrap();
info!("Created subnode '{}/{}'.", path.to_str().unwrap(), name.to_str().unwrap());