libfdt: Add phandle APIs

This CL adds following APIs
  - Fdt::max_phandle(): Returns the max phandle
  - Fdt::node_with_phandle(): Returns the node with phandle

Bug: 277993056
Test: atest liblibfdt.integration_test
Change-Id: I155242718b09897e832834a621f9e244ed2d9807
diff --git a/libs/libfdt/Android.bp b/libs/libfdt/Android.bp
index 0a05471..b889ee5 100644
--- a/libs/libfdt/Android.bp
+++ b/libs/libfdt/Android.bp
@@ -57,6 +57,7 @@
         ":fdt_test_tree_multiple_memory_ranges_dtb",
         ":fdt_test_tree_empty_memory_range_dtb",
         ":fdt_test_tree_no_memory_node_dtb",
+        ":fdt_test_tree_phandle_dtb",
     ],
     prefer_rlib: true,
     rustlibs: [
@@ -91,3 +92,10 @@
     srcs: ["tests/data/test_tree_no_memory_node.dts"],
     out: ["data/test_tree_no_memory_node.dtb"],
 }
+
+genrule {
+    name: "fdt_test_tree_phandle_dtb",
+    defaults: ["dts_to_dtb"],
+    srcs: ["tests/data/test_tree_phandle.dts"],
+    out: ["data/test_tree_phandle.dtb"],
+}
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index 5a7bd14..2a547e8 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -499,6 +499,27 @@
     }
 }
 
+/// Phandle of a FDT node
+#[repr(transparent)]
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub struct Phandle(u32);
+
+impl Phandle {
+    /// Creates a new Phandle
+    pub fn new(value: u32) -> Result<Self> {
+        if value == 0 || value > libfdt_bindgen::FDT_MAX_PHANDLE {
+            return Err(FdtError::BadPhandle);
+        }
+        Ok(Self(value))
+    }
+}
+
+impl From<Phandle> for u32 {
+    fn from(phandle: Phandle) -> u32 {
+        phandle.0
+    }
+}
+
 /// Mutable FDT node.
 pub struct FdtNodeMut<'a> {
     fdt: &'a mut Fdt,
@@ -879,6 +900,23 @@
         CompatibleIterator::new(self, compatible)
     }
 
+    /// Returns max phandle in the tree.
+    pub fn max_phandle(&self) -> Result<Phandle> {
+        let mut phandle: u32 = 0;
+        // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+        let ret = unsafe { libfdt_bindgen::fdt_find_max_phandle(self.as_ptr(), &mut phandle) };
+
+        fdt_err_expect_zero(ret)?;
+        Phandle::new(phandle)
+    }
+
+    /// Returns a node with the phandle
+    pub fn node_with_phandle(&self, phandle: Phandle) -> Result<Option<FdtNode>> {
+        // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+        let ret = unsafe { libfdt_bindgen::fdt_node_offset_by_phandle(self.as_ptr(), phandle.0) };
+        Ok(fdt_err_or_option(ret)?.map(|offset| FdtNode { fdt: self, offset }))
+    }
+
     /// Returns the mutable root node of the tree.
     pub fn root_mut(&mut self) -> Result<FdtNodeMut> {
         self.node_mut(CStr::from_bytes_with_nul(b"/\0").unwrap())?.ok_or(FdtError::Internal)
diff --git a/libs/libfdt/tests/api_test.rs b/libs/libfdt/tests/api_test.rs
index ccbf9a5..5484185 100644
--- a/libs/libfdt/tests/api_test.rs
+++ b/libs/libfdt/tests/api_test.rs
@@ -16,7 +16,7 @@
 
 //! Integration tests of the library libfdt.
 
-use libfdt::{Fdt, FdtError};
+use libfdt::{Fdt, FdtError, Phandle};
 use std::ffi::CStr;
 use std::fs;
 use std::ops::Range;
@@ -32,6 +32,7 @@
     "data/test_tree_multiple_memory_ranges.dtb";
 const TEST_TREE_WITH_EMPTY_MEMORY_RANGE_PATH: &str = "data/test_tree_empty_memory_range.dtb";
 const TEST_TREE_WITH_NO_MEMORY_NODE_PATH: &str = "data/test_tree_no_memory_node.dtb";
+const TEST_TREE_PHANDLE_PATH: &str = "data/test_tree_phandle.dtb";
 
 #[test]
 fn retrieving_memory_from_fdt_with_one_memory_range_succeeds() {
@@ -138,3 +139,33 @@
         assert_eq!(supernode.name().unwrap().to_str().unwrap(), *expect);
     }
 }
+
+#[test]
+fn phandle_new() {
+    let phandle_u32 = 0x55;
+    let phandle = Phandle::new(phandle_u32).unwrap();
+
+    assert_eq!(u32::from(phandle), phandle_u32);
+}
+
+#[test]
+fn max_phandle() {
+    let data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+    let fdt = Fdt::from_slice(&data).unwrap();
+
+    assert_eq!(fdt.max_phandle().unwrap(), Phandle::new(0xFF).unwrap());
+}
+
+#[test]
+fn node_with_phandle() {
+    let data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+    let fdt = Fdt::from_slice(&data).unwrap();
+
+    // Test linux,phandle
+    let node = fdt.node_with_phandle(Phandle::new(0xFF).unwrap()).unwrap().unwrap();
+    assert_eq!(node.name().unwrap().to_str().unwrap(), "node_zz");
+
+    // Test phandle
+    let node = fdt.node_with_phandle(Phandle::new(0x22).unwrap()).unwrap().unwrap();
+    assert_eq!(node.name().unwrap().to_str().unwrap(), "node_abc");
+}
diff --git a/libs/libfdt/tests/data/test_tree_phandle.dts b/libs/libfdt/tests/data/test_tree_phandle.dts
new file mode 100644
index 0000000..0438241
--- /dev/null
+++ b/libs/libfdt/tests/data/test_tree_phandle.dts
@@ -0,0 +1,35 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+    node_a {
+        phandle = <0x1>;
+        node_ab {
+            node_abc {
+                linux,phandle = <0x22>;
+            };
+        };
+    };
+
+    node_b {
+    };
+
+    node_c {
+    };
+
+    node_z {
+        node_za {
+        };
+
+        node_zb {
+
+        };
+
+        node_zz {
+            phandle = <0xFF>;
+
+            node_zzz {
+            };
+        };
+    };
+};
\ No newline at end of file