Implement LLNDK for AVF
libavf will be a new LLNDK serving stable C APIs for AVF. For now it
supports creating a raw config, setting part of fields of raw configs,
spawning/connecting to virtmgr, and then creating a VM.
Bug: 369588412
Test: run a VM with libavf
Change-Id: If0ef5ec4e4121a872e65e862698f674e0352c372
diff --git a/libs/libavf/Android.bp b/libs/libavf/Android.bp
new file mode 100644
index 0000000..e143709
--- /dev/null
+++ b/libs/libavf/Android.bp
@@ -0,0 +1,58 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_bindgen {
+ name: "libavf_bindgen",
+ wrapper_src: "include/android/virtualization.h",
+ crate_name: "avf_bindgen",
+ defaults: ["avf_build_flags_rust"],
+ source_stem: "bindings",
+ bindgen_flags: ["--default-enum-style rust"],
+ apex_available: ["com.android.virt"],
+}
+
+rust_defaults {
+ name: "libavf.default",
+ crate_name: "avf",
+ defaults: ["avf_build_flags_rust"],
+ srcs: ["src/lib.rs"],
+ edition: "2021",
+ rustlibs: [
+ "libvmclient",
+ "android.system.virtualizationcommon-rust",
+ "android.system.virtualizationservice-rust",
+ "libavf_bindgen",
+ "libbinder_rs",
+ "liblibc",
+ "liblog_rust",
+ "librpcbinder_rs",
+ ],
+ apex_available: ["com.android.virt"],
+}
+
+rust_ffi_static {
+ name: "libavf_impl",
+ defaults: ["libavf.default"],
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "libavf",
+ llndk: {
+ symbol_file: "libavf.map.txt",
+ moved_to_apex: true,
+ },
+ whole_static_libs: ["libavf_impl"],
+ shared_libs: [
+ "libbinder_ndk",
+ "libbinder_rpc_unstable",
+ "liblog",
+ ],
+ export_static_lib_headers: ["libavf_impl"],
+ apex_available: ["com.android.virt"],
+ version_script: "libavf.map.txt",
+ stubs: {
+ symbol_file: "libavf.map.txt",
+ },
+}
diff --git a/libs/libavf/include/android/virtualization.h b/libs/libavf/include/android/virtualization.h
new file mode 100644
index 0000000..f33ee75
--- /dev/null
+++ b/libs/libavf/include/android/virtualization.h
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2024 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.
+ */
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+__BEGIN_DECLS
+
+/**
+ * Represents a handle on a virtual machine raw config.
+ */
+typedef struct AVirtualMachineRawConfig AVirtualMachineRawConfig;
+
+/**
+ * Create a new virtual machine raw config object with no properties.
+ *
+ * This only creates the raw config object. `name` and `kernel` must be set with
+ * calls to {@link AVirtualMachineRawConfig_setName} and {@link AVirtualMachineRawConfig_setKernel}.
+ * Other properties, set by {@link AVirtualMachineRawConfig_setMemoryMib},
+ * {@link AVirtualMachineRawConfig_setInitRd}, {@link AVirtualMachineRawConfig_addDisk},
+ * {@link AVirtualMachineRawConfig_setProtectedVm}, and {@link AVirtualMachineRawConfig_setBalloon}
+ * are optional.
+ *
+ * The caller takes ownership of the returned raw config object, and is responsible for creating a
+ * VM by calling {@link AVirtualMachine_createRaw} or releasing it by calling
+ * {@link AVirtualMachineRawConfig_destroy}.
+ *
+ * \return A new virtual machine raw config object.
+ */
+AVirtualMachineRawConfig* AVirtualMachineRawConfig_create();
+
+/**
+ * Destroy a virtual machine config object.
+ *
+ * \param config a virtual machine config object.
+ *
+ * `AVirtualMachineRawConfig_destroy` does nothing if `config` is null. A destroyed config object
+ * must not be reused.
+ */
+void AVirtualMachineRawConfig_destroy(AVirtualMachineRawConfig* config);
+
+/**
+ * Set a name of a virtual machine.
+ *
+ * \param config a virtual machine config object.
+ * \param name a pointer to a null-terminated string for the name.
+ *
+ * \return If successful, it returns 0.
+ */
+int AVirtualMachineRawConfig_setName(AVirtualMachineRawConfig* config, const char* name);
+
+/**
+ * Set an instance ID of a virtual machine.
+ *
+ * \param config a virtual machine config object.
+ * \param instanceId a pointer to a 64-byte buffer for the instance ID.
+ *
+ * \return If successful, it returns 0.
+ */
+int AVirtualMachineRawConfig_setInstanceId(AVirtualMachineRawConfig* config,
+ const int8_t* instanceId);
+
+/**
+ * Set a kernel image of a virtual machine.
+ *
+ * \param config a virtual machine config object.
+ * \param fd a readable file descriptor containing the kernel image, or -1 to unset.
+ * `AVirtualMachineRawConfig_setKernel` takes ownership of `fd`.
+ *
+ * \return If successful, it returns 0.
+ */
+int AVirtualMachineRawConfig_setKernel(AVirtualMachineRawConfig* config, int fd);
+
+/**
+ * Set an init rd of a virtual machine.
+ *
+ * \param config a virtual machine config object.
+ * \param fd a readable file descriptor containing the kernel image, or -1 to unset.
+ * `AVirtualMachineRawConfig_setInitRd` takes ownership of `fd`.
+ *
+ * \return If successful, it returns 0.
+ */
+int AVirtualMachineRawConfig_setInitRd(AVirtualMachineRawConfig* config, int fd);
+
+/**
+ * Add a disk for a virtual machine.
+ *
+ * \param config a virtual machine config object.
+ * \param fd a readable file descriptor containing the disk image.
+ * `AVirtualMachineRawConfig_addDisk` takes ownership of `fd`.
+ *
+ * \return If successful, it returns 0. If `fd` is invalid, it returns -EINVAL.
+ */
+int AVirtualMachineRawConfig_addDisk(AVirtualMachineRawConfig* config, int fd);
+
+/**
+ * Set how much memory will be given to a virtual machine.
+ *
+ * \param config a virtual machine config object.
+ * \param memoryMib the amount of RAM to give the virtual machine, in MiB. 0 or negative to use the
+ * default.
+ *
+ * \return If successful, it returns 0.
+ */
+int AVirtualMachineRawConfig_setMemoryMib(AVirtualMachineRawConfig* config, int32_t memoryMib);
+
+/**
+ * Set whether a virtual machine is protected or not.
+ *
+ * \param config a virtual machine config object.
+ * \param protectedVm whether the virtual machine should be protected.
+ *
+ * \return If successful, it returns 0.
+ */
+int AVirtualMachineRawConfig_setProtectedVm(AVirtualMachineRawConfig* config, bool protectedVm);
+
+/**
+ * Set whether a virtual machine uses memory ballooning or not.
+ *
+ * \param config a virtual machine config object.
+ * \param balloon whether the virtual machine should use memory ballooning.
+ *
+ * \return If successful, it returns 0.
+ */
+int AVirtualMachineRawConfig_setBalloon(AVirtualMachineRawConfig* config, bool balloon);
+
+/**
+ * Set whether to use an alternate, hypervisor-specific authentication method
+ * for protected VMs. You don't want to use this.
+ *
+ * \return If successful, it returns 0. It returns `-ENOTSUP` if the hypervisor doesn't have an
+ * alternate auth mode.
+ */
+int AVirtualMachineRawConfig_setHypervisorSpecificAuthMethod(AVirtualMachineRawConfig* config,
+ bool enable);
+
+/**
+ * Use the specified fd as the backing memfd for a range of the guest
+ * physical memory.
+ *
+ * \param config a virtual machine config object.
+ * \param fd a memfd
+ * \param rangeStart range start IPA
+ * \param rangeEnd range end IPA
+ *
+ * \return If successful, it returns 0. It returns `-ENOTSUP` if the hypervisor doesn't support
+ * backing memfd.
+ */
+int AVirtualMachineRawConfig_addCustomMemoryBackingFile(AVirtualMachineRawConfig* config, int fd,
+ size_t rangeStart, size_t rangeEnd);
+
+/**
+ * Represents a handle on a virtualization service, responsible for managing virtual machines.
+ */
+typedef struct AVirtualizationService AVirtualizationService;
+
+/**
+ * Spawn a new instance of `virtmgr`, a child process that will host the `VirtualizationService`
+ * service, and connect to the child process.
+ *
+ * The caller takes ownership of the returned service object, and is responsible for releasing it
+ * by calling {@link AVirtualizationService_destroy}.
+ *
+ * \param early set to true when running a service for early virtual machines. See
+ * [`early_vm.md`](../../../../docs/early_vm.md) for more details on early virtual machines.
+ * \param service an out parameter that will be set to the service handle.
+ *
+ * \return
+ * - If successful, it sets `service` and returns 0.
+ * - If it fails to spawn `virtmgr`, it leaves `service` untouched and returns a negative value
+ * representing the OS error code.
+ * - If it fails to connect to the spawned `virtmgr`, it leaves `service` untouched and returns
+ * `-ECONNREFUSED`.
+ */
+int AVirtualizationService_create(AVirtualizationService** service, bool early);
+
+/**
+ * Destroy a VirtualizationService object.
+ *
+ * `AVirtualizationService_destroy` does nothing if `service` is null. A destroyed service object
+ * must not be reused.
+ *
+ * \param service a handle on a virtualization service.
+ */
+void AVirtualizationService_destroy(AVirtualizationService* service);
+
+/**
+ * Represents a handle on a virtual machine.
+ */
+typedef struct AVirtualMachine AVirtualMachine;
+
+/**
+ * The reason why a virtual machine stopped.
+ * @see AVirtualMachine_waitForStop
+ */
+enum StopReason : int32_t {
+ /**
+ * VirtualizationService died.
+ */
+ VIRTUALIZATION_SERVICE_DIED = 1,
+ /**
+ * There was an error waiting for the virtual machine.
+ */
+ INFRASTRUCTURE_ERROR = 2,
+ /**
+ * The virtual machine was killed.
+ */
+ KILLED = 3,
+ /**
+ * The virtual machine stopped for an unknown reason.
+ */
+ UNKNOWN = 4,
+ /**
+ * The virtual machine requested to shut down.
+ */
+ SHUTDOWN = 5,
+ /**
+ * crosvm had an error starting the virtual machine.
+ */
+ START_FAILED = 6,
+ /**
+ * The virtual machine requested to reboot, possibly as the result of a kernel panic.
+ */
+ REBOOT = 7,
+ /**
+ * The virtual machine or crosvm crashed.
+ */
+ CRASH = 8,
+ /**
+ * The pVM firmware failed to verify the VM because the public key doesn't match.
+ */
+ PVM_FIRMWARE_PUBLIC_KEY_MISMATCH = 9,
+ /**
+ * The pVM firmware failed to verify the VM because the instance image changed.
+ */
+ PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED = 10,
+ /**
+ * The virtual machine was killed due to hangup.
+ */
+ HANGUP = 11,
+ /**
+ * VirtualizationService sent a stop reason which was not recognised by the client library.
+ */
+ UNRECOGNISED = 0,
+};
+
+/**
+ * Create a virtual machine with given raw `config`.
+ *
+ * The created virtual machine is in stopped state. To run the created virtual machine, call
+ * {@link AVirtualMachine_start}.
+ *
+ * The caller takes ownership of the returned virtual machine object, and is responsible for
+ * releasing it by calling {@link AVirtualMachine_destroy}.
+ *
+ * \param service a handle on a virtualization service.
+ * \param config a virtual machine config object. Ownership will always be transferred from the
+ * caller, even if unsuccessful. `config` must not be reused.
+ * \param consoleOutFd a writable file descriptor for the console output, or -1. Ownership will
+ * always be transferred from the caller, even if unsuccessful.
+ * \param consoleInFd a readable file descriptor for the console input, or -1. Ownership will always
+ * be transferred from the caller, even if unsuccessful.
+ * \param logFd a writable file descriptor for the log output, or -1. Ownership will always be
+ * transferred from the caller, even if unsuccessful.
+ * \param vm an out parameter that will be set to the virtual machine handle.
+ *
+ * \return If successful, it sets `vm` and returns 0. Otherwise, it leaves `vm` untouched and
+ * returns `-EIO`.
+ */
+int AVirtualMachine_createRaw(const AVirtualizationService* service,
+ AVirtualMachineRawConfig* config, int consoleOutFd, int consoleInFd,
+ int logFd, AVirtualMachine** vm);
+
+/**
+ * Start a virtual machine.
+ *
+ * \param vm a handle on a virtual machine.
+ *
+ * \return If successful, it returns 0. Otherwise, it returns `-EIO`.
+ */
+int AVirtualMachine_start(AVirtualMachine* vm);
+
+/**
+ * Stop a virtual machine.
+ *
+ * \param vm a handle on a virtual machine.
+ *
+ * \return If successful, it returns 0. Otherwise, it returns `-EIO`.
+ */
+int AVirtualMachine_stop(AVirtualMachine* vm);
+
+/**
+ * Wait until a virtual machine stops.
+ *
+ * \param vm a handle on a virtual machine.
+ *
+ * \return The reason why the virtual machine stopped.
+ */
+enum StopReason AVirtualMachine_waitForStop(AVirtualMachine* vm);
+
+/**
+ * Destroy a virtual machine.
+ *
+ * `AVirtualMachine_destroy` does nothing if `vm` is null. A destroyed virtual machine must not be
+ * reused.
+ *
+ * \param vm a handle on a virtual machine.
+ */
+void AVirtualMachine_destroy(AVirtualMachine* vm);
+
+__END_DECLS
diff --git a/libs/libavf/libavf.map.txt b/libs/libavf/libavf.map.txt
new file mode 100644
index 0000000..ecb4cc9
--- /dev/null
+++ b/libs/libavf/libavf.map.txt
@@ -0,0 +1,24 @@
+LIBAVF {
+ global:
+ AVirtualMachineRawConfig_create; # apex llndk
+ AVirtualMachineRawConfig_destroy; # apex llndk
+ AVirtualMachineRawConfig_setName; # apex llndk
+ AVirtualMachineRawConfig_setInstanceId; # apex llndk
+ AVirtualMachineRawConfig_setKernel; # apex llndk
+ AVirtualMachineRawConfig_setInitRd; # apex llndk
+ AVirtualMachineRawConfig_addDisk; # apex llndk
+ AVirtualMachineRawConfig_setMemoryMib; # apex llndk
+ AVirtualMachineRawConfig_setProtectedVm; # apex llndk
+ AVirtualMachineRawConfig_setBalloon; # apex llndk
+ AVirtualMachineRawConfig_setHypervisorSpecificAuthMethod; # apex llndk
+ AVirtualMachineRawConfig_addCustomMemoryBackingFile; # apex llndk
+ AVirtualizationService_create; # apex llndk
+ AVirtualizationService_destroy; # apex llndk
+ AVirtualMachine_createRaw; # apex llndk
+ AVirtualMachine_start; # apex llndk
+ AVirtualMachine_stop; # apex llndk
+ AVirtualMachine_waitForStop; # apex llndk
+ AVirtualMachine_destroy; # apex llndk
+ local:
+ *;
+};
diff --git a/libs/libavf/src/lib.rs b/libs/libavf/src/lib.rs
new file mode 100644
index 0000000..0a8f891
--- /dev/null
+++ b/libs/libavf/src/lib.rs
@@ -0,0 +1,413 @@
+// Copyright 2024 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.
+
+//! Stable C library for AVF.
+
+use std::ffi::CStr;
+use std::fs::File;
+use std::os::fd::FromRawFd;
+use std::os::raw::{c_char, c_int};
+use std::ptr;
+
+use android_system_virtualizationservice::{
+ aidl::android::system::virtualizationservice::{
+ DiskImage::DiskImage, IVirtualizationService::IVirtualizationService,
+ VirtualMachineConfig::VirtualMachineConfig,
+ VirtualMachineRawConfig::VirtualMachineRawConfig,
+ },
+ binder::{ParcelFileDescriptor, Strong},
+};
+use avf_bindgen::StopReason;
+use vmclient::{DeathReason, VirtualizationService, VmInstance};
+
+/// Create a new virtual machine config object with no properties.
+#[no_mangle]
+pub extern "C" fn AVirtualMachineRawConfig_create() -> *mut VirtualMachineRawConfig {
+ let config = Box::new(VirtualMachineRawConfig {
+ platformVersion: "~1.0".to_owned(),
+ ..Default::default()
+ });
+ Box::into_raw(config)
+}
+
+/// Destroy a virtual machine config object.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `config` must not be
+/// used after deletion.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachineRawConfig_destroy(config: *mut VirtualMachineRawConfig) {
+ if !config.is_null() {
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachineRawConfig_create. It's the only reference to the object.
+ unsafe {
+ let _ = Box::from_raw(config);
+ }
+ }
+}
+
+/// Set a name of a virtual machine.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachineRawConfig_setName(
+ config: *mut VirtualMachineRawConfig,
+ name: *const c_char,
+) -> c_int {
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachineRawConfig_create. It's the only reference to the object.
+ let config = unsafe { &mut *config };
+ // SAFETY: `name` is assumed to be a pointer to a valid C string.
+ config.name = unsafe { CStr::from_ptr(name) }.to_string_lossy().into_owned();
+ 0
+}
+
+/// Set an instance ID of a virtual machine.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `instanceId` must be a
+/// valid, non-null pointer to 64-byte data.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachineRawConfig_setInstanceId(
+ config: *mut VirtualMachineRawConfig,
+ instance_id: *const u8,
+) -> c_int {
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachineRawConfig_create. It's the only reference to the object.
+ let config = unsafe { &mut *config };
+ // SAFETY: `instanceId` is assumed to be a valid pointer to 64 bytes of memory. `config`
+ // is assumed to be a valid object returned by AVirtuaMachineConfig_create.
+ // Both never overlap.
+ unsafe {
+ ptr::copy_nonoverlapping(instance_id, config.instanceId.as_mut_ptr(), 64);
+ }
+ 0
+}
+
+/// Set a kernel image of a virtual machine.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
+/// file descriptor or -1. `AVirtualMachineRawConfig_setKernel` takes ownership of `fd` and `fd`
+/// will be closed upon `AVirtualMachineRawConfig_delete`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachineRawConfig_setKernel(
+ config: *mut VirtualMachineRawConfig,
+ fd: c_int,
+) -> c_int {
+ let file = get_file_from_fd(fd);
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachineRawConfig_create. It's the only reference to the object.
+ let config = unsafe { &mut *config };
+ config.kernel = file.map(ParcelFileDescriptor::new);
+ 0
+}
+
+/// Set an init rd of a virtual machine.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
+/// file descriptor or -1. `AVirtualMachineRawConfig_setInitRd` takes ownership of `fd` and `fd`
+/// will be closed upon `AVirtualMachineRawConfig_delete`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachineRawConfig_setInitRd(
+ config: *mut VirtualMachineRawConfig,
+ fd: c_int,
+) -> c_int {
+ let file = get_file_from_fd(fd);
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachineRawConfig_create. It's the only reference to the object.
+ let config = unsafe { &mut *config };
+ config.initrd = file.map(ParcelFileDescriptor::new);
+ 0
+}
+
+/// Add a disk for a virtual machine.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `fd` must be a valid
+/// file descriptor. `AVirtualMachineRawConfig_addDisk` takes ownership of `fd` and `fd` will be
+/// closed upon `AVirtualMachineRawConfig_delete`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachineRawConfig_addDisk(
+ config: *mut VirtualMachineRawConfig,
+ fd: c_int,
+ writable: bool,
+) -> c_int {
+ let file = get_file_from_fd(fd);
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachineRawConfig_create. It's the only reference to the object.
+ let config = unsafe { &mut *config };
+ match file {
+ // partition not supported yet
+ None => -libc::EINVAL,
+ Some(file) => {
+ config.disks.push(DiskImage {
+ image: Some(ParcelFileDescriptor::new(file)),
+ writable,
+ ..Default::default()
+ });
+ 0
+ }
+ }
+}
+
+/// Set how much memory will be given to a virtual machine.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachineRawConfig_setMemoryMib(
+ config: *mut VirtualMachineRawConfig,
+ memory_mib: i32,
+) -> c_int {
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachineRawConfig_create. It's the only reference to the object.
+ let config = unsafe { &mut *config };
+ config.memoryMib = memory_mib;
+ 0
+}
+
+/// Set whether a virtual machine is protected or not.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachineRawConfig_setProtectedVm(
+ config: *mut VirtualMachineRawConfig,
+ protected_vm: bool,
+) -> c_int {
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachineRawConfig_create. It's the only reference to the object.
+ let config = unsafe { &mut *config };
+ config.protectedVm = protected_vm;
+ 0
+}
+
+/// Set whether a virtual machine uses memory ballooning or not.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachineRawConfig_setBalloon(
+ config: *mut VirtualMachineRawConfig,
+ balloon: bool,
+) -> c_int {
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachineRawConfig_create. It's the only reference to the object.
+ let config = unsafe { &mut *config };
+ config.noBalloon = !balloon;
+ 0
+}
+
+/// NOT IMPLEMENTED.
+///
+/// # Returns
+/// It always returns `-ENOTSUP`.
+#[no_mangle]
+pub extern "C" fn AVirtualMachineRawConfig_setHypervisorSpecificAuthMethod(
+ _config: *mut VirtualMachineRawConfig,
+ _enable: bool,
+) -> c_int {
+ -libc::ENOTSUP
+}
+
+/// NOT IMPLEMENTED.
+///
+/// # Returns
+/// It always returns `-ENOTSUP`.
+#[no_mangle]
+pub extern "C" fn AVirtualMachineRawConfig_addCustomMemoryBackingFile(
+ _config: *mut VirtualMachineRawConfig,
+ _fd: c_int,
+ _range_start: usize,
+ _range_end: usize,
+) -> c_int {
+ -libc::ENOTSUP
+}
+
+/// Spawn a new instance of `virtmgr`, a child process that will host the `VirtualizationService`
+/// AIDL service, and connect to the child process.
+///
+/// # Safety
+/// `service_ptr` must be a valid, non-null pointer to a mutable raw pointer.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualizationService_create(
+ service_ptr: *mut *mut Strong<dyn IVirtualizationService>,
+ early: bool,
+) -> c_int {
+ let virtmgr =
+ if early { VirtualizationService::new_early() } else { VirtualizationService::new() };
+ let virtmgr = match virtmgr {
+ Ok(virtmgr) => virtmgr,
+ Err(e) => return -e.raw_os_error().unwrap_or(libc::EIO),
+ };
+ match virtmgr.connect() {
+ Ok(service) => {
+ // SAFETY: `service` is assumed to be a valid, non-null pointer to a mutable raw
+ // pointer. `service` is the only reference here and `config` takes
+ // ownership.
+ unsafe {
+ *service_ptr = Box::into_raw(Box::new(service));
+ }
+ 0
+ }
+ Err(_) => -libc::ECONNREFUSED,
+ }
+}
+
+/// Destroy a VirtualizationService object.
+///
+/// # Safety
+/// `service` must be a pointer returned by `AVirtualizationService_create` or
+/// `AVirtualizationService_create_early`. `service` must not be reused after deletion.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualizationService_destroy(
+ service: *mut Strong<dyn IVirtualizationService>,
+) {
+ if !service.is_null() {
+ // SAFETY: `service` is assumed to be a valid, non-null pointer returned by
+ // `AVirtualizationService_create`. It's the only reference to the object.
+ unsafe {
+ let _ = Box::from_raw(service);
+ }
+ }
+}
+
+/// Create a virtual machine with given `config`.
+///
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`. `service` must be a
+/// pointer returned by `AVirtualMachineRawConfig_create`. `vm_ptr` must be a valid, non-null
+/// pointer to a mutable raw pointer. `console_out_fd`, `console_in_fd`, and `log_fd` must be a
+/// valid file descriptor or -1. `AVirtualMachine_create` takes ownership of `console_out_fd`,
+/// `console_in_fd`, and `log_fd`, and taken file descriptors must not be reused.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachine_createRaw(
+ service: *const Strong<dyn IVirtualizationService>,
+ config: *mut VirtualMachineRawConfig,
+ console_out_fd: c_int,
+ console_in_fd: c_int,
+ log_fd: c_int,
+ vm_ptr: *mut *mut 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
+ // reference to the object.
+ let service = unsafe { &*service };
+
+ // SAFETY: `config` is assumed to be a valid, non-null pointer returned by
+ // `AVirtualMachineRawConfig_create`. It's the only reference to the object.
+ let config = unsafe { *Box::from_raw(config) };
+ let config = VirtualMachineConfig::RawConfig(config);
+
+ let console_out = get_file_from_fd(console_out_fd);
+ 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) {
+ 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));
+ }
+ 0
+ }
+ Err(_) => -libc::EIO,
+ }
+}
+
+/// Start a virtual machine.
+///
+/// # Safety
+/// `vm` must be a pointer returned by `AVirtualMachine_createRaw`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachine_start(vm: *const VmInstance) -> 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() {
+ Ok(_) => 0,
+ Err(_) => -libc::EIO,
+ }
+}
+
+/// Stop a virtual machine.
+///
+/// # Safety
+/// `vm` must be a pointer returned by `AVirtualMachine_create`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachine_stop(vm: *const VmInstance) -> 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.stop() {
+ Ok(_) => 0,
+ Err(_) => -libc::EIO,
+ }
+}
+
+/// Wait until a virtual machine stops.
+///
+/// # Safety
+/// `vm` must be a pointer returned by `AVirtualMachine_createRaw`.
+#[no_mangle]
+pub unsafe extern "C" fn AVirtualMachine_waitForStop(vm: *const VmInstance) -> StopReason {
+ // SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
+ // AVirtualMachine_create. It's the only reference to the object.
+ let vm = unsafe { &*vm };
+ match vm.wait_for_death() {
+ DeathReason::VirtualizationServiceDied => StopReason::VIRTUALIZATION_SERVICE_DIED,
+ DeathReason::InfrastructureError => StopReason::INFRASTRUCTURE_ERROR,
+ DeathReason::Killed => StopReason::KILLED,
+ DeathReason::Unknown => StopReason::UNKNOWN,
+ DeathReason::Shutdown => StopReason::SHUTDOWN,
+ DeathReason::StartFailed => StopReason::START_FAILED,
+ DeathReason::Reboot => StopReason::REBOOT,
+ DeathReason::Crash => StopReason::CRASH,
+ DeathReason::PvmFirmwarePublicKeyMismatch => StopReason::PVM_FIRMWARE_PUBLIC_KEY_MISMATCH,
+ DeathReason::PvmFirmwareInstanceImageChanged => {
+ StopReason::PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED
+ }
+ DeathReason::Hangup => StopReason::HANGUP,
+ _ => StopReason::UNRECOGNISED,
+ }
+}
+
+/// Destroy a virtual machine.
+///
+/// # Safety
+/// `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) {
+ 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);
+ }
+ }
+}
+
+fn get_file_from_fd(fd: i32) -> Option<File> {
+ if fd == -1 {
+ None
+ } else {
+ // SAFETY: transferring ownership of `fd` from the caller
+ Some(unsafe { File::from_raw_fd(fd) })
+ }
+}
diff --git a/libs/libvmclient/src/lib.rs b/libs/libvmclient/src/lib.rs
index 13630c0..c0baea5 100644
--- a/libs/libvmclient/src/lib.rs
+++ b/libs/libvmclient/src/lib.rs
@@ -243,6 +243,11 @@
self.vm.start()
}
+ /// Stops the VM.
+ pub fn stop(&self) -> BinderResult<()> {
+ self.vm.stop()
+ }
+
/// Returns the CID used for vsock connections to the VM.
pub fn cid(&self) -> i32 {
self.cid