libfdt: Use zerocopy for safe fdt_header wrapper
Provide a way to get a safe &(mut) fdt_header from any <T: Libfdt(Mut)>.
Instead of manually doing the conversion from the BE-encoded DT fields,
use zerocopy to implement a zero-cost wrapper where each field has a
getter and setter function that transparently handles the endianness.
Combine those to remove the need for unsafe code in Fdt.
Test: m pvmfw
Test: atest liblibfdt.integration_test
Change-Id: I5ccaf49b48c13855d80ad386d41be3abbf3fdfe8
diff --git a/libs/libfdt/Android.bp b/libs/libfdt/Android.bp
index b5f7471..1bb5692 100644
--- a/libs/libfdt/Android.bp
+++ b/libs/libfdt/Android.bp
@@ -40,6 +40,7 @@
"libcstr",
"liblibfdt_bindgen",
"libmemoffset_nostd",
+ "libstatic_assertions",
"libzerocopy_nostd",
],
whole_static_libs: [
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index 1961232..6328f4c 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -27,7 +27,7 @@
PropertyIterator, RangesIterator, Reg, RegIterator, SubnodeIterator,
};
pub use result::{FdtError, Result};
-pub use safe_types::{NodeOffset, Phandle, PropOffset, StringOffset};
+pub use safe_types::{FdtHeader, NodeOffset, Phandle, PropOffset, StringOffset};
use core::ffi::{c_void, CStr};
use core::ops::Range;
@@ -778,13 +778,14 @@
self.buffer.as_ptr().cast()
}
- fn header(&self) -> &libfdt_bindgen::fdt_header {
- let p = self.as_ptr().cast();
+ fn header(&self) -> &FdtHeader {
+ let p = self.as_ptr().cast::<libfdt_bindgen::fdt_header>();
// SAFETY: A valid FDT (verified by constructor) must contain a valid fdt_header.
- unsafe { &*p }
+ let header = unsafe { &*p };
+ header.as_ref()
}
fn totalsize(&self) -> usize {
- u32::from_be(self.header().totalsize) as usize
+ self.header().totalsize.get().try_into().unwrap()
}
}
diff --git a/libs/libfdt/src/safe_types.rs b/libs/libfdt/src/safe_types.rs
index 5ec7f7c..3848542 100644
--- a/libs/libfdt/src/safe_types.rs
+++ b/libs/libfdt/src/safe_types.rs
@@ -19,6 +19,67 @@
use crate::result::FdtRawResult;
use crate::{FdtError, Result};
+use zerocopy::byteorder::big_endian;
+use zerocopy::{FromBytes, FromZeroes};
+
+macro_rules! assert_offset_eq {
+ // TODO(stable_feature(offset_of)): mem::offset_of
+ // TODO(const_feature(assert_eq)): assert_eq!()
+ ($t:ty, $u:ty, $id:ident) => {
+ static_assertions::const_assert_eq!(
+ memoffset::offset_of!($t, $id),
+ memoffset::offset_of!($u, $id),
+ );
+ };
+}
+
+/// Thin wrapper around `libfdt_bindgen::fdt_header` for transparent endianness handling.
+#[repr(C)]
+#[derive(Debug, FromZeroes, FromBytes)]
+pub struct FdtHeader {
+ /// magic word FDT_MAGIC
+ pub magic: big_endian::U32,
+ /// total size of DT block
+ pub totalsize: big_endian::U32,
+ /// offset to structure
+ pub off_dt_struct: big_endian::U32,
+ /// offset to strings
+ pub off_dt_strings: big_endian::U32,
+ /// offset to memory reserve map
+ pub off_mem_rsvmap: big_endian::U32,
+ /// format version
+ pub version: big_endian::U32,
+ /// last compatible version
+ pub last_comp_version: big_endian::U32,
+ /* version 2 fields below */
+ /// Which physical CPU id we're booting on
+ pub boot_cpuid_phys: big_endian::U32,
+ /* version 3 fields below */
+ /// size of the strings block
+ pub size_dt_strings: big_endian::U32,
+ /* version 17 fields below */
+ /// size of the structure block
+ pub size_dt_struct: big_endian::U32,
+}
+assert_offset_eq!(libfdt_bindgen::fdt_header, FdtHeader, magic);
+assert_offset_eq!(libfdt_bindgen::fdt_header, FdtHeader, totalsize);
+assert_offset_eq!(libfdt_bindgen::fdt_header, FdtHeader, off_dt_struct);
+assert_offset_eq!(libfdt_bindgen::fdt_header, FdtHeader, off_dt_strings);
+assert_offset_eq!(libfdt_bindgen::fdt_header, FdtHeader, off_mem_rsvmap);
+assert_offset_eq!(libfdt_bindgen::fdt_header, FdtHeader, version);
+assert_offset_eq!(libfdt_bindgen::fdt_header, FdtHeader, last_comp_version);
+assert_offset_eq!(libfdt_bindgen::fdt_header, FdtHeader, boot_cpuid_phys);
+assert_offset_eq!(libfdt_bindgen::fdt_header, FdtHeader, size_dt_strings);
+assert_offset_eq!(libfdt_bindgen::fdt_header, FdtHeader, size_dt_struct);
+
+impl AsRef<FdtHeader> for libfdt_bindgen::fdt_header {
+ fn as_ref(&self) -> &FdtHeader {
+ let ptr = self as *const _ as *const _;
+ // SAFETY: Types have the same layout (u32 and U32 have the same storage) and alignment.
+ unsafe { &*ptr }
+ }
+}
+
/// Wrapper guaranteed to contain a valid phandle.
#[repr(transparent)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]