Start a VM in suspended state if vendor_tee_services were requested

If a VM requests access to vendor_tee_services, virtmngr will tell
crosvm to start a VM in a suspended state. Then it will get the vm_fd
that represents this VM, and pass it to the new HAL to grant access for
this VM to the tee services. After that VM will be resumed.

This patch just implements the "start VM suspended" and "resume VM"
parts. The get vm_fd and communication with the HAL will be implemented
in the follow up patches.

Bug: 360102915
Test: vm run-microdroid --tee-services vendor.test_pkvm_tee_service
Change-Id: I57df7547dd0245222af4d768396d4ae5fa9e15f2
diff --git a/android/virtmgr/src/aidl.rs b/android/virtmgr/src/aidl.rs
index 76fc657..66d32ce 100644
--- a/android/virtmgr/src/aidl.rs
+++ b/android/virtmgr/src/aidl.rs
@@ -751,16 +751,11 @@
             }
         }
 
-        // TODO(b/391774181): handle vendor tee services (which require talking to HAL) as well.
-        if !vendor_tee_services.is_empty() {
-            if !is_vm_capabilities_hal_supported() {
-                return Err(anyhow!(
-                    "requesting access to tee services requires {VM_CAPABILITIES_HAL_IDENTIFIER}"
-                ))
-                .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
-            }
-            return Err(anyhow!("support for vendor tee services is coming soon!"))
-                .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
+        if !vendor_tee_services.is_empty() && !is_vm_capabilities_hal_supported() {
+            return Err(anyhow!(
+                "requesting access to tee services requires {VM_CAPABILITIES_HAL_IDENTIFIER}"
+            ))
+            .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
         }
 
         // TODO(b/391774181): remove this check in a follow-up patch.
@@ -1003,6 +998,7 @@
             enable_hypervisor_specific_auth_method: config.enableHypervisorSpecificAuthMethod,
             instance_id,
             custom_memory_backing_files,
+            start_suspended: !vendor_tee_services.is_empty(),
         };
         let instance = Arc::new(
             VmInstance::new(
@@ -1011,6 +1007,7 @@
                 requester_uid,
                 requester_debug_pid,
                 vm_context,
+                vendor_tee_services,
             )
             .with_context(|| format!("Failed to create VM with config {:?}", config))
             .with_log()
@@ -1711,6 +1708,16 @@
     fn create(instance: Arc<VmInstance>) -> Strong<dyn IVirtualMachine::IVirtualMachine> {
         BnVirtualMachine::new_binder(VirtualMachine { instance }, BinderFeatures::default())
     }
+
+    fn handle_vendor_tee_services(&self) -> binder::Result<()> {
+        // TODO(b/360102915): get vm_fd from crosvm
+        // TODO(b/360102915): talk to HAL
+        self.instance
+            .resume_full()
+            .with_context(|| format!("Error resuming VM with CID {}", self.instance.cid))
+            .with_log()
+            .or_service_specific_exception(-1)
+    }
 }
 
 impl Interface for VirtualMachine {}
@@ -1753,7 +1760,12 @@
             .start()
             .with_context(|| format!("Error starting VM with CID {}", self.instance.cid))
             .with_log()
-            .or_service_specific_exception(-1)
+            .or_service_specific_exception(-1)?;
+        if !self.instance.vendor_tee_services.is_empty() {
+            self.handle_vendor_tee_services()
+        } else {
+            Ok(())
+        }
     }
 
     fn stop(&self) -> binder::Result<()> {
diff --git a/android/virtmgr/src/crosvm.rs b/android/virtmgr/src/crosvm.rs
index 15a4199..c264185 100644
--- a/android/virtmgr/src/crosvm.rs
+++ b/android/virtmgr/src/crosvm.rs
@@ -142,6 +142,7 @@
     pub instance_id: [u8; 64],
     // (memfd, guest address, size)
     pub custom_memory_backing_files: Vec<(OwnedFd, u64, u64)>,
+    pub start_suspended: bool,
 }
 
 #[derive(Debug)]
@@ -419,6 +420,8 @@
     pub vm_metric: Mutex<VmMetric>,
     // Whether virtio-balloon is enabled
     pub balloon_enabled: bool,
+    /// List of vendor tee services this VM might access.
+    pub vendor_tee_services: Vec<String>,
     /// The latest lifecycle state which the payload reported itself to be in.
     payload_state: Mutex<PayloadState>,
     /// Represents the condition that payload_state was updated
@@ -446,6 +449,7 @@
         requester_uid: u32,
         requester_debug_pid: i32,
         vm_context: VmContext,
+        vendor_tee_services: Vec<String>,
     ) -> Result<VmInstance, Error> {
         validate_config(&config)?;
         let cid = config.cid;
@@ -473,6 +477,7 @@
             payload_state_updated: Condvar::new(),
             requester_uid_name,
             balloon_enabled,
+            vendor_tee_services,
         };
         info!("{} created", &instance);
         Ok(instance)
@@ -826,6 +831,18 @@
         }
         Ok(())
     }
+
+    /// Performs full resume of VM.
+    pub fn resume_full(&self) -> Result<(), Error> {
+        let socket_path_cstring = path_to_cstring(&self.crosvm_control_socket_path);
+        // SAFETY: Pointer is valid for the lifetime of the call.
+        let success =
+            unsafe { crosvm_control::crosvm_client_resume_vm_full(socket_path_cstring.as_ptr()) };
+        if !success {
+            bail!("Failed to resume VM");
+        }
+        Ok(())
+    }
 }
 
 impl Rss {
@@ -1431,6 +1448,10 @@
         }
     }
 
+    if config.start_suspended {
+        command.arg("--suspended");
+    }
+
     print_crosvm_args(&command);
 
     let result = SharedChild::spawn(&mut command)?;