Add an arg to run-microdroid/run-app cmds to provide custom kernel

This arg will be used to compare performance of Microdroid VMs booted
with different kernels.

Bug: 283822676
Test: vm run-microdroid
Change-Id: Ice300dfbd922467a75f2f5d6f3449bd0b02307f7
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index 232485a..b03addf 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -130,6 +130,7 @@
             cpuTopology: cpu_topology,
             taskProfiles: parameters.task_profiles.clone(),
             gdbPort: 0, // Don't start gdb-server
+            customKernelImage: None,
         });
 
         // Let logs go to logcat.
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index f57cb59..86c8596 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -309,10 +309,12 @@
                 // VirtualMachineAppConfig:
                 // - controlling CPUs;
                 // - specifying a config file in the APK;
-                // - gdbPort is set, meaning that crosvm will start a gdb server.
+                // - gdbPort is set, meaning that crosvm will start a gdb server;
+                // - using anything other than the default kernel.
                 !config.taskProfiles.is_empty()
                     || matches!(config.payload, Payload::ConfigPath(_))
                     || config.gdbPort > 0
+                    || config.customKernelImage.as_ref().is_some()
             }
         };
         if is_custom {
@@ -593,6 +595,10 @@
     let vm_config_file = File::open(vm_config_path)?;
     let mut vm_config = VmConfig::load(&vm_config_file)?.to_parcelable()?;
 
+    if let Some(file) = config.customKernelImage.as_ref() {
+        vm_config.kernel = Some(ParcelFileDescriptor::new(clone_file(file)?))
+    }
+
     if config.memoryMib > 0 {
         vm_config.memoryMib = config.memoryMib;
     }
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
index c467c2f..5e05bb9 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
@@ -73,6 +73,10 @@
     /**
      * Port at which crosvm will start a gdb server to debug guest kernel.
      * If set to zero, then gdb server won't be started.
+     *
+     * Note: Specifying a value here requires android.permission.USE_CUSTOM_VIRTUAL_MACHINE.
+     *
+     * TODO(b/286225150): move to a separate struct
      */
     int gdbPort = 0;
 
@@ -92,6 +96,17 @@
      * List of task profile names to apply for the VM
      *
      * Note: Specifying a value here requires android.permission.USE_CUSTOM_VIRTUAL_MACHINE.
+     *
+     * TODO(b/286225150): move to a separate struct
      */
     String[] taskProfiles;
+
+    /**
+     * If specified, boot Microdroid VM with the given kernel.
+     *
+     * Note: Specifying a value here requires android.permission.USE_CUSTOM_VIRTUAL_MACHINE.
+     *
+     * TODO(b/286225150): move to a separate struct
+     */
+    @nullable ParcelFileDescriptor customKernelImage;
 }
diff --git a/vm/src/main.rs b/vm/src/main.rs
index 1d9f50b..bc3f4da 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -107,6 +107,10 @@
         /// Note: this is only supported on Android kernels android14-5.15 and higher.
         #[clap(long)]
         gdb: Option<NonZeroU16>,
+
+        /// Path to custom kernel image to use when booting Microdroid.
+        #[clap(long)]
+        kernel: Option<PathBuf>,
     },
     /// Run a virtual machine with Microdroid inside
     RunMicrodroid {
@@ -163,6 +167,10 @@
         /// Note: this is only supported on Android kernels android14-5.15 and higher.
         #[clap(long)]
         gdb: Option<NonZeroU16>,
+
+        /// Path to custom kernel image to use when booting Microdroid.
+        #[clap(long)]
+        kernel: Option<PathBuf>,
     },
     /// Run a virtual machine
     Run {
@@ -277,6 +285,7 @@
             task_profiles,
             extra_idsigs,
             gdb,
+            kernel,
         } => command_run_app(
             name,
             get_service()?.as_ref(),
@@ -296,6 +305,7 @@
             task_profiles,
             &extra_idsigs,
             gdb,
+            kernel.as_deref(),
         ),
         Opt::RunMicrodroid {
             name,
@@ -310,6 +320,7 @@
             cpu_topology,
             task_profiles,
             gdb,
+            kernel,
         } => command_run_microdroid(
             name,
             get_service()?.as_ref(),
@@ -324,6 +335,7 @@
             cpu_topology,
             task_profiles,
             gdb,
+            kernel.as_deref(),
         ),
         Opt::Run { name, config, cpu_topology, task_profiles, console, log, gdb } => {
             command_run(
diff --git a/vm/src/run.rs b/vm/src/run.rs
index 36edc64..54c1de4 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -60,6 +60,7 @@
     task_profiles: Vec<String>,
     extra_idsigs: &[PathBuf],
     gdb: Option<NonZeroU16>,
+    kernel: Option<&Path>,
 ) -> Result<(), Error> {
     let apk_file = File::open(apk).context("Failed to open APK file")?;
 
@@ -115,6 +116,8 @@
         None
     };
 
+    let kernel = kernel.map(|p| open_parcel_file(p, false)).transpose()?;
+
     let extra_idsig_files: Result<Vec<File>, _> = extra_idsigs.iter().map(File::open).collect();
     let extra_idsig_fds = extra_idsig_files?.into_iter().map(ParcelFileDescriptor::new).collect();
 
@@ -147,6 +150,7 @@
         cpuTopology: cpu_topology,
         taskProfiles: task_profiles,
         gdbPort: gdb.map(u16::from).unwrap_or(0) as i32, // 0 means no gdb
+        customKernelImage: kernel,
     });
     run(service, &config, &payload_config_str, console_path, log_path)
 }
@@ -189,6 +193,7 @@
     cpu_topology: CpuTopology,
     task_profiles: Vec<String>,
     gdb: Option<NonZeroU16>,
+    kernel: Option<&Path>,
 ) -> Result<(), Error> {
     let apk = find_empty_payload_apk_path()?;
     println!("found path {}", apk.display());
@@ -220,6 +225,7 @@
         task_profiles,
         &extra_sig,
         gdb,
+        kernel,
     )
 }