libfdt: Move pack(), unpack(), apply_overlay()
Fix the lifetime of overlay in Fdt::apply_overlay() as there is no need
for it to share the lifetime of the Fdt (only that the reference is
valid during the execution of the function call) and fix the Safety
comment to clarify that overlay always gets corrupted.
Add FFI for fdt_open_into(), fdt_pack(), and fdt_overlay_apply().
Note that Rust does not allow aliasing mutable and const references so,
to properly expose the fdt_open_into() API to clients, provide it
through distinct read-only and read-write functions: Libfdt::open_into()
and LibfdtMut::open_into_self().
Test: m pvmfw
Test: atest liblibfdt.integration_test
Change-Id: Ib95154317fcba22f8ce2f89032f2859ea62e9c6e
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index ab3c83f..925c473 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -637,40 +637,30 @@
/// Unpacks the DT to cover the whole slice it is contained in.
pub fn unpack(&mut self) -> Result<()> {
- // SAFETY: "Opens" the DT in-place (supported use-case) by updating its header and
- // internal structures to make use of the whole self.fdt slice but performs no accesses
- // outside of it and leaves the DT in a state that will be detected by other functions.
- let ret = unsafe {
- libfdt_bindgen::fdt_open_into(
- self.as_ptr(),
- self.as_mut_ptr(),
- self.capacity().try_into().map_err(|_| FdtError::Internal)?,
- )
- };
- fdt_err_expect_zero(ret)
+ self.open_into_self()
}
/// Packs the DT to take a minimum amount of memory.
///
/// Doesn't shrink the underlying memory slice.
pub fn pack(&mut self) -> Result<()> {
- // SAFETY: "Closes" the DT in-place by updating its header and relocating its structs.
- let ret = unsafe { libfdt_bindgen::fdt_pack(self.as_mut_ptr()) };
- fdt_err_expect_zero(ret)
+ LibfdtMut::pack(self)
}
/// Applies a DT overlay on the base DT.
///
/// # Safety
///
- /// On failure, the library corrupts the DT and overlay so both must be discarded.
- pub unsafe fn apply_overlay<'a>(&'a mut self, overlay: &'a mut Fdt) -> Result<&'a mut Self> {
- let ret =
- // SAFETY: Both pointers are valid because they come from references, and fdt_overlay_apply
- // doesn't keep them after it returns. It may corrupt their contents if there is an error,
- // but that's our caller's responsibility.
- unsafe { libfdt_bindgen::fdt_overlay_apply(self.as_mut_ptr(), overlay.as_mut_ptr()) };
- fdt_err_expect_zero(ret)?;
+ /// As libfdt corrupts the input DT on failure, `self` should be discarded on error:
+ ///
+ /// let fdt = fdt.apply_overlay(overlay)?;
+ ///
+ /// Furthermore, `overlay` is _always_ corrupted by libfdt and will never refer to a valid
+ /// `Fdt` after this function returns and must therefore be discarded by the caller.
+ pub unsafe fn apply_overlay<'a>(&'a mut self, overlay: &mut Fdt) -> Result<&'a mut Self> {
+ // SAFETY: Our caller will properly discard overlay and/or self as needed.
+ unsafe { self.overlay_apply(overlay) }?;
+
Ok(self)
}
@@ -785,14 +775,6 @@
self.buffer.as_ptr().cast()
}
- fn as_mut_ptr(&mut self) -> *mut c_void {
- self.buffer.as_mut_ptr().cast::<_>()
- }
-
- fn capacity(&self) -> usize {
- self.buffer.len()
- }
-
fn header(&self) -> &libfdt_bindgen::fdt_header {
let p = self.as_ptr().cast();
// SAFETY: A valid FDT (verified by constructor) must contain a valid fdt_header.
diff --git a/libs/libfdt/src/libfdt.rs b/libs/libfdt/src/libfdt.rs
index 7737718..9c8d2b1 100644
--- a/libs/libfdt/src/libfdt.rs
+++ b/libs/libfdt/src/libfdt.rs
@@ -286,6 +286,13 @@
CStr::from_bytes_until_nul(bytes).map_err(|_| FdtError::Internal)
}
+
+ /// Safe wrapper around `fdt_open_into()` (C function).
+ fn open_into(&self, dest: &mut [u8]) -> Result<()> {
+ let fdt = self.as_fdt_slice().as_ptr().cast();
+
+ open_into(fdt, dest)
+ }
}
/// Wrapper for the read-write libfdt.h functions.
@@ -423,6 +430,48 @@
fdt_err_expect_zero(ret)
}
+
+ /// Safe and aliasing-compatible wrapper around `fdt_open_into()` (C function).
+ ///
+ /// The C API allows both input (`const void*`) and output (`void *`) to point to the same
+ /// memory region but the borrow checker would reject an API such as
+ ///
+ /// self.open_into(&mut self.buffer)
+ ///
+ /// so this wrapper is provided to implement such a common aliasing case.
+ fn open_into_self(&mut self) -> Result<()> {
+ let fdt = self.as_fdt_slice_mut();
+
+ open_into(fdt.as_ptr().cast(), fdt)
+ }
+
+ /// Safe wrapper around `fdt_pack()` (C function).
+ fn pack(&mut self) -> Result<()> {
+ let fdt = self.as_fdt_slice_mut().as_mut_ptr().cast();
+ // SAFETY: Accesses (R/W) are constrained to the DT totalsize (validated by ctor).
+ let ret = unsafe { libfdt_bindgen::fdt_pack(fdt) };
+
+ fdt_err_expect_zero(ret)
+ }
+
+ /// Wrapper around `fdt_overlay_apply()` (C function).
+ ///
+ /// # Safety
+ ///
+ /// This function safely wraps the C function call but is unsafe because the caller must
+ ///
+ /// - discard `overlay` as a &LibfdtMut because libfdt corrupts its header before returning;
+ /// - on error, discard `self` as a &LibfdtMut for the same reason.
+ unsafe fn overlay_apply(&mut self, overlay: &mut Self) -> Result<()> {
+ let fdt = self.as_fdt_slice_mut().as_mut_ptr().cast();
+ let overlay = overlay.as_fdt_slice_mut().as_mut_ptr().cast();
+ // SAFETY: Both pointers are valid because they come from references, and fdt_overlay_apply
+ // doesn't keep them after it returns. It may corrupt their contents if there is an error,
+ // but that's our caller's responsibility.
+ let ret = unsafe { libfdt_bindgen::fdt_overlay_apply(fdt, overlay) };
+
+ fdt_err_expect_zero(ret)
+ }
}
pub(crate) fn get_slice_at_ptr(s: &[u8], p: *const u8, len: usize) -> Option<&[u8]> {
@@ -449,6 +498,19 @@
})
}
+fn open_into(fdt: *const u8, dest: &mut [u8]) -> Result<()> {
+ let fdt = fdt.cast();
+ let len = dest.len().try_into().map_err(|_| FdtError::Internal)?;
+ let dest = dest.as_mut_ptr().cast();
+ // SAFETY: Reads the whole fdt slice (based on the validated totalsize) and, if it fits, copies
+ // it to the (properly mutable) dest buffer of size len. On success, the resulting dest
+ // contains a valid DT with the nodes and properties of the original one but of a different
+ // size, reflected in its fdt_header::totalsize.
+ let ret = unsafe { libfdt_bindgen::fdt_open_into(fdt, dest, len) };
+
+ fdt_err_expect_zero(ret)
+}
+
// 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