virtualizationservice: Add dtbo to VirtualMachineRawConfig

Bug: 381971307
Test: T/H
Change-Id: Iea377f4e6d2d4c8ae24dc6349c2b0b3ca432e944
diff --git a/android/virtmgr/src/aidl.rs b/android/virtmgr/src/aidl.rs
index daa755e..0cde751 100644
--- a/android/virtmgr/src/aidl.rs
+++ b/android/virtmgr/src/aidl.rs
@@ -29,6 +29,7 @@
     ErrorCode::ErrorCode,
 };
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
+    AssignedDevices::AssignedDevices,
     AssignableDevice::AssignableDevice,
     CpuTopology::CpuTopology,
     DiskImage::DiskImage,
@@ -587,7 +588,19 @@
             check_gdb_allowed(config)?;
         }
 
-        let device_tree_overlay = maybe_create_device_tree_overlay(config, &temporary_directory)?;
+        let mut device_tree_overlays = vec![];
+        if let Some(dt_overlay) = maybe_create_reference_dt_overlay(config, &temporary_directory)? {
+            device_tree_overlays.push(dt_overlay);
+        }
+        if let Some(dtbo) = get_dtbo(config) {
+            let dtbo = File::from(
+                dtbo.as_ref()
+                    .try_clone()
+                    .context("Failed to create VM DTBO from ParcelFileDescriptor")
+                    .or_binder_exception(ExceptionCode::BAD_PARCELABLE)?,
+            );
+            device_tree_overlays.push(dtbo);
+        }
 
         let debug_config = DebugConfig::new(config);
         let ramdump = if !uses_gki_kernel(config) && debug_config.is_ramdump_needed() {
@@ -715,29 +728,30 @@
             }
         };
 
-        let (vfio_devices, dtbo) = if !config.devices.is_empty() {
-            let mut set = HashSet::new();
-            for device in config.devices.iter() {
-                let path = canonicalize(device)
-                    .with_context(|| format!("can't canonicalize {device}"))
-                    .or_service_specific_exception(-1)?;
-                if !set.insert(path) {
-                    return Err(anyhow!("duplicated device {device}"))
-                        .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT);
+        let (vfio_devices, dtbo) = match &config.devices {
+            AssignedDevices::Devices(devices) if !devices.is_empty() => {
+                let mut set = HashSet::new();
+                for device in devices.iter() {
+                    let path = canonicalize(device)
+                        .with_context(|| format!("can't canonicalize {device}"))
+                        .or_service_specific_exception(-1)?;
+                    if !set.insert(path) {
+                        return Err(anyhow!("duplicated device {device}"))
+                            .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT);
+                    }
                 }
+                let devices = GLOBAL_SERVICE.bindDevicesToVfioDriver(devices)?;
+                let dtbo_file = File::from(
+                    GLOBAL_SERVICE
+                        .getDtboFile()?
+                        .as_ref()
+                        .try_clone()
+                        .context("Failed to create VM DTBO from ParcelFileDescriptor")
+                        .or_binder_exception(ExceptionCode::BAD_PARCELABLE)?,
+                );
+                (devices, Some(dtbo_file))
             }
-            let devices = GLOBAL_SERVICE.bindDevicesToVfioDriver(&config.devices)?;
-            let dtbo_file = File::from(
-                GLOBAL_SERVICE
-                    .getDtboFile()?
-                    .as_ref()
-                    .try_clone()
-                    .context("Failed to create VM DTBO from ParcelFileDescriptor")
-                    .or_binder_exception(ExceptionCode::BAD_PARCELABLE)?,
-            );
-            (devices, Some(dtbo_file))
-        } else {
-            (vec![], None)
+            _ => (vec![], None),
         };
         let display_config = if cfg!(paravirtualized_devices) {
             config
@@ -834,7 +848,7 @@
             gdb_port,
             vfio_devices,
             dtbo,
-            device_tree_overlay,
+            device_tree_overlays,
             display_config,
             input_device_options,
             hugepages: config.hugePages,
@@ -931,7 +945,7 @@
     Err(anyhow!("No hashtree digest is extracted from microdroid vendor image"))
 }
 
-fn maybe_create_device_tree_overlay(
+fn maybe_create_reference_dt_overlay(
     config: &VirtualMachineConfig,
     temporary_directory: &Path,
 ) -> binder::Result<Option<File>> {
@@ -1000,6 +1014,16 @@
     Ok(device_tree_overlay)
 }
 
+fn get_dtbo(config: &VirtualMachineConfig) -> Option<&ParcelFileDescriptor> {
+    let VirtualMachineConfig::RawConfig(config) = config else {
+        return None;
+    };
+    match &config.devices {
+        AssignedDevices::Dtbo(dtbo) => dtbo.as_ref(),
+        _ => None,
+    }
+}
+
 fn write_zero_filler(zero_filler_path: &Path) -> Result<()> {
     let file = OpenOptions::new()
         .create_new(true)
@@ -1260,7 +1284,7 @@
             append_kernel_param("androidboot.microdroid.mount_vendor=1", &mut vm_config)
         }
 
-        vm_config.devices.clone_from(&custom_config.devices);
+        vm_config.devices = AssignedDevices::Devices(custom_config.devices.clone());
         vm_config.networkSupported = custom_config.networkSupported;
 
         for param in custom_config.extraKernelCmdlineParams.iter() {
diff --git a/android/virtmgr/src/crosvm.rs b/android/virtmgr/src/crosvm.rs
index affd430..f3b669f 100644
--- a/android/virtmgr/src/crosvm.rs
+++ b/android/virtmgr/src/crosvm.rs
@@ -125,7 +125,7 @@
     pub gdb_port: Option<NonZeroU16>,
     pub vfio_devices: Vec<VfioDevice>,
     pub dtbo: Option<File>,
-    pub device_tree_overlay: Option<File>,
+    pub device_tree_overlays: Vec<File>,
     pub display_config: Option<DisplayConfig>,
     pub input_device_options: Vec<InputDeviceOption>,
     pub hugepages: bool,
@@ -1157,9 +1157,10 @@
         .context("failed to create control listener")?;
     command.arg("--socket").arg(add_preserved_fd(&mut preserved_fds, control_sock));
 
-    if let Some(dt_overlay) = config.device_tree_overlay {
-        command.arg("--device-tree-overlay").arg(add_preserved_fd(&mut preserved_fds, dt_overlay));
-    }
+    config.device_tree_overlays.into_iter().for_each(|dt_overlay| {
+        let arg = add_preserved_fd(&mut preserved_fds, dt_overlay);
+        command.arg("--device-tree-overlay").arg(arg);
+    });
 
     if cfg!(paravirtualized_devices) {
         if let Some(gpu_config) = &config.gpu_config {
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/AssignedDevices.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/AssignedDevices.aidl
new file mode 100644
index 0000000..984f596
--- /dev/null
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/AssignedDevices.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2021 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.
+ */
+package android.system.virtualizationservice;
+
+/** Assigned devices */
+union AssignedDevices {
+    /** List of SysFS nodes of devices to be assigned for VFIO */
+    String[] devices = {};
+
+    /** Device tree overlay for non-VFIO case */
+    ParcelFileDescriptor dtbo;
+}
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
index 9ebb7fe..62a6d57 100644
--- a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
@@ -15,6 +15,7 @@
  */
 package android.system.virtualizationservice;
 
+import android.system.virtualizationservice.AssignedDevices;
 import android.system.virtualizationservice.AudioConfig;
 import android.system.virtualizationservice.CpuTopology;
 import android.system.virtualizationservice.DiskImage;
@@ -91,8 +92,8 @@
      */
     boolean hugePages;
 
-    /** List of SysFS nodes of devices to be assigned */
-    String[] devices;
+    /** Assigned devices */
+    AssignedDevices devices;
 
     @nullable DisplayConfig displayConfig;
 
diff --git a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
index d6b38ea..6311168 100644
--- a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -40,6 +40,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.sysprop.HypervisorProperties;
+import android.system.virtualizationservice.AssignedDevices;
 import android.system.virtualizationservice.DiskImage;
 import android.system.virtualizationservice.Partition;
 import android.system.virtualizationservice.SharedPath;
@@ -807,7 +808,7 @@
         config.memoryMib = bytesToMebiBytes(mMemoryBytes);
         config.cpuTopology = (byte) this.mCpuTopology;
         config.consoleInputDevice = mConsoleInputDevice;
-        config.devices = EMPTY_STRING_ARRAY;
+        config.devices = AssignedDevices.devices(EMPTY_STRING_ARRAY);
         config.platformVersion = "~1.0";
         config.audioConfig =
                 Optional.ofNullable(customImageConfig.getAudioConfig())
diff --git a/libs/vmconfig/src/lib.rs b/libs/vmconfig/src/lib.rs
index e520f0e..8357f99 100644
--- a/libs/vmconfig/src/lib.rs
+++ b/libs/vmconfig/src/lib.rs
@@ -15,6 +15,7 @@
 //! Struct for VM configuration with JSON (de)serialization and AIDL parcelables
 
 use android_system_virtualizationservice::{
+    aidl::android::system::virtualizationservice::AssignedDevices::AssignedDevices,
     aidl::android::system::virtualizationservice::CpuTopology::CpuTopology,
     aidl::android::system::virtualizationservice::DiskImage::DiskImage as AidlDiskImage,
     aidl::android::system::virtualizationservice::Partition::Partition as AidlPartition,
@@ -124,13 +125,16 @@
             memoryMib: memory_mib,
             cpuTopology: cpu_topology,
             platformVersion: self.platform_version.to_string(),
-            devices: self
-                .devices
-                .iter()
-                .map(|x| {
-                    x.to_str().map(String::from).ok_or(anyhow!("Failed to convert {x:?} to String"))
-                })
-                .collect::<Result<_>>()?,
+            devices: AssignedDevices::Devices(
+                self.devices
+                    .iter()
+                    .map(|x| {
+                        x.to_str()
+                            .map(String::from)
+                            .ok_or(anyhow!("Failed to convert {x:?} to String"))
+                    })
+                    .collect::<Result<_>>()?,
+            ),
             consoleInputDevice: self.console_input_device.clone(),
             usbConfig: usb_config,
             balloon: true,