libavf: implement AVirtualMachineRawConfig_addCustomMemoryBackingFile

Bug: 381972137
Bug: 368143426
Test: m
Change-Id: Ic62ad99b1923d45406366c00541789fb1fdd9b74
diff --git a/android/virtmgr/src/aidl.rs b/android/virtmgr/src/aidl.rs
index 9ba1c04..ef76822 100644
--- a/android/virtmgr/src/aidl.rs
+++ b/android/virtmgr/src/aidl.rs
@@ -802,6 +802,25 @@
 
         let detect_hangup = is_app_config && gdb_port.is_none();
 
+        let custom_memory_backing_files = config
+            .customMemoryBackingFiles
+            .iter()
+            .map(|memory_backing_file| {
+                Ok((
+                    clone_file(
+                        memory_backing_file
+                            .file
+                            .as_ref()
+                            .context("missing CustomMemoryBackingFile FD")
+                            .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)?,
+                    )?
+                    .into(),
+                    memory_backing_file.rangeStart as u64,
+                    memory_backing_file.size as u64,
+                ))
+            })
+            .collect::<binder::Result<_>>()?;
+
         // Actually start the VM.
         let crosvm_config = CrosvmConfig {
             cid,
@@ -846,6 +865,7 @@
             dump_dt_fd,
             enable_hypervisor_specific_auth_method: config.enableHypervisorSpecificAuthMethod,
             instance_id,
+            custom_memory_backing_files,
         };
         let instance = Arc::new(
             VmInstance::new(
diff --git a/android/virtmgr/src/crosvm.rs b/android/virtmgr/src/crosvm.rs
index bb7712e..59f115e 100644
--- a/android/virtmgr/src/crosvm.rs
+++ b/android/virtmgr/src/crosvm.rs
@@ -140,6 +140,8 @@
     pub dump_dt_fd: Option<File>,
     pub enable_hypervisor_specific_auth_method: bool,
     pub instance_id: [u8; 64],
+    // (memfd, guest address, size)
+    pub custom_memory_backing_files: Vec<(OwnedFd, u64, u64)>,
 }
 
 #[derive(Debug)]
@@ -1370,6 +1372,13 @@
         }
     }
 
+    for (fd, addr, size) in config.custom_memory_backing_files {
+        command.arg("--file-backed-mapping").arg(format!(
+            "{},addr={addr:#0x},size={size:#0x},rw,ram",
+            add_preserved_fd(&mut preserved_fds, fd)
+        ));
+    }
+
     debug!("Preserving FDs {:?}", preserved_fds);
     command.preserved_fds(preserved_fds);
 
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/CustomMemoryBackingFile.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/CustomMemoryBackingFile.aidl
new file mode 100644
index 0000000..721ad26
--- /dev/null
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/CustomMemoryBackingFile.aidl
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+package android.system.virtualizationservice;
+
+/** Custom memfd for a subset of guest memory. */
+parcelable CustomMemoryBackingFile {
+    /** The memfd. */
+    ParcelFileDescriptor file;
+
+    /**
+     * Start of range in guest physical address space.
+     *
+     * The value should be interpreted as an unsigned 64 bit integer.
+     */
+    long rangeStart;
+
+    /**
+     * Size of range in bytes.
+     *
+     * The value should be interpreted as an unsigned 64 bit integer.
+     */
+    long size;
+}
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
index a822423..c5fe982 100644
--- a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
@@ -18,6 +18,7 @@
 import android.system.virtualizationservice.AssignedDevices;
 import android.system.virtualizationservice.AudioConfig;
 import android.system.virtualizationservice.CpuOptions;
+import android.system.virtualizationservice.CustomMemoryBackingFile;
 import android.system.virtualizationservice.DiskImage;
 import android.system.virtualizationservice.DisplayConfig;
 import android.system.virtualizationservice.GpuConfig;
@@ -123,4 +124,7 @@
      * VMs.
      */
     boolean enableHypervisorSpecificAuthMethod;
+
+    /** Custom memfds for a subset of guest memory */
+    CustomMemoryBackingFile[] customMemoryBackingFiles;
 }
diff --git a/libs/libavf/include/android/virtualization.h b/libs/libavf/include/android/virtualization.h
index 8d96fac..4bfe47a 100644
--- a/libs/libavf/include/android/virtualization.h
+++ b/libs/libavf/include/android/virtualization.h
@@ -189,7 +189,7 @@
  * physical memory.
  *
  * \param config a virtual machine config object.
- * \param fd a memfd
+ * \param fd a memfd. Ownership is transferred, even if the function is not successful.
  * \param rangeStart range start of guest memory addresses
  * \param rangeEnd range end of guest memory addresses
  *
diff --git a/libs/libavf/src/lib.rs b/libs/libavf/src/lib.rs
index 3fa1b75..8a4970a 100644
--- a/libs/libavf/src/lib.rs
+++ b/libs/libavf/src/lib.rs
@@ -24,8 +24,9 @@
 use android_system_virtualizationservice::{
     aidl::android::system::virtualizationservice::{
         AssignedDevices::AssignedDevices, CpuOptions::CpuOptions,
-        CpuOptions::CpuTopology::CpuTopology, DiskImage::DiskImage,
-        IVirtualizationService::IVirtualizationService, VirtualMachineConfig::VirtualMachineConfig,
+        CpuOptions::CpuTopology::CpuTopology, CustomMemoryBackingFile::CustomMemoryBackingFile,
+        DiskImage::DiskImage, IVirtualizationService::IVirtualizationService,
+        VirtualMachineConfig::VirtualMachineConfig,
         VirtualMachineRawConfig::VirtualMachineRawConfig,
     },
     binder::{ParcelFileDescriptor, Strong},
@@ -254,18 +255,35 @@
     0
 }
 
-/// NOT IMPLEMENTED.
+/// Use the specified fd as the backing memfd for a range of the guest physical memory.
 ///
-/// # Returns
-/// It always returns `-ENOTSUP`.
+/// # Safety
+/// `config` must be a pointer returned by `AVirtualMachineRawConfig_create`.
 #[no_mangle]
-pub extern "C" fn AVirtualMachineRawConfig_addCustomMemoryBackingFile(
-    _config: *mut VirtualMachineRawConfig,
-    _fd: c_int,
-    _range_start: u64,
-    _range_end: u64,
+pub unsafe extern "C" fn AVirtualMachineRawConfig_addCustomMemoryBackingFile(
+    config: *mut VirtualMachineRawConfig,
+    fd: c_int,
+    range_start: u64,
+    range_end: u64,
 ) -> c_int {
-    -libc::ENOTSUP
+    // 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 };
+
+    let Some(file) = get_file_from_fd(fd) else {
+        return -libc::EINVAL;
+    };
+    let Some(size) = range_end.checked_sub(range_start) else {
+        return -libc::EINVAL;
+    };
+    config.customMemoryBackingFiles.push(CustomMemoryBackingFile {
+        file: Some(ParcelFileDescriptor::new(file)),
+        // AIDL doesn't support unsigned ints, so we've got to reinterpret the bytes into a signed
+        // int.
+        rangeStart: range_start as i64,
+        size: size as i64,
+    });
+    0
 }
 
 /// Add device tree overlay blob