Implement platform API to assign devices

Clients can specify either device types or sysfs nodes when creating a
VM. The app should have USE_CUSTOM_VIRTUAL_MACHINE permission to do so.

Bug: 287379025
Test: TH
Test: adb root && adb shell /apex/com.android.virt/bin/vm \
      run-microdroid --devices /sys/bus/platform/devices/16d00000.eh \
      --protected
Change-Id: I375d455fa1fa9cbad6e552cdb7b3e9a2138f9278
diff --git a/vm/src/main.rs b/vm/src/main.rs
index 0c99acb..64bcd02 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -119,6 +119,10 @@
         /// Path to disk image containing vendor-specific modules.
         #[clap(long)]
         vendor: Option<PathBuf>,
+
+        /// SysFS nodes of devices to assign to VM
+        #[clap(long)]
+        devices: Vec<PathBuf>,
     },
     /// Run a virtual machine with Microdroid inside
     RunMicrodroid {
@@ -187,6 +191,10 @@
         /// Path to disk image containing vendor-specific modules.
         #[clap(long)]
         vendor: Option<PathBuf>,
+
+        /// SysFS nodes of devices to assign to VM
+        #[clap(long)]
+        devices: Vec<PathBuf>,
     },
     /// Run a virtual machine
     Run {
@@ -308,6 +316,7 @@
             gdb,
             kernel,
             vendor,
+            devices,
         } => command_run_app(
             name,
             get_service()?.as_ref(),
@@ -330,6 +339,7 @@
             gdb,
             kernel.as_deref(),
             vendor.as_deref(),
+            devices,
         ),
         Opt::RunMicrodroid {
             name,
@@ -347,6 +357,7 @@
             gdb,
             kernel,
             vendor,
+            devices,
         } => command_run_microdroid(
             name,
             get_service()?.as_ref(),
@@ -364,6 +375,7 @@
             gdb,
             kernel.as_deref(),
             vendor.as_deref(),
+            devices,
         ),
         Opt::Run { name, config, cpu_topology, task_profiles, console, console_in, log, gdb } => {
             command_run(
@@ -420,6 +432,18 @@
         println!("/dev/kvm does not exist.");
     }
 
+    if Path::new("/dev/vfio/vfio").exists() {
+        println!("/dev/vfio/vfio exists.");
+    } else {
+        println!("/dev/vfio/vfio does not exist.");
+    }
+
+    if Path::new("/sys/bus/platform/drivers/vfio-platform").exists() {
+        println!("VFIO-platform is supported.");
+    } else {
+        println!("VFIO-platform is not supported.");
+    }
+
     Ok(())
 }
 
diff --git a/vm/src/run.rs b/vm/src/run.rs
index f50bd50..250c56c 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -66,6 +66,7 @@
     gdb: Option<NonZeroU16>,
     kernel: Option<&Path>,
     vendor: Option<&Path>,
+    devices: Vec<PathBuf>,
 ) -> Result<(), Error> {
     let apk_file = File::open(apk).context("Failed to open APK file")?;
 
@@ -148,6 +149,12 @@
         gdbPort: gdb.map(u16::from).unwrap_or(0) as i32, // 0 means no gdb
         taskProfiles: task_profiles,
         vendorImage: vendor,
+        devices: devices
+            .iter()
+            .map(|x| {
+                x.to_str().map(String::from).ok_or(anyhow!("Failed to convert {x:?} to String"))
+            })
+            .collect::<Result<_, _>>()?,
     };
 
     let config = VirtualMachineConfig::AppConfig(VirtualMachineAppConfig {
@@ -208,6 +215,7 @@
     gdb: Option<NonZeroU16>,
     kernel: Option<&Path>,
     vendor: Option<&Path>,
+    devices: Vec<PathBuf>,
 ) -> Result<(), Error> {
     let apk = find_empty_payload_apk_path()?;
     println!("found path {}", apk.display());
@@ -242,6 +250,7 @@
         gdb,
         kernel,
         vendor,
+        devices,
     )
 }