Enable --extended-status for crosvm and return reason why VM died.
This will allow us to detect guest VM kernel panics because they can be
configured to reboot on panic.
Bug: 211704107
Test: Ran some VMs manually with vm tool
Change-Id: I3845bb9d569ad0dc098013b527b69b31352e7e08
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/DeathReason.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/DeathReason.aidl
new file mode 100644
index 0000000..2f454a9
--- /dev/null
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/DeathReason.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022 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;
+
+/**
+ * The reason why a VM died.
+ */
+@Backing(type="int")
+enum DeathReason {
+ /** The VM requested to shut down. */
+ SHUTDOWN = 0,
+ /** The VM requested to reboot, possibly as the result of a kernel panic. */
+ REBOOT = 1,
+ /** The VM was killed. */
+ KILLED = 2,
+ /** The VM died for an unknown reason. */
+ UNKNOWN = 3,
+ /** There was an error waiting for the VM. */
+ INFRASTRUCTURE_ERROR = 4,
+}
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachineCallback.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachineCallback.aidl
index aa8105f..12a056c 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachineCallback.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachineCallback.aidl
@@ -15,6 +15,8 @@
*/
package android.system.virtualizationservice;
+import android.system.virtualizationservice.DeathReason;
+
/**
* An object which a client may register with the VirtualizationService to get callbacks about the
* state of a particular VM.
@@ -50,5 +52,5 @@
* Note that this will not be called if the VirtualizationService itself dies, so you should
* also use `link_to_death` to handle that.
*/
- void onDied(int cid);
+ void onDied(int cid, in DeathReason reason);
}
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index c264270..5a7322b 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -22,6 +22,7 @@
use ::binder::unstable_api::AsNative;
use android_os_permissions_aidl::aidl::android::os::IPermissionController;
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
+ DeathReason::DeathReason,
DiskImage::DiskImage,
IVirtualMachine::{BnVirtualMachine, IVirtualMachine},
IVirtualMachineCallback::IVirtualMachineCallback,
@@ -790,10 +791,10 @@
}
/// Call all registered callbacks to say that the VM has died.
- pub fn callback_on_died(&self, cid: Cid) {
+ pub fn callback_on_died(&self, cid: Cid, reason: DeathReason) {
let callbacks = &*self.0.lock().unwrap();
for callback in callbacks {
- if let Err(e) = callback.onDied(cid as i32) {
+ if let Err(e) = callback.onDied(cid as i32, reason) {
error!("Error notifying exit of VM CID {}: {}", cid, e);
}
}
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index 0b1429c..76f4f47 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -21,19 +21,24 @@
use log::{debug, error, info};
use shared_child::SharedChild;
use std::fs::{remove_dir_all, File};
+use std::io;
use std::mem;
use std::num::NonZeroU32;
use std::os::unix::io::{AsRawFd, RawFd};
use std::path::PathBuf;
-use std::process::Command;
+use std::process::{Command, ExitStatus};
use std::sync::{Arc, Mutex};
use std::thread;
use vsock::VsockStream;
+use android_system_virtualizationservice::aidl::android::system::virtualizationservice::DeathReason::DeathReason;
use android_system_virtualmachineservice::binder::Strong;
use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
const CROSVM_PATH: &str = "/apex/com.android.virt/bin/crosvm";
+/// The exit status which crosvm returns when a VM requests a reboot.
+const CROSVM_REBOOT_STATUS: i32 = 32;
+
/// Configuration for a VM to run with crosvm.
#[derive(Debug)]
pub struct CrosvmConfig {
@@ -182,7 +187,8 @@
/// This takes a separate reference to the `SharedChild` rather than using the one in
/// `self.vm_state` to avoid holding the lock on `vm_state` while it is running.
fn monitor(&self, child: Arc<SharedChild>) {
- match child.wait() {
+ let result = child.wait();
+ match &result {
Err(e) => error!("Error waiting for crosvm({}) instance to die: {}", child.id(), e),
Ok(status) => info!("crosvm({}) exited with status {}", child.id(), status),
}
@@ -192,7 +198,7 @@
// Ensure that the mutex is released before calling the callbacks.
drop(vm_state);
- self.callbacks.callback_on_died(self.cid);
+ self.callbacks.callback_on_died(self.cid, death_reason(&result));
// Delete temporary files.
if let Err(e) = remove_dir_all(&self.temporary_directory) {
@@ -232,13 +238,31 @@
}
}
+fn death_reason(result: &Result<ExitStatus, io::Error>) -> DeathReason {
+ if let Ok(status) = result {
+ match status.code() {
+ None => DeathReason::KILLED,
+ Some(0) => DeathReason::SHUTDOWN,
+ Some(CROSVM_REBOOT_STATUS) => DeathReason::REBOOT,
+ Some(_) => DeathReason::UNKNOWN,
+ }
+ } else {
+ DeathReason::INFRASTRUCTURE_ERROR
+ }
+}
+
/// Starts an instance of `crosvm` to manage a new VM.
fn run_vm(config: CrosvmConfig) -> Result<SharedChild, Error> {
validate_config(&config)?;
let mut command = Command::new(CROSVM_PATH);
// TODO(qwandor): Remove --disable-sandbox.
- command.arg("run").arg("--disable-sandbox").arg("--cid").arg(config.cid.to_string());
+ command
+ .arg("--extended-status")
+ .arg("run")
+ .arg("--disable-sandbox")
+ .arg("--cid")
+ .arg(config.cid.to_string());
if config.protected {
command.arg("--protected-vm");