Add option to dump device tree blob in VM config

As part of adding tests for backwards compatibility check, dumping the
device tree will be essential in validating the state of the VM.

Bug: 360388014
Test: m
Change-Id: I1f0835590d1e668c1737abe58e1cb7163aa6759f
diff --git a/android/virtmgr/src/aidl.rs b/android/virtmgr/src/aidl.rs
index 87fb611..4b203d6 100644
--- a/android/virtmgr/src/aidl.rs
+++ b/android/virtmgr/src/aidl.rs
@@ -221,6 +221,7 @@
         console_out_fd: Option<&ParcelFileDescriptor>,
         console_in_fd: Option<&ParcelFileDescriptor>,
         log_fd: Option<&ParcelFileDescriptor>,
+        dump_dt_fd: Option<&ParcelFileDescriptor>,
     ) -> binder::Result<Strong<dyn IVirtualMachine>> {
         let mut is_protected = false;
         let ret = self.create_vm_internal(
@@ -229,6 +230,7 @@
             console_in_fd,
             log_fd,
             &mut is_protected,
+            dump_dt_fd,
         );
         write_vm_creation_stats(config, is_protected, &ret);
         ret
@@ -485,6 +487,7 @@
         console_in_fd: Option<&ParcelFileDescriptor>,
         log_fd: Option<&ParcelFileDescriptor>,
         is_protected: &mut bool,
+        dump_dt_fd: Option<&ParcelFileDescriptor>,
     ) -> binder::Result<Strong<dyn IVirtualMachine>> {
         let requester_uid = get_calling_uid();
         let requester_debug_pid = get_calling_pid();
@@ -527,6 +530,7 @@
             clone_or_prepare_logger_fd(console_out_fd, format!("Console({})", cid))?;
         let console_in_fd = console_in_fd.map(clone_file).transpose()?;
         let log_fd = clone_or_prepare_logger_fd(log_fd, format!("Log({})", cid))?;
+        let dump_dt_fd = dump_dt_fd.map(clone_file).transpose()?;
 
         // Counter to generate unique IDs for temporary image files.
         let mut next_temporary_image_id = 0;
@@ -744,6 +748,7 @@
             audio_config,
             no_balloon: config.noBalloon,
             usb_config,
+            dump_dt_fd,
         };
         let instance = Arc::new(
             VmInstance::new(
diff --git a/android/virtmgr/src/crosvm.rs b/android/virtmgr/src/crosvm.rs
index b2be736..25271f8 100644
--- a/android/virtmgr/src/crosvm.rs
+++ b/android/virtmgr/src/crosvm.rs
@@ -135,6 +135,7 @@
     pub audio_config: Option<AudioConfig>,
     pub no_balloon: bool,
     pub usb_config: UsbConfig,
+    pub dump_dt_fd: Option<File>,
 }
 
 #[derive(Debug)]
@@ -985,6 +986,11 @@
     // Keep track of what file descriptors should be mapped to the crosvm process.
     let mut preserved_fds = config.indirect_files.into_iter().map(|f| f.into()).collect();
 
+    if let Some(dump_dt_fd) = config.dump_dt_fd {
+        let dump_dt_fd = add_preserved_fd(&mut preserved_fds, dump_dt_fd);
+        command.arg("--dump-device-tree-blob").arg(dump_dt_fd);
+    }
+
     // Setup the serial devices.
     // 1. uart device: used as the output device by bootloaders and as early console by linux
     // 2. uart device: used to report the reason for the VM failing.
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
index 234d8d0..0c3f6b7 100644
--- a/android/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
@@ -35,11 +35,14 @@
      * `consoleInFd` is provided then console input to the VM will be read from it. If `osLogFd` is
      * provided then the OS-level logs will be sent to it. `osLogFd` is supported only when the OS
      * running in the VM has the logging system. In case of Microdroid, the logging system is logd.
+     * `dumpDtFd` is the file where to dump the VM's device tree. It is only used in
+     * debugging/testing.
      */
     IVirtualMachine createVm(in VirtualMachineConfig config,
             in @nullable ParcelFileDescriptor consoleOutFd,
             in @nullable ParcelFileDescriptor consoleInFd,
-            in @nullable ParcelFileDescriptor osLogFd);
+            in @nullable ParcelFileDescriptor osLogFd,
+            in @nullable ParcelFileDescriptor dumpDtFd);
 
     /**
      * Allocate an instance_id to the (newly created) VM.
diff --git a/android/vm/src/main.rs b/android/vm/src/main.rs
index 609bbdf..81ca8fa 100644
--- a/android/vm/src/main.rs
+++ b/android/vm/src/main.rs
@@ -114,6 +114,10 @@
     #[cfg(debuggable_vms_improvements)]
     #[arg(long)]
     enable_earlycon: bool,
+
+    /// Path to file to dump VM device tree.
+    #[arg(long)]
+    dump_device_tree: Option<PathBuf>,
 }
 
 impl DebugConfig {
diff --git a/android/vm/src/run.rs b/android/vm/src/run.rs
index 823546f..0e1f4cc 100644
--- a/android/vm/src/run.rs
+++ b/android/vm/src/run.rs
@@ -203,6 +203,7 @@
         config.debug.console.as_ref().map(|p| p.as_ref()),
         config.debug.console_in.as_ref().map(|p| p.as_ref()),
         config.debug.log.as_ref().map(|p| p.as_ref()),
+        config.debug.dump_device_tree.as_ref().map(|p| p.as_ref()),
     )
 }
 
@@ -284,6 +285,7 @@
         config.debug.console.as_ref().map(|p| p.as_ref()),
         config.debug.console_in.as_ref().map(|p| p.as_ref()),
         config.debug.log.as_ref().map(|p| p.as_ref()),
+        config.debug.dump_device_tree.as_ref().map(|p| p.as_ref()),
     )
 }
 
@@ -306,6 +308,7 @@
     console_out_path: Option<&Path>,
     console_in_path: Option<&Path>,
     log_path: Option<&Path>,
+    dump_device_tree: Option<&Path>,
 ) -> Result<(), Error> {
     let console_out = if let Some(console_out_path) = console_out_path {
         Some(File::create(console_out_path).with_context(|| {
@@ -330,9 +333,17 @@
     } else {
         Some(duplicate_fd(io::stdout())?)
     };
+    let dump_dt = if let Some(dump_device_tree) = dump_device_tree {
+        Some(File::create(dump_device_tree).with_context(|| {
+            format!("Failed to open file to dump device tree: {:?}", dump_device_tree)
+        })?)
+    } else {
+        None
+    };
     let callback = Box::new(Callback {});
-    let vm = VmInstance::create(service, config, console_out, console_in, log, Some(callback))
-        .context("Failed to create VM")?;
+    let vm =
+        VmInstance::create(service, config, console_out, console_in, log, dump_dt, Some(callback))
+            .context("Failed to create VM")?;
     vm.start().context("Failed to start VM")?;
 
     let debug_level = get_debug_level(config).unwrap_or(DebugLevel::NONE);
diff --git a/android/vm_demo_native/main.cpp b/android/vm_demo_native/main.cpp
index bc42036..d7ff02e 100644
--- a/android/vm_demo_native/main.cpp
+++ b/android/vm_demo_native/main.cpp
@@ -226,8 +226,10 @@
     ScopedFileDescriptor console_out_fd(fcntl(fileno(stdout), F_DUPFD_CLOEXEC));
     ScopedFileDescriptor console_in_fd(fcntl(fileno(stdin), F_DUPFD_CLOEXEC));
     ScopedFileDescriptor log_fd(fcntl(fileno(stdout), F_DUPFD_CLOEXEC));
+    ScopedFileDescriptor dump_dt_fd(-1);
 
-    ScopedAStatus ret = service.createVm(config, console_out_fd, console_in_fd, log_fd, &vm);
+    ScopedAStatus ret =
+            service.createVm(config, console_out_fd, console_in_fd, log_fd, dump_dt_fd, &vm);
     if (!ret.isOk()) {
         return Error() << "Failed to create VM";
     }
diff --git a/guest/rialto/tests/test.rs b/guest/rialto/tests/test.rs
index 582b69e..7ec5647 100644
--- a/guest/rialto/tests/test.rs
+++ b/guest/rialto/tests/test.rs
@@ -335,6 +335,14 @@
     let virtmgr = vmclient::VirtualizationService::new().context("Failed to spawn VirtMgr")?;
     let service = virtmgr.connect().context("Failed to connect to VirtMgr")?;
     info!("Connected to VirtMgr for service VM");
-    VmInstance::create(service.as_ref(), &config, console, /* consoleIn */ None, log, None)
-        .context("Failed to create VM")
+    VmInstance::create(
+        service.as_ref(),
+        &config,
+        console,
+        /* consoleIn */ None,
+        log,
+        /* dump_dt */ None,
+        None,
+    )
+    .context("Failed to create VM")
 }
diff --git a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachine.java b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachine.java
index 3b16a8a..b278610 100644
--- a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachine.java
+++ b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachine.java
@@ -1578,7 +1578,8 @@
                                 : createVirtualMachineConfigForAppFrom(vmConfig, service);
 
                 mVirtualMachine =
-                        service.createVm(vmConfigParcel, consoleOutFd, consoleInFd, mLogWriter);
+                        service.createVm(
+                                vmConfigParcel, consoleOutFd, consoleInFd, mLogWriter, null);
                 mVirtualMachine.registerCallback(new CallbackTranslator(service));
                 if (mMemoryManagementCallbacks != null) {
                     mContext.registerComponentCallbacks(mMemoryManagementCallbacks);
diff --git a/libs/libcompos_common/compos_client.rs b/libs/libcompos_common/compos_client.rs
index 107f8d0..316eaa9 100644
--- a/libs/libcompos_common/compos_client.rs
+++ b/libs/libcompos_common/compos_client.rs
@@ -152,6 +152,7 @@
             console_fd,
             /* console_in_fd */ None,
             log_fd,
+            /* dump_dt */ None,
             Some(callback),
         )
         .context("Failed to create VM")?;
diff --git a/libs/libservice_vm_manager/src/lib.rs b/libs/libservice_vm_manager/src/lib.rs
index d7b4dd6..0f322bb 100644
--- a/libs/libservice_vm_manager/src/lib.rs
+++ b/libs/libservice_vm_manager/src/lib.rs
@@ -244,8 +244,9 @@
     let console_out = Some(android_log_fd()?);
     let console_in = None;
     let log = Some(android_log_fd()?);
+    let dump_dt = None;
     let callback = None;
-    VmInstance::create(service.as_ref(), &config, console_out, console_in, log, callback)
+    VmInstance::create(service.as_ref(), &config, console_out, console_in, log, dump_dt, callback)
         .context("Failed to create service VM")
 }
 
diff --git a/libs/libvmclient/src/lib.rs b/libs/libvmclient/src/lib.rs
index ce7d5a5..13630c0 100644
--- a/libs/libvmclient/src/lib.rs
+++ b/libs/libvmclient/src/lib.rs
@@ -208,14 +208,21 @@
         console_out: Option<File>,
         console_in: Option<File>,
         log: Option<File>,
+        dump_dt: Option<File>,
         callback: Option<Box<dyn VmCallback + Send + Sync>>,
     ) -> BinderResult<Self> {
         let console_out = console_out.map(ParcelFileDescriptor::new);
         let console_in = console_in.map(ParcelFileDescriptor::new);
         let log = log.map(ParcelFileDescriptor::new);
+        let dump_dt = dump_dt.map(ParcelFileDescriptor::new);
 
-        let vm =
-            service.createVm(config, console_out.as_ref(), console_in.as_ref(), log.as_ref())?;
+        let vm = service.createVm(
+            config,
+            console_out.as_ref(),
+            console_in.as_ref(),
+            log.as_ref(),
+            dump_dt.as_ref(),
+        )?;
 
         let cid = vm.getCid()?;
 
diff --git a/microfuchsia/microfuchsiad/src/instance_starter.rs b/microfuchsia/microfuchsiad/src/instance_starter.rs
index 15fcc06..61a024f 100644
--- a/microfuchsia/microfuchsiad/src/instance_starter.rs
+++ b/microfuchsia/microfuchsiad/src/instance_starter.rs
@@ -90,6 +90,7 @@
             console_out,
             console_in,
             /* log= */ None,
+            /* dump_dt= */ None,
             None,
         )
         .context("Failed to create VM")?;
diff --git a/tests/vm_accessor/accessor/src/run.rs b/tests/vm_accessor/accessor/src/run.rs
index 932baab..6dcc507 100644
--- a/tests/vm_accessor/accessor/src/run.rs
+++ b/tests/vm_accessor/accessor/src/run.rs
@@ -128,6 +128,7 @@
         Some(android_log_fd()?), /* console_out */
         None,                    /* console_in */
         Some(android_log_fd()?), /* log */
+        None,                    /* dump_dt */
         Some(Box::new(Callback {})),
     )
     .context("Failed to create VM")?;
diff --git a/tests/vmbase_example/src/main.rs b/tests/vmbase_example/src/main.rs
index e0563b7..34a2b0b 100644
--- a/tests/vmbase_example/src/main.rs
+++ b/tests/vmbase_example/src/main.rs
@@ -119,6 +119,7 @@
         Some(console),
         /* consoleIn */ None,
         Some(log_writer),
+        /* dump_dt */ None,
         None,
     )
     .context("Failed to create VM")?;