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