Merge "libfdt: Introduce Libfdt traits & Move node funcs" into main
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index 72cd3e9..ce2a9f6 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -29,11 +29,12 @@
use core::ffi::{c_int, c_void, CStr};
use core::ops::Range;
-use core::ptr;
use cstr::cstr;
use result::{fdt_err, fdt_err_expect_zero, fdt_err_or_option};
use zerocopy::AsBytes as _;
+use crate::libfdt::{Libfdt, LibfdtMut};
+
/// Value of a #address-cells property.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum AddrCells {
@@ -164,25 +165,14 @@
impl<'a> FdtNode<'a> {
/// Returns parent node.
pub fn parent(&self) -> Result<Self> {
- // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
- let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
- let offset = fdt_err(ret)?;
+ let offset = self.fdt.parent_offset(self.offset)?;
Ok(Self { fdt: self.fdt, offset })
}
/// Returns supernode with depth. Note that root is at depth 0.
pub fn supernode_at_depth(&self, depth: usize) -> Result<Self> {
- // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
- let ret = unsafe {
- libfdt_bindgen::fdt_supernode_atdepth_offset(
- self.fdt.as_ptr(),
- self.offset,
- depth.try_into().unwrap(),
- ptr::null_mut(),
- )
- };
- let offset = fdt_err(ret)?;
+ let offset = self.fdt.supernode_atdepth_offset(self.offset, depth)?;
Ok(Self { fdt: self.fdt, offset })
}
@@ -321,15 +311,7 @@
/// Returns the compatible node of the given name that is next after this node.
pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
- // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
- let ret = unsafe {
- libfdt_bindgen::fdt_node_offset_by_compatible(
- self.fdt.as_ptr(),
- self.offset,
- compatible.as_ptr(),
- )
- };
- let offset = fdt_err_or_option(ret)?;
+ let offset = self.fdt.node_offset_by_compatible(self.offset, compatible)?;
Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
}
@@ -340,17 +322,11 @@
}
fn address_cells(&self) -> Result<AddrCells> {
- // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
- let ret = unsafe { libfdt_bindgen::fdt_address_cells(self.fdt.as_ptr(), self.offset) };
-
- usize::try_from(ret).map_err(|_| FdtError::Internal)?.try_into()
+ self.fdt.address_cells(self.offset)?.try_into()
}
fn size_cells(&self) -> Result<SizeCells> {
- // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
- let ret = unsafe { libfdt_bindgen::fdt_size_cells(self.fdt.as_ptr(), self.offset) };
-
- usize::try_from(ret).map_err(|_| FdtError::Internal)?.try_into()
+ self.fdt.size_cells(self.offset)?.try_into()
}
/// Returns an iterator of subnodes
@@ -359,17 +335,13 @@
}
fn first_subnode(&self) -> Result<Option<Self>> {
- // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
- let ret = unsafe { libfdt_bindgen::fdt_first_subnode(self.fdt.as_ptr(), self.offset) };
- let offset = fdt_err_or_option(ret)?;
+ let offset = self.fdt.first_subnode(self.offset)?;
Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
}
fn next_subnode(&self) -> Result<Option<Self>> {
- // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
- let ret = unsafe { libfdt_bindgen::fdt_next_subnode(self.fdt.as_ptr(), self.offset) };
- let offset = fdt_err_or_option(ret)?;
+ let offset = self.fdt.next_subnode(self.offset)?;
Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
}
@@ -380,15 +352,7 @@
}
fn next_node(&self, depth: usize) -> Result<Option<(Self, usize)>> {
- let mut next_depth: c_int = depth.try_into().unwrap();
- // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
- let ret = unsafe {
- libfdt_bindgen::fdt_next_node(self.fdt.as_ptr(), self.offset, &mut next_depth)
- };
- let Ok(depth) = usize::try_from(next_depth) else {
- return Ok(None);
- };
- if let Some(offset) = fdt_err_or_option(ret)? {
+ if let Some((offset, depth)) = self.fdt.next_node(self.offset, depth)? {
Ok(Some((Self { fdt: self.fdt, offset }, depth)))
} else {
Ok(None)
@@ -427,31 +391,17 @@
/// Returns the subnode of the given name. The name doesn't need to be nul-terminated.
pub fn subnode(&self, name: &CStr) -> Result<Option<Self>> {
let name = name.to_bytes();
- let offset = self.subnode_offset(name)?;
+ let offset = self.fdt.subnode_offset_namelen(self.offset, name)?;
Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
}
/// Returns the subnode of the given name bytes
pub fn subnode_with_name_bytes(&self, name: &[u8]) -> Result<Option<Self>> {
- let offset = self.subnode_offset(name)?;
+ let offset = self.fdt.subnode_offset_namelen(self.offset, name)?;
Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
}
-
- fn subnode_offset(&self, name: &[u8]) -> Result<Option<c_int>> {
- let namelen = name.len().try_into().unwrap();
- // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
- let ret = unsafe {
- libfdt_bindgen::fdt_subnode_offset_namelen(
- self.fdt.as_ptr(),
- self.offset,
- name.as_ptr().cast::<_>(),
- namelen,
- )
- };
- fdt_err_or_option(ret)
- }
}
impl<'a> PartialEq for FdtNode<'a> {
@@ -652,7 +602,7 @@
/// Adds new subnodes to the given node.
pub fn add_subnodes(&mut self, names: &[&CStr]) -> Result<()> {
for name in names {
- self.add_subnode_offset(name.to_bytes())?;
+ self.fdt.add_subnode_namelen(self.offset, name.to_bytes())?;
}
Ok(())
}
@@ -660,7 +610,7 @@
/// Adds a new subnode to the given node and return it as a FdtNodeMut on success.
pub fn add_subnode(&'a mut self, name: &CStr) -> Result<Self> {
let name = name.to_bytes();
- let offset = self.add_subnode_offset(name)?;
+ let offset = self.fdt.add_subnode_namelen(self.offset, name)?;
Ok(Self { fdt: self.fdt, offset })
}
@@ -669,80 +619,46 @@
/// on success.
pub fn add_subnode_with_namelen(&'a mut self, name: &CStr, namelen: usize) -> Result<Self> {
let name = &name.to_bytes()[..namelen];
- let offset = self.add_subnode_offset(name)?;
+ let offset = self.fdt.add_subnode_namelen(self.offset, name)?;
Ok(Self { fdt: self.fdt, offset })
}
- fn add_subnode_offset(&mut self, name: &[u8]) -> Result<c_int> {
- let namelen = name.len().try_into().unwrap();
- // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
- let ret = unsafe {
- libfdt_bindgen::fdt_add_subnode_namelen(
- self.fdt.as_mut_ptr(),
- self.offset,
- name.as_ptr().cast::<_>(),
- namelen,
- )
- };
- fdt_err(ret)
- }
-
/// Returns the first subnode of this
pub fn first_subnode(&'a mut self) -> Result<Option<Self>> {
- // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
- let ret = unsafe { libfdt_bindgen::fdt_first_subnode(self.fdt.as_ptr(), self.offset) };
- let offset = fdt_err_or_option(ret)?;
+ let offset = self.fdt.first_subnode(self.offset)?;
Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
}
/// Returns the next subnode that shares the same parent with this
pub fn next_subnode(self) -> Result<Option<Self>> {
- // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
- let ret = unsafe { libfdt_bindgen::fdt_next_subnode(self.fdt.as_ptr(), self.offset) };
- let offset = fdt_err_or_option(ret)?;
+ let offset = self.fdt.next_subnode(self.offset)?;
Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
}
/// Deletes the current node and returns the next subnode
pub fn delete_and_next_subnode(self) -> Result<Option<Self>> {
- // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
- let ret = unsafe { libfdt_bindgen::fdt_next_subnode(self.fdt.as_ptr(), self.offset) };
-
- let next_offset = fdt_err_or_option(ret)?;
+ let next_offset = self.fdt.next_subnode(self.offset)?;
self.delete_and_next(next_offset)
}
- fn next_node_offset(&self, depth: usize) -> Result<Option<(c_int, usize)>> {
- let mut next_depth: c_int = depth.try_into().or(Err(FdtError::BadValue))?;
- // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
- let ret = unsafe {
- libfdt_bindgen::fdt_next_node(self.fdt.as_ptr(), self.offset, &mut next_depth)
- };
- let Ok(next_depth) = usize::try_from(next_depth) else {
- return Ok(None);
- };
- Ok(fdt_err_or_option(ret)?.map(|offset| (offset, next_depth)))
- }
-
/// Returns the next node
pub fn next_node(self, depth: usize) -> Result<Option<(Self, usize)>> {
- let next = self.next_node_offset(depth)?;
+ let next = self.fdt.next_node(self.offset, depth)?;
Ok(next.map(|(offset, depth)| (Self { fdt: self.fdt, offset }, depth)))
}
fn next_node_skip_subnodes(&mut self, depth: usize) -> Result<Option<(c_int, usize)>> {
- let mut iter = self.next_node_offset(depth)?;
+ let mut iter = self.fdt.next_node(self.offset, depth)?;
while let Some((descendant_offset, descendant_depth)) = iter {
if descendant_depth <= depth {
return Ok(Some((descendant_offset, descendant_depth)));
}
- let descendant = FdtNodeMut { fdt: self.fdt, offset: descendant_offset };
- iter = descendant.next_node_offset(descendant_depth)?;
+ iter = self.fdt.next_node(descendant_offset, descendant_depth)?;
}
Ok(None)
@@ -765,15 +681,7 @@
/// Returns the compatible node of the given name that is next after this node.
pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
- // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
- let ret = unsafe {
- libfdt_bindgen::fdt_node_offset_by_compatible(
- self.fdt.as_ptr(),
- self.offset,
- compatible.as_ptr(),
- )
- };
- let offset = fdt_err_or_option(ret)?;
+ let offset = self.fdt.node_offset_by_compatible(self.offset, compatible)?;
Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
}
@@ -791,46 +699,24 @@
// mutable reference to DT, so we can't use current node (which also has a mutable reference to
// DT).
pub fn delete_and_next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
- // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
- let ret = unsafe {
- libfdt_bindgen::fdt_node_offset_by_compatible(
- self.fdt.as_ptr(),
- self.offset,
- compatible.as_ptr(),
- )
- };
- let next_offset = fdt_err_or_option(ret)?;
+ let next_offset = self.fdt.node_offset_by_compatible(self.offset, compatible)?;
self.delete_and_next(next_offset)
}
- fn delete_and_next(mut self, next_offset: Option<c_int>) -> Result<Option<Self>> {
+ fn delete_and_next(self, next_offset: Option<c_int>) -> Result<Option<Self>> {
if Some(self.offset) == next_offset {
return Err(FdtError::Internal);
}
- // SAFETY: nop_self() only touches bytes of the self and its properties and subnodes, and
- // doesn't alter any other blob in the tree. self.fdt and next_offset would remain valid.
- unsafe { self.nop_self()? };
+ self.fdt.nop_node(self.offset)?;
Ok(next_offset.map(|offset| Self { fdt: self.fdt, offset }))
}
/// Deletes this node effectively from DT, by setting it with FDT_NOP
- pub fn nop(mut self) -> Result<()> {
- // SAFETY: This consumes self, so invalid node wouldn't be used any further
- unsafe { self.nop_self() }
- }
-
- /// Deletes this node effectively from DT, by setting it with FDT_NOP.
- /// This only changes bytes of the node and its properties and subnodes, and doesn't alter or
- /// move any other part of the tree.
- /// SAFETY: This node is no longer valid.
- unsafe fn nop_self(&mut self) -> Result<()> {
- // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
- let ret = unsafe { libfdt_bindgen::fdt_nop_node(self.fdt.as_mut_ptr(), self.offset) };
-
- fdt_err_expect_zero(ret)
+ pub fn nop(self) -> Result<()> {
+ self.fdt.nop_node(self.offset)
}
}
@@ -841,6 +727,22 @@
buffer: [u8],
}
+// SAFETY: Fdt calls check_full() before safely returning a &Self, making it impossible for trait
+// methods to be called on invalid device trees.
+unsafe impl Libfdt for Fdt {
+ fn as_fdt_slice(&self) -> &[u8] {
+ &self.buffer[..self.totalsize()]
+ }
+}
+
+// SAFETY: Fdt calls check_full() before safely returning a &Self, making it impossible for trait
+// methods to be called on invalid device trees.
+unsafe impl LibfdtMut for Fdt {
+ fn as_fdt_slice_mut(&mut self) -> &mut [u8] {
+ &mut self.buffer
+ }
+}
+
impl Fdt {
/// Wraps a slice containing a Flattened Device Tree.
///
@@ -994,7 +896,7 @@
/// Returns a tree node by its full path.
pub fn node(&self, path: &CStr) -> Result<Option<FdtNode>> {
- let offset = self.path_offset(path.to_bytes())?;
+ let offset = self.path_offset_namelen(path.to_bytes())?;
Ok(offset.map(|offset| FdtNode { fdt: self, offset }))
}
@@ -1039,26 +941,14 @@
/// Returns a mutable tree node by its full path.
pub fn node_mut(&mut self, path: &CStr) -> Result<Option<FdtNodeMut>> {
- let offset = self.path_offset(path.to_bytes())?;
+ let offset = self.path_offset_namelen(path.to_bytes())?;
Ok(offset.map(|offset| FdtNodeMut { fdt: self, offset }))
}
/// Returns the device tree as a slice (may be smaller than the containing buffer).
pub fn as_slice(&self) -> &[u8] {
- &self.buffer[..self.totalsize()]
- }
-
- fn path_offset(&self, path: &[u8]) -> Result<Option<c_int>> {
- let len = path.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.
- let ret = unsafe {
- // *_namelen functions don't include the trailing nul terminator in 'len'.
- libfdt_bindgen::fdt_path_offset_namelen(self.as_ptr(), path.as_ptr().cast::<_>(), len)
- };
-
- fdt_err_or_option(ret)
+ self.as_fdt_slice()
}
fn get_from_ptr(&self, ptr: *const c_void, len: usize) -> Result<&[u8]> {
diff --git a/libs/libfdt/src/libfdt.rs b/libs/libfdt/src/libfdt.rs
index bd9ae1e..7e3b65a 100644
--- a/libs/libfdt/src/libfdt.rs
+++ b/libs/libfdt/src/libfdt.rs
@@ -13,8 +13,15 @@
// limitations under the License.
//! Low-level libfdt_bindgen wrapper, easy to integrate safely in higher-level APIs.
+//!
+//! These traits decouple the safe libfdt C function calls from the representation of those
+//! user-friendly higher-level types, allowing the trait to be shared between different ones,
+//! adapted to their use-cases (e.g. alloc-based userspace or statically allocated no_std).
-use crate::{fdt_err_expect_zero, Result};
+use core::ffi::{c_int, CStr};
+use core::ptr;
+
+use crate::{fdt_err, fdt_err_expect_zero, fdt_err_or_option, FdtError, Result};
// Function names are the C function names without the `fdt_` prefix.
@@ -43,3 +50,170 @@
fdt_err_expect_zero(ret)
}
+
+/// Wrapper for the read-only libfdt.h functions.
+///
+/// # Safety
+///
+/// Implementors must ensure that at any point where a method of this trait is called, the
+/// underlying type returns the bytes of a valid device tree (as validated by `check_full`)
+/// through its `.as_fdt_slice` method.
+pub(crate) unsafe trait Libfdt {
+ /// Provides an immutable slice containing the device tree.
+ ///
+ /// The implementation must ensure that the size of the returned slice and
+ /// `fdt_header::totalsize` match.
+ fn as_fdt_slice(&self) -> &[u8];
+
+ /// Safe wrapper around `fdt_path_offset_namelen()` (C function).
+ fn path_offset_namelen(&self, path: &[u8]) -> Result<Option<c_int>> {
+ let fdt = self.as_fdt_slice().as_ptr().cast();
+ // *_namelen functions don't include the trailing nul terminator in 'len'.
+ let len = path.len().try_into().map_err(|_| FdtError::BadPath)?;
+ let path = path.as_ptr().cast();
+ // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) and the
+ // function respects the passed number of characters.
+ let ret = unsafe { libfdt_bindgen::fdt_path_offset_namelen(fdt, path, len) };
+
+ fdt_err_or_option(ret)
+ }
+
+ /// Safe wrapper around `fdt_node_offset_by_compatible()` (C function).
+ fn node_offset_by_compatible(&self, prev: c_int, compatible: &CStr) -> Result<Option<c_int>> {
+ let fdt = self.as_fdt_slice().as_ptr().cast();
+ let compatible = compatible.as_ptr();
+ // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+ let ret = unsafe { libfdt_bindgen::fdt_node_offset_by_compatible(fdt, prev, compatible) };
+
+ fdt_err_or_option(ret)
+ }
+
+ /// Safe wrapper around `fdt_next_node()` (C function).
+ fn next_node(&self, node: c_int, depth: usize) -> Result<Option<(c_int, usize)>> {
+ let fdt = self.as_fdt_slice().as_ptr().cast();
+ let mut depth = depth.try_into().unwrap();
+ // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+ let ret = unsafe { libfdt_bindgen::fdt_next_node(fdt, node, &mut depth) };
+
+ match fdt_err_or_option(ret)? {
+ Some(offset) if depth >= 0 => {
+ let depth = depth.try_into().unwrap();
+ Ok(Some((offset, depth)))
+ }
+ _ => Ok(None),
+ }
+ }
+
+ /// Safe wrapper around `fdt_parent_offset()` (C function).
+ ///
+ /// Note that this function returns a `Err` when called on a root.
+ fn parent_offset(&self, node: c_int) -> Result<c_int> {
+ let fdt = self.as_fdt_slice().as_ptr().cast();
+ // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+ let ret = unsafe { libfdt_bindgen::fdt_parent_offset(fdt, node) };
+
+ fdt_err(ret)
+ }
+
+ /// Safe wrapper around `fdt_supernode_atdepth_offset()` (C function).
+ ///
+ /// Note that this function returns a `Err` when called on a node at a depth shallower than
+ /// the provided `depth`.
+ fn supernode_atdepth_offset(&self, node: c_int, depth: usize) -> Result<c_int> {
+ let fdt = self.as_fdt_slice().as_ptr().cast();
+ let depth = depth.try_into().unwrap();
+ let nodedepth = ptr::null_mut();
+ let ret =
+ // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+ unsafe { libfdt_bindgen::fdt_supernode_atdepth_offset(fdt, node, depth, nodedepth) };
+
+ fdt_err(ret)
+ }
+
+ /// Safe wrapper around `fdt_subnode_offset_namelen()` (C function).
+ fn subnode_offset_namelen(&self, parent: c_int, name: &[u8]) -> Result<Option<c_int>> {
+ let fdt = self.as_fdt_slice().as_ptr().cast();
+ let namelen = name.len().try_into().unwrap();
+ let name = name.as_ptr().cast();
+ // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
+ let ret = unsafe { libfdt_bindgen::fdt_subnode_offset_namelen(fdt, parent, name, namelen) };
+
+ fdt_err_or_option(ret)
+ }
+ /// Safe wrapper around `fdt_first_subnode()` (C function).
+ fn first_subnode(&self, node: c_int) -> Result<Option<c_int>> {
+ let fdt = self.as_fdt_slice().as_ptr().cast();
+ // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+ let ret = unsafe { libfdt_bindgen::fdt_first_subnode(fdt, node) };
+
+ fdt_err_or_option(ret)
+ }
+
+ /// Safe wrapper around `fdt_next_subnode()` (C function).
+ fn next_subnode(&self, node: c_int) -> Result<Option<c_int>> {
+ let fdt = self.as_fdt_slice().as_ptr().cast();
+ // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+ let ret = unsafe { libfdt_bindgen::fdt_next_subnode(fdt, node) };
+
+ fdt_err_or_option(ret)
+ }
+
+ /// Safe wrapper around `fdt_address_cells()` (C function).
+ fn address_cells(&self, node: c_int) -> Result<usize> {
+ let fdt = self.as_fdt_slice().as_ptr().cast();
+ // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
+ let ret = unsafe { libfdt_bindgen::fdt_address_cells(fdt, node) };
+
+ Ok(fdt_err(ret)?.try_into().unwrap())
+ }
+
+ /// Safe wrapper around `fdt_size_cells()` (C function).
+ fn size_cells(&self, node: c_int) -> Result<usize> {
+ let fdt = self.as_fdt_slice().as_ptr().cast();
+ // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
+ let ret = unsafe { libfdt_bindgen::fdt_size_cells(fdt, node) };
+
+ Ok(fdt_err(ret)?.try_into().unwrap())
+ }
+}
+
+/// Wrapper for the read-write libfdt.h functions.
+///
+/// # Safety
+///
+/// Implementors must ensure that at any point where a method of this trait is called, the
+/// underlying type returns the bytes of a valid device tree (as validated by `check_full`)
+/// through its `.as_fdt_slice_mut` method.
+///
+/// Some methods may make previously returned values such as node or string offsets or phandles
+/// invalid by modifying the device tree (e.g. by inserting or removing new nodes or properties).
+/// As most methods take or return such values, instead of marking them all as unsafe, this trait
+/// is marked as unsafe as implementors must ensure that methods that modify the validity of those
+/// values are never called while the values are still in use.
+pub(crate) unsafe trait LibfdtMut {
+ /// Provides a mutable pointer to a buffer containing the device tree.
+ ///
+ /// The implementation must ensure that the size of the returned slice is at least
+ /// `fdt_header::totalsize`, to allow for device tree growth.
+ fn as_fdt_slice_mut(&mut self) -> &mut [u8];
+
+ /// Safe wrapper around `fdt_nop_node()` (C function).
+ fn nop_node(&mut self, node: c_int) -> Result<()> {
+ let fdt = self.as_fdt_slice_mut().as_mut_ptr().cast();
+ // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
+ let ret = unsafe { libfdt_bindgen::fdt_nop_node(fdt, node) };
+
+ fdt_err_expect_zero(ret)
+ }
+
+ /// Safe wrapper around `fdt_add_subnode_namelen()` (C function).
+ fn add_subnode_namelen(&mut self, node: c_int, name: &[u8]) -> Result<c_int> {
+ let fdt = self.as_fdt_slice_mut().as_mut_ptr().cast();
+ let namelen = name.len().try_into().unwrap();
+ let name = name.as_ptr().cast();
+ // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
+ let ret = unsafe { libfdt_bindgen::fdt_add_subnode_namelen(fdt, node, name, namelen) };
+
+ fdt_err(ret)
+ }
+}