libfdt: Add FdtNode::supernode_at_depth()

This is helpful to locate special node (e.g. __overlay__).

Bug: 277993056
Test: atest liblibfdt.integration_test
Change-Id: I833e586437f0e5934a448dddf80d1bd947c3bd4f
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index 03a1f8e..5a7bd14 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -29,6 +29,7 @@
 use core::fmt;
 use core::mem;
 use core::ops::Range;
+use core::ptr;
 use core::result;
 use zerocopy::AsBytes as _;
 
@@ -279,6 +280,21 @@
         Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
     }
 
+    /// 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(),
+            )
+        };
+
+        Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
+    }
+
     /// Returns the standard (deprecated) device_type <string> property.
     pub fn device_type(&self) -> Result<Option<&CStr>> {
         self.getprop_str(CStr::from_bytes_with_nul(b"device_type\0").unwrap())
diff --git a/libs/libfdt/tests/api_test.rs b/libs/libfdt/tests/api_test.rs
index 9be1bf9..ccbf9a5 100644
--- a/libs/libfdt/tests/api_test.rs
+++ b/libs/libfdt/tests/api_test.rs
@@ -21,6 +21,12 @@
 use std::fs;
 use std::ops::Range;
 
+macro_rules! cstr {
+    ($str:literal) => {{
+        CStr::from_bytes_with_nul(concat!($str, "\0").as_bytes()).unwrap()
+    }};
+}
+
 const TEST_TREE_WITH_ONE_MEMORY_RANGE_PATH: &str = "data/test_tree_one_memory_range.dtb";
 const TEST_TREE_WITH_MULTIPLE_MEMORY_RANGES_PATH: &str =
     "data/test_tree_multiple_memory_ranges.dtb";
@@ -83,7 +89,7 @@
     let chosen = fdt.chosen().unwrap().unwrap();
     assert_eq!(chosen.name().unwrap().to_str().unwrap(), "chosen");
 
-    let nested_node_path = CStr::from_bytes_with_nul(b"/cpus/PowerPC,970@0\0").unwrap();
+    let nested_node_path = cstr!("/cpus/PowerPC,970@0");
     let nested_node = fdt.node(nested_node_path).unwrap().unwrap();
     assert_eq!(nested_node.name().unwrap().to_str().unwrap(), "PowerPC,970@0");
 }
@@ -119,3 +125,16 @@
         assert_eq!(prop.value().unwrap(), value);
     }
 }
+
+#[test]
+fn node_supernode_at_depth() {
+    let data = fs::read(TEST_TREE_WITH_NO_MEMORY_NODE_PATH).unwrap();
+    let fdt = Fdt::from_slice(&data).unwrap();
+    let node = fdt.node(cstr!("/cpus/PowerPC,970@1")).unwrap().unwrap();
+    let expected = &["", "cpus", "PowerPC,970@1"];
+
+    for (depth, expect) in expected.iter().enumerate() {
+        let supernode = node.supernode_at_depth(depth).unwrap();
+        assert_eq!(supernode.name().unwrap().to_str().unwrap(), *expect);
+    }
+}