pvmfw: Apply VM DTBO

This CL only applies assigned VM DTBO for the simplest case, which
iommu, phandle, nor aliases aren't involved.

Next CLs will handle following cases:
  - Apply iommu. Platform DT will be also updated to have pre-populated
    pvmiommu node
  - Validate patched values (reg, iommu, ..)
  - Handle __local_fixup__, __fixups__ (i.e. handle phandle in VM DTBO)
  - Handle /alias in VM DTBO
  - ...

Bug: 277993056
Test: atest libpvmfw.device_assignment.test, launch protected VM
Change-Id: I4e4aea0885da925ae419921d729380a1d71707e0
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index 9c929a9..ed73bc9 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -83,7 +83,12 @@
 }
 
 impl<'a> MemorySlices<'a> {
-    fn new(fdt: usize, kernel: usize, kernel_size: usize) -> Result<Self, RebootReason> {
+    fn new(
+        fdt: usize,
+        kernel: usize,
+        kernel_size: usize,
+        vm_dtbo: Option<&mut [u8]>,
+    ) -> Result<Self, RebootReason> {
         let fdt_size = NonZeroUsize::new(crosvm::FDT_MAX_SIZE).unwrap();
         // TODO - Only map the FDT as read-only, until we modify it right before jump_to_payload()
         // e.g. by generating a DTBO for a template DT in main() and, on return, re-map DT as RW,
@@ -95,12 +100,12 @@
 
         // SAFETY: The tracker validated the range to be in main memory, mapped, and not overlap.
         let fdt = unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) };
+
+        let info = fdt::sanitize_device_tree(fdt, vm_dtbo)?;
         let fdt = libfdt::Fdt::from_mut_slice(fdt).map_err(|e| {
-            error!("Failed to spawn the FDT wrapper: {e}");
+            error!("Failed to load sanitized FDT: {e}");
             RebootReason::InvalidFdt
         })?;
-
-        let info = fdt::sanitize_device_tree(fdt)?;
         debug!("Fdt passed validation!");
 
         let memory_range = info.memory_range;
@@ -207,7 +212,7 @@
         RebootReason::InvalidConfig
     })?;
 
-    let (bcc_slice, debug_policy) = appended.get_entries();
+    let (bcc_slice, debug_policy, vm_dtbo) = appended.get_entries();
 
     // Up to this point, we were using the built-in static (from .rodata) page tables.
     MEMORY.lock().replace(MemoryTracker::new(
@@ -217,7 +222,7 @@
         Some(memory::appended_payload_range()),
     ));
 
-    let slices = MemorySlices::new(fdt, payload, payload_size)?;
+    let slices = MemorySlices::new(fdt, payload, payload_size, vm_dtbo)?;
 
     // This wrapper allows main() to be blissfully ignorant of platform details.
     let next_bcc = crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc_slice, debug_policy)?;
@@ -427,10 +432,10 @@
         }
     }
 
-    fn get_entries(&mut self) -> (&mut [u8], Option<&mut [u8]>) {
+    fn get_entries(&mut self) -> (&mut [u8], Option<&mut [u8]>, Option<&mut [u8]>) {
         match self {
             Self::Config(ref mut cfg) => cfg.get_entries(),
-            Self::LegacyBcc(ref mut bcc) => (bcc, None),
+            Self::LegacyBcc(ref mut bcc) => (bcc, None, None),
         }
     }
 }