Merge changes I5f3920b7,I013dce63 into main
* changes:
libavf: Add callback support
libvmclient: Take VmCallback in VmInstance::start()
diff --git a/android/vm/src/run.rs b/android/vm/src/run.rs
index 0037327..0855fa0 100644
--- a/android/vm/src/run.rs
+++ b/android/vm/src/run.rs
@@ -339,11 +339,10 @@
} else {
None
};
+ let vm = VmInstance::create(service, config, console_out, console_in, log, dump_dt)
+ .context("Failed to create VM")?;
let callback = Box::new(Callback {});
- let vm =
- VmInstance::create(service, config, console_out, console_in, log, dump_dt, Some(callback))
- .context("Failed to create VM")?;
- vm.start().context("Failed to start VM")?;
+ vm.start(Some(callback)).context("Failed to start VM")?;
let debug_level = get_debug_level(config).unwrap_or(DebugLevel::NONE);
diff --git a/guest/rialto/tests/test.rs b/guest/rialto/tests/test.rs
index d68c568..c650046 100644
--- a/guest/rialto/tests/test.rs
+++ b/guest/rialto/tests/test.rs
@@ -338,7 +338,6 @@
/* consoleIn */ None,
log,
/* dump_dt */ None,
- None,
)
.context("Failed to create VM")
}
diff --git a/guest/trusty/security_vm/launcher/src/main.rs b/guest/trusty/security_vm/launcher/src/main.rs
index 8dd7c43..2fedc16 100644
--- a/guest/trusty/security_vm/launcher/src/main.rs
+++ b/guest/trusty/security_vm/launcher/src/main.rs
@@ -101,10 +101,9 @@
None, // console_out
None, // log
None, // dump_dt
- None, // callback
)
.context("Failed to create VM")?;
- vm.start().context("Failed to start VM")?;
+ vm.start(None /* callback */).context("Failed to start VM")?;
println!("started trusty_security_vm_launcher VM");
let death_reason = vm.wait_for_death();
diff --git a/libs/libavf/include/android/virtualization.h b/libs/libavf/include/android/virtualization.h
index ef57325..03d04a9 100644
--- a/libs/libavf/include/android/virtualization.h
+++ b/libs/libavf/include/android/virtualization.h
@@ -337,17 +337,30 @@
int AVirtualMachine_createRaw(const AVirtualizationService* _Nonnull service,
AVirtualMachineRawConfig* _Nonnull config, int consoleOutFd,
int consoleInFd, int logFd,
- AVirtualMachine* _Null_unspecified* _Nonnull vm) __INTRODUCED_IN(36);
+ const AVirtualMachine* _Null_unspecified* _Nonnull vm)
+ __INTRODUCED_IN(36);
+
+/**
+ * A callback to be called when virtual machine stops.
+ *
+ * \param vm stopped vm
+ * \param reason stop reason
+ */
+typedef void (*AVirtualMachine_stopCallback)(const AVirtualMachine* _Nonnull vm,
+ enum AVirtualMachineStopReason reason);
/**
* Start a virtual machine. `AVirtualMachine_start` is synchronous and blocks until the virtual
* machine is initialized and free to start executing code, or until an error happens.
*
* \param vm a handle on a virtual machine.
+ * \param callback an optional callback to be called when VM stops.
*
* \return If successful, it returns 0. Otherwise, it returns `-EIO`.
*/
-int AVirtualMachine_start(AVirtualMachine* _Nonnull vm) __INTRODUCED_IN(36);
+int AVirtualMachine_start(const AVirtualMachine* _Nonnull vm,
+ const AVirtualMachine_stopCallback _Nullable callback)
+ __INTRODUCED_IN(36);
/**
* Stop a virtual machine. Stopping a virtual machine is like pulling the plug on a real computer;
@@ -366,7 +379,7 @@
*
* \return If successful, it returns 0. Otherwise, it returns `-EIO`.
*/
-int AVirtualMachine_stop(AVirtualMachine* _Nonnull vm) __INTRODUCED_IN(36);
+int AVirtualMachine_stop(const AVirtualMachine* _Nonnull vm) __INTRODUCED_IN(36);
/**
* Open a vsock connection to the VM on the given port. The caller takes ownership of the returned
@@ -379,7 +392,8 @@
*
* \return If successful, it returns a valid file descriptor. Otherwise, it returns `-EIO`.
*/
-int AVirtualMachine_connectVsock(AVirtualMachine* _Nonnull vm, uint32_t port) __INTRODUCED_IN(36);
+int AVirtualMachine_connectVsock(const AVirtualMachine* _Nonnull vm, uint32_t port)
+ __INTRODUCED_IN(36);
/**
* Wait until a virtual machine stops or the given timeout elapses.
@@ -393,7 +407,7 @@
* sets `reason` and returns true.
* - If the timeout expired, it returns `false`.
*/
-bool AVirtualMachine_waitForStop(AVirtualMachine* _Nonnull vm,
+bool AVirtualMachine_waitForStop(const AVirtualMachine* _Nonnull vm,
const struct timespec* _Nullable timeout,
enum AVirtualMachineStopReason* _Nonnull reason)
__INTRODUCED_IN(36);
@@ -408,6 +422,6 @@
*
* \param vm a handle on a virtual machine.
*/
-void AVirtualMachine_destroy(AVirtualMachine* _Nullable vm) __INTRODUCED_IN(36);
+void AVirtualMachine_destroy(const AVirtualMachine* _Nullable vm) __INTRODUCED_IN(36);
__END_DECLS
diff --git a/libs/libavf/src/lib.rs b/libs/libavf/src/lib.rs
index 6532ace..256803f 100644
--- a/libs/libavf/src/lib.rs
+++ b/libs/libavf/src/lib.rs
@@ -19,6 +19,7 @@
use std::os::fd::{FromRawFd, IntoRawFd};
use std::os::raw::{c_char, c_int};
use std::ptr;
+use std::sync::Arc;
use std::time::Duration;
use android_system_virtualizationservice::{
@@ -29,10 +30,10 @@
},
binder::{ParcelFileDescriptor, Strong},
};
-use avf_bindgen::AVirtualMachineStopReason;
+use avf_bindgen::{AVirtualMachineStopReason, AVirtualMachine_stopCallback};
use libc::timespec;
use log::error;
-use vmclient::{DeathReason, VirtualizationService, VmInstance};
+use vmclient::{DeathReason, ErrorCode, VirtualizationService, VmCallback, VmInstance};
/// Create a new virtual machine config object with no properties.
#[no_mangle]
@@ -342,6 +343,49 @@
}
}
+struct LocalVmInstance {
+ vm: Arc<VmInstance>,
+ callback: AVirtualMachine_stopCallback,
+}
+
+impl VmCallback for LocalVmInstance {
+ fn on_payload_started(&self, _cid: i32) {
+ // Microdroid only. no-op.
+ }
+
+ fn on_payload_ready(&self, _cid: i32) {
+ // Microdroid only. no-op.
+ }
+
+ fn on_payload_finished(&self, _cid: i32, _exit_code: i32) {
+ // Microdroid only. no-op.
+ }
+
+ fn on_error(&self, _cid: i32, _error_code: ErrorCode, _message: &str) {
+ // Microdroid only. no-op.
+ }
+
+ fn on_died(&self, _cid: i32, death_reason: DeathReason) {
+ let Some(callback) = self.callback else {
+ return;
+ };
+ let stop_reason = death_reason_to_stop_reason(death_reason);
+ let vm_ptr: *const VmInstance = Arc::into_raw(Arc::clone(&self.vm));
+
+ // SAFETY: `callback` is assumed to be a valid, non-null function pointer passed by
+ // `AVirtualMachine_start`.
+ unsafe {
+ callback(vm_ptr.cast(), stop_reason);
+ }
+
+ // drop ptr after use.
+ // SAFETY: `vm_ptr` is a valid, non-null pointer casted above.
+ unsafe {
+ let _ = Arc::from_raw(vm_ptr);
+ }
+ }
+}
+
/// Create a virtual machine with given `config`.
///
/// # Safety
@@ -357,7 +401,7 @@
console_out_fd: c_int,
console_in_fd: c_int,
log_fd: c_int,
- vm_ptr: *mut *mut VmInstance,
+ vm_ptr: *mut *const VmInstance,
) -> c_int {
// SAFETY: `service` is assumed to be a valid, non-null pointer returned by
// `AVirtualizationService_create` or `AVirtualizationService_create_early`. It's the only
@@ -373,12 +417,11 @@
let console_in = get_file_from_fd(console_in_fd);
let log = get_file_from_fd(log_fd);
- match VmInstance::create(service.as_ref(), &config, console_out, console_in, log, None, None) {
+ match VmInstance::create(service.as_ref(), &config, console_out, console_in, log, None) {
Ok(vm) => {
// SAFETY: `vm_ptr` is assumed to be a valid, non-null pointer to a mutable raw pointer.
- // `vm` is the only reference here and `vm_ptr` takes ownership.
unsafe {
- *vm_ptr = Box::into_raw(Box::new(vm));
+ *vm_ptr = Arc::into_raw(Arc::new(vm));
}
0
}
@@ -394,11 +437,20 @@
/// # Safety
/// `vm` must be a pointer returned by `AVirtualMachine_createRaw`.
#[no_mangle]
-pub unsafe extern "C" fn AVirtualMachine_start(vm: *const VmInstance) -> c_int {
+pub unsafe extern "C" fn AVirtualMachine_start(
+ vm: *const VmInstance,
+ callback: AVirtualMachine_stopCallback,
+) -> c_int {
// SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
// `AVirtualMachine_createRaw`. It's the only reference to the object.
- let vm = unsafe { &*vm };
- match vm.start() {
+ let vm = unsafe { Arc::from_raw(vm) };
+ let callback = callback.map(|_| {
+ let cb: Box<dyn VmCallback + Send + Sync> =
+ Box::new(LocalVmInstance { vm: Arc::clone(&vm), callback });
+ cb
+ });
+
+ match vm.start(callback) {
Ok(_) => 0,
Err(e) => {
error!("AVirtualMachine_start failed: {e:?}");
@@ -509,12 +561,12 @@
/// `vm` must be a pointer returned by `AVirtualMachine_createRaw`. `vm` must not be reused after
/// deletion.
#[no_mangle]
-pub unsafe extern "C" fn AVirtualMachine_destroy(vm: *mut VmInstance) {
+pub unsafe extern "C" fn AVirtualMachine_destroy(vm: *const VmInstance) {
if !vm.is_null() {
// SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
// AVirtualMachine_create. It's the only reference to the object.
unsafe {
- let _ = Box::from_raw(vm);
+ let _ = Arc::from_raw(vm);
}
}
}
diff --git a/libs/libcompos_common/compos_client.rs b/libs/libcompos_common/compos_client.rs
index 6872582..c2b4936 100644
--- a/libs/libcompos_common/compos_client.rs
+++ b/libs/libcompos_common/compos_client.rs
@@ -148,19 +148,14 @@
// Let logs go to logcat.
let (console_fd, log_fd) = (None, None);
- let callback = Box::new(Callback {});
let instance = VmInstance::create(
- service,
- &config,
- console_fd,
- /* console_in_fd */ None,
- log_fd,
+ service, &config, console_fd, /* console_in_fd */ None, log_fd,
/* dump_dt */ None,
- Some(callback),
)
.context("Failed to create VM")?;
- instance.start()?;
+ let callback = Box::new(Callback {});
+ instance.start(Some(callback))?;
let ready = instance.wait_until_ready(TIMEOUTS.vm_max_time_to_ready);
if ready == Err(VmWaitError::Finished) && debug_level != DebugLevel::NONE {
diff --git a/libs/libservice_vm_manager/src/lib.rs b/libs/libservice_vm_manager/src/lib.rs
index 0f322bb..77e7a4a 100644
--- a/libs/libservice_vm_manager/src/lib.rs
+++ b/libs/libservice_vm_manager/src/lib.rs
@@ -152,7 +152,7 @@
let vsock_listener = VsockListener::bind_with_cid_port(VMADDR_CID_HOST, vm_type.port())?;
// Starts the service VM.
- vm.start().context("Failed to start service VM")?;
+ vm.start(None).context("Failed to start service VM")?;
info!("Service VM started");
// Accepts the connection from the service VM.
@@ -245,8 +245,7 @@
let console_in = None;
let log = Some(android_log_fd()?);
let dump_dt = None;
- let callback = None;
- VmInstance::create(service.as_ref(), &config, console_out, console_in, log, dump_dt, callback)
+ VmInstance::create(service.as_ref(), &config, console_out, console_in, log, dump_dt)
.context("Failed to create service VM")
}
diff --git a/libs/libvmclient/src/lib.rs b/libs/libvmclient/src/lib.rs
index 8dd3cd3..2c6abb5 100644
--- a/libs/libvmclient/src/lib.rs
+++ b/libs/libvmclient/src/lib.rs
@@ -209,7 +209,6 @@
console_in: Option<File>,
log: Option<File>,
dump_dt: Option<File>,
- callback: Option<Box<dyn VmCallback + Send + Sync>>,
) -> BinderResult<Self> {
let console_out = console_out.map(ParcelFileDescriptor::new);
let console_in = console_in.map(ParcelFileDescriptor::new);
@@ -226,20 +225,19 @@
let cid = vm.getCid()?;
- // Register callback before starting VM, in case it dies immediately.
let state = Arc::new(Monitor::new(VmState::default()));
- let callback = BnVirtualMachineCallback::new_binder(
- VirtualMachineCallback { state: state.clone(), client_callback: callback },
- BinderFeatures::default(),
- );
- vm.registerCallback(&callback)?;
let death_recipient = wait_for_binder_death(&mut vm.as_binder(), state.clone())?;
Ok(Self { vm, cid, state, _death_recipient: death_recipient })
}
/// Starts the VM.
- pub fn start(&self) -> BinderResult<()> {
+ pub fn start(&self, callback: Option<Box<dyn VmCallback + Send + Sync>>) -> BinderResult<()> {
+ let callback = BnVirtualMachineCallback::new_binder(
+ VirtualMachineCallback { state: self.state.clone(), client_callback: callback },
+ BinderFeatures::default(),
+ );
+ self.vm.registerCallback(&callback)?;
self.vm.start()
}
diff --git a/microfuchsia/microfuchsiad/src/instance_starter.rs b/microfuchsia/microfuchsiad/src/instance_starter.rs
index e3c4e8d..7c4f32d 100644
--- a/microfuchsia/microfuchsiad/src/instance_starter.rs
+++ b/microfuchsia/microfuchsiad/src/instance_starter.rs
@@ -96,7 +96,6 @@
console_in,
/* log= */ None,
/* dump_dt= */ None,
- None,
)
.context("Failed to create VM")?;
if let Some(pty) = &pty {
@@ -105,7 +104,7 @@
.setHostConsoleName(&pty.follower_name)
.context("Setting host console name")?;
}
- vm_instance.start().context("Starting VM")?;
+ vm_instance.start(None).context("Starting VM")?;
Ok(MicrofuchsiaInstance {
_vm_instance: vm_instance,
diff --git a/tests/backcompat_test/src/main.rs b/tests/backcompat_test/src/main.rs
index 4113881..a7fb074 100644
--- a/tests/backcompat_test/src/main.rs
+++ b/tests/backcompat_test/src/main.rs
@@ -117,10 +117,9 @@
/* consoleIn */ None,
None,
Some(dump_dt),
- None,
)
.context("Failed to create VM")?;
- vm.start().context("Failed to start VM")?;
+ vm.start(None).context("Failed to start VM")?;
info!("Started example VM.");
// Wait for VM to finish
diff --git a/tests/early_vm_test/src/main.rs b/tests/early_vm_test/src/main.rs
index a3c80ca..7d630f8 100644
--- a/tests/early_vm_test/src/main.rs
+++ b/tests/early_vm_test/src/main.rs
@@ -96,7 +96,6 @@
None, // console_out
None, // log
None, // dump_dt
- None, // callback
)
.context("Failed to create VM")?;
diff --git a/tests/old_images_avf_test/src/main.rs b/tests/old_images_avf_test/src/main.rs
index 018a80e..d3ab5eb 100644
--- a/tests/old_images_avf_test/src/main.rs
+++ b/tests/old_images_avf_test/src/main.rs
@@ -92,7 +92,7 @@
AVirtualMachineRawConfig_setMemoryMiB(config, VM_MEMORY_MB);
}
- let mut vm = std::ptr::null_mut();
+ let mut vm = std::ptr::null();
let mut service = std::ptr::null_mut();
ensure!(
@@ -132,7 +132,7 @@
// SAFETY: vm is the only reference to a valid object
unsafe {
- AVirtualMachine_start(vm);
+ AVirtualMachine_start(vm, None /* callback */);
}
info!("VM started");
diff --git a/tests/vm_accessor/accessor/src/run.rs b/tests/vm_accessor/accessor/src/run.rs
index 6dcc507..5bdb8f1 100644
--- a/tests/vm_accessor/accessor/src/run.rs
+++ b/tests/vm_accessor/accessor/src/run.rs
@@ -129,10 +129,9 @@
None, /* console_in */
Some(android_log_fd()?), /* log */
None, /* dump_dt */
- Some(Box::new(Callback {})),
)
.context("Failed to create VM")?;
- vm.start().context("Failed to start VM")?;
+ vm.start(Some(Box::new(Callback {}))).context("Failed to start VM")?;
info!("started IAccessor VM with CID {}", vm.cid());
diff --git a/tests/vmbase_example/src/main.rs b/tests/vmbase_example/src/main.rs
index cbe90d8..d427164 100644
--- a/tests/vmbase_example/src/main.rs
+++ b/tests/vmbase_example/src/main.rs
@@ -115,10 +115,9 @@
/* consoleIn */ None,
Some(log_writer),
/* dump_dt */ None,
- None,
)
.context("Failed to create VM")?;
- vm.start().context("Failed to start VM")?;
+ vm.start(None).context("Failed to start VM")?;
info!("Started example VM.");
// Wait for VM to finish, and check that it shut down cleanly.
diff --git a/tests/vts/src/vts_libavf_test.rs b/tests/vts/src/vts_libavf_test.rs
index dc37aad..c59271a 100644
--- a/tests/vts/src/vts_libavf_test.rs
+++ b/tests/vts/src/vts_libavf_test.rs
@@ -19,6 +19,8 @@
use std::fs::File;
use std::io::{self, BufWriter, Write};
use std::os::fd::IntoRawFd;
+use std::sync::mpsc::{self, Sender};
+use std::sync::{LazyLock, Mutex};
use std::time::{Duration, Instant};
use vsock::{VsockListener, VsockStream, VMADDR_CID_HOST};
@@ -33,6 +35,13 @@
const WRITE_TIMEOUT: Duration = Duration::from_secs(10);
const STOP_TIMEOUT: timespec = timespec { tv_sec: 10, tv_nsec: 0 };
+static ON_STOPPED_EVENT: LazyLock<Mutex<Sender<(usize, AVirtualMachineStopReason)>>> =
+ LazyLock::new(|| {
+ // Returning stub here because `Receiver` isn't `Send`.
+ let (tx, _) = mpsc::channel();
+ Mutex::new(tx)
+ });
+
/// Processes the request in the service VM.
fn process_request(vsock_stream: &mut VsockStream, request: Request) -> Result<Response> {
write_request(vsock_stream, &ServiceVmRequest::Process(request))?;
@@ -73,11 +82,23 @@
}
}
+unsafe extern "C" fn on_stopped(vm: *const AVirtualMachine, reason: AVirtualMachineStopReason) {
+ ON_STOPPED_EVENT.lock().unwrap().send((vm as usize, reason)).unwrap();
+
+ // SAFETY: `vm` is a valid pointer created by AVirtualMachine_create().
+ unsafe {
+ AVirtualMachine_destroy(vm);
+ }
+}
+
fn run_rialto(protected_vm: bool) -> Result<()> {
let kernel_file =
File::open("/data/local/tmp/rialto.bin").context("Failed to open kernel file")?;
let kernel_fd = kernel_file.into_raw_fd();
+ let (tx, rx) = mpsc::channel();
+ (*ON_STOPPED_EVENT.lock().unwrap()) = tx;
+
// SAFETY: AVirtualMachineRawConfig_create() isn't unsafe but rust_bindgen forces it to be seen
// as unsafe
let config = unsafe { AVirtualMachineRawConfig_create() };
@@ -92,7 +113,7 @@
AVirtualMachineRawConfig_setMemoryMiB(config, VM_MEMORY_MB);
}
- let mut vm = std::ptr::null_mut();
+ let mut vm = std::ptr::null();
let mut service = std::ptr::null_mut();
ensure!(
@@ -101,11 +122,6 @@
"AVirtualizationService_create failed"
);
- scopeguard::defer! {
- // SAFETY: service is a valid pointer to AVirtualizationService
- unsafe { AVirtualizationService_destroy(service); }
- }
-
ensure!(
// SAFETY: &mut vm is a valid pointer to *AVirtualMachine
unsafe {
@@ -132,9 +148,11 @@
// SAFETY: vm is the only reference to a valid object
unsafe {
- AVirtualMachine_start(vm);
+ AVirtualMachine_start(vm, Some(on_stopped));
}
+ let vm_ptr = vm as usize;
+
info!("VM started");
let mut vsock_stream = listener_thread.join().unwrap()?;
@@ -166,6 +184,14 @@
"AVirtualMachine_waitForStop failed"
);
+ assert_eq!(AVirtualMachineStopReason::AVIRTUAL_MACHINE_SHUTDOWN, stop_reason);
+
+ let timeout = Duration::from_secs(STOP_TIMEOUT.tv_sec.try_into().unwrap());
+ let (stopped_vm_ptr, stopped_reason) =
+ rx.recv_timeout(timeout).expect("Callback should have been called");
+ assert_eq!(stopped_vm_ptr, vm_ptr);
+ assert_eq!(stopped_reason, stop_reason);
+
info!("stopped");
Ok(())