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/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) })
+    }
+}