virtmgr: Pass VM DTBO path to crosvm if devices assigned

crosvm needs to know the path of VM DTBO to be able to populate the
guest device tree. Add a new method on the global VirtualizationService
which creates the file in a temporary directory common to all VMs and
has the file populated by VfioHandler.

Client virtmgr instances can then request a read-only FD for the file
from VirtualizationService. They then pass it as a preserved FD to
crosvm.

Bug: 297313212
Test: vm run-microdroid and see crosvm argument
Change-Id: I5491b9d084a8e845c1ad82d5cfad45b3374bf495
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index 9a50776..2ba0e0e 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -116,6 +116,7 @@
     pub detect_hangup: bool,
     pub gdb_port: Option<NonZeroU16>,
     pub vfio_devices: Vec<VfioDevice>,
+    pub dtbo: Option<File>,
     pub dtbo_vendor: Option<File>,
 }
 
@@ -723,11 +724,23 @@
     }
 }
 
-fn append_platform_devices(command: &mut Command, config: &CrosvmConfig) -> Result<(), Error> {
+fn append_platform_devices(
+    command: &mut Command,
+    preserved_fds: &mut Vec<RawFd>,
+    config: &CrosvmConfig,
+) -> Result<(), Error> {
+    if config.vfio_devices.is_empty() {
+        return Ok(());
+    }
+
+    let Some(dtbo) = &config.dtbo else {
+        bail!("VFIO devices assigned but no DTBO available");
+    };
+    command.arg(format!("--device-tree-overlay={},filter", add_preserved_fd(preserved_fds, dtbo)));
+
     for device in &config.vfio_devices {
         command.arg(vfio_argument_for_platform_device(device)?);
     }
-    // TODO(b/291192693): add dtbo to command line when assigned device is not empty.
     Ok(())
 }
 
@@ -889,7 +902,7 @@
 
     // TODO(b/285855436): Pass dtbo_vendor after --device-tree-overlay crosvm option is supported.
 
-    append_platform_devices(&mut command, &config)?;
+    append_platform_devices(&mut command, &mut preserved_fds, &config)?;
 
     debug!("Preserving FDs {:?}", preserved_fds);
     command.preserved_fds(preserved_fds);