Merge "Remove misleading test" into main
diff --git a/libs/libfdt/Android.bp b/libs/libfdt/Android.bp
index 7231904..6a7f7f2 100644
--- a/libs/libfdt/Android.bp
+++ b/libs/libfdt/Android.bp
@@ -40,6 +40,7 @@
     rustlibs: [
         "libcstr",
         "liblibfdt_bindgen",
+        "libmemoffset_nostd",
         "libzerocopy_nostd",
     ],
     whole_static_libs: [
diff --git a/libs/libfdt/src/ctypes.rs b/libs/libfdt/src/ctypes.rs
new file mode 100644
index 0000000..640d447
--- /dev/null
+++ b/libs/libfdt/src/ctypes.rs
@@ -0,0 +1,52 @@
+// Copyright 2024, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Safe zero-cost wrappers around integer values used by libfdt.
+
+use crate::{FdtError, Result};
+
+/// Wrapper guaranteed to contain a valid phandle.
+#[repr(transparent)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+pub struct Phandle(u32);
+
+impl Phandle {
+    /// Minimum valid value for device tree phandles.
+    pub const MIN: Self = Self(1);
+    /// Maximum valid value for device tree phandles.
+    pub const MAX: Self = Self(libfdt_bindgen::FDT_MAX_PHANDLE);
+
+    /// Creates a new Phandle
+    pub const fn new(value: u32) -> Option<Self> {
+        if Self::MIN.0 <= value && value <= Self::MAX.0 {
+            Some(Self(value))
+        } else {
+            None
+        }
+    }
+}
+
+impl From<Phandle> for u32 {
+    fn from(phandle: Phandle) -> u32 {
+        phandle.0
+    }
+}
+
+impl TryFrom<u32> for Phandle {
+    type Error = FdtError;
+
+    fn try_from(value: u32) -> Result<Self> {
+        Self::new(value).ok_or(FdtError::BadPhandle)
+    }
+}
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index 0249d0d..ab3c83f 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -17,10 +17,12 @@
 
 #![no_std]
 
+mod ctypes;
 mod iterators;
 mod libfdt;
 mod result;
 
+pub use ctypes::Phandle;
 pub use iterators::{
     AddressRange, CellIterator, CompatibleIterator, DescendantsIterator, MemRegIterator,
     PropertyIterator, RangesIterator, Reg, RegIterator, SubnodeIterator,
@@ -30,6 +32,7 @@
 use core::ffi::{c_int, c_void, CStr};
 use core::ops::Range;
 use cstr::cstr;
+use libfdt::get_slice_at_ptr;
 use result::{fdt_err, fdt_err_expect_zero, fdt_err_or_option};
 use zerocopy::AsBytes as _;
 
@@ -92,17 +95,7 @@
 
 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.
-        let prop = unsafe { &*prop };
-        Ok(prop.as_ref())
+        Ok(fdt.get_property_by_offset(offset)?.as_ref())
     }
 
     fn name_offset(&self) -> c_int {
@@ -143,11 +136,7 @@
     }
 
     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) };
-
-        if let Some(offset) = fdt_err_or_option(ret)? {
+        if let Some(offset) = self.fdt.next_property_offset(self.offset)? {
             Ok(Some(Self::new(self.fdt, offset)?))
         } else {
             Ok(None)
@@ -216,13 +205,7 @@
 
     /// Returns the node name.
     pub fn name(&self) -> Result<&'a CStr> {
-        let mut len: c_int = 0;
-        // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor). On success, the
-        // function returns valid null terminating string and otherwise returned values are dropped.
-        let name = unsafe { libfdt_bindgen::fdt_get_name(self.fdt.as_ptr(), self.offset, &mut len) }
-            as *const c_void;
-        let len = usize::try_from(fdt_err(len)?).unwrap();
-        let name = self.fdt.get_from_ptr(name, len + 1)?;
+        let name = self.fdt.get_name(self.offset)?;
         CStr::from_bytes_with_nul(name).map_err(|_| FdtError::Internal)
     }
 
@@ -264,44 +247,7 @@
 
     /// Returns the value of a given property.
     pub fn getprop(&self, name: &CStr) -> Result<Option<&'a [u8]>> {
-        if let Some((prop, len)) = Self::getprop_internal(self.fdt, self.offset, name)? {
-            Ok(Some(self.fdt.get_from_ptr(prop, len)?))
-        } else {
-            Ok(None)
-        }
-    }
-
-    /// Returns the pointer and size of the property named `name`, in a node at offset `offset`, in
-    /// a device tree `fdt`. The pointer is guaranteed to be non-null, in which case error returns.
-    fn getprop_internal(
-        fdt: &'a Fdt,
-        offset: c_int,
-        name: &CStr,
-    ) -> Result<Option<(*const c_void, usize)>> {
-        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.
-        let prop = unsafe {
-            libfdt_bindgen::fdt_getprop_namelen(
-                fdt.as_ptr(),
-                offset,
-                name.as_ptr(),
-                // *_namelen functions don't include the trailing nul terminator in 'len'.
-                name.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?,
-                &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).unwrap();
-
-        if prop.is_null() {
-            // We expected an error code in len but still received a valid value?!
-            return Err(FdtError::Internal);
-        }
-        Ok(Some((prop.cast::<c_void>(), len)))
+        self.fdt.getprop_namelen(self.offset, name.to_bytes())
     }
 
     /// Returns reference to the containing device tree.
@@ -365,11 +311,7 @@
     }
 
     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) };
-
-        if let Some(offset) = fdt_err_or_option(ret)? {
+        if let Some(offset) = self.fdt.first_property_offset(self.offset)? {
             Ok(Some(FdtProperty::new(self.fdt, offset)?))
         } else {
             Ok(None)
@@ -410,41 +352,6 @@
     }
 }
 
-/// Phandle of a FDT node
-#[repr(transparent)]
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
-pub struct Phandle(u32);
-
-impl Phandle {
-    /// Minimum valid value for device tree phandles.
-    pub const MIN: Self = Self(1);
-    /// Maximum valid value for device tree phandles.
-    pub const MAX: Self = Self(libfdt_bindgen::FDT_MAX_PHANDLE);
-
-    /// Creates a new Phandle
-    pub const fn new(value: u32) -> Option<Self> {
-        if Self::MIN.0 <= value && value <= Self::MAX.0 {
-            Some(Self(value))
-        } else {
-            None
-        }
-    }
-}
-
-impl From<Phandle> for u32 {
-    fn from(phandle: Phandle) -> u32 {
-        phandle.0
-    }
-}
-
-impl TryFrom<u32> for Phandle {
-    type Error = FdtError;
-
-    fn try_from(value: u32) -> Result<Self> {
-        Self::new(value).ok_or(FdtError::BadPhandle)
-    }
-}
-
 /// Mutable FDT node.
 #[derive(Debug)]
 pub struct FdtNodeMut<'a> {
@@ -455,54 +362,20 @@
 impl<'a> FdtNodeMut<'a> {
     /// Appends a property name-value (possibly empty) pair to the given node.
     pub fn appendprop<T: AsRef<[u8]>>(&mut self, name: &CStr, value: &T) -> Result<()> {
-        // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
-        let ret = unsafe {
-            libfdt_bindgen::fdt_appendprop(
-                self.fdt.as_mut_ptr(),
-                self.offset,
-                name.as_ptr(),
-                value.as_ref().as_ptr().cast::<c_void>(),
-                value.as_ref().len().try_into().map_err(|_| FdtError::BadValue)?,
-            )
-        };
-
-        fdt_err_expect_zero(ret)
+        self.fdt.appendprop(self.offset, name, value.as_ref())
     }
 
     /// Appends a (address, size) pair property to the given node.
     pub fn appendprop_addrrange(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
-        // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
-        let ret = unsafe {
-            libfdt_bindgen::fdt_appendprop_addrrange(
-                self.fdt.as_mut_ptr(),
-                self.parent()?.offset,
-                self.offset,
-                name.as_ptr(),
-                addr,
-                size,
-            )
-        };
-
-        fdt_err_expect_zero(ret)
+        let parent = self.parent()?.offset;
+        self.fdt.appendprop_addrrange(parent, self.offset, name, addr, size)
     }
 
     /// Sets a property name-value pair to the given node.
     ///
     /// This may create a new prop or replace existing value.
     pub fn setprop(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
-        // SAFETY: New value size is constrained to the DT totalsize
-        //          (validated by underlying libfdt).
-        let ret = unsafe {
-            libfdt_bindgen::fdt_setprop(
-                self.fdt.as_mut_ptr(),
-                self.offset,
-                name.as_ptr(),
-                value.as_ptr().cast::<c_void>(),
-                value.len().try_into().map_err(|_| FdtError::BadValue)?,
-            )
-        };
-
-        fdt_err_expect_zero(ret)
+        self.fdt.setprop(self.offset, name, value)
     }
 
     /// Sets the value of the given property with the given value, and ensure that the given
@@ -510,18 +383,7 @@
     ///
     /// This can only be used to replace existing value.
     pub fn setprop_inplace(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
-        // SAFETY: fdt size is not altered
-        let ret = unsafe {
-            libfdt_bindgen::fdt_setprop_inplace(
-                self.fdt.as_mut_ptr(),
-                self.offset,
-                name.as_ptr(),
-                value.as_ptr().cast::<c_void>(),
-                value.len().try_into().map_err(|_| FdtError::BadValue)?,
-            )
-        };
-
-        fdt_err_expect_zero(ret)
+        self.fdt.setprop_inplace(self.offset, name, value)
     }
 
     /// Sets the value of the given (address, size) pair property with the given value, and
@@ -530,63 +392,35 @@
     /// This can only be used to replace existing value.
     pub fn setprop_addrrange_inplace(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
         let pair = [addr.to_be(), size.to_be()];
-        self.setprop_inplace(name, pair.as_bytes())
+        self.fdt.setprop_inplace(self.offset, name, pair.as_bytes())
     }
 
     /// Sets a flag-like empty property.
     ///
     /// This may create a new prop or replace existing value.
     pub fn setprop_empty(&mut self, name: &CStr) -> Result<()> {
-        self.setprop(name, &[])
+        self.fdt.setprop(self.offset, name, &[])
     }
 
     /// Deletes the given property.
     pub fn delprop(&mut self, name: &CStr) -> Result<()> {
-        // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
-        // library locates the node's property. Removing the property may shift the offsets of
-        // other nodes and properties but the borrow checker should prevent this function from
-        // being called when FdtNode instances are in use.
-        let ret = unsafe {
-            libfdt_bindgen::fdt_delprop(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
-        };
-
-        fdt_err_expect_zero(ret)
+        self.fdt.delprop(self.offset, name)
     }
 
     /// Deletes the given property effectively from DT, by setting it with FDT_NOP.
     pub fn nop_property(&mut self, name: &CStr) -> Result<()> {
-        // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
-        // library locates the node's property.
-        let ret = unsafe {
-            libfdt_bindgen::fdt_nop_property(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
-        };
-
-        fdt_err_expect_zero(ret)
+        self.fdt.nop_property(self.offset, name)
     }
 
     /// Trims the size of the given property to new_size.
     pub fn trimprop(&mut self, name: &CStr, new_size: usize) -> Result<()> {
-        let (prop, len) =
-            FdtNode::getprop_internal(self.fdt, self.offset, name)?.ok_or(FdtError::NotFound)?;
-        if len == new_size {
-            return Ok(());
-        }
-        if new_size > len {
-            return Err(FdtError::NoSpace);
-        }
+        let prop = self.as_node().getprop(name)?.ok_or(FdtError::NotFound)?;
 
-        // SAFETY: new_size is smaller than the old size
-        let ret = unsafe {
-            libfdt_bindgen::fdt_setprop(
-                self.fdt.as_mut_ptr(),
-                self.offset,
-                name.as_ptr(),
-                prop.cast::<c_void>(),
-                new_size.try_into().map_err(|_| FdtError::BadValue)?,
-            )
-        };
-
-        fdt_err_expect_zero(ret)
+        match prop.len() {
+            x if x == new_size => Ok(()),
+            x if x < new_size => Err(FdtError::NoSpace),
+            _ => self.fdt.setprop_placeholder(self.offset, name, new_size).map(|_| ()),
+        }
     }
 
     /// Returns reference to the containing device tree.
@@ -896,30 +730,21 @@
 
     /// 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.try_into()
+        self.find_max_phandle()
     }
 
     /// Returns a node with the phandle
     pub fn node_with_phandle(&self, phandle: Phandle) -> Result<Option<FdtNode>> {
-        let offset = self.node_offset_with_phandle(phandle)?;
+        let offset = self.node_offset_by_phandle(phandle)?;
+
         Ok(offset.map(|offset| FdtNode { fdt: self, offset }))
     }
 
     /// Returns a mutable node with the phandle
     pub fn node_mut_with_phandle(&mut self, phandle: Phandle) -> Result<Option<FdtNodeMut>> {
-        let offset = self.node_offset_with_phandle(phandle)?;
-        Ok(offset.map(|offset| FdtNodeMut { fdt: self, offset }))
-    }
+        let offset = self.node_offset_by_phandle(phandle)?;
 
-    fn node_offset_with_phandle(&self, phandle: Phandle) -> Result<Option<c_int>> {
-        // SAFETY: Accesses are constrained to the DT totalsize.
-        let ret = unsafe { libfdt_bindgen::fdt_node_offset_by_phandle(self.as_ptr(), phandle.0) };
-        fdt_err_or_option(ret)
+        Ok(offset.map(|offset| FdtNodeMut { fdt: self, offset }))
     }
 
     /// Returns the mutable root node of the tree.
@@ -952,20 +777,7 @@
     }
 
     fn get_from_ptr(&self, ptr: *const c_void, len: usize) -> Result<&[u8]> {
-        let ptr = ptr as usize;
-        let offset = ptr.checked_sub(self.as_ptr() as usize).ok_or(FdtError::Internal)?;
-        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) })
+        get_slice_at_ptr(self.as_fdt_slice(), ptr.cast(), len).ok_or(FdtError::Internal)
     }
 
     /// Returns a shared pointer to the device tree.
diff --git a/libs/libfdt/src/libfdt.rs b/libs/libfdt/src/libfdt.rs
index 7e3b65a..7737718 100644
--- a/libs/libfdt/src/libfdt.rs
+++ b/libs/libfdt/src/libfdt.rs
@@ -19,9 +19,10 @@
 //! adapted to their use-cases (e.g. alloc-based userspace or statically allocated no_std).
 
 use core::ffi::{c_int, CStr};
+use core::mem;
 use core::ptr;
 
-use crate::{fdt_err, fdt_err_expect_zero, fdt_err_or_option, FdtError, Result};
+use crate::{fdt_err, fdt_err_expect_zero, fdt_err_or_option, FdtError, Phandle, Result};
 
 // Function names are the C function names without the `fdt_` prefix.
 
@@ -78,6 +79,16 @@
         fdt_err_or_option(ret)
     }
 
+    /// Safe wrapper around `fdt_node_offset_by_phandle()` (C function).
+    fn node_offset_by_phandle(&self, phandle: Phandle) -> Result<Option<c_int>> {
+        let fdt = self.as_fdt_slice().as_ptr().cast();
+        let phandle = phandle.into();
+        // SAFETY: Accesses are constrained to the DT totalsize.
+        let ret = unsafe { libfdt_bindgen::fdt_node_offset_by_phandle(fdt, phandle) };
+
+        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();
@@ -175,6 +186,106 @@
 
         Ok(fdt_err(ret)?.try_into().unwrap())
     }
+
+    /// Safe wrapper around `fdt_get_name()` (C function).
+    fn get_name(&self, node: c_int) -> Result<&[u8]> {
+        let fdt = self.as_fdt_slice().as_ptr().cast();
+        let mut len = 0;
+        // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor). On success, the
+        // function returns valid null terminating string and otherwise returned values are dropped.
+        let name = unsafe { libfdt_bindgen::fdt_get_name(fdt, node, &mut len) };
+        let len = usize::try_from(fdt_err(len)?).unwrap().checked_add(1).unwrap();
+
+        get_slice_at_ptr(self.as_fdt_slice(), name.cast(), len).ok_or(FdtError::Internal)
+    }
+
+    /// Safe wrapper around `fdt_getprop_namelen()` (C function).
+    fn getprop_namelen(&self, node: c_int, name: &[u8]) -> Result<Option<&[u8]>> {
+        let fdt = self.as_fdt_slice().as_ptr().cast();
+        let namelen = name.len().try_into().map_err(|_| FdtError::BadPath)?;
+        let name = name.as_ptr().cast();
+        let mut len = 0;
+        let prop =
+            // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) and the
+            // function respects the passed number of characters.
+            unsafe { libfdt_bindgen::fdt_getprop_namelen(fdt, node, name, namelen, &mut len) };
+
+        if let Some(len) = fdt_err_or_option(len)? {
+            let len = usize::try_from(len).unwrap();
+            let bytes = get_slice_at_ptr(self.as_fdt_slice(), prop.cast(), len);
+
+            Ok(Some(bytes.ok_or(FdtError::Internal)?))
+        } else {
+            Ok(None)
+        }
+    }
+
+    /// Safe wrapper around `fdt_get_property_by_offset()` (C function).
+    fn get_property_by_offset(&self, offset: c_int) -> Result<&libfdt_bindgen::fdt_property> {
+        let mut len = 0;
+        let fdt = self.as_fdt_slice().as_ptr().cast();
+        // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+        let prop = unsafe { libfdt_bindgen::fdt_get_property_by_offset(fdt, offset, &mut len) };
+
+        let data_len = fdt_err(len)?.try_into().unwrap();
+        // TODO(stable_feature(offset_of)): mem::offset_of!(fdt_property, data).
+        let data_offset = memoffset::offset_of!(libfdt_bindgen::fdt_property, data);
+        let len = data_offset.checked_add(data_len).ok_or(FdtError::Internal)?;
+
+        if !is_aligned(prop) || get_slice_at_ptr(self.as_fdt_slice(), prop.cast(), len).is_none() {
+            return Err(FdtError::Internal);
+        }
+
+        // SAFETY: The pointer is properly aligned, struct is fully contained in the DT slice.
+        let prop = unsafe { &*prop };
+
+        if data_len != u32::from_be(prop.len).try_into().unwrap() {
+            return Err(FdtError::BadLayout);
+        }
+
+        Ok(prop)
+    }
+
+    /// Safe wrapper around `fdt_first_property_offset()` (C function).
+    fn first_property_offset(&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_property_offset(fdt, node) };
+
+        fdt_err_or_option(ret)
+    }
+
+    /// Safe wrapper around `fdt_next_property_offset()` (C function).
+    fn next_property_offset(&self, prev: 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_property_offset(fdt, prev) };
+
+        fdt_err_or_option(ret)
+    }
+
+    /// Safe wrapper around `fdt_find_max_phandle()` (C function).
+    fn find_max_phandle(&self) -> Result<Phandle> {
+        let fdt = self.as_fdt_slice().as_ptr().cast();
+        let mut phandle = 0;
+        // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+        let ret = unsafe { libfdt_bindgen::fdt_find_max_phandle(fdt, &mut phandle) };
+
+        fdt_err_expect_zero(ret)?;
+
+        phandle.try_into()
+    }
+
+    /// Safe wrapper around `fdt_string()` (C function).
+    fn string(&self, offset: c_int) -> Result<&CStr> {
+        let fdt = self.as_fdt_slice().as_ptr().cast();
+        // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+        let ptr = unsafe { libfdt_bindgen::fdt_string(fdt, offset) };
+        let bytes =
+            get_slice_from_ptr(self.as_fdt_slice(), ptr.cast()).ok_or(FdtError::Internal)?;
+
+        CStr::from_bytes_until_nul(bytes).map_err(|_| FdtError::Internal)
+    }
 }
 
 /// Wrapper for the read-write libfdt.h functions.
@@ -216,4 +327,129 @@
 
         fdt_err(ret)
     }
+
+    /// Safe wrapper around `fdt_setprop()` (C function).
+    fn setprop(&mut self, node: c_int, name: &CStr, value: &[u8]) -> Result<()> {
+        let fdt = self.as_fdt_slice_mut().as_mut_ptr().cast();
+        let name = name.as_ptr();
+        let len = value.len().try_into().map_err(|_| FdtError::BadValue)?;
+        let value = value.as_ptr().cast();
+        // SAFETY: New value size is constrained to the DT totalsize
+        //          (validated by underlying libfdt).
+        let ret = unsafe { libfdt_bindgen::fdt_setprop(fdt, node, name, value, len) };
+
+        fdt_err_expect_zero(ret)
+    }
+
+    /// Safe wrapper around `fdt_setprop_placeholder()` (C function).
+    fn setprop_placeholder(&mut self, node: c_int, name: &CStr, size: usize) -> Result<&mut [u8]> {
+        let fdt = self.as_fdt_slice_mut().as_mut_ptr().cast();
+        let name = name.as_ptr();
+        let len = size.try_into().unwrap();
+        let mut data = ptr::null_mut();
+        let ret =
+            // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
+            unsafe { libfdt_bindgen::fdt_setprop_placeholder(fdt, node, name, len, &mut data) };
+
+        fdt_err_expect_zero(ret)?;
+
+        get_mut_slice_at_ptr(self.as_fdt_slice_mut(), data.cast(), size).ok_or(FdtError::Internal)
+    }
+
+    /// Safe wrapper around `fdt_setprop_inplace()` (C function).
+    fn setprop_inplace(&mut self, node: c_int, name: &CStr, value: &[u8]) -> Result<()> {
+        let fdt = self.as_fdt_slice_mut().as_mut_ptr().cast();
+        let name = name.as_ptr();
+        let len = value.len().try_into().map_err(|_| FdtError::BadValue)?;
+        let value = value.as_ptr().cast();
+        // SAFETY: New value size is constrained to the DT totalsize
+        //          (validated by underlying libfdt).
+        let ret = unsafe { libfdt_bindgen::fdt_setprop_inplace(fdt, node, name, value, len) };
+
+        fdt_err_expect_zero(ret)
+    }
+
+    /// Safe wrapper around `fdt_appendprop()` (C function).
+    fn appendprop(&mut self, node: c_int, name: &CStr, value: &[u8]) -> Result<()> {
+        let fdt = self.as_fdt_slice_mut().as_mut_ptr().cast();
+        let name = name.as_ptr();
+        let len = value.len().try_into().map_err(|_| FdtError::BadValue)?;
+        let value = value.as_ptr().cast();
+        // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
+        let ret = unsafe { libfdt_bindgen::fdt_appendprop(fdt, node, name, value, len) };
+
+        fdt_err_expect_zero(ret)
+    }
+
+    /// Safe wrapper around `fdt_appendprop_addrrange()` (C function).
+    fn appendprop_addrrange(
+        &mut self,
+        parent: c_int,
+        node: c_int,
+        name: &CStr,
+        addr: u64,
+        size: u64,
+    ) -> Result<()> {
+        let fdt = self.as_fdt_slice_mut().as_mut_ptr().cast();
+        let name = name.as_ptr();
+        // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
+        let ret = unsafe {
+            libfdt_bindgen::fdt_appendprop_addrrange(fdt, parent, node, name, addr, size)
+        };
+
+        fdt_err_expect_zero(ret)
+    }
+
+    /// Safe wrapper around `fdt_delprop()` (C function).
+    fn delprop(&mut self, node: c_int, name: &CStr) -> Result<()> {
+        let fdt = self.as_fdt_slice_mut().as_mut_ptr().cast();
+        let name = name.as_ptr();
+        // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
+        // library locates the node's property. Removing the property may shift the offsets of
+        // other nodes and properties but the borrow checker should prevent this function from
+        // being called when FdtNode instances are in use.
+        let ret = unsafe { libfdt_bindgen::fdt_delprop(fdt, node, name) };
+
+        fdt_err_expect_zero(ret)
+    }
+
+    /// Safe wrapper around `fdt_nop_property()` (C function).
+    fn nop_property(&mut self, node: c_int, name: &CStr) -> Result<()> {
+        let fdt = self.as_fdt_slice_mut().as_mut_ptr().cast();
+        let name = name.as_ptr();
+        // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
+        // library locates the node's property.
+        let ret = unsafe { libfdt_bindgen::fdt_nop_property(fdt, node, name) };
+
+        fdt_err_expect_zero(ret)
+    }
+}
+
+pub(crate) fn get_slice_at_ptr(s: &[u8], p: *const u8, len: usize) -> Option<&[u8]> {
+    let offset = get_slice_ptr_offset(s, p)?;
+
+    s.get(offset..offset.checked_add(len)?)
+}
+
+fn get_mut_slice_at_ptr(s: &mut [u8], p: *mut u8, len: usize) -> Option<&mut [u8]> {
+    let offset = get_slice_ptr_offset(s, p)?;
+
+    s.get_mut(offset..offset.checked_add(len)?)
+}
+
+fn get_slice_from_ptr(s: &[u8], p: *const u8) -> Option<&[u8]> {
+    s.get(get_slice_ptr_offset(s, p)?..)
+}
+
+fn get_slice_ptr_offset(s: &[u8], p: *const u8) -> Option<usize> {
+    s.as_ptr_range().contains(&p).then(|| {
+        // SAFETY: Both pointers are in bounds, derive from the same object, and size_of::<T>()=1.
+        (unsafe { p.offset_from(s.as_ptr()) }) as usize
+        // TODO(stable_feature(ptr_sub_ptr)): p.sub_ptr()
+    })
+}
+
+// TODO(stable_feature(pointer_is_aligned)): p.is_aligned()
+fn is_aligned<T>(p: *const T) -> bool {
+    (p as usize) % mem::align_of::<T>() == 0
 }
diff --git a/rialto/Android.bp b/rialto/Android.bp
index c102c89..d7aac35 100644
--- a/rialto/Android.bp
+++ b/rialto/Android.bp
@@ -133,11 +133,11 @@
         "libandroid_logger",
         "libanyhow",
         "libbssl_avf_nostd",
-        "libciborium",
         "libclient_vm_csr",
         "libcoset",
         "liblibc",
         "liblog_rust",
+        "libhwtrust",
         "libservice_vm_comm",
         "libservice_vm_fake_chain",
         "libservice_vm_manager",
diff --git a/rialto/tests/test.rs b/rialto/tests/test.rs
index 1302bcd..8899875 100644
--- a/rialto/tests/test.rs
+++ b/rialto/tests/test.rs
@@ -23,9 +23,9 @@
 };
 use anyhow::{bail, Context, Result};
 use bssl_avf::{sha256, EcKey, PKey};
-use ciborium::value::Value;
 use client_vm_csr::generate_attestation_key_and_csr;
 use coset::{CborSerializable, CoseMac0, CoseSign};
+use hwtrust::{rkp, session::Session};
 use log::info;
 use service_vm_comm::{
     ClientVmAttestationParams, Csr, CsrPayload, EcdsaP256KeyPair, GenerateCertificateRequestParams,
@@ -37,7 +37,6 @@
 use service_vm_manager::ServiceVm;
 use std::fs;
 use std::fs::File;
-use std::io;
 use std::panic;
 use std::path::PathBuf;
 use std::str::FromStr;
@@ -272,16 +271,8 @@
     Ok(())
 }
 
-/// TODO(b/300625792): Check the CSR with libhwtrust once the CSR is complete.
 fn check_csr(csr: Vec<u8>) -> Result<()> {
-    let mut reader = io::Cursor::new(csr);
-    let csr: Value = ciborium::from_reader(&mut reader)?;
-    match csr {
-        Value::Array(arr) => {
-            assert_eq!(4, arr.len());
-        }
-        _ => bail!("Incorrect CSR format: {csr:?}"),
-    }
+    let _csr = rkp::Csr::from_cbor(&Session::default(), &csr[..]).context("Failed to parse CSR")?;
     Ok(())
 }
 
diff --git a/service_vm/requests/src/rkp.rs b/service_vm/requests/src/rkp.rs
index 9901a92..569ab01 100644
--- a/service_vm/requests/src/rkp.rs
+++ b/service_vm/requests/src/rkp.rs
@@ -76,13 +76,10 @@
         public_keys.push(public_key.to_cbor_value()?);
     }
     // Builds `CsrPayload`.
-    // TODO(b/299256925): The device information is currently empty as we do not
-    // have sufficient details to include.
-    let device_info = Value::Map(Vec::new());
     let csr_payload = cbor!([
         Value::Integer(CSR_PAYLOAD_SCHEMA_V3.into()),
         Value::Text(String::from(CERTIFICATE_TYPE)),
-        device_info,
+        device_info(),
         Value::Array(public_keys),
     ])?;
     let csr_payload = cbor_util::serialize(&csr_payload)?;
@@ -107,6 +104,22 @@
     Ok(cbor_util::serialize(&auth_req)?)
 }
 
+/// Generates the device info required by the RKP server as a temporary placeholder.
+/// More details in b/301592917.
+fn device_info() -> Value {
+    cbor!({"brand" => "aosp-avf",
+    "manufacturer" => "aosp-avf",
+    "product" => "avf",
+    "model" => "avf",
+    "device" => "avf",
+    "vbmeta_digest" => Value::Bytes(vec![0u8; 0]),
+    "system_patch_level" => 202402,
+    "boot_patch_level" => 20240202,
+    "vendor_patch_level" => 20240202,
+    "fused" => 1})
+    .unwrap()
+}
+
 fn derive_hmac_key(dice_artifacts: &dyn DiceArtifacts) -> Result<Zeroizing<[u8; HMAC_KEY_LENGTH]>> {
     let mut key = Zeroizing::new([0u8; HMAC_KEY_LENGTH]);
     kdf(dice_artifacts.cdi_seal(), &HMAC_KEY_SALT, HMAC_KEY_INFO, key.as_mut()).map_err(|e| {
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 602c670..771863b 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -1410,6 +1410,70 @@
     }
 }
 
+struct SecretkeeperProxy(Strong<dyn ISecretkeeper>);
+
+impl Interface for SecretkeeperProxy {}
+
+impl ISecretkeeper for SecretkeeperProxy {
+    fn processSecretManagementRequest(&self, req: &[u8]) -> binder::Result<Vec<u8>> {
+        // Pass the request to the channel, and read the response.
+        self.0.processSecretManagementRequest(req)
+    }
+
+    fn getAuthGraphKe(&self) -> binder::Result<Strong<dyn IAuthGraphKeyExchange>> {
+        let ag = AuthGraphKeyExchangeProxy(self.0.getAuthGraphKe()?);
+        Ok(BnAuthGraphKeyExchange::new_binder(ag, BinderFeatures::default()))
+    }
+
+    fn deleteIds(&self, ids: &[SecretId]) -> binder::Result<()> {
+        self.0.deleteIds(ids)
+    }
+
+    fn deleteAll(&self) -> binder::Result<()> {
+        self.0.deleteAll()
+    }
+}
+
+struct AuthGraphKeyExchangeProxy(Strong<dyn IAuthGraphKeyExchange>);
+
+impl Interface for AuthGraphKeyExchangeProxy {}
+
+impl IAuthGraphKeyExchange for AuthGraphKeyExchangeProxy {
+    fn create(&self) -> binder::Result<SessionInitiationInfo> {
+        self.0.create()
+    }
+
+    fn init(
+        &self,
+        peer_pub_key: &PubKey,
+        peer_id: &Identity,
+        peer_nonce: &[u8],
+        peer_version: i32,
+    ) -> binder::Result<KeInitResult> {
+        self.0.init(peer_pub_key, peer_id, peer_nonce, peer_version)
+    }
+
+    fn finish(
+        &self,
+        peer_pub_key: &PubKey,
+        peer_id: &Identity,
+        peer_signature: &SessionIdSignature,
+        peer_nonce: &[u8],
+        peer_version: i32,
+        own_key: &Key,
+    ) -> binder::Result<SessionInfo> {
+        self.0.finish(peer_pub_key, peer_id, peer_signature, peer_nonce, peer_version, own_key)
+    }
+
+    fn authenticationComplete(
+        &self,
+        peer_signature: &SessionIdSignature,
+        shared_keys: &[AuthgraphArc; 2],
+    ) -> binder::Result<[AuthgraphArc; 2]> {
+        self.0.authenticationComplete(peer_signature, shared_keys)
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -1626,67 +1690,3 @@
         Ok(())
     }
 }
-
-struct SecretkeeperProxy(Strong<dyn ISecretkeeper>);
-
-impl Interface for SecretkeeperProxy {}
-
-impl ISecretkeeper for SecretkeeperProxy {
-    fn processSecretManagementRequest(&self, req: &[u8]) -> binder::Result<Vec<u8>> {
-        // Pass the request to the channel, and read the response.
-        self.0.processSecretManagementRequest(req)
-    }
-
-    fn getAuthGraphKe(&self) -> binder::Result<Strong<dyn IAuthGraphKeyExchange>> {
-        let ag = AuthGraphKeyExchangeProxy(self.0.getAuthGraphKe()?);
-        Ok(BnAuthGraphKeyExchange::new_binder(ag, BinderFeatures::default()))
-    }
-
-    fn deleteIds(&self, ids: &[SecretId]) -> binder::Result<()> {
-        self.0.deleteIds(ids)
-    }
-
-    fn deleteAll(&self) -> binder::Result<()> {
-        self.0.deleteAll()
-    }
-}
-
-struct AuthGraphKeyExchangeProxy(Strong<dyn IAuthGraphKeyExchange>);
-
-impl Interface for AuthGraphKeyExchangeProxy {}
-
-impl IAuthGraphKeyExchange for AuthGraphKeyExchangeProxy {
-    fn create(&self) -> binder::Result<SessionInitiationInfo> {
-        self.0.create()
-    }
-
-    fn init(
-        &self,
-        peer_pub_key: &PubKey,
-        peer_id: &Identity,
-        peer_nonce: &[u8],
-        peer_version: i32,
-    ) -> binder::Result<KeInitResult> {
-        self.0.init(peer_pub_key, peer_id, peer_nonce, peer_version)
-    }
-
-    fn finish(
-        &self,
-        peer_pub_key: &PubKey,
-        peer_id: &Identity,
-        peer_signature: &SessionIdSignature,
-        peer_nonce: &[u8],
-        peer_version: i32,
-        own_key: &Key,
-    ) -> binder::Result<SessionInfo> {
-        self.0.finish(peer_pub_key, peer_id, peer_signature, peer_nonce, peer_version, own_key)
-    }
-
-    fn authenticationComplete(
-        &self,
-        peer_signature: &SessionIdSignature,
-        shared_keys: &[AuthgraphArc; 2],
-    ) -> binder::Result<[AuthgraphArc; 2]> {
-        self.0.authenticationComplete(peer_signature, shared_keys)
-    }
-}