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)]