libfdt: Create Rust wrapper

libfdt is the industry standard library for parsing and generating
DeviceTree format. Create a wrapper around it for use in bare-metal Rust
projects. The low-level functions are not exposed directly but rather
wrapper by an Fdt object that exposes information parsed from the DT
using high-level constructs.

Only parsing of '/memory' entries is implemented for now and tested in
the vmbase example kernel. Other information and FDT generation will be
added at a later time.

Bug: 255521657
Test: atest vmbase_example.integration_test
Change-Id: If095ff3c4534d18bdf8ee5ebb072dde7a6e6efab
diff --git a/libs/libfdt/Android.bp b/libs/libfdt/Android.bp
new file mode 100644
index 0000000..72399b0
--- /dev/null
+++ b/libs/libfdt/Android.bp
@@ -0,0 +1,45 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_bindgen {
+    name: "liblibfdt_bindgen",
+    crate_name: "libfdt_bindgen",
+    wrapper_src: "bindgen/fdt.h",
+    source_stem: "bindings",
+    bindgen_flags: [
+        "--size_t-is-usize",
+        "--allowlist-type=fdt_.*",
+        "--allowlist-function=fdt_.*",
+        "--allowlist-var=FDT_.*",
+        "--use-core",
+        "--raw-line=#![no_std]",
+        "--ctypes-prefix=core::ffi",
+    ],
+    static_libs: [
+        "libfdt",
+    ],
+    apex_available: ["com.android.virt"],
+}
+
+rust_library_rlib {
+    name: "liblibfdt",
+    crate_name: "libfdt",
+    srcs: [
+        "src/lib.rs",
+        ":liblibfdt_bindgen",
+    ],
+    edition: "2021",
+    no_stdlibs: true,
+    prefer_rlib: true,
+    stdlibs: [
+        "libcore.rust_sysroot",
+    ],
+    rustlibs: [
+        "liblibfdt_bindgen",
+    ],
+    whole_static_libs: [
+        "libfdt",
+    ],
+    apex_available: ["com.android.virt"],
+}
diff --git a/libs/libfdt/bindgen/fdt.h b/libs/libfdt/bindgen/fdt.h
new file mode 100644
index 0000000..16f2784
--- /dev/null
+++ b/libs/libfdt/bindgen/fdt.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <libfdt.h>
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
new file mode 100644
index 0000000..d15f3c4
--- /dev/null
+++ b/libs/libfdt/src/lib.rs
@@ -0,0 +1,447 @@
+// Copyright 2022, 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.
+
+//! Wrapper around libfdt library. Provides parsing/generating functionality
+//! to a bare-metal environment.
+
+#![no_std]
+
+use core::ffi::{c_int, c_void, CStr};
+use core::fmt;
+use core::mem;
+use core::ops::Range;
+use core::result;
+use core::slice;
+
+/// Error type corresponding to libfdt error codes.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum FdtError {
+    /// FDT_ERR_NOTFOUND
+    NotFound,
+    /// FDT_ERR_EXISTS
+    Exists,
+    /// FDT_ERR_NOSPACE
+    NoSpace,
+    /// FDT_ERR_BADOFFSET
+    BadOffset,
+    /// FDT_ERR_BADPATH
+    BadPath,
+    /// FDT_ERR_BADPHANDLE
+    BadPhandle,
+    /// FDT_ERR_BADSTATE
+    BadState,
+    /// FDT_ERR_TRUNCATED
+    Truncated,
+    /// FDT_ERR_BADMAGIC
+    BadMagic,
+    /// FDT_ERR_BADVERSION
+    BadVersion,
+    /// FDT_ERR_BADSTRUCTURE
+    BadStructure,
+    /// FDT_ERR_BADLAYOUT
+    BadLayout,
+    /// FDT_ERR_INTERNAL
+    Internal,
+    /// FDT_ERR_BADNCELLS
+    BadNCells,
+    /// FDT_ERR_BADVALUE
+    BadValue,
+    /// FDT_ERR_BADOVERLAY
+    BadOverlay,
+    /// FDT_ERR_NOPHANDLES
+    NoPhandles,
+    /// FDT_ERR_BADFLAGS
+    BadFlags,
+    /// FDT_ERR_ALIGNMENT
+    Alignment,
+    /// Unexpected error code
+    Unknown(i32),
+}
+
+impl fmt::Display for FdtError {
+    /// Prints error messages from libfdt.h documentation.
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Self::NotFound => write!(f, "The requested node or property does not exist"),
+            Self::Exists => write!(f, "Attempted to create an existing node or property"),
+            Self::NoSpace => write!(f, "Insufficient buffer space to contain the expanded tree"),
+            Self::BadOffset => write!(f, "Structure block offset is out-of-bounds or invalid"),
+            Self::BadPath => write!(f, "Badly formatted path"),
+            Self::BadPhandle => write!(f, "Invalid phandle length or value"),
+            Self::BadState => write!(f, "Received incomplete device tree"),
+            Self::Truncated => write!(f, "Device tree or sub-block is improperly terminated"),
+            Self::BadMagic => write!(f, "Device tree header missing its magic number"),
+            Self::BadVersion => write!(f, "Device tree has a version which can't be handled"),
+            Self::BadStructure => write!(f, "Device tree has a corrupt structure block"),
+            Self::BadLayout => write!(f, "Device tree sub-blocks in unsupported order"),
+            Self::Internal => write!(f, "libfdt has failed an internal assertion"),
+            Self::BadNCells => write!(f, "Bad format or value of #address-cells or #size-cells"),
+            Self::BadValue => write!(f, "Unexpected property value"),
+            Self::BadOverlay => write!(f, "Overlay cannot be applied"),
+            Self::NoPhandles => write!(f, "Device tree doesn't have any phandle available anymore"),
+            Self::BadFlags => write!(f, "Invalid flag or invalid combination of flags"),
+            Self::Alignment => write!(f, "Device tree base address is not 8-byte aligned"),
+            Self::Unknown(e) => write!(f, "Unknown libfdt error '{e}'"),
+        }
+    }
+}
+
+/// Result type with FdtError enum.
+pub type Result<T> = result::Result<T, FdtError>;
+
+fn fdt_err(val: c_int) -> Result<c_int> {
+    if val >= 0 {
+        Ok(val)
+    } else {
+        Err(match -val as _ {
+            libfdt_bindgen::FDT_ERR_NOTFOUND => FdtError::NotFound,
+            libfdt_bindgen::FDT_ERR_EXISTS => FdtError::Exists,
+            libfdt_bindgen::FDT_ERR_NOSPACE => FdtError::NoSpace,
+            libfdt_bindgen::FDT_ERR_BADOFFSET => FdtError::BadOffset,
+            libfdt_bindgen::FDT_ERR_BADPATH => FdtError::BadPath,
+            libfdt_bindgen::FDT_ERR_BADPHANDLE => FdtError::BadPhandle,
+            libfdt_bindgen::FDT_ERR_BADSTATE => FdtError::BadState,
+            libfdt_bindgen::FDT_ERR_TRUNCATED => FdtError::Truncated,
+            libfdt_bindgen::FDT_ERR_BADMAGIC => FdtError::BadMagic,
+            libfdt_bindgen::FDT_ERR_BADVERSION => FdtError::BadVersion,
+            libfdt_bindgen::FDT_ERR_BADSTRUCTURE => FdtError::BadStructure,
+            libfdt_bindgen::FDT_ERR_BADLAYOUT => FdtError::BadLayout,
+            libfdt_bindgen::FDT_ERR_INTERNAL => FdtError::Internal,
+            libfdt_bindgen::FDT_ERR_BADNCELLS => FdtError::BadNCells,
+            libfdt_bindgen::FDT_ERR_BADVALUE => FdtError::BadValue,
+            libfdt_bindgen::FDT_ERR_BADOVERLAY => FdtError::BadOverlay,
+            libfdt_bindgen::FDT_ERR_NOPHANDLES => FdtError::NoPhandles,
+            libfdt_bindgen::FDT_ERR_BADFLAGS => FdtError::BadFlags,
+            libfdt_bindgen::FDT_ERR_ALIGNMENT => FdtError::Alignment,
+            _ => FdtError::Unknown(val),
+        })
+    }
+}
+
+fn fdt_err_expect_zero(val: c_int) -> Result<()> {
+    match fdt_err(val)? {
+        0 => Ok(()),
+        _ => Err(FdtError::Unknown(val)),
+    }
+}
+
+/// Value of a #address-cells property.
+#[derive(Copy, Clone, Debug)]
+enum AddrCells {
+    Single = 1,
+    Double = 2,
+}
+
+impl TryFrom<c_int> for AddrCells {
+    type Error = FdtError;
+
+    fn try_from(res: c_int) -> Result<Self> {
+        match fdt_err(res)? {
+            x if x == Self::Single as c_int => Ok(Self::Single),
+            x if x == Self::Double as c_int => Ok(Self::Double),
+            _ => Err(FdtError::BadNCells),
+        }
+    }
+}
+
+/// Value of a #size-cells property.
+#[derive(Copy, Clone, Debug)]
+enum SizeCells {
+    None = 0,
+    Single = 1,
+    Double = 2,
+}
+
+impl TryFrom<c_int> for SizeCells {
+    type Error = FdtError;
+
+    fn try_from(res: c_int) -> Result<Self> {
+        match fdt_err(res)? {
+            x if x == Self::None as c_int => Ok(Self::None),
+            x if x == Self::Single as c_int => Ok(Self::Single),
+            x if x == Self::Double as c_int => Ok(Self::Double),
+            _ => Err(FdtError::BadNCells),
+        }
+    }
+}
+
+/// Iterator over cells of a DT property.
+#[derive(Debug)]
+pub struct CellIterator<'a> {
+    chunks: slice::ChunksExact<'a, u8>,
+}
+
+impl<'a> CellIterator<'a> {
+    fn new(bytes: &'a [u8]) -> Self {
+        const CHUNK_SIZE: usize = mem::size_of::<<CellIterator as Iterator>::Item>();
+
+        Self { chunks: bytes.chunks_exact(CHUNK_SIZE) }
+    }
+}
+
+impl<'a> Iterator for CellIterator<'a> {
+    type Item = u32;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        Some(Self::Item::from_be_bytes(self.chunks.next()?.try_into().ok()?))
+    }
+}
+
+/// Iterator over a 'reg' property of a DT node.
+#[derive(Debug)]
+pub struct RegIterator<'a> {
+    cells: CellIterator<'a>,
+    addr_cells: AddrCells,
+    size_cells: SizeCells,
+}
+
+/// Represents a contiguous region within the address space defined by the parent bus.
+/// Commonly means the offsets and lengths of MMIO blocks, but may have a different meaning on some
+/// bus types. Addresses in the address space defined by the root node are CPU real addresses.
+#[derive(Copy, Clone, Debug)]
+pub struct Reg<T> {
+    /// Base address of the region.
+    pub addr: T,
+    /// Size of the region (optional).
+    pub size: Option<T>,
+}
+
+impl<'a> RegIterator<'a> {
+    fn new(cells: CellIterator<'a>, addr_cells: AddrCells, size_cells: SizeCells) -> Self {
+        Self { cells, addr_cells, size_cells }
+    }
+}
+
+impl<'a> Iterator for RegIterator<'a> {
+    type Item = Reg<u64>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let make_double = |a, b| (u64::from(a) << 32) | u64::from(b);
+
+        let addr = match self.addr_cells {
+            AddrCells::Single => self.cells.next()?.into(),
+            AddrCells::Double => make_double(self.cells.next()?, self.cells.next()?),
+        };
+        // If the parent node specifies a value of 0 for #size-cells, 'size' shall be omitted.
+        let size = match self.size_cells {
+            SizeCells::None => None,
+            SizeCells::Single => Some(self.cells.next()?.into()),
+            SizeCells::Double => Some(make_double(self.cells.next()?, self.cells.next()?)),
+        };
+
+        Some(Self::Item { addr, size })
+    }
+}
+
+/// Iterator over the address ranges defined by the /memory/ node.
+#[derive(Debug)]
+pub struct MemRegIterator<'a> {
+    reg: RegIterator<'a>,
+}
+
+impl<'a> MemRegIterator<'a> {
+    fn new(reg: RegIterator<'a>) -> Result<Self> {
+        Ok(Self { reg })
+    }
+}
+
+impl<'a> Iterator for MemRegIterator<'a> {
+    type Item = Range<usize>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let next = self.reg.next()?;
+        let addr = usize::try_from(next.addr).ok()?;
+        let size = usize::try_from(next.size?).ok()?;
+
+        Some(addr..addr.checked_add(size)?)
+    }
+}
+
+/// DT node.
+pub struct FdtNode<'a> {
+    fdt: &'a Fdt,
+    offset: c_int,
+}
+
+impl<'a> FdtNode<'a> {
+    /// Find parent node.
+    pub fn parent(&self) -> Result<Self> {
+        // SAFETY - Accesses (read-only) are constrained to the DT totalsize.
+        let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
+
+        Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
+    }
+
+    /// Retrieve the standard (deprecated) device_type <string> property.
+    pub fn device_type(&self) -> Result<&CStr> {
+        self.getprop_str(CStr::from_bytes_with_nul(b"device_type\0").unwrap())
+    }
+
+    /// Retrieve the standard reg <prop-encoded-array> property.
+    pub fn reg(&self) -> Result<RegIterator<'a>> {
+        let parent = self.parent()?;
+
+        let addr_cells = parent.address_cells()?;
+        let size_cells = parent.size_cells()?;
+        let cells = self.getprop_cells(CStr::from_bytes_with_nul(b"reg\0").unwrap())?;
+
+        Ok(RegIterator::new(cells, addr_cells, size_cells))
+    }
+
+    /// Retrieve the value of a given <string> property.
+    pub fn getprop_str(&self, name: &CStr) -> Result<&CStr> {
+        CStr::from_bytes_with_nul(self.getprop(name)?).map_err(|_| FdtError::BadValue)
+    }
+
+    /// Retrieve the value of a given property as an array of cells.
+    pub fn getprop_cells(&self, name: &CStr) -> Result<CellIterator<'a>> {
+        Ok(CellIterator::new(self.getprop(name)?))
+    }
+
+    /// Retrieve the value of a given <u32> property.
+    pub fn getprop_u32(&self, name: &CStr) -> Result<u32> {
+        let prop = self.getprop(name)?.try_into().map_err(|_| FdtError::BadValue)?;
+        Ok(u32::from_be_bytes(prop))
+    }
+
+    /// Retrieve the value of a given <u64> property.
+    pub fn getprop_u64(&self, name: &CStr) -> Result<u64> {
+        let prop = self.getprop(name)?.try_into().map_err(|_| FdtError::BadValue)?;
+        Ok(u64::from_be_bytes(prop))
+    }
+
+    /// Retrieve the value of a given property.
+    pub fn getprop(&self, name: &CStr) -> Result<&'a [u8]> {
+        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(
+                self.fdt.as_ptr(),
+                self.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;
+        if prop.is_null() {
+            return fdt_err(len).and(Err(FdtError::Internal));
+        }
+        let len = usize::try_from(fdt_err(len)?).map_err(|_| FdtError::Internal)?;
+        let base =
+            (prop as usize).checked_sub(self.fdt.as_ptr() as usize).ok_or(FdtError::Internal)?;
+
+        self.fdt.bytes.get(base..(base + len)).ok_or(FdtError::Internal)
+    }
+
+    /// Get reference to the containing device tree.
+    pub fn fdt(&self) -> &Fdt {
+        self.fdt
+    }
+
+    fn address_cells(&self) -> Result<AddrCells> {
+        // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor).
+        unsafe { libfdt_bindgen::fdt_address_cells(self.fdt.as_ptr(), self.offset) }
+            .try_into()
+            .map_err(|_| FdtError::Internal)
+    }
+
+    fn size_cells(&self) -> Result<SizeCells> {
+        // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor).
+        unsafe { libfdt_bindgen::fdt_size_cells(self.fdt.as_ptr(), self.offset) }
+            .try_into()
+            .map_err(|_| FdtError::Internal)
+    }
+}
+
+/// Wrapper around low-level read-only libfdt functions.
+#[repr(transparent)]
+pub struct Fdt {
+    bytes: [u8],
+}
+
+impl Fdt {
+    /// Wraps a slice containing a Flattened Device Tree.
+    ///
+    /// Fails if the FDT does not pass validation.
+    pub fn from_slice(fdt: &[u8]) -> Result<&Self> {
+        // SAFETY - The FDT will be validated before it is returned.
+        let fdt = unsafe { Self::unchecked_from_slice(fdt) };
+        fdt.check_full()?;
+        Ok(fdt)
+    }
+
+    /// Wraps a slice containing a Flattened Device Tree.
+    ///
+    /// # Safety
+    ///
+    /// The returned FDT might be invalid, only use on slices containing a valid DT.
+    pub unsafe fn unchecked_from_slice(fdt: &[u8]) -> &Self {
+        mem::transmute::<&[u8], &Self>(fdt)
+    }
+
+    /// Return an iterator of memory banks specified the "/memory" node.
+    ///
+    /// NOTE: This does not support individual "/memory@XXXX" banks.
+    pub fn memory(&self) -> Result<MemRegIterator> {
+        let memory = CStr::from_bytes_with_nul(b"/memory\0").unwrap();
+        let device_type = CStr::from_bytes_with_nul(b"memory\0").unwrap();
+
+        let node = self.node(memory)?;
+        if node.device_type()? != device_type {
+            return Err(FdtError::BadValue);
+        }
+
+        MemRegIterator::new(node.reg()?)
+    }
+
+    /// Retrieve the standard /chosen node.
+    pub fn chosen(&self) -> Result<FdtNode> {
+        self.node(CStr::from_bytes_with_nul(b"/chosen\0").unwrap())
+    }
+
+    /// Find a tree node by its full path.
+    pub fn node(&self, path: &CStr) -> Result<FdtNode> {
+        let offset = self.path_offset(path)?;
+        Ok(FdtNode { fdt: self, offset })
+    }
+
+    fn path_offset(&self, path: &CStr) -> Result<c_int> {
+        let len = path.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?;
+        // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor) and the
+        // function respects the passed number of characters.
+        let ret = unsafe {
+            // *_namelen functions don't include the trailing nul terminator in 'len'.
+            libfdt_bindgen::fdt_path_offset_namelen(self.as_ptr(), path.as_ptr(), len)
+        };
+
+        fdt_err(ret)
+    }
+
+    fn check_full(&self) -> Result<()> {
+        let len = self.bytes.len();
+        // SAFETY - Only performs read accesses within the limits of the slice. If successful, this
+        // call guarantees to other unsafe calls that the header contains a valid totalsize (w.r.t.
+        // 'len' i.e. the self.fdt slice) that those C functions can use to perform bounds
+        // checking. The library doesn't maintain an internal state (such as pointers) between
+        // calls as it expects the client code to keep track of the objects (DT, nodes, ...).
+        let ret = unsafe { libfdt_bindgen::fdt_check_full(self.as_ptr(), len) };
+        fdt_err_expect_zero(ret)
+    }
+
+    fn as_ptr(&self) -> *const c_void {
+        self as *const _ as *const c_void
+    }
+}
diff --git a/vmbase/example/Android.bp b/vmbase/example/Android.bp
index 0f1e66a..505de6b 100644
--- a/vmbase/example/Android.bp
+++ b/vmbase/example/Android.bp
@@ -12,9 +12,13 @@
         "libaarch64_paging",
         "libbuddy_system_allocator",
         "libdice_nostd",
+        "liblibfdt",
         "liblog_rust_nostd",
         "libvmbase",
     ],
+    static_libs: [
+        "libarm-optimized-routines-mem",
+    ],
     apex_available: ["com.android.virt"],
 }
 
diff --git a/vmbase/example/src/main.rs b/vmbase/example/src/main.rs
index 03f0603..3d74168 100644
--- a/vmbase/example/src/main.rs
+++ b/vmbase/example/src/main.rs
@@ -27,9 +27,13 @@
     bionic_tls, dtb_range, print_addresses, rodata_range, stack_chk_guard, text_range,
     writable_region, DEVICE_REGION,
 };
-use aarch64_paging::{idmap::IdMap, paging::Attributes};
+use aarch64_paging::{
+    idmap::IdMap,
+    paging::{Attributes, MemoryRegion},
+};
 use alloc::{vec, vec::Vec};
 use buddy_system_allocator::LockedHeap;
+use libfdt::Fdt;
 use log::{info, LevelFilter};
 use vmbase::{logger, main, println};
 
@@ -57,6 +61,7 @@
     assert_eq!(arg0, dtb_range().start.0 as u64);
     check_data();
     check_stack_guard();
+    check_fdt();
 
     unsafe {
         HEAP_ALLOCATOR.lock().init(HEAP.as_mut_ptr() as usize, HEAP.len());
@@ -138,6 +143,20 @@
     info!("Data looks good");
 }
 
+fn check_fdt() {
+    info!("Checking FDT...");
+    let fdt = MemoryRegion::from(layout::dtb_range());
+    let fdt = unsafe { core::slice::from_raw_parts_mut(fdt.start().0 as *mut u8, fdt.len()) };
+
+    let reader = Fdt::from_slice(fdt).unwrap();
+    info!("FDT passed verification.");
+    for reg in reader.memory().unwrap() {
+        info!("memory @ {reg:#x?}");
+    }
+
+    info!("FDT checks done.");
+}
+
 fn check_alloc() {
     info!("Allocating a Vec...");
     let mut vector: Vec<u32> = vec![1, 2, 3, 4];